下面的代码会报错,为何?python
In [1]: class A(object): ...: x = 1 ...: gen = (x for _ in xrange(10)) ...: In [2]: if __name__ == "__main__": ...: print(list(A.gen)) ...: --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-2-bb15a85da3e4> in <module>() 1 if __name__ == "__main__": ----> 2 print(list(A.gen)) 3 <ipython-input-1-45646dbd84a7> in <genexpr>((_,)) 1 class A(object): 2 x = 1 ----> 3 gen = (x for _ in xrange(10)) 4 NameError: global name 'x' is not defined
这个问题是变量做用域问题,在gen=(x for _ in xrange(10))中gen是一个generator,在generator中变量有本身的一套做用域,与其他做用域空间相互隔离。所以,将会出现这样的 NameError: name ‘x’ is not defined的问题,那么解决方案是什么呢?答案是:用lambda 。app
In [7]: class A(object): ...: x = 1 ...: gen = (lambda x: (x for _ in xrange(10)))(x) ...: In [9]: if __name__ == "__main__": print(list(A.gen)) ...: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
装饰器函数
我想写一个类装饰器用来度量函数/方法运行时间ui
In [10]: import time In [11]: class Timeit(object): ....: def __init__(self, func): ....: self._wrapped = func ....: def __call__(self, *args, **kws): ....: start_time = time.time() ....: result = self._wrapped(*args, **kws) ....: print("elapsed time is %s ")%(time.time() - start_time) ....: return result ....: # 这个装饰器可以运行在普通函数上: In [12]: @Timeit ....: def func(): ....: time.sleep(2) ....: return "invoking function func" ....: In [13]: if __name__ == "__main__": ....: func() ....: elapsed time is 2.00392723083
可是运行在方法上会报错,为何?code
In [14]: class A(object): ....: @Timeit ....: def func(self): ....: time.sleep(1) ....: return "invoking method func" ....: In [15]: if __name__ == "__main__": ....: a = A() ....: a.func() ....: --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-15-eb4fb185288b> in <module>() 1 if __name__ == "__main__": 2 a = A() ----> 3 a.func() 4 <ipython-input-11-aedd3f23516b> in __call__(self, *args, **kws) 4 def __call__(self, *args, **kws): 5 start_time = time.time() ----> 6 result = self._wrapped(*args, **kws) 7 print("elapsed time is %s ")%(time.time() - start_time) 8 return result TypeError: func() takes exactly 1 argument (0 given)
若是我坚持使用类装饰器,应该如何修改?ip
使用类装饰器后,在调用 func 函数的过程当中其对应的 instance 并不会传递给 call 方法,形成其 mehtod unbound ,那么解决方法是什么呢?描述符赛高:ci
In [16]: class Timeit(object): ....: def __init__(self, func): ....: self.func = func ....: def __call__(self, *args, **kwargs): ....: print("invoking Timer") ....: def __get__(self, instance, owner): ....: return lambda *args, **kwargs: self.func(instance, *args, **kwargs) ....: In [17]: class A(object): ....: @Timeit ....: def func(self): ....: time.sleep(1) ....: return "invoking method func" ....: In [18]: if __name__ == "__main__": ....: a = A() ....: a.func() ....:
Python调用机制作用域
咱们知道 call 方法能够用来重载圆括号调用,好的,觉得问题就这么简单?Naive!文档
In [19]: class A(object): ....: def __call__(self): ....: print("invoking __call__ from A!") ....: In [20]: if __name__ == "__main__": ....: a = A() ....: a() ....: invoking __call__ from A!
如今咱们能够看到a()彷佛等价于a.__call__(),看起来很 Easy 对吧,好的,我如今想做死,又写出了以下的代码,get
In [21]: a.__call__ = lambda: "invoking __call__ from lambda" In [22]: a.__call__() Out[22]: 'invoking __call__ from lambda' In [23]: a() invoking __call__ from A!
请大佬们解释下,为何a()没有调用出a.__call__()(此题由 USTC 王子博前辈提出)
缘由在于,在 Python 中,新式类( new class )的内建特殊方法,和实例的属性字典是相互隔离的,具体能够看看 Python 官方文档对于这一状况的说明
For new-style classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary. That behaviour is the reason why the following code raises an exception (unlike the equivalent example with old-style classes):
同时官方也给出了一个例子:
In [24]: class C(object): ....: pass ....: In [25]: c = C() In [26]: c.__len__ = lambda: 5 In [27]: len(c) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-27-c6494b964a51> in <module>() ----> 1 len(c) TypeError: object of type 'C' has no len() In [28]: c.__len__ Out[28]: <function __main__.<lambda>> In [29]: c.__len__() Out[29]: 5
回到咱们的例子上来,当咱们在执行 a.__call__=lambda:"invoking call from lambda" 时,的确在咱们在 a.__dict__ 中新增长了一个 key 为 call 的 item,可是当咱们执行 a() 时,由于涉及特殊方法的调用,所以咱们的调用过程不会从 a.__dict__ 中寻找属性,而是从 type(a).__dict__ 中寻找属性。所以,就会出现如上所述的状况。