描述符:将某种特殊类型的类的实例指派给另一个类的属性
特殊类型:
至少实现以下三个魔法方法中的一个(描述符属性的方法):
__set__(self, instance, value):将在属性分配操作中调用,不反回任何内容
__get__(self, instance, owner) 用于访问属性,返回属性值
__delete__(self, instance):控制删除操作,不反悔任何内容
注意这三个参数的含义:self是指描述符类的实例对象,instance是指派给的类的实例对象,owner为指派给的类
从描述符的使用分析,首先要在一个类Test_test中实例化该描述符类Test,这个实例化对象 t 为为类Test_test的一个属性,此时对该属性 t 进行赋值操作时,由于t 为Test的一个对象,因此会调用 方法 __set__(),此时,self即为绑定的对象 t而 instance为t所属的类实例化的对象(test).
1 class Test(): 2 def __set__(self, instance, value): 3 print('setting', self, instance, value) 4 5 def __get__(self, instance, owner): 6 print('getting', self, instance, owner) 7 8 def __delete__(self, instance): 9 print('delete', self, instance) 10 11 class Test_test(): 12 t = Test() 13 14 test = Test_test() 15 test.t = 10 16 test.t 17 del test.t 18 ########################################## 19 20 setting <__main__.Test object at 0x01ADB050> <__main__.Test_test object at 0x01ADB030> 10 21 getting <__main__.Test object at 0x01ADB050> <__main__.Test_test object at 0x01ADB030> <class '__main__.Test_test'> 22 delete <__main__.Test object at 0x01ADB050> <__main__.Test_test object at 0x01ADB030>
class Celsius(): def __init__(self, fset = None, fget = None, fdel = None): self.fset = fset self.fget = fget self.fdel = fdel def __get__(self, instance, owner): return self.fget(instance)*1.8 + 30 def __set__(self, instance, value): self.fset(instance, value) def __delete__(self, instance): self.fdel(instance) class Tempeature(): def __init__(self): self.__x = None def __fset(self, value): self.__x = value def __fget(self): return self.__x def __fdel(self): del self.__x c = Celsius(__fset, __fget, __fdel) t = Tempeature() t.c = 10 print(t.__dict__) print(t.c) del t.c print(t.__dict__) ####################################### {'_Tempeature__x': 10} 48.0 {}
描述符的意义在于,可以默认的调用一些函数,而通过初始化这个类可以将外部的函数作为参数传递进来,并在描述符类中魔法方法被自动调用时使传入的函数参数被自动的执行。如上例所示,通过将Tempeature类中的函数作为参数传入 描述符,使得在描述符中的魔法方法被自动调用时实现 实例对象 t 内部函数的自动调用。
注意,描述符的意义不在于给 描述符赋值,而是通过描述符隐式的为类中的参数赋值。因此在 魔法方法__set__(self, instance, value)中并不会出现属性名。属性值的设置需要包含的被自动调用的魔法方法中,可以很好的隐藏类中的属性。(个人理解)
定制容器:
容器类型的协议:
希望定制的容器时不可变的:只需要 __len__() 与 __getitem__()
希望定制的容器是可变的: __len__() __getitem__() __setitem__() __delitem__()
__len__(self):定义当被 len()调用时的行为
__getitem__(self, key): 定义获取容器中指定元素的行为
__setitem__(self, key, value): 定义设置容器中元素的行为 self[key] = value
__delitem__(self, key):定义删除容器中指定元素的行为
_iter__(self):定义迭代容器中元素的行为
__reversed__(self):定义 reversed()的行为
__contains__(self, item):定义使用成员运算符 in 或 not in的行为
自定义一个不可变的列表, 并记录每个值被访问的次数。
1 class Mylist(): 2 def __init__(self, *args): 3 self.list = [x for x in args] 4 self.length = len(self.list) 5 self.times = [0]*self.length 6 7 def __len__(self): 8 return self.length 9 10 def __getitem__(self, key): 11 if key < self.length: 12 self.times[key] += 1 13 return self.list[key] 14 else: 15 print('KeyError') 16 17 l = Mylist(1,2,3,5) 18 print(len(l)) 19 l[2] 20 print(l.times) 21 22 ############################# 23 4 24 [0, 0, 1, 0]
迭代器:
iter()
__iter__(self):return self返回实例对象本身
next()
__next__(self):定义迭代规则
1 >>> class F(): 2 def __init__(self): 3 self.a = 0 4 self.b = 1 5 def __iter__(self): 6 return self 7 def __next__(self): 8 self.a, self.b = self.b, self.a + self.b 9 return self.a 10 11 >>> f = F() 12 >>> for i in f: 13 print(i)
加入迭代大小控制:
1 >>> class F(): 2 def __init__(self, n = 10): 3 self.a = 0 4 self.b = 1 5 self.n = n 6 def __iter__(self): 7 return self 8 def __next__(self): 9 self.a, self.b = self.b, self.a + self.b 10 if self.a < self.n: 11 return self.a 12 else: 13 raise StopIteration 14 15 16 >>> f = F(100) 17 >>> for i in f: 18 print(i) 19 20 21 1 22 1 23 2 24 3 25 5 26 8 27 13 28 21 29 34 30 55 31 89
生成器:yield
协同程序:可以运行的独立函数调用,函数可以暂停或者挂起,并在需要的时候在程序离开的地方继续或重新开始。
yield 类似于return,执行到yield则停止执行,下次继续执行
1 >>> def MyGen(): 2 a = 0 3 b = 1 4 while True: 5 a,b = b, a + b 6 yield a 7 8 9 >>> g = MyGen() 10 >>> next(g) 11 1 12 >>> next(g) 13 1 14 >>> next(g) 15 2 16 >>>