一篇文章踩遍Python中的坑
程序君個人微信
和我聊聊編程和創業的事
加好友
作者丨未知
http://python.jobbole.com/86869/
下面的代碼會報錯,為什麼?
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-45646
dbd84a7>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 。
7 class A (object)In [
...: x =
1
...: gen = (
lambda
x: (xfor
_in
xrange(10
)))(x)...:
In [
9
]:if
__name__ =="__main__"
:print(list(A.gen))
...:
[
1
,1
,1
,1
,1
,1
,1
,1
,1
,1
]裝飾器
我想寫一個類裝飾器用來度量函數/方法運行時間
10In [
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
但是運行在方法上會報錯,為什麼?
14 class A objectIn [
....: @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
resultTypeError:
func() takes exactly1
argument (0
given)如果我堅持使用類裝飾器,應該如何修改?
使用類裝飾器後,在調用 func 函數的過程中其對應的 instance 並不會傳遞給 call 方法,造成其 mehtod unbound ,那麼解決方法是什麼呢?描述符賽高:
16 class Timeit objectIn [
....:
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!
19 class A (object)In [
....:
def
__call__
(self)
:....: print(
"invoking __call__ from A!"
)....:
In [
20
]:if
__name__ =="__main__"
:....: a = A()
....: a()
....:
invoking __call__
from
A!現在我們可以看到a()似乎等價於a.__call__(),看起來很 Easy 對吧,好的,我現在想作死,又寫出了如下的代碼,
In 21 "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"
hasno
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__ 中尋找屬性。因此,就會出現如上所述的情況。
推薦↓↓↓
長
按
關
注
??
【
16個技術公眾號
】都在這裡!
涵蓋:程序員大咖、源碼共讀、程序員共讀、數據結構與演算法、黑客技術和網路安全、大數據科技、編程前端、Java、Python、Web編程開發、Android、iOS開發、Linux、資料庫研發、幽默程序員等。
![](https://pic.pimg.tw/zzuyanan/1488615166-1259157397.png)
![](https://pic.pimg.tw/zzuyanan/1482887990-2595557020.jpg)
※可能是最淺顯易懂的一篇文章,關於Python引用、賦值、複製
※python3 拼接字元串的7種方法
TAG:Python開發 |