面向对象的三大特征
三大特征简介
- 封装(隐藏)
隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将“细节封装起来”,只对外暴露“相关调用方法”。通过前面学习的“私有属性、私有方法”的方式,实现“封装”。Python追求简洁的语法,没有严格的语法级别的“访问控制符”,更多的是依靠程序员自觉实现。 - 继承
继承可以让子类具有父类的特性,提高了代码的重用性。从设计上是一种增量进化,原有父类设计不变的情况下,可以增加新的功能,或者改进已有的算法。 - 多态
多态是指同一个方法调用由于对象不同会产生不同的行为。生活中这样的例子比比皆是:同样是休息方法,人不同休息方法不同。张三休息是睡觉,李四休息是玩游戏,程序员休息是“敲几行代码”。
1.继承
- 简介:
子类扩展父类已有的类,我们称为“父类或者基类”,新的类,我们称为“子类或者派生类”。 - 语法:
Python支持多重继承,一个子类可以继承多个父类。继承的语法格式如下:class 子类类名(父类1[,父类2,...]): 类体
- 注意:
如果在类定义中没有指定父类,则默认父类是object
类 。也就是说,object
是所有类的父类,里面定义了一些所有类共有的默认实现,比如:__new__()
- 关于构造函数
- 子类不重写
__init__
,实例化子类时,会自动调用父类定义的__init__
。 - 子类重写了
__init__
时,实例化子类,就不会调用父类已经定义的__init__
。 - 如果重写了
__init__
时,要使用父类的构造方法,可以使用super
关键字,也可以使用如下格式调用:父类名.__init__(self, 参数列表)
- 子类不重写
- 继承和方法重写
- 成员继承:子类继承了父类除构造方法之外的所有成员。⚠️(私有属性、私有方法也被继承)
- 方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为“重写”
- 代码示例
class Person: def __init__(self, name, age): print('创建Person类') self.name = name self.age = age def say_age(self): print('{0}的年龄是{1}'.format(self.name, self.age)) def say_name(self): print('我是', self.name) class Student(Person): def __init__(self, name, age, score): print('创建Student类') # self.name = name # 由于这两个实例属性和父类的相同,所以可以调用父类的构造方法 # self.age = age # Person.__init__(self,name,age) # 1.这是调用父类构造方法的第一种方式 super(Student, self).__init__(name, age) # 2.这是调用父类构造方法的第二种方式 self.score = score def day_score(self): print('{0}的年龄是{1},分数是{2}'.format(self.name, self.age, self.score)) def say_name(self): # 重写父类的方法 print('报告老师,我是', self.name) a = Student('张三', 18, 99) a.say_age() a.day_score()
2.查看类的继承层次结构
- 简介:
通过类的方法mro()
或者类的属性__mro__
可以输出这个类的继承层次结构。 - 代码示例:
class A:pass class B(A):pass class C(B):pass print(C.mro()) # 运行结果 [<class '__main__.C'>, <class '__main__.B'>,<class '__main__.A'>, <class 'object'>]
- 类结构图:
3.object
根类
- 简介:
object 类是所有类的父类,因此所有的类都有 object 类的属性和方法。 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("zhangsan",18) print(dir(s2)) # 运行结果 ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__'] ['__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__', 'age', 'name', 'say_age']
- 从上面的代码我们可以发现这样几个要点:
- Person 对象增加了六个属性:
__dict__
__module__
__weakref__
age
name
say_age
object
的所有属性,Person
类作为object
的子类,显然包含了所有的属性- 我们打印
age
、name
、say_age
,发现say_age
虽然是方法,实际上也是属性。只不过,这个属性的类型是method
而已。age <class 'int'> name <class 'str'> say_age <class 'method'>
- Person 对象增加了六个属性:
4.重写 str() 方法
- 简介:
object
有一个__str__()
方法,用于返回一个对于“对象的描述”。内置函数str(对象)
,调用的就是__str__()
__str__()
经常用于print()
方法,帮助我们查看对象的信息。__str__()
可以重写
- 代码示例:
class Person: def __init__(self, name, age): self.name = name self.age = age def __str__(self): '''将一个对象转化成一个字符串,一般用于print方法''' print('重写__str__方法') return '名字是:{0},年龄是{1}'.format(self.name, self.age) p = Person('张三', 19) # 实例化对象 print(p) # 调用__str__方法一般print输出的就是一个字符串 “重写__str__方法”;输出了__str__方法中的return “名字是:张三,年龄是19” s = str(p) # 又转换str又调用了一次__str__方法又打印了一次 “重写__str__方法“
5.多重继承
- 简介:
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()
- 类结构图:
6.MRO方法解析顺序
- 简介:
Python支持多继承,如果父类中有相同名字的方法,在子类没有指
定父类名时,解释器将“从左向右”按顺序搜索。MRO(Method Resolution Order):方法解析顺序。 我们可以通过 mro() 方法获得“类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找的。 - 代码示例:
''' MRO(方法解析顺序):通过mro()获得类的层次结构,方法解析顺序也是按这个类的层次结构寻找的 ''' class A: def aa(self): print('aa') def say(self): print('say AAA') class B: def bb(self): print('bb') def say(self): print('say BBB') class C(B, A): def cc(self): print('cc') c = C() print(C.mro()) # [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>] c.say() # say BBB
7.super()
获得父类定义
- 简介:
在子类中,如果想要获得父类的方法时,我们可以通过super()
来做。super()
代表父类的定义,不是父类对象。- ❤️想调用父类的构造方法:
super(子类名称,self).__init__(参数列表)
父类名.__init__(self,参数列表)
- ❤️想调用父类的方法:
super(子类名称,self).父类方法名(参数列表)
父类名.父类方法名(self,参数列表)
- ❤️想调用父类的构造方法:
- 实例代码:
''' 我们知道想要调用父类的构造方法时可以这样做: super(子类名,self).__init__(参数列表) 或 父类名.__init__(self,参数列表) 那么想要调用父类的方法该怎么做呢?如下: super().父类方法名 或 父类名.父类方法名(self) ''' class A: def __init__(self): print('A的构造方法') def say(self): print('A:', self) # A: <__main__.B object at 0x0000023B9F987C40> 这里的self就是b = B()的实例化对象 print('say AAA') class B(A): def __init__(self): super(B, self).__init__() # 调用父类的构造方法1 # A.__init__(self) # 调用父类的构造方法2 print('B的构造方法') def say(self): super(B, self).say() # 调用父类的实例方法1 # A.say(self) # 调用父类的实例方法2,相当于把A类的say方法的方法体(注意是方法体)继承在B类的say方法中了 print('say BBB') # 上面的say方法还可以改写成如下形式,两种运行结果是一样的 # def say(self): # print('A:', self) # print('say AAA') # print('say BBB') b = B() # 类对象在没有构造方法__init__之前就已经创建好了,只要对象一实例化,就调用了包括他自己B类和父类A类的构造方法(初始化方法) b.say() print(b) # <__main__.B object at 0x0000023B9F987C40> # 运行结果 A的构造方法 B的构造方法 A: <__main__.B object at 0x00000190E746AD40> say AAA <__main__.B object at 0x00000190E746AD40> say BBB <__main__.B object at 0x00000190E746AD40>
8.多态
- 简介:
多态(polymorphism)是指同一个方法调用由于对象不同可能会
产生不同的行为。 - 注意:
- 多态是方法的多态,属性没有多态。
- 多态的存在有2个必要条件:继承、方法重写
- 简单继承代码示例:
''' 多态指:对象不同对应的对象行为不同,如中国人和外国人吃饭 注意: 1.多态是方法的多态,属性没有多态 2.多态的存在有2个必要条件:继承,方法重写 ''' class Animal: def shout(self): print('动物叫了一声!') class Dog(Animal): # 多态条件之一:继承 def shout(self): # 多态条件之二:方法重写 print('小狗,汪汪汪') class Cat(Animal): # 多态条件之一:继承 def shout(self): # # 多态条件之二:方法重写 print('小猫,喵喵喵') def animalShout(a): a.shout() animalShout(Dog()) # 运行结果 小狗,汪汪汪
9.特殊方法和运算符重载
- 简介:
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__
比较运算符 |
^
&
__or__
__xor__
__and__
或、异或、与 <<
>>
__lshift__
__rshift__
左移、右移 *
/
%
//
__mul__
__truediv__
__mod__
__floordiv__
乘、浮点除、模运算(取余)、整数除 **
__pow__
指数运算 - 测试代码:
''' 像我们常见的+,-,*,/都可以重新修改变为我们想要的运算符 这就是运算符的重载 ''' class Person: def __init__(self,name): self.name = name def __add__(self,other): if isinstance(other,Person): return '{0} Love {1}'.format(self.name,other.name) ''' 这里对这个other.name解释一下,p1+p2其实解释器解释时会解释为: p1.__add__(p2),而这个p2刚好是Person类的实例化对象,所以便有了.name的实例化属性 ''' else: return '不是同类对象不能相加' def __mul__(self,other): if isinstance(other,int): return self.name*other '''这个解释器会把p1*3解释为p1.__mul__(3)''' else: return '不是同类不能相乘' p1 = Person('I') p2 = Person('Python') x = p1+p2 print(x) # I Love Python print(p1*3) # III
- 关于5测试代码中
isinstance
的解释- 简介:
isinstance()
函数来判断一个对象是否是一个已知的类型,类似type()
。 - 注意:
isinstance()
与type()
区别:type()
不会认为子类是一种父类类型,不考虑继承关系。isinstance()
会认为子类是一种父类类型,考虑继承关系。- 如果要判断两个类型是否相同推荐使用
isinstance()
。 int
,float
,bool
,complex
,str(字符串)
,list
,dict(字典)
,set
,tuple
要注意的是,classinfo
的字符串是str
而不是string
,字典也是简写dict
。
实例:arg=123 isinstance(arg, int) #输出True isinstance(arg, str) #输出False isinstance(arg, string) #报错
- 语法:
isinstance(object, classinfo) # 参数 object -- 实例对象。 classinfo -- 可以是直接或间接类名、基本类型或者由它们组成的元组。
- 测试代码:
- 基本语法测试
>>>a = 2 >>> isinstance (a,int) True >>> isinstance (a,str) False >>> isinstance (a,(str,int,list)) # 是元组中的一个返回 True True
- type() 与 isinstance()区别:
class A: pass class B(A): pass isinstance(A(), A) # returns True type(A()) == A # returns True isinstance(B(), A) # returns True type(B()) == A # returns False
- 基本语法测试
- 简介:
10.特殊属性
- 简介:
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(c.__dict__) print(c.__class__) print(C.__bases__) print(C.mro()) print(A.__subclasses__()) # 运行结果 {'nn': 3} # c对象的属性字典 <class '__main__.C'> # c对象所属的类 (<class '__main__.B'>, <class '__main__.A'>) # C类的父类(多继承时,多个父类放到一个元组中) [<class '__main__.C'>, <class '__main__.B'>, <class'__main__.A'>, <class 'object'>] # C类的层次结构 [<class '__main__.C'>] # A类的子类列表
11.对象的浅拷贝和深拷贝
- 浅拷贝
Python拷贝一般都是浅拷贝。浅拷贝:拷贝时,拷贝源对象,但对象包含的子对象内容不拷贝。 - 深拷贝
使用 copy 模块的 deepcopy 函数,递归拷贝对象中包含的子对象。深拷贝:拷贝时,拷贝源对象,也递归拷贝对象中包含的子对象 - 测试代码:
import copy class Phone: def __init__(self,cpu): self.cup = cpu class CPU: pass c = CPU() p = Phone(c) # 通过这里能学到类和函数一样也可以嵌套,也就是组合 print('------浅拷贝------') p2 = copy.copy(p) print('p:',id(p)) # p: 1292810020576 print('p2:',id(p2)) # p2: 1292810026240 print('p的cpu:',id(p.cup)) # p的cpu: 1292810020576 print('p2的cpu:',id(p2.cup)) # p2的cpu: 1292810018080 print('------深拷贝------') p3 = copy.deepcopy(p) print('p:',id(p)) # p: 1292810020576 print('p3:',id(p3)) # p3: 1292810466832 print('p的cpu:',id(p.cup)) # p的cpu: 1292810018080 print('p3的cpu:',id(p3.cup)) # p3的cpu: 1292810467120
- 深度理解浅拷贝和深拷贝
12.组合
- 简介:
除了继承,“组合”也能实现代码的复用!“组合”核心是“将父类对象
作为子类的属性”。 - 继承和组合的使用选择:
is-a
关系,我们可以使用“继承”。从而实现子类拥有的父类的方法和属性。is-a
关系指的是类似这样的关系:狗是动物,dog is animal。狗类就应该继承动物类。has-a
关系,我们可以使用“组合”,也能实现一个类拥有另一个类的方法和属性。has-a
关系指的是这样的关系:手机拥有CPU。 MobilePhone has a CPU。
- 测试代码:
class MobilePhone: def __init__(self,cpu,screen): self.cpu = cpu self.screen = screen class CPU: def calculate(self): print("计算") class Screen: def show(self): print("显示一个好看的画面") c = CPU() s = Screen() m = MobilePhone(c,s) m.cpu.calculate() #通过组合,我们也能调用cpu对象的方法。相当于手机对象间接拥有了“cpu的方法” m.screen.show() # 运行结果 计算 显示一个好看的画面
13.设计模式_工厂模式的实现
- 工厂模式
- 简介:
工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进行统一的管理和控制。 - 代码示例:
''' 1.工厂模式:顾名思义,工厂可以批量化生产 ''' class Benz: def say_price(self, p): print(p) class BMW: def say_price(self, p): print(p) class BYD: def say_price(self, p): print(p) class CarFactory: def createCar(self, brand): if brand == '奔驰': return Benz() elif brand == '宝马': return BMW() elif brand == '比亚迪': return BYD() else: return '未知品牌' factory = CarFactory() c1 = factory.createCar('奔驰') c1.say_price(1000000) # 运行结果 1000000
- 简介:
- 单例模式
- 简介
单例模式(Singleton Pattern)的核心作用是确保一个类只有一个实例,并且提供一个访问该实例的全局访问点。单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较多的资源,如读取配置文件、产生其他依赖对象时,可以产生一个“单例对象”,然后永久驻留内存中,从而极大的降低开销。 - 代码示例:
⚠️单例模式有多种实现的方式,我们这里推荐重写__new__()
的方法。# 单例模式 class MySingleton: __obj_flag = None __init_flag = True def __new__(cls, *arg, **kwargs): if __obj_flag is None: cls.__obj_flag = object.__new__(cls) return cls.__obj_flag def __init__(self, name): if MySingleton.__init_flag: print('初始化第一个对象......') self.name = name MySingleton.__init_flag = False a = MySingleton('a') print(a) # <__main__.MySingleton object at 0x0000021F087740A0> b = MySingleton('b') print(b) # None 单例模式只能创建一个对象 c = MySingleton('c') print(c) # None 单例模式只能创建一个对象
- 简介
- 工厂和单例模式结合
- 简介:
设计模式称之为“模式”,就是一些固定的套路。我们很容易用到其他场景上,比如前面讲的工厂模式,我们需要将工厂类定义成“单例”,只需要简单的套用即可实现 - 测试代码:
class Benz: def say_price(self, p): print(p) class BMW: def say_price(self, p): print(p) class BYD: def say_price(self, p): print(p) class CarFactory: __obj_flag = None __init_flag = True def __new__(cls, *arg, **kwargs): if cls.__obj_flag is None: cls.__obj_flag = object.__new__(cls) return cls.__obj_flag def __init__(self): if CarFactory.__init_flag: print('init CarFactory ...) CarFactory.__init_flag = False def createCar(self, brand): if brand == '奔驰': return Benz() # 这里为什么要加括号(),因为要实例化对象例如 c = Benz(),没初始化方法没实例属性的实例对象。以下同理。 elif brand == '宝马': return BMW() elif brand == '比亚迪': return BYD() else: return '未知品牌' factory = CarFactory() car1 = factory.createCar("奔驰") car2 = factory.createCar("比亚迪") print(car1) # 两个不同的汽车实例对象 print(car2) # 两个不同的汽车实例对象 # 在继续创建汽车工厂类CarFactory,测试单例模式 factory2 = CarFactory() print(factory) print(factory2) # 运行结果 init CarFactory ... <__main__.Benz object at 0x000001B17E9D74C0> <__main__.BYD object at 0x000001B17E9D7490> <__main__.CarFactory object at 0x000001B17E9D7520> <__main__.CarFactory object at 0x000001B17E9D7520>
- 简介:
END