2011-04-16 106 views

回答

6

後者。這只是一條線,你很快就會看到它在做什麼。除此之外,只有一個訪問T可能會更快一點。

+1

在CPython上,您還可以將循環移動到C代碼中,這使得代碼更快*。 – ncoghlan 2011-04-17 14:47:40

8

後者。這個名字只能綁定一次,而不是發生n + 1次。

+0

哪個變量? – 2011-04-16 00:02:35

+0

蓄電池。 – 2011-04-16 00:02:54

+0

@neil我相信它的「T」只會被綁定一次。 – 2011-04-16 00:04:10

1

爲了澄清我對伊格納西奧的回答關於增強作業評論總是重新綁定的名稱,即使是可變對象,我提供了下面的例子:正如我在我的評論說

class Broken(list): 
    def __iadd__(self, other): 
     list.__iadd__(self, other) # This is not quite right 

a = b = Broken() 
a += [123] 
print(b) # OK 
print(a) # What!? 

,增強分配實際上擴展到對於可變對象,相當於a = a.__iadd__([123])。證明這一點的其他經典方法是使用元組:

>>> t = [], 
>>> t[0] += [123] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: 'tuple' object does not support item assignment 
>>> t[0] 
[123] 

在上面的例子Broken該bug是不從__iadd__方法返回一個值。它應以return self結尾,否則只需將調用結果返回給父類方法即可。

class Correct(list): 
    def __iadd__(self, other): 
     list.__iadd__(self, other) 
     return self 

class AlsoCorrect(list): 
    def __iadd__(self, other): 
     return list.__iadd__(self, other) 
+0

「Broken」並不完全像列表那樣行事,這是非常令人驚訝的。有沒有辦法讓它重載'__iadd__',同時避免重新綁定? – 2011-04-18 05:35:43

+0

另外,什麼是Python的這種令人驚訝的行爲的理由? – 2011-04-18 05:36:36

+0

+1爲這penatrating知識和罰款演示代碼 – eyquem 2011-04-18 17:26:28

2

ncoghlan是正確的:在更嚴格的意義上說,增強assignement始終重新綁定的名稱或暗示的項目。但是,由於他的帖子很橢圓,我會嘗試給出更多的基本解釋。

在DOC:

object.__iadd__(self, other)

object.__isub__(self, other)

等等

這些方法稱爲實施 增強算術作業 (+ =, - =等等......)。這些方法應該 嘗試做了手術就地 (修改個體經營)返回結果 (可以是,但不必 是個體經營)。 (....)

例如,爲了 執行該語句x += y,其中x是 ,其具有 __iadd__()方法,x.__iadd__(y一個類的一個實例)被調用。

http://docs.python.org/reference/datamodel.html#object。__add__

我明白這一點如下:

如果x是具有方法__iadd__()的實例中,指令x += y觸發像,該方法的調用:x.__iadd__(y)其執行,據我所知,如x = __iadd__(x,y)

