Wird beim Entpacken von Argumenten Iteration oder Item-Geting verwendet?

Ich benutze Python 2.7.3.

Betrachten Sie eine Dummy-Klasse mit benutzerdefinierten (wenn auch schlechten) Iterationen und Verhalten beim Abrufen von Elementen:

class FooList(list):
    def __iter__(self):
        return iter(self)
    def next(self):
        return 3
    def __getitem__(self, idx):
        return 3

Machen Sie ein Beispiel und sehen Sie das seltsame Verhalten:

>>> zz = FooList([1,2,3])

>>> [x for x in zz]
# Hangs because of the self-reference in `__iter__`.

>>> zz[0]
3

>>> zz[1]
3

Aber jetzt lasst uns eine Funktion erstellen und dann das Argument weiter entpackenzz:

def add3(a, b, c):
    return a + b + c

>>> add3(*zz)
6
# I expected either 9 or for the interpreter to hang like the comprehension!

Das Entpacken von Argumenten bezieht also irgendwie die Artikeldaten vonzz aber nicht durch Iteration über das Objekt mit seinem implementierten Iterator und auch nicht durch Iteration und Aufrufen eines armen Mannes__getitem__ für so viele Gegenstände wie das Objekt hat.

Die Frage ist also: Wie funktioniert die Syntax?add3(*zz) erwerben Sie die Datenmitglieder vonzz wenn nicht nach diesen Methoden? Fehlt mir nur ein anderes allgemeines Muster, um Datenelemente von einem Typ wie diesem zu erhalten?

Mein Ziel ist es, herauszufinden, ob ich eine Klasse schreiben kann, die Iteration oder Elementabruf so implementiert, dass sie die Bedeutung der Syntax für das Entpacken des Arguments für diese Klasse ändert. Nachdem ich die beiden obigen Beispiele ausprobiert habe, frage ich mich nun, wie das Entpacken von Argumenten zu den zugrunde liegenden Daten führt und ob der Programmierer dieses Verhalten beeinflussen kann. Google hat dafür nur ein Meer von Ergebnissen zurückgegeben, die die grundlegende Verwendung des*args Syntax.

Ich habe keinen Anwendungsfall, um dies zu tun, und ich behaupte nicht, dass es eine gute Idee ist. Ich möchte nur aus Neugier sehen, wie es geht.

Hinzugefügt

Da die eingebauten Typen speziell behandelt werden, hier ein Beispiel mitobject Hier pflege ich nur ein Listenobjekt und implementiere mein eigenes Get- und Set-Verhalten, um eine Liste zu emulieren.

class FooList(object):
    def __init__(self, lst):
        self.lst = lst
    def __iter__(self): raise ValueError
    def next(self): return 3
    def __getitem__(self, idx): return self.lst.__getitem__(idx)
    def __setitem__(self, idx, itm): self.lst.__setitem__(idx, itm)

In diesem Fall,

In [234]: zz = FooList([1,2,3])

In [235]: [x for x in zz]
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-235-ad3bb7659c84> in <module>()
----> 1 [x for x in zz]

<ipython-input-233-dc9284300db1> in __iter__(self)
      2     def __init__(self, lst):
      3         self.lst = lst
----> 4     def __iter__(self): raise ValueError
      5     def next(self): return 3
      6     def __getitem__(self, idx): return self.lst.__getitem__(idx)

ValueError:

In [236]: add_3(*zz)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-236-f9bbfdc2de5c> in <module>()
----> 1 add_3(*zz)

<ipython-input-233-dc9284300db1> in __iter__(self)
      2     def __init__(self, lst):
      3         self.lst = lst
----> 4     def __iter__(self): raise ValueError
      5     def next(self): return 3
      6     def __getitem__(self, idx): return self.lst.__getitem__(idx)

ValueError:

Aber wenn ich dafür sorge, dass die Iteration stoppt und immer 3 zurückgibt, kann ich das bekommen, womit ich im ersten Fall herumgespielt habe:

class FooList(object):
    def __init__(self, lst):
        self.lst = lst
        self.iter_loc = -1
    def __iter__(self): return self
    def next(self): 
        if self.iter_loc < len(self.lst)-1:
            self.iter_loc += 1
            return 3
        else:
            self.iter_loc = -1
            raise StopIteration
    def __getitem__(self, idx): return self.lst.__getitem__(idx)
    def __setitem__(self, idx, itm): self.lst.__setitem__(idx, itm)

Dann sehe ich das, was ich ursprünglich erwartet hatte:

In [247]: zz = FooList([1,2,3])

In [248]: ix = iter(zz)

In [249]: ix.next()
Out[249]: 3

In [250]: ix.next()
Out[250]: 3

In [251]: ix.next()
Out[251]: 3

In [252]: ix.next()
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-252-29d4ae900c28> in <module>()
----> 1 ix.next()

<ipython-input-246-5479fdc9217b> in next(self)
     10         else:
     11             self.iter_loc = -1
---> 12             raise StopIteration
     13     def __getitem__(self, idx): return self.lst.__getitem__(idx)
     14     def __setitem__(self, idx, itm): self.lst.__setitem__(idx, itm)

StopIteration:

In [253]: ix = iter(zz)

In [254]: ix.next()
Out[254]: 3

In [255]: ix.next()
Out[255]: 3

In [256]: ix.next()
Out[256]: 3

In [257]: ix.next()
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-257-29d4ae900c28> in <module>()
----> 1 ix.next()

<ipython-input-246-5479fdc9217b> in next(self)
     10         else:
     11             self.iter_loc = -1
---> 12             raise StopIteration
     13     def __getitem__(self, idx): return self.lst.__getitem__(idx)
     14     def __setitem__(self, idx, itm): self.lst.__setitem__(idx, itm)

StopIteration:

In [258]: add_3(*zz)
Out[258]: 9

In [259]: zz[0]
Out[259]: 1

In [260]: zz[1]
Out[260]: 2

In [261]: zz[2]
Out[261]: 3

In [262]: [x for x in zz]
Out[262]: [3, 3, 3]

Zusammenfassung

Die Syntax*args stützt sich nur auf Iteration. Bei integrierten Typen geschieht dies auf eine Weise, die in Klassen, die vom integrierten Typ erben, nicht direkt überschrieben werden kann.

Diese beiden sind funktional gleichwertig:

foo(*[x for x in args])

foo(*args)

Diese sind auch für endliche Datenstrukturen nicht äquivalent.

foo(*args)

foo(*[args[i] for i in range(len(args))])

Antworten auf die Frage(1)

Ihre Antwort auf die Frage