面向对象的深入

面向对象三大特征介绍

Python是面向对象的语言也支持面向对象编程的三大特性:继承、封装(隐藏)、多态。

封装(隐藏)

隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将"细节封装起来",只

对外暴露"相关调用方法"。

通过前面学习的"私有属性、私有方法"的方式实现"封装"。Python追求简洁的

语法,没有严格的语法级别的"访问控制符”,更多的是依靠程序员自觉实现。

继承

继承可以让子类具有父类的特性,提高了代码的重用性。

从设计上是一种增量进化,原有父类设计不变的情况下,可以增加新的功能,或者改进

已有的算法。

多态

多态是指同一个方法调用由于对象不同会产生不同的行为。生活中这样的例子比比皆

是:同样是休息方法,人不同休息方法不同。张三休息是睡觉,李四休息是玩游戏,程序员 休息是"敲几行代码"。

继承

继承是面向对象程序设计的重要特征,也是实现"代码复用”的重要手段。

如果一个新类继承自一个设计好的类,就直接具备了已有类的特征,就大大降低了工作 难度。已有的类,我们称为"父类或者基类",新的类,我们称为"子类或者派生类"。

语法格式

Python支持多重继承一个子类可以继承多个父类。继承的语法格式如下 class子类类名(父类1父类2...]):

类体

如果在类定义中没有指定父类,则默认父类是object类。也就是说object是所有类的父 类里面定义了一些所有类共有的默认实现比如:_new__()

定义子类时,必须在其构造函数中调用父类的构造函数。调用格式如下:

父类名.__init_(self,参数列表)

class Person:

def __init__(self,name,age):
self.name = name

self.__age = age

def say_age(self):

print(self.name,"的年龄是:",self.__age)

class Student(Person):

def __init__(self,name,age,score):

self.score = score

Person.__init__(self,name,age) #构造函数中包含调用父类构 造函数。根据需要,不是必须。子类并不会自动调用父类的_init__(),我 们必须显式的调用它。

s1 = Student("张三",15,85) s1.say_age() print(dir(s1))

运行结果:

张三的年龄是:15