也就是說:__iadd__()是兩個參數的函數(參見http://docs.python.org/tutorial/classes.html#method-objects),並且由於每個函數__iadd__(x,y)都返回分配給x的內容。這東西是對象x就地修改,與內存中的相同的地址,如果就地操作是可能的;否則它是另一個對象,另一個地址返回並被賦值爲x

所以,嚴格來說,科格倫是正確的:總是進行重新綁定標識符x的操作,即使__iadd__()已執行和就地修改的同一對象已經由函數返回__iadd__()。當我寫«同一個對象»,這意味着«在同一地址»。在這種情況下,Python所做的工作是無用的,它不會改變標識符x指向的地址,但Python無論如何都不會。

重新綁定到相同的對象改性就地由於x += y在正常情況下,當x有一個方法__iadd__(),是如下的結果的原因是:

a = b = [] 

print 'b ==',b,' id(b) ==',id(b) 
print 'a ==',a,' id(a) ==',id(a) 
a += [123] 
print '\nb ==',b,' id(b) ==',id(b) # OK 
print 'a ==',a,' id(a) ==',id(a) 

給出

b == [] id(b) == 18691096 
a == [] id(a) == 18691096 

b == [123] id(b) == 18691096 
a == [123] id(a) == 18691096 

說這個a在這個例子中沒有被重新綁定只有在結果出現的水平才具有某種意義,條件是「重新綁定」的意思是「重新綁定到另一個對象的另一個對象」,這並不是嚴格的意義。實際上,在具體操作層面,重新綁定被有效處理。

在以下爲例:

class Broken(list): 
    def __iadd__(self, other): 
     list.__iadd__(self, other) 

a = b = Broken() 
print 'b ==',b,' id(b) ==',id(b) 
print 'a ==',a,' id(a) ==',id(a) 
a += [123] 
print '\nb ==',b,' id(b) ==',id(b) # OK 
print 'a ==',a,' id(a) ==',id(a) # What!? 

,讓

b == [] id(b) == 18711152 
a == [] id(a) == 18711152 

b == [123] id(b) == 18711152 
a == None id(a) == 505338052 

Broken的方法__iadd__()是不規則的,因爲在它的代碼中沒有return聲明。順便說一句,這就是爲什麼coghlan將它命名爲Broken

正在發生的事情是這樣的:

  • 指令a += [123]觸發a.__iadd__([123])執行,因此是真正執行什麼的a = Broken.__iadd__(a,[123])

  • 執行是那麼list.__iadd__(a,[123])返回到Broken.__iadd__()塊該對象修改了in_place。 BUT該精確對象不被Broken.__iadd__()返回,SO這後者的功能通過返回None結束。

  • 這樣的Broken.__iadd__(a,[123])結果是None,這None被處理到a最初指向對象,以及其中b仍然指向期間分配給a

  • 反正,已被修改到位。

  • 即在已經完全沒有被rebinded標識符b結束,指向在所述存儲器中的相同位置修改的對象,並且在已經完全rebinded別處的標識符a

這段代碼沒問題:它保留了功能list.__iadd__()所執行的操作,並且它通過a的方法__iadd__()湮滅了其結果的分配。顯示這種湮滅的後果與a += [123]的結果不同,這證明這種分配確實存在。

class UnBroken(list): 
    def __iadd__(self, other): 
     return list.__iadd__(self, other) 

a = b = UnBroken() 
print 'b ==',b,' id(b) ==',id(b) 
print 'a ==',a,' id(a) ==',id(a) 
a += [123] 
print '\nb ==',b,' id(b) ==',id(b) # OK 
print 'a ==',a,' id(a) ==',id(a) # OK 

b == [] id(b) == 18711200 
a == [] id(a) == 18711200 

b == [123] id(b) == 18711200 
a == [123] id(a) == 18711200 

:此證明的事實,恢復return語句恢復正常的行爲確認。

wmwmwmwmwmwmwmwmwmwmwmwmwmwmwmwmwmwm

下面的代碼片段:

t = ([],) 
t[0] += [145] 

結果

TypeError: 'tuple' object does not support item assignment 

但是,這個錯誤是由於這樣的事實,一個元組的元素的值不能改變或一個事實,即元組拒絕分配的過程?

的第二個原因是正確的,因爲下面的代碼證明了一個元組的值可以改變:

t = ([],) 
print 't before ==',t,' id(t) ==',id(t) 
el = t[0] 
el += [608] 
print 't after ==',t,' id(t) ==',id(t) 

,給出了一個不同的值,以相同的對象(在同一地點):

t before == ([],) id(t) == 11891216 
t after == ([608],) id(t) == 11891216 

的原因是,雖然元組是不可變的,它的價值可以改變:

的immuta的價值如果 後者的值發生更改,則包含對可變對象的引用的對象可以更改;然而 容器仍然被認爲是 不可變的,因爲它包含的對象集合 無法更改。 所以,不變性不是嚴格的 相同有一個不可改變的價值, 它更加微妙。一個對象的 可變性由它的類型

http://docs.python.org/reference/datamodel.html#objects-values-and-types

確定。

這微妙允許編寫更好的指出代碼是怎麼回事下:

t = ([],) 
print 't before ==',t,' id(t) ==',id(t) 
print 't[0] ==',t[0],' id(t[0]) ==',id(t[0]) 

try: 
    t[0] += [4744] 
except TypeError: 
    print 't after ==',t,' id(t) ==',id(t) 
    print 't[0] ==',t[0],' id(t[0]) ==',id(t[0]) 
    t[0] += [8000] 

結果

t before == ([],) id(t) == 18707856 
t[0] == [] id(t[0]) == 18720608 
t after == ([4744],) id(t) == 18707856 
t[0] == [4744] id(t[0]) == 18720608 

Traceback (most recent call last): 
    File "I:\what.py", line 64, in <module> 
    t[0] += [8000] 
TypeError: 'tuple' object does not support item assignment 

行爲是使用類似於第一個片段的行爲壞了():

  • 指令t[0] += [4744]觸發t[0]「s法__iadd__()執行以下動作t[0] .__iadd__([4744])

  • t[0]是一個列表,該執行是具體t[0] = list.__iadd__(t[0] ,[4744])

  • 所以,通過t[0]引用的列表就地被第一修改

  • 和然後,試圖使分配完成

  • 此嘗試不是試圖將名稱綁定到就地修改的對象(在那裏我沒有暗示的名字);它試圖將一個項目(一個複合對象中的一個位置)綁定到一個對象,也就是說在這裏把就地修改對象的地址放在一個名爲t[0]的位置:複合對象的元素在對其他對象的事實的參考,也就是說這些其他對象

  • ,但把地址在該位置t[0]之前地址,解釋驗證的t類型允許此操作:這是我的理解看到的句子以上:«物體的可變性由其類型»確定。此時,解釋器檢測到不允許執行該操作並且產生TypeError

  • 在我的代碼中,這個錯誤被try-except攔截,這個技巧允許在除了部分看到由t[0]引用的對象確實已被修改。

因此,在Broken()的摘錄中採用了類似的方法:將對就地修改的觀察與對分配的觀察分開。

不同之處在於,這裏的分配是通過其行爲的後果來證明的,而不是其缺席的結果。

wmwmwmwmwmwmwmwm

整蠱片段,我覺得。我很難理解這些基礎機制。

+0

謝謝。這回答我的問題ncoghlan。 – 2011-04-18 17:49:00