Python168的学习笔记6
如何派生内置不可变类型并修改实例化行为。
个人理解,如何派生出自己想要的类。
1 class IntTuple(tuple): 2 3 def __new__(cls,iterable): 4 g = (x for x in iterable if isinstance(x, int) and x>0) 5 return super(IntTuple,cls).__new__(cls,g) 6 7 def __init__(self,iterable): 8 super(IntTuple,self).__init__(iterable) 9 10 t = IntTuple([1,-1,'abc',6,[7,'y'],3]) 11 print t
类的创建是在__new__中实现的,所以要修改new。
关于使用__slots__,在创建class时,定义slots属性,声明实例属性列表,这就等于关闭掉__dict__属性,dict的作用是允许对class进行动态添加属性。使用slots可以节省内存,对封装有好处。
关于上下文管理,也就是在class中设置__enter__,__exit__属性,这样就可以配合with使用了。ps:with语句中,无论如何都会执行exit方法。
在设置__enter__属性时,要注意返回的对象是不是self,一般来说都是。
在设置__exit__属性时,要注意接收的参数, def __exit__(self,exc_type,exc_val,exc_tab),需要接收异常信息。
注意,如果在exit中设置了return Ture。则错误会被包在with中而不传回给最上层。
关于property的用法。
1 class Circle(object): 2 def __init__(self,r): 3 self.r = r 4 5 def getR(self): 6 return round(self.r,2)#四舍五入两位小数 7 8 def setR(self,value): 9 if not isinstance(value,(int,long,float)): 10 raise ValueError('wrong type.') 11 self.r = float(value) 12 13 def getArea(self): 14 return round(self.r ** 2* pi) 15 16 R = property(getR,setR)#将方法定义到属性 17 18 c =Circle(3.2) 19 print c.R 20 c.R = 5
为类添加比较的属性,如__lt__,__le__,__gt__,__ge__,__eq__,__ne__这些属性。(小于,小于等于,大于,大于等于,等于,不等于)
比较两个类的大小,如 r1<r2 实际上就是 r1.__lt__(r2)方法的调用。
但是如果要添加6个属性显得很麻烦,所以可以使用functools.total_ordering方法,将其作为装饰器,赋给class,然后只用定义__lt__,__eq__就可以实现6个属性的比较了。
1 from functools import total_ordering 2 from abc import abstractmethod,ABCMeta 3 4 @total_ordering 5 class Shape(object): 6 __metaclass__ = ABCMeta#定义这个类为抽象类,不可以被实例化 7 @abstractmethod#声明定义抽象方法,让各个子类去完善 8 def area(self): 9 pass 10 11 def __lt__(self,obj): 12 if not isinstance(obj,Shape): 13 raise TypeError('obj is not shape.') 14 return self.area() <obj.area() 15 16 def __eq__(self,obj): 17 if not isinstance(obj,Shape): 18 raise TypeError('obj is not shape.') 19 return self.area() == obj.area() 20 21 22 class Rectangle(Shape): 23 def __init__(self,w,h): 24 self.w = w 25 self.h = h 26 27 def area(self): 28 return self.w *self.h 29 30 31 class Circle(Shape): 32 def __init__(self,r): 33 self.r = r 34 35 def area(self): 36 return self.r ** 2 *3.14 37 38 r1 = Rectangle(3,5) 39 r2 = Rectangle(5,5) 40 c1 = Circle(5) 41 print r1 > r2 42 print c1 > r1
ps:要注意ABCMeta,abstractmethod的作用,理解抽象类以及抽象方法的定义。
深入了解类的构成,如 __get__,__set__,__del__ 这些方法的作用。
1 #coding:utf8 2 class Attr(object): 3 def __init__(self,name,type_): 4 self.name = name 5 self.type_ = type_ 6 7 def __get__(self,instance,cls): 8 return instance.__dict__[self.name] 9 10 def __set__(self,instance,value): 11 if not isinstance(value,self.type_): 12 raise TypeError('expected an %s' % self.type_) 13 instance.__dict__[self.name] = value 14 15 def __delete__(self,instance): 16 del instance.__dict__[self.name] 17 18 class Person(object): 19 name = Attr('name',str) 20 age = Attr('age',int) 21 height = Attr('height',float) 22 23 p = Person() 24 25 p.age = '17' 26 print p.age#这里就会报错
引入弱引用weakref的概念,弱引用可以创建一种能访问对象,但是不增加访问计数的对象。
ps:当对象被引用时,对象的引用计数+1,而当对象的引用计数为0时,对象将被回收。弱引用的好处是,在某种循环引用里,解决回收不及时的问题。
1 #coding:utf8 2 import weakref 3 4 class Data(object): 5 def __init__(self,value,owner): 6 self.owner = weakref.ref(owner)#如果不是弱引用,则删除实例也不会回收 7 self.value = value 8 9 def __del__(self):#回收函数 10 print 'in Data.__del__' 11 12 class Node(object): 13 def __init__(self,value): 14 self.data = Data(value,self) 15 16 def __del__(self): 17 print 'in Node.__del__' 18 19 node = Node(100) 20 del node
ps:注意,需要引用 弱引用的对象时,要用其方法,如:self.owner = weakref.ref(owner),调用self.owner时应为self.owner()
通过实例化 方法的名字 来调用
1 class s1(object): 2 def A(self): 3 return 'A' 4 5 class s2(object): 6 def B(self): 7 return 'B' 8 9 class s3(object): 10 def C(self): 11 return 'C' 12 13 def getS(s): 14 for name in ('A','B','C'): 15 f = getattr(s, name,None)#在这里寻找对应的方法,如果找不到就返回none 16 if f: 17 return f() 18 19 a = s1() 20 b = s2() 21 c = s3() 22 t = [a,b,c] 23 print map(getS,t)
用operator.methodcaller也有类似功能。但是不太明白为什么要用这个。