['_Person__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__',                                                        '__hash__',     '__init__',

'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'say_age', 'score']

类成员的继承和重写

  1. 成员继承:子类继承了父类除构造方法之外的所有成员。
  2. 方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为"重写"

【操作】继承和重写的案例

class Person:

def __init__(self/name/age):

self.name = name

self.age = age

def say_age(self): print(self.name,"的年龄是:self.age)

def say_name(self):

print(”我是”,self.name)

class Student(Person):

def _init_(self,name,age,score):

self.score = score

Person._init_(self,name,age) #构造函数中包含调用父类构造函数

def say_score(self): print(self.name,"的分数是:self.score)

def say_name(self):         #重写父类的方法

print("报告老师我是二self.name)

s1 = Student("张三",15,85)

s1.say_score()

s1.say_name()

s1.say age()

执行结果:

张三 的分数是: 85 报告老师,我是张三

张三 的年龄是: 15

查看类的继承层次结构

通过类的方法mroO或者类的属性__mro__可以输出这个类的继承层次结构。

【操作】查看类的继承层次结构

class A:pass

class B(A):pass

class C(B):pass

print(C.mro())

执行结果:

[<class ,__main__.CI>/ <class '__main__.B‘>, <class '__main__A>, <class 'object'〉]

object根类

object类是所有类的父类,因此所有的类都有。bject类的属性和方法。我们显然有必要深 入研究一下。bject类的结构。对于我们继续深入学习Python很有好处。

dir()查看对象属性

为了深入学习对象,我们先学习内置函数dir。,他可以让我们方便的看到指定对象所有的 属性。

【测试】查看对象所有属性以及和object进行比对

class Person:

def __init__(self,name,age):

self.name = name self.age = age

def say_age(self):

print(self.name,"的年龄是:",self.age)

obj = object()

print(dir(obj))

s2 = Person("高淇",18)

print(dir(s2))

执行结果:

从上面我们可以发现这样几个要点:

  1. Person对象增加了六个属性:

__dict_ __module_ __weakref_             age name say_age

  1. object的所有属性Person类作为object的子类显然包含了所有的属性。
  2. 我们打印agenamesay_age,发现say_age虽然是方法,实际上也是属性。只不过 这个属性的类型是"method”而已。

age <class 'int'>

name <class 'str'>

say_age <class 'method'>

【注】关于。bject这些属性的详细学习,会在后面学习中逐个涉及。在此,无法一一展开。

重写__st r__()方法

object有一个__str__()方法用于返回一个对于"对象的描述"对应于内置函数str。 经常用于print。方法帮助我们查看对象的信息。__str_0可以重写。

class Person:

def __init__(self,name,age):

self.name = name

self.__age = age

def __str__(self):

”将对象转化成一个字符串,—般用于print方法’

return "名字是:{0},年龄是{1}".format(self.name,self.__age)

p = Person("高淇",18)

print(p)

运行结果:

名字是:高淇,年龄是18

多重继承

Python支持多重继承,一个子类可以有多个"直接父类"。这样,就具备了 "多个父 类”的特点。但是由于,这样会被"类的整体层次"搞的异常复杂,尽量避免使用。

#多重继承

class A:

def aa(self): print("aa")

class B:

def bb(self): print("bb")

class C(B,A):

def cc(self): print("cc")

c = C()

c.cc()

c.bb()

c.aa()

运算结果:

cc bb aa

MRO()

Python支持多继承,如果父类中有相同名字的方法在子类没有指定父类名时解释器将 "从左向右”按顺序搜索。

MRO( Method Resolution Order):方法解析顺序。我们可以通过mro()方法获得

"类的层次结构”,方法解析顺序也是按照这个"类的层次结构”寻找的。

#多重继承

class A:

def aa(self):

print("aa")

def say(self):

print("sayAAA!")

class B:

def bb(self): print("bb")

def say(self):

print("say BBB!")

class C(B,A):

def cc(self):

print("cc")

c = C0

print(C.mroO)                 #打印类的层次结构

c.say()                             #解释器寻找方法是"从左到右的方式寻找,此时会执行B

类中的say()

super()获得父类定义

在子类中如果想要获得父类的方法时我们可以通过super()来做。

super()代表父类的定义,不是父类对象。

#super()

class A:

def say(self):

print("A: ",self) print("sayAAA")

class B(A):

def say(self):

#A.say(self)  调用父类的say方法

super().say() #通过super。调用父类的方法

print("say BBB")

b= B()

b.say()

运行结果:

A: <__main__.B object at 0x007A5690>

say AAA

say BBB

多态

多态(polymorphism )是指同一个方法调用由于对象不同可能会产生不同的行为。在现实 生活中,我们有很多例子。比如:同样是调用人的休息方法,张三的休息是睡觉,李四的休 息是玩游戏,高淇老师是敲代码。同样是吃饭的方法,中国人用筷子吃饭,英国人用刀叉吃 饭,印度人用手吃饭。

关于多态要注意以下2点:

  1. 多态是方法的多态,属性没有多态。
  2. 多态的存在有2个必要条件:继承、方法重写。

#多态

(gBnnoqs 布

(()6oa)Jnoqs-a5E-uaJ 。匝 昏鱼牌S昌坦Enoqs愕卷与业# ()冬OLISG

X-O5IU-U<a5)a)uu3su-S-*:
奇)Jnoqs-eE-ueM-ep
(细富 ' whFMd

osnnoqsM-ap

x-05lu-u<& ssep (w由 ' sr^l

osnnoqsM-ap

x-a5lu-u<6oa ssep (--眼—lsm 昇伊-nuE

osnnoqsM-ap

Mlu-uv ssep

 

*s SB

 

特殊方法和运算符重载

Python的运算符实际上是通过调用对象的特殊方法实现的。比如: a = 20 b = 30 c = a+b

d = a.__add__(b) print("c=",c) print("d = ",d)

运算结果:

c= 50

d= 50

常见的特殊方法统计如下:

方法

说明

例子

__init__

构造方法

对象创建:p = Person()

__del__

析构方法

对象回收

__repr__,__str__

打印,转换

print(a)

__call__

函数调用

a()

_getattr_

点号运算

a.xxx

_setattr_

属性赋值

a.xxx = value

_getitem_

索引运算

a[key]

_setitem_

索引赋值

a[key]=value

_len_

长度

len(a)

每个运算符实际上都对应了相应的方法,统计如下:

运算符

特殊方法

说明

运算符+

__add__

加法

运算符-

__sub__

减法

<,<=,==

lt,le , eq

比较运算符

>,>=,! =

__gt__,__ge__,__ne__

|,气&

_o r__,_xor__,_a nd_

或、异或、与

< <,>>

__lshift__,__rs h ift__

左移、右移

*,/,%,//

_mul_,_truediv_,_mod_,_

_floordiv_

乘、浮点除、模运算

(取余)、整数除

**

_pow_

指数运算

我们可以重写上面的特殊方法,即实现了 "运算符的重载"。

#测试运算符的重载

class Person:

def _init_(self, name):
self. name = name

def _add_(self, other): if isinstance (other,Person):

return " {0} 一一{1} ". format (self. name, other. name) else:

return ”不是同类对象,不能相加”

def _mul_(self, other): if isinstance (other,int):

return self. name*other

else:

return ”不是同类对象,不能相乘”

pl 二 Person("高淇”)

p2 二 Person("高希希”)

x = pl + p2 print (x)

print (p1*3)

运算结果:

高淇-高希希

高淇高淇高淇

特殊属性

Python对象中包含了很多双下划线开始和结束的属性这些是特殊属性有特殊用法。这 里我们列出常见的特殊属性:

特殊方法

含义

obj.dict

对象的属性字典

obj.class

对象所属的类

class._bases_

类的基类元组(多继承)

class._base_

类的基类

class._mro_

类层次结构

class._subclasses_()

子类列表

#测试特殊属性

class A:

pass

class B:

pass

class C(B, A):

def _init (self, nn): self. nn 二 nn

def cc (self): print ("cc")

c = C(3)

print (dir (c))

print (c. _dict ) print (c. _class_) print (C. _bases ) print (C. mro ())

print (A. _subclasses_())

运行结果:

「__class__', '__delattr__',                                                                                  '__ge_J,

'_getattribute_‘'_hash_‘'_init_‘'_init_subclass_‘'_le_‘'_lt_', '_module_‘’_ne_‘’_new_‘’_reduce_‘’_reduce_ex_‘’_repr_‘’_setattr_‘, '_sizeof_‘'_str_‘'_subclasshook_‘'_weakref_‘’cc‘’nn']

{'nn': 3}

<class '_main_.C’>

(<class '_main_.B'>, <class '_main_.A'>)

[<class '_main_.C'>, <class '_main_.B'>, <class '_main_.A'>, <class 'object'〉] [<class '_main_.C'>]

对象的浅拷贝和深拷贝

变量的赋值操作

只是形成两个变量,实际还是指向同一个对象。

浅拷贝

Python拷贝一般都是浅拷贝。拷贝时对象包含的子对象内容不拷贝。因此,源对象 和拷贝对象会弓I用同一个子对象。

深拷贝

使用copy模块的deepcopy函数,递归拷贝对象中包含的子对象。源对象和拷贝对象 所有的子对象也不同。

#测试对象的弓 I用赋值、浅拷贝、深拷贝 import copy

class MobilePhone:

def __init__(self,cpu,screen): self.cpu = cpu self.screen = screen

class CPU:

def calculate(self):

print("计算,算个 12345") print("CPU 对象:",self)

class Screen:

def show(self):

print("显示一个好看的画面,亮瞎你的钛合金大眼")

print("屏幕对象:",self)

c = CPU()

s = Screen()

m = MobilePhone(c,s)

m.cpu.calculate()

n = m               #两个变量,但是指向了同一个对象

print(m,n)

m2 = copy.copy(m)   #m2是新拷贝的另一个手机对象

print(m,m2)

m.cpu.calculate()

m2.cpu.calculate()  #m2和 m拥有了一样的 cpu对象和screen对象

m3 = copy.deepcopy(m)

m3.cpu.calculate()     #m3 和 m 拥有不一样的 cp u 对象和 scre en

对象

运算结果:

计算算个12345

CPU 对象:<__main__.CPU object at 0x00685690>

<_main_.MobilePhone object at 0x00685B50> <_main_.MobilePhone object at

0x00685B50>

<__main__.MobilePhone object at 0x00685B50> <__main__.MobilePhone object at 0x0069B490>

计算算个12345

CPU 对象:<__main__.CPU object at 0x00685690>

计算算个12345

CPU 对象:<__main__.CPU object at 0x00685690>

计算算个12345

CPU 对象:<__main__.CPU object at 0x006A5DB0>

组合

"is-a”关系,我们可以使用"继承"。从而实现子类拥有的父类的方法和属性。"is-a” 关系指的是类似这样的关系:狗是动物,dog is animal。狗类就应该继承动物类。

“has-a”关系,我们可以使用"组合”,也能实现一个类拥有另一个类的方法和属性。has-a”关系指的是这样的关系:手机拥有CPUMobilePhone has a CPU

#组合测试

class MobilePhone:

def __init__(self,cpu,screen): self.cpu = cpu self.screen = screen

class CPU:

def calculate(self): print("计算,算个 12345") class Screen:

def show(self):

print("显示一个好看的画面,亮瞎你的钛合金大眼")

c = CPU()

s = Screen()

m = MobilePhone(c,s)

m.cpu.calculate()      #通过组合,我们也能调用cpu对象的方法。相

当于手机对象间接拥有了 "cpu的方法"

m.screen.show()

运算结果:

计算算个12345

显示一个好看的画面,亮瞎你的钛合金大眼

设计模式一工厂模式实现

设计模式是面向对象语言特有的内容,是我们在面临某一类问题时候固定的做法,设计 模式有很多种比较流行的是GOF( Goup Of Four)23种设计模式。当然我们没有 必要全部学习,学习几个常用的即可。

对于初学者,我们学习两个最常用的模式:工厂模式和单例模式。

工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进 行统一的管理和控制。

#工厂模式

class CarFactory:

def createCar(self,brand):

if brand =="奔驰"

return Benz()

elif brand =="宝马:

return BMW()

elif brand =='比亚迪'

return BYD()

else:

return "未知品牌无法创建"

class Benz:

pass

class BMW:

pass

class BYD:

pass

factory = CarFactory()

cl = factory.createCar("奔驰") c2 = factory.createCar("宝马") print(cl)

print(c2)

运行结果:

<__main__.Benz object at 0x021C5770> <__main__.BMW object at 0x021C5790>

设计模式一单例模式实现

单例模式(Singleton Pattern)的核心作用是确保一个类只有一个实例并且提供一 个访问该实例的全局访问点。

单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较 多的资源,如读取配置文件、产生其他依赖对象时,可以产生一个"单例对象",然后永久 驻留内存中,从而极大的降低开销。

单例模式有多种实现的方式我们这里推荐重写__new__()的方法。

#单例模式

class MySingleton:

_obj = None

_init_flag = True

def _new (cis, *args, **kwargs):

if cis._obj == None: cis._obj = object._new (cis)

return cis._obj

def _init (seif, name):

if MySingieton._init_fiag: print ("init....") seif. name = name

MySingieton._init_fiag = Faise

a 二 MySingieton("aa") print (a)

b 二 MySingieton("bb") print (b)

运算结果:

init....

<__main__.MySingleton object at 0x01E15610>

<_main_.MySingleton object at 0x01E15610>

设计模式称之为"模式",就是一些固定的套路。我们很容易用到其他场景上,比如前面讲 的工厂模式,我们需要将工厂类定义成“单例",只需要简单的套用即可实现:

#测试工厂模式和单例模式的整合使用

class CarFactory:

_obj = None                      #类属性

_init_flag = True

def create_car(self,brand):

if brand =="奔驰

return Benz()

elif brand =="宝马

return BMW()

elif brand == ”比亚迪”:

return BYD()

else:

return "未知品牌,无法创建"

def __new__(cls, *args, **kwargs):

if cls._obj ==None: cls._obj = object._new_(cls)

return cls._obj

def __init__(self):

if CarFactory._init_flag: print("init CarFactory....") CarFactory._init_flag = False class Benz:

pass

class BMW:

pass

class BYD:

pass

factory = CarFactoryO

c1 = factory.create_car("奔驰")

c2 = factory.create_car("比亚迪")

print(c1)

print(c2)

factory2 = CarFactory() print(factory) print(factory2)

运算结果:

init CarFactory...

<_main_.Benz object at 0x01E36E90>

<__main__.BYD object at 0x01E36C30>

<_main_.CarFactory object at 0x01E36730> <_main_.CarFactory object at 0x01E36730>

实操作业

  1. 如下代码测试对象的浅拷贝、深拷贝,请绘制出内存示意图。

#测试对象的引用赋值、浅拷贝、深拷贝

import copy

class MobilePhone:

def __init__(self,cpu,screen): self.cpu = cpu self.screen = screen

class CPU:

def calculate(self):

print("计算,算个 12345") print("CPU 对象:",self) class Screen:

def show(self):

print("显示一个好看的画面,亮瞎你的钛合金大眼") print("屏幕对象:",self)

c = CPU()

s = Screen()

m = MobilePhone(c,s)

m.cpu.calculate()

n = m print(m,n)

#两个变量,但是指向了同一个对象

m2 = copy.copy(m) print(m,m2)

#m2是新拷贝的另一个手机对象

m.cpu.calculate()

m2.cpu.calculate()

#m2和m拥有了一样的cpu对象和screen对象

m3 = copy.deepcopy(m)

m3.cpu.calculate()     #m3 和 m 拥有不一样的 cp u 对象和 scre en

对象

  1. 定义发动机类Motor、底盘类Chassis.座椅类Seat ,车辆外壳类Shell ,并使用组合 关系定义汽车类。其他要求如下:

定义汽车的run()方法里面需要调用Motor类的work。方法也需要调用座椅 类Seatwork。方法也需要调用底盘类Chassiswork。方法。

  1. 使用工厂模式、单例模式实现如下需求:

(1)电脑工厂类ComputerFactory用于生产电脑Computer。工厂类使用单例模式, 也就是说只能有一个工厂对象。

工厂类中可以生产各种品牌的电脑:联想、华硕、神舟

(3)各种品牌的电脑使用继承实现

⑷ 父类是Computer定义了 calculate方法

(5)各品牌电脑类需要重写父类的calculate方法

  1. 定义一个Employee雇员类要求如下:

(1)属性有:idnamesalary

运算符重载+:实现两个对象相加时,默认返回他们的薪水和

(3)构造方法要求:输入name^ salary不输入ido id采用自增的方式1000开 始自增第一个新增对象是1001第二个新增对象是1002

⑷ 根据salary属性使用@property设置属性的getset方法。set方法要求输 入1000-50000范围的数字。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值