Python 构造函数:
class FooBar:
def __init__(self):
self.somevar = 42
运行结果
>>> f = FooBar()
>>> f.somevar
42
接受参数的构造函数:
class FooBar:
def __init__(self, value=42):
self.somevar = value
结果:
>>> f = FooBar('This is a constructor argument')
>>> f.somevar
析构函数
__del__
由于Python有carbage colletion的机制,所以,建议尽量不要使用析构函数
调用超类super的构造函数
由于在子类中,超类的构造函数被重载,所以超类构造函数里面定义的变量,在子类中都不会声明,要避免这种情况,需要在子类的构造函数中调用超类的构造函数,这里有两种方法。
第一种,直接利用超类名来构造。
class SongBird(Bird):
def __init__(self):
Bird.__init__(self)
self.sound = 'Squawk!'
def sing(self):
print self.sound
第二种方法,利用super函数。
__metaclass__ = type # super only works with new-style classes
class Bird:
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print 'Aaaah...'
self.hungry = False
else:
print 'No, thanks!'
class SongBird(Bird):
def __init__(self):
super(SongBird, self).__init__()
self.sound = 'Squawk!'
def sing(self):
print self.sound
最好使用super函数的方法,这有很多好处,特别是在有多个超类的时候。
Basic Sequence and Mapping protocol
Sequence 和 Mapping是最基本的数据的集合。要实现他们的行为,如果是immutable的需要完成两个protocol,如果是mutable的话需要完成4个protocol
__len__(self): 用来返回集合中的元素个数,如果是Sequence的话,返回值则是Sequence里面的元素的个数,如果是Mapping的话,返回的值是key-value对的数目。如果__len__返回0,那么在需要布尔值的情况会转换为false。.
__getitem__(self, key): 用来返回key所对应的值。入股是Sequence,这个key就是一个小于元素个数的整数。如果是mapping那么就可以是任意一个值。
__setitem__(self, key, value): 将value存储在key所对应的位置。
__delitem__(self, key): 当使用del语句时,用来删除key对对应的元素。
exstra:
在一个Sequence中如果这个整数是一个负数,那么则相当与从底端开始x[-n] 相当与x[len(x)-n]
当键值不匹配的时候抛出一个TypeError异常
当索引的类型正确,但是越界了,则抛出一个IndexError
下面是一个无穷数列的代码:
def checkIndex(key):
"""
Is the given key an acceptable index?
To be acceptable, the key should be a non-negative integer. If it
is not an integer, a TypeError is raised; if it is negative, an
IndexError is raised (since the sequence is of infinite length).
"""
if not isinstance(key, (int, long)): raise TypeError
if key<0: raise IndexError
class ArithmeticSequence:
def __init__(self, start=0, step=1):
"""
Initialize the arithmetic sequence.
start - the first value in the sequence
step - the difference between two adjacent values
changed - a dictionary of values that have been modified by
the user
"""
self.start = start
# Store the start value
self.step = step
# Store the step value
self.changed = {}
# No items have been modified
def __getitem__(self, key):
"""
Get an item from the arithmetic sequence.
"""
checkIndex(key)
try: return self.changed[key] # Modified?
except KeyError: # otherwise...
return self.start + key*self.step # ...calculate the value
def __setitem__(self, key, value):
"""
Change an item in the arithmetic sequence.
"""
checkIndex(key)
self.changed[key] = value # Store the changed value
结果:
>>> s = ArithmeticSequence(1, 2)
>>>s[4]
9
>>>s[4] = 2
>>>s[4]
2
>>>s[5]
11
类中的属性,用访问器(accessor)来访问的话,可以通过property函数来使得他与普通的属性访问方法相同,比如:
__metaclass__ = type
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
size = property(getSize, setSize)
运行结果:
>>> r = Rectangle()
>>> r.width = 10
>>> r.height = 5
>>> r.size
(10, 5)
>>> r.size = 150, 100
>>> r.width
150
访问起来,类似于C++中重载了 =操作符,一点要注意的是property函数第一个参数是getter,第二个是setter,还有这是new style的class。
property可以接受,0个,1个,2个,3个和四个参数,四个参数分别为fget, fset, fdel, doc,如果是0个参数,那么就不可读也不可写,如果是1个参数,那么是只读;两个参数是可读可写,三个参数是可以删除,第四个是document。
静态方法(static method)和类方法(class method)
静态方法和类方法可以直接通过类来访问,静态方法不需要参数,类方法需要一个类似与self的参数cls。定义的方法如下:
__metaclass__ = type
class MyClass:
def smeth():
print 'This is a static method'
smeth = staticmethod(smeth)
def cmeth(cls):
print 'This is a class method of', cls
cmeth = classmethod(cmeth)
2.4以后的版本,提供了一种新的定义方法,如下:
__metaclass__ = type
class MyClass:
@staticmethod
def smeth():
print 'This is a static method'
@classmethod
def cmeth(cls):
print 'This is a class method of', cls
运行的结果如下:
>>> MyClass.smeth()
This is a static method
>>> MyClass.cmeth()
This is a class method of <class '__main__.MyClass'>
由于一些历史原因python静态方法和类方法用的不是很多,但是现在在一些特殊场合,比如工厂方法的时候,经常使用。
迭代器(iterator)
除了List和dict以外,其他的对象也能迭代,只要实现了__iter__这个magic 方法,__iter__不不接收参数,返回一个任意实现了next方法对象的迭代器。next()函数用来返回迭代器的“下一个值“,如果下一个值不返回任何的东西的话,需要抛出一个StopIteration的异常。
在python3.0中迭代器协议发生了改变, 迭代的对象需要实现__next__函数,而不是next,next()是一个新的内嵌函数比如next(it)等价与之前版本的it.next().
下面是一个无穷队列Fibolacci数列:
class Fibs:
def __init__(self):
self.a = 0
self.b = 1
def next(self):
self.a, self.b = self.b, self.a+self.b
return self.a
def __iter__(self):
return self
内嵌的iter函数,可以将可迭代的对象转化为迭代器
>>> it = iter([1, 2, 3])
>>> it.next()
1
>>> it.next()
2
也可以从迭代器来生成一个序列:
lass TestIterator:
value = 0
def next(self):
self.value += 1
if self.value > 10: raise StopIteration
return self.value
def __iter__(self):
return self
运行结果:
>>> ti = TestIterator()
>>> list(ti)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
生产器(generator)
生产器是一种特殊的迭代器,它的定义类似于函数的定义,可以看看如下的例子,下面的代码是一个生产器,把一个多维数组平滑输出,既将一个数组的数组里面的数据按顺序输出成一列
nested = [[1, 2], [3, 4], [5]]
def flatten(nested):
for sublist in nested:
for element in sublist:
yield element
你可以这样使用生产器
>>> nested = [[1, 2], [3, 4], [5]]
>>> for num in flatten(nested):
print num
也可以这样使用
>>> list(flatten(nested))
[1, 2, 3, 4, 5]
在2.4以后的版本,提供了一种新的语法,就是生产器表达式(generator comprehension or generator expression),它类似与List conprehension,但是它不是生成一个list而是返回一个生产器,这样的话,就可以方便你一步一步的处理
>>> g = ((i+2)**2 for i in range(2,27))
>>> g.next()
16
生产器还有一个优点,就是你如果在函数调用中使用生产器,那么不需要多余的括号,如下
sum(i**2 for i in range(10))
递归生产器
上面的例子只能flatten两层的list,如果是一个多层次混合的话,就需要递归了:
def flatten(nested):
try:
for sublist in nested:
for element in flatten(sublist):
yield element
except TypeError:
yield nested
结果:
>>> list(flatten(['foo', ['bar', ['baz']]]))
['foo', 'bar', 'baz']
如果没有yield的话,可以模拟生产器,代码如下:
def flatten(nested):
result = []
try:
# Don't iterate over string-like objects:
try: nested + ''
except TypeError: pass
else: raise TypeError
for sublist in nested:
for element in flatten(sublist):
result.append(element)
except TypeError:
result.append(nested)
return result