笔者的上一篇python文章阅读量不错,看来python爱好者很多,所以再写一篇,以飨读者。
先接着上一篇讲一个问题,下面这段code有没有问题?
defcountcalls(func):
count=0def wrapper(*args, **kwargs):
count+= 1
print('num of calls: {}'.format(count))return func(*args, **kwargs)returnwrapper
@countcallsdeffoo(x):print (x+1)
foo(1)
运行时会发现:
UnboundLocalError: local variable 'count' referenced before assignment
原因是count在wrapper下面除非global,不然是不可见的,那么就没有初始化了。但是这是不能加global的,因为它不是global,如果移出函数外,那么结果又不对了。怎么解决呢?
python3的解决方案:
nonlocal count
count += 1
python2的解决方案:
defcountcalls(func):def wrapper(*args, **kwargs):
wrapper.count+= 1
print('num of calls: {}'.format(wrapper.count))return func(*args, **kwargs)
wrapper.count=0return wrapper
基于class的decorator
classPrintLog:def __init__(self, func):
self.func=funcdef __call__(self, *args, **kwargs):print ('CALLING: {}'.format(self.func.__name__))return self.func(*args, **kwargs)
主要是__call__决定的,任何object只要定义了__call__方法就能当函数用。下面对比一下:
classPrintLog:def __init__(self, func):
self.func=funcdef __call__(self, *args, **kwargs):print ('CALLING: {}'.format(self.func.__name__))return self.func(*args, **kwargs)defprintlog(func):def wrapper(*args, **kwargs):print("CALLING:" + func.__name__)return func(*args, **kwargs)returnwrapper
@printlogdeff(n):return n+2@PrintLogdeff_class(n):return n+2
CALLING: f
5
CALLING: f_class
5
完全等效。__call__就相当于wrapper function
magic methods
相当于是python中的syntax sugar,让+,-,*,/啥的拿过来就用,举例如下:
classAngle:def __init__(self, value):
self.value= value % 360
def __add__(self, other_angle):return Angle(self.value +other_angle.value)
a= Angle(45)
b= Angle(90)
c= a +bprint (c.value)
135
常用方法如下:
数学运算__add__ a +b__sub__ a -b__mul__ a *b__truediv__ a/b(浮点除)__mod__ a %b__pow__ a **b
位运算__lshift__ a <>b__and__ a &b__xor__ a ^b__or__ a | b
比较运算
__eq__ a == b
__ne__ a != b
__lt__ a < b
__le__ a <= b
__gt__ a > b
__ge__ a >= b
举个例子:
classMoney(object):def __init__(self, dollar, cent):
self.dollars=dollar
self.cents=centdef __str__(self):return "$" + str(self.dollars) + "." + "{:02}".format(self.cents)def __repr__(self):return "Money(" + str(self.dollars) + "," + str(self.cents) + ")"
def __add__(self, other):
cents= (self.cents + other.cents) % 100dollars= self.dollars + other.dollars + (self.cents + other.cents)/100
returnMoney(dollars, cents)def __sub__(self, other):if self.cents
cents= 100 + self.cents -other.cents
dollars= self.dollars - other.dollars - 1
else:
cents= self.cents -other.cents
dollars= self.dollars -other.dollarsreturnMoney(dollars, cents)def __mul__(self, other):
cents= (self.cents * other) % 100dollars= self.dollars * other + (self.cents * other) / 100
returnMoney(dollars, cents)def __eq__(self, other):return self.dollars == other.dollars and self.cents ==other.centsdef __ge__(self, other):return self.dollars >= other.dollars and self.cents >=other.centsdef __lt__(self, other):return self.dollars < other.dollars and self.cents < other.dollars
__str__和__repr__也是会时常用到的方法,它们都会放回string。__str__被用在print()时,同时str()也会调用它。而__repr__则是告诉你如何重现这个object,python命令行交互中输入object会调用__repr__,同时repr会调用它。
python collection类型
__getitem__相当于[index]
据两个例子大家就明白了,第一个例子是关于list,第二个例子是针对dict
classUniqueList:def __init__(self, items):
self.items=[]for item initems:
self.append(item)defappend(self, item):if item not inself.items:
self.items.append(item)def __getitem__(self, index):returnself.items[index]
ul= UniqueList([2,2,2,3,3,3,4,4,4])printul[0]print ul[1]print ul[2]
2
3
4
当然在具体实现的过程中对于index 要有input check,有问题需要raise IndexException,negative index要考虑语义的支持。
classMultiDict:def __init__(self):
self.data={}definsert(self, key, value):if key not inself.data:
self.data[key]=[]
self.data[key].append(value)defget_values(self, key):returnself.data[key]defget(self, key):return self.get_values(key)[-1]def __getitem__(self, key):return self.get_values(key)[-1]
md=MultiDict()
md.insert("a", 1)
md.insert("a", 2)print md['a']#2
Iterator
回到list那个例子,你觉得下面这个语法能够工作吗?
for i inul:print i
答:可以的,通过__getitem__我们不小心实现了iterator,事实上只要__getitem__能够接受0,1,2,... 并且在访问越界的时候raise IndexException,就相当于实现了iterator
Iterator的另一种实现是:
def __iter__(self):returnselfdef __next__(self):
...raiseStopIterationreturn ...
可见__getitem__显得更简洁。