Python:面向对象编程2
- types.MethodType
- __slot__
- @property, @xxx.setter
- Python的多重继承和MinIn
如何在class创建后,给实例绑定属性和方法? (动态绑定/定义)
class Student(object): pass s = Student() s.name = 'Michael' # 动态给实例绑定一个属性 def set_age(self, age): # 定义一个函数作为s的实例方法 self.age = age from types import MethodType s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
types.MethodType(obj, s) 给一个实例绑定一个方法。这个方法的第一参数即默认是“使用这个方法的对象”,一般用self表示。
上面的代码给实例s绑定了一个方法set_age, 但其他实例不共享set_age。
如果要所有实例都能使用的话,可以这样:
Student.set_score = set_score #Python3
如此,就给Student绑定了一个方法。它的所有实例都可以调用。
解释一下:
动态定义方法,就是在代码开始运行后,通过定义一个新方法并绑定到已经定义的类或实例对象上,让类的所有实例或实例自身可以使用它。
绑定即bound。特点是绑定后可以在函数内使用self关键字。self就代表实例对象了。如果是这样:
s.set_age = set_age #还是上面的例子
那么,set_age指针只是指向了set_age方法而已。
types.MethodType的主要用途
The type of methods of user-defined class instances.即判断一个方法是不是自定义的类的实例方法。
class Student(object): pass def set_name(self, name): self.name = name #将方法绑定在类上(没有None参数) if type(Student().set_name) == MethodType: print('It is true!')
#https://github.com/python/cpython/blob/3.8/Lib/types.py class _C: def _m(self): pass MethodType = type(_C()._m)
#<class 'Method'>
但是,我们想要Student的实例的属性不能随意添加,必须限制,怎么做? 👇
使用__slots__
class Student(object): __slot__ = ('name', 'age')
如果再想要,给实例添加其他属性就会报告❌AttributeError。
使用@property
给类的实例添加属性。
首先,一个对一个实例属性的正常操作包括写入和读取,即要两个方法。
class Student(object): def get_score(self): return self._score def set_score(self, value): self._score = value
但这么写很费劲,而且调用的时候,不能直接使用属性的名字score。
Python也没有类似Ruby的写法:⚠️下面代码是模仿Ruby。Ruby的 "name="也可以是方法名,并且有了赋值的功能。
class Student(object): def score(self): return self._score def score=(self, value): self._score = value s = Student() s.score = 11 print(s.score)
所以说很费劲。因此Python使用了自己的方法,这就是装饰器(decorator)的方法:@property
class Student(object): @property def score(self): return self._score @score.setter def score(self, value): self._score = value
>>> s = Student() >>> s.score = 60 # OK,实际转化为s.set_score(60) >>> s.score # OK,实际转化为s.get_score() 60
相当于score定义外面加了一层或几层定义def,这种代码的写法叫做装饰器。
@property相当于加上读方法,@xxx.setter相当于加上写方法。
⚠️Ruby更简单直接用attr_accessor(symbol)来完成读写方法的定义。
Python例子:
- ⚠️如果使用@property,那么格式需要注意:self._xxx。加上一个下划线。
- resolution是一个只读属性。他的内部是用self._width * self._height。
- 如果设resolution为一个普通方法,返回的是<bound method Screen.resolution of <__main__.Screen object at 0x1094e2f10>>。必须@property装饰后,才能用_xxx格式。
class Screen(object): @property def width(self): return self._width @width.setter def width(self, value): self._width = value @property def height(self): return self._width @height.setter def height(self, value): self._height = value @property def resolution(self): a = self._width * self._height return a s = Screen() s.width = 1024 s.height = 768 print('resolution =', s.resolution) if s.resolution == 786432: print('测试通过!') else: print('测试失败!')
没找到types, MethodType方法带参数的源代码
后来浏览了https://stackoverflow.com/questions/46525069/how-is-types-methodtype-used。
多重继承
例子:Dog类继承哺乳动物类和,跑类。
class Dog(Mammal, Runnable): pass
通过多重继承,一个子类就可以同时获得多个父类的所有功能。
MixIn
Python有多重继承 ,但一般设计,主要是单一继承。如果需要混入额外的功能,就通过多重继承,这就是混入。不过混入的类的名字加上MixIn,以便看出继承关系。
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn): pass
简单来说,搜索的方式,是深度优先,从左到右。
- 查找一个方法runing, 先看Dog。没有的话:
- 查找Manmmal。没有的话,递归到Manmmal的基类去搜索,如果还没有,👇:
- 再到RunnalbeMixIn搜索,并依次类推。
真实情况更复杂,方法的解析顺序可能发生动态改变。这是因为要支持super()方法的协调。这种方式也被其他语言叫做后续方法调用
动态改变顺序必要行:因为所有多重继承的情况都会显示出一个或更多的菱形关联(即至少有一个父类可通过多条路径被最底层类所访问)。 比如object类是其他所有类的父类,但在一次搜索中,无需访问多次,所以需要动态协调。
Ruby的单一继承和混入
Ruby不支持多重继承,有一个继承链条。但也支持混入模块,Module#include。
需要注意的是,混入的模块加入到了继承的链条上去。所以一个实例寻找方法,是安装链条上的继承关系来从下向上找的。
Ruby用Module#ancestor来得到继承链条。superclass则返回一个类的父类。
Python有两个内置函数可被用于继承机制
-
issubclass(class, classinfo)检查类的父类。如果class是classinfo的子类(直接,间接,虚拟的),返回true - isinstance() 检查一个实例的类型
浙公网安备 33010602011771号