魔法方法,属性和迭代器
1. 魔法方法
-
所有以双下划线开头和结尾的方法称为魔法方法
-
构造方法,__init__ ,在对象创建是自动调用
>>> class fooBar(object): def __init__(self,): self.somevar = 42 >>> f = fooBar() >>> f.somevar 42
-
重写一般方法和构造方法,通过继承子类调用方法没有找到时就会在超类中找,继承可以增加新的方法,也可以重写超类的方法
>>> class A: def hello(self): print "Hello, I'm A" >>> class B(A): pass >>> a = A() >>> b = B() >>> a.hello() Hello, I'm A >>> b.hello() Hello, I'm A #重写class B >>> class B(A): def hello(self): print "Hello, I'm B" >>> b = B() >>> b.hello() Hello, I'm B #如果构造方法被重新了,就需要调用超类的方法,子类不仅要初始化自己,还需要初始化超类代码 >>> class Bird(object): def __init__(self): self.hungry = True def eat(self): if self.hungry: print 'Aaaah...' self.hungry = False else: print 'No, thanks!' >>> b = Bird() >>> b.eat() Aaaah... >>> b.eat() No, thanks! #添加子类SongBird,添加唱歌行为 >>> class SongBird(Bird): def __init__(self): self.sound = 'Squawk!' def sing(self): print self.sound >>> sb = SongBird() >>> sb.sing() Squawk! >>> sb.eat() Traceback (most recent call last): File "<pyshell#55>", line 1, in <module> sb.eat() File "<pyshell#41>", line 5, in eat if self.hungry: AttributeError: SongBird instance has no attribute 'hungry' #由于SongBird重新了Bird的构造方法,但是构造方法中没有任何关于初始化hungry特性的代码 #调用超类的构造方法有两种技术:调用超类构造方法的未绑定版本,或者使用super函数 >>> class SongBird(Bird): def __init__(self): Bird.__init__(self) self.sound = 'Squawk!' def sing(self): print self.sound #当前类和对象作为super函数的参数,调用函数返回对象的任何方法都是调用超类方法,super只在新式类中起作用 >>> class SongBird(Bird): def __init__(self): super(SongBird, self).__init__() Bird.__init__(self) self.sound = 'Squawk!' def sing(self): print self.sound
-
其他魔法方法,只要遵守序列或映射的规则,可以实现类似序列或映射的行为,实现序列或映射的魔法方法
-
基本序列和映射规则
魔法方法 说明 __len__(self) 这个方法应该返回集合中的所含项目的数量,序列返回元素个数,映射返回键值对的数量,如果返回0并且为重新__nonzero__,当作布尔值False,调用len()函数返回此方法值 __getitem__(self,key) 这个方法返回所给键对应的值,对于序列键为0~n-1,映射可以使用任何种类的键,通过[]运算访问是使用此方法 __setitem__(self, key, value) 设置给定的键对应的值,只能为可修改对象定义此方法 __delitem__(self, key) 调用del语句是调用,只能为可修改对象定义此方法 对序列来说如果键是负整数,那么从末尾计数, 如果键是不合适的类型,引发TypeError 如果索引正确,但超出范围引发IndexError
#创建无穷序列 >>> def checkIndex(key): if not isinstance(key, (int, long)): raise TypeError if key < 0: raise IndexError >>> class ArithmeticSequence(object): def __init__(self, start = 0, step = 1): self.start = start self.step = step self.changed = {} def __getitem__(self, key): checkIndex(key) try: return self.changed[key] except KeyError: return self.start + key * self.step def __setitem__(self, key, value): checkIndex(key) self.changed[key] = value >>> s = ArithmeticSequence(1, 2) >>> s[4] 9 >>> s[4] = 2 >>> s[4] 2 >>> s[5] 11 >>> del s[4] Traceback (most recent call last): File "<pyshell#95>", line 1, in <module> del s[4] AttributeError: __delitem__ #没有__len__方法,不能用len函数,引发TypeError >>> len(s) Traceback (most recent call last): File "<pyshell#96>", line 1, in <module> len(s) TypeError: object of type 'AtithmeticSequence' has no len() #索引类型正确,但超出范围 >>> s[-1] Traceback (most recent call last): File "<pyshell#97>", line 1, in <module> s[-1] File "<pyshell#86>", line 7, in __getitem__ checkIndex(key) File "<pyshell#90>", line 5, in checkIndex raise IndexError IndexError #索引类型不正确 >>> s['a'] Traceback (most recent call last): File "<pyshell#98>", line 1, in <module> s['a'] File "<pyshell#86>", line 7, in __getitem__ checkIndex(key) File "<pyshell#90>", line 3, in checkIndex raise TypeError TypeError
-
-
子类化内建的类型,可以很容易创建相似的类型
如以下通过继承list实现一个带计数功能的CountList
>>> class CountList(list): def __init__(self, *args): super(CountList, self).__init__(*args) self.count = 0 def __getitem__(self, index): self.count += 1 return super(CountList, self).__getitem__(index) >>> cl = CountList(range(10)) >>> cl [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> cl.reverse() >>> cl [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] >>> del cl[3:6] >>> cl [9, 8, 7, 3, 2, 1, 0] >>> cl.count 0 >>> cl[4] + cl[2] 2
2. 属性
- 示例, getSize,setSize为名为Size特性的访问器方法
>>> class Rectangle(): def __init__(self): self.width = 0 self.height = 0 def setSize(self, size): self.width, self.height = size def getSize(self): return self.width, self.height >>> r = Rectangle() >>> r.width = 10 >>> r.height = 5 >>> r.getSize()
新式类中更优雅实现,使用property
>>> class Rectangle(object): def __init__(self): self.width = 0 self.height = 0 def setSize(self, size): self.width, self.height = size def getSize(self): return self.width, self.height size = property(getSize, setSize) >>> r = Rectangle() >>> r.width = 10 >>> r.height = 5 >>> r.size (10, 5) >>> r.size = (100, 50) >>> r.width 100 #size特性还是由getSize和setSize中计算,但是看起来和普通属性一样的
- property函数可以使用0,1,3,4个参数,如果没有参数,产生的属性既不可读也不可写,如果只使用一个参数,产生的的属性是只读的,第三个参数(可选)是用于删除特性的方法,第四个为文档字符串,四个参数分别叫fget,fset,fdel,doc,如果只读和文档字符串,可以使用关键字参数的方式来实现
3. 静态方法和成员方法
- 静态方法,装入到staticmethod类型的对象中,静态方法的定义没有self,且能够被本身直接调用,静态方法不能访问类的特性和方法
- 类成员方法,装入classmethod类型的对象中,定义时需要名为cls的类似self的参数,类成员方法可以直接用类的具体对象调用,但cls参数自动绑定到类
>>> class MyClass(object):
var = 2
@staticmethod
def smeth():
print var
print 'This is a static method'
@classmethod
def cmeth(cls):
print cls.var
print 'This is a class method', cls
>>> MyClass.cmeth()
This is a class method <class '__main__.MyClass'>
>>> MyClass.smeth()
Traceback (most recent call last):
File "<pyshell#178>", line 1, in <module>
MyClass.smeth()
File "<pyshell#175>", line 5, in smeth
print var
NameError: global name 'var' is not defined
>>>
4. __getattr__,__setattr__和他的朋友们
-
拦截(intercept)对象所有特性(attribute)的访问是可能的,为了在访问特性时可以执行代码,必须使用一些魔法方法。
魔法方法 说明 __getattribute__(self, name) 当特性name被访问时被自动调用 __getattr__(self, name) 当特性name被访问且不存在时被自动调用,先调getattribute,getattribute判断特性不存在,再调用此方法 __setattr__(self, name, value) 当试图给特性赋值时被自动调用,默认是可以直接给特性赋值增加特性 __delattr__(self, name) 当试图删除特性时被自动调用 getattr(obj, name)函数 本质调用对象对应的魔法方法 setattr(obj, name, value)函数 本质调用对象对应的魔法方法 delattr(obj, name)函数 本质调用对象对应的魔法方法 hasattr(obj, name)函数 本质调用getattr魔法属性判断是否存在特性 callable(obj)函数 python 2中函数判断是否可调用,python 3中使用hasattr(obj, ‘__call__’)