一、多态的概念
多态是一种事物具备多种不同的形态,例如水是有多种状态,固态,液态,气态,变形金刚里面的大黄蜂,它可以变汽车人,汽车,还有飞机。
官方解释为:多个不同类对象可以响应同一种方法,产生不同的结果。
多态不是一种特殊的语法,而是一种状态,特征(既多个不同对象可以响应同一种方法,产生不同的结果)多个对象有相同的使用方法。
好处是对使用者而言大大的降低了使用难度,我们之前写的USB接口下的鼠标,键盘就属于多态
实现多态:
要管理 鸡 鸭 鹅 如何能够最方便的 管理,就是我说同一句话,他们都能理解 既它们拥有相同的方法 class Ji: def bark(self): print('咯咯咯') def spawn(self): print("下鸡蛋") class Duck: def bark(self): print('嘎嘎嘎') def spawn(self): print('下鸭蛋') class E: def bark(self): print('eee') def spawn(self): print('xiaedan') j = Ji() y = Duck() e = E() def manage(obj): obj.spawn() manage(j) manage(y) manage(e)
在python中到处都有多态
a = 10 b = "10" c = [10] print(type(a)) print(type(b)) print(type(c)) 输出为:<class 'int'> <class 'str'> <class 'list'>
二、OOP相关内置函数
1.isinstance用法,判断一个对象是否是某个类的实例
def add_num(a,b): if isinstance(a,int) and isinstance(b,int): a为对象,int为数据类型 return a+b return None print(add_num(20, 10)) 输出:30
在isinstance中,参数1 为要判断的对象,参数2 为要判断的类型
2.issubclass用法,判断一个类是否是另一个类的子类
class Animal: def eat(self): print("动物得吃东西") class Pig(Animal): def eat(self): print("猪得吃猪食...") class Tree: def light(self): print("植物光合作用...") pig = Pig() t = Tree() def manage(obj): if issubclass(type(obj),Animal): obj.eat() else: print("不是一头动物") manage(pig) manage(t) 输出:猪得吃猪食... 不是一头动物
在issubclass中,参数1是子类,参数2是父类
三、__str__,__repr__,__format__
改变对象的字符串显示__str__,__repr__
自定制格式化字符串__format__
format_dict = { 'nat':'{obj.name}-{obj.addr}-{obj.type}', # 学校名-学校地址-学校类型 'tna':'{obj.type}:{obj.name}:{obj.addr}', # 学校类型:学校名:学校地址 'tan':'{obj.type}/{obj.addr}/{obj.name}', # 学校类型/学校地址/学校名 } class School: def __init__(self, name, addr, type): self.name = name self.addr = addr self.type = type def __repr__(self): return 'School(%s,%s)'%(self.name,self.addr) def __str__(self): return '(%s,%s)'%(self.name, self.addr) def __format__(self, format_spec): if not format_spec or format_spec not in format_dict: format_spec = 'nat' fmt = format_dict[format_spec] return fmt.format(obj = self) s1 = School('oldboy1','北京','私立') print('from repr:',repr(s1)) print('from str:',str(s1)) print(s1) ''' str函数或者print函数---->obj.__str__() repr或者交互式解释器---->obj.repr__() 如果__str__没有被定义,那么就会使用__repr__来代替输出 注意:这俩的方法的返回值必须是字符串,否则抛出异常 ''' print(format(s1,'nat')) print(format(s1,'tna')) print(format(s1,'tan')) print(format(s1,"1")) 输出为:from repr: School(oldboy1,北京) from str: (oldboy1,北京) (oldboy1,北京) oldboy1-北京-私立 私立:oldboy1:北京 私立/北京/oldboy1 oldboy1-北京-私立
四、__slots__
1.__slots__是什么?
是一个类变量,变量值可以是列表,元组,或者可迭代对象,也可以是一个字符串(意味着所有实例只是一个数据属性)
2.引子:
使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例是独立的)
3.为何使用__slots__:
字典会占用大量内存,如果有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slot__取代实例的__dict__,当定义__slots__后。__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组和列表很相似,在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__sloats__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
4.注意事项:
__slots__的很多特性都依赖于普通的基于字典的实现。
另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。
大多数情况下,你应该只在那些经常被使用到的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百
万个实例对象 。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达
到这样的目的,但是这个并不是它的初衷。 更多的是用来作为一个内存优化工具。
class Foo: __slots__ = 'x' # 所有实例只有一个数据属性 x f1 = Foo() f1.x = 1 # f1.y = 2 #报错 print(f1.__slots__) # f1不再有__dict__ print(Foo.__dict__) # 打印类的字典名称 class Bar: __slots__ = ['x','y'] # 可以是元组、列表、字符串、可迭代对象 n = Bar() n.x = 1 n.y = 2 # n.z = 3 # 报错 print(n.__slots__) # 不会再有__dict__ print(Bar.__dict__) # 打印类的名称字典 输出为:x {'__module__': '__main__', '__slots__': 'x', 'x': <member 'x' of 'Foo' objects>, '__doc__': None} ['x', 'y'] {'__module__': '__main__', '__slots__': ['x', 'y'], 'x': <member 'x' of 'Bar' objects>, 'y': <member 'y' of 'Bar' objects>, '__doc__': None}
class Foo: __slots__ = ['name', 'age'] f1 = Foo() f1.name = 'alex' f1.age = 18 print(f1.__slots__) # 查看类变量 f2 = Foo() f2.name = 'egon' f2.age = 19 print(f2.__slots__) # 查看类变量 print(Foo.__dict__) # 查看类的名称字典 # f1与f2都没有字典属性__dict__了,统一归__slots__管,节省内存 输出为:['name', 'age'] ['name', 'age'] {'__module__': '__main__', '__slots__': ['name', 'age'], 'age': <member 'age' of 'Foo' objects>, 'name': <member 'name' of 'Foo' objects>, '__doc__': None}
另外,当类中出现了slots时会将导致这个类的对象无法在添加新的属性。
五、__call__
对象后面加括号,触发__call__的运行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名(),而对于__call__方法执行的是由对象后面加括号触发的,即对象()或者类()()
class People: def __init__(self,name): self.name = name def __call__(self, *args, **kwargs):# 实例化对象加括号,触发它的运行 print('call') p = People('Dawn') # 实例化对象 print(callable(People)) # 查看People是否可被调回,返回True可调 print(callable(p)) # 查看p是否可被调回,返回True为可调 p() # 对象调用__call__ People("michael")() # 对象调用__call__ 输出为:True True call call
六、__iter__和__next__实现迭代器协议
class foo: def __init__(self,start): self.start = start def __iter__(self): return self def __next__(self): if self.start > 5: # 当self.start的值大于5的时候主动抛出异常 raise StopIteration n = self.start self.start += 1 # 每次取一次,自加一 return n f = foo(0) for i in f: print(i) 输出为:0 1 2 3 4 5
七、__del__
析构方法,当对象在内存中被释放时,会自动触发执行。
注:此方法一般无需定义,因为Python是一门高级语言,程序员在使用时无需关心内存中的分配和释放,因为此工作都是交给Python解释器来执行的,所以,析构函数的调用是由解释器在禁辛辣及回收时自动触发执行的。
class Open: def __init__(self, filepath, mode='r', encode='utf-8'): self.f = open(filepath, mode=mode, encoding=encode) def write(self): pass def __getattr__(self, item): return getattr(self.f,item) def __del__(self): # 当名称空间里有名字引用为空的时候,会触发它的执行。 print('......>del') self.f.close() # 关闭文件 f = Open('a.txt','w') del f # 删除后,f 就没有引用了,此时,__del__触发执行 print('====>继续其他逻辑') 输出为:......>del ====>继续其他逻辑