2016-09-24 6 views
8

Ich las diesen interessanten Beitrag https://asmeurer.github.io/blog/posts/tuples/Tupel-Zuweisung in Python, Ist das ein Fehler in Python?

Am Fußnote Autor präsentieren dieses Beispiel

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

Obwohl Python eine Ausnahme ausgelöst hat, aber es hat

>>> t 
(1, 2, [3, 4, 5, 6]) 

Nicht sicher das Tupel zu ändern, was ist geht es hier weiter, ist das ein Fehler?

Das Verhalten ist das gleiche in 2.7.10 und 3.5.1

+0

Beachten Sie, dass 't [2] = t [2] + [1,2]' erhöht, aber das Tupel nicht ändert –

Antwort

3

So ist das Verhalten von += ein bisschen komisch ist. Für unveränderliche Objekte wie ganze Zahlen hat es ein neues Objekt mit dem gleichen Namen zuweisen:

a = 4 
a += 3 

für änderbare Typen, z.B. Listen, das Objekt wird an Ort und Stelle geändert, gibt aber auch das gleiche Objekt zurück, das demselben Namen zugewiesen werden soll. Der erste Schritt funktioniert mit Ihrem Tupel, aber nicht der zweite.

Aus diesem Grund wird die Ausnahme ausgelöst, nachdem die Liste erweitert wurde.

3

Dies ist, weil der +=-Operator (der __iadd__ ist, oder direkt intern hinzugefügt) tatsächlich etwas zurückgeben, nachdem die Zuordnung passiert ist. In einem list dies übersetzt in einen extend Aufruf (oder so ähnlich) und damit die neuen Elemente bereits eingegangen, bevor der Verweis auf die Liste zur Zuordnung an t[2] zurückgegeben wurde, die dann die Ausnahme auslösen. Jetzt überprüfen Sie den Wert, den Sie sehen können, dass es hinzugefügt wurde. Im Folgenden ist der minimale Code zu demonstrieren dies:

>>> class AddIDemo(object): 
...  def __init__(self, items): 
...   self.items = list(items) 
...  def __iadd__(self, other): 
...   print('extending other %r' % other) 
...   self.items.extend(other.items) 
...   print('returning self to complete +=') 
...   return self 
...  def __repr__(self): 
...   return self.items.__repr__() 
... 
>>> demo = AddIDemo([1, 2]) 
>>> demo += AddIDemo([3, 4]) 
extending other [3, 4] 
returning self to complete += 
>>> demo 
[1, 2, 3, 4] 
>>> t = 1, 2, demo 
>>> t 
(1, 2, [1, 2, 3, 4]) 
>>> 
>>> t[2] += AddIDemo([5, 6]) 
extending other [5, 6] 
returning self to complete += 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: 'tuple' object does not support item assignment 
>>> t 
(1, 2, [1, 2, 3, 4, 5, 6]) 
>>> 

Bitte beachten, dass ich noch ein paar print-Anweisungen hinzugefügt, um anzuzeigen, dass die Funktion aufgerufen wird und wie die Operation passiert ist, wie es in einer Standard-list Manipulation über += tun würde oder __iadd__ .

0

Dies ist kein Fehler, aber der Status, in dem Sie verbleiben, wenn die Ausnahme ausgelöst wird, kann verwirrend sein.

Ein Tupel von Entwurf darf nicht geändert werden, aber das gilt nur für das Tupel und nicht für seine Elemente. Wenn es ein veränderbares Element enthält, kann dieses Element geändert werden. In Ihrem Fall ist die Liste ein solcher Punkt, der geändert werden kann.

Der Operator += bildet nun mehr oder weniger eine + ab und weist sie dann der ursprünglichen Variablen zu. Die + Operation ist möglich, Sie können zwei Listen hinzufügen. Aus Gründen der Optimierung wird der + wie ein extend() implementiert und aktualisiert. Aber die Zuweisung schlägt fehl, da das Tupel-Objekt nicht geändert werden kann.

Es gibt grundsätzlich zwei Lektionen:

  1. Python Betreiber von Typ implementiert werden kann und vielleicht verhalten sich anders als die trivialen Erwartungen.
  2. Wenn eine Ausnahme ausgelöst wird, während eine Funktion aufgerufen und nicht richtig behandelt wurde, hat sie möglicherweise etwas getan, was nicht bereinigt wurde.