Ich möchte Folgendes tun:

class Foo(object):
    def __init__(self):
        self.member = 10
        pass

def factory(foo):
    foo = Foo()

aTestFoo = None
factory(aTestFoo)

print aTestFoo.member

Es stürzt jedoch mit AttributeError: 'NoneType' object has no attribute 'member' ab: Das Objekt aTestFoo wurde im Aufruf der Funktion factory nicht geändert.

Was ist die pythonische Art, das auszuführen? Ist es ein zu vermeidendes Muster? Wenn es sich um einen aktuellen Fehler handelt, wie heißt er?

In C ++ hätte ich im Funktionsprototyp einen Verweis auf den Zeiger hinzugefügt, der in der Factory erstellt werden soll ... aber vielleicht ist dies nicht die Art von Dingen, über die ich in Python nachdenken sollte.

In C # gibt es das Schlüsselwort ref, mit dem die Referenz selbst geändert werden kann, ganz in der Nähe der C ++ - Methode. Ich weiß es nicht in Java ... und ich wundere mich in Python.

4
Stephane Rolland 9 Okt. 2012 im 14:13

3 Antworten

Ihre Factory-Methode gibt nichts zurück - und standardmäßig hat sie den Rückgabewert None. Sie weisen aTestFoo None zu, weisen es jedoch nie wieder zu - daher kommt Ihr tatsächlicher Fehler.

Behebung dieser Probleme:

class Foo(object):
    def __init__(self):
        self.member = 10
        pass

def factory(obj):
    return obj()

aTestFoo = factory(Foo)
print aTestFoo.member

Dies sollte das tun, wonach Sie suchen, obwohl solche Muster in Python nicht so typisch sind (dh Factory-Methoden).

1
Burhan Khalid 9 Okt. 2012 im 10:22

Der Abschnitt Zuweisungsanweisungen in den Python-Dokumenten könnte interessant sein.

Die Anweisung = in Python verhält sich je nach Situation unterschiedlich. In dem von Ihnen präsentierten Fall wird das neue Objekt jedoch nur an eine neue lokale Variable gebunden:

def factory(foo):
    # This makes a new instance of Foo,
    # and binds it to a local variable `foo`,
    foo = Foo()

# This binds `None` to a top-level variable `aTestFoo`
aTestFoo = None

# Call `factory` with first argument of `None`
factory(aTestFoo)

print aTestFoo.member

Obwohl es möglicherweise eher verwirrend als hilfreich sein kann, kann das dis Modul angezeigt werden Sie die Bytecode-Darstellung einer Funktion, die zeigen kann, wie Python intern funktioniert. Hier ist die Demontage von `Fabrik:

>>> dis.dis(factory)
  4           0 LOAD_GLOBAL              0 (Foo)
              3 CALL_FUNCTION            0
              6 STORE_FAST               0 (foo)
              9 LOAD_CONST               0 (None)
             12 RETURN_VALUE        

Das heißt, Python lädt die globale Foo - Klasse nach Name (0) und ruft sie auf (3, Instanziierung und Aufruf sind sehr ähnlich). Anschließend speichert sie das Ergebnis in einer lokalen Variablen (6, siehe STORE_FAST). Dann lädt es den Standardrückgabewert None (9) und gibt ihn zurück (12)

Was ist die pythonische Art, das auszuführen? Ist es ein zu vermeidendes Muster? Wenn es sich um einen aktuellen Fehler handelt, wie heißt er?

Werksfunktionen sind in Python selten erforderlich. In den gelegentlichen Fällen, in denen sie erforderlich sind, geben Sie die neue Instanz einfach aus Ihrer Factory zurück (anstatt zu versuchen, sie einer übergebenen Variablen zuzuweisen):

class Foo(object):
    def __init__(self):
        self.member = 10
        pass

def factory():
    return Foo()

aTestFoo = factory()

print aTestFoo.member
3
dbr 9 Okt. 2012 im 10:36

Sie machen hier einen Fehler, weil in Python

"We call the argument passing technique _call by sharing_,
because the argument objects are shared between the
caller and the called routine.  This technique does not
correspond to most traditional argument passing techniques
(it is similar to argument passing in LISP).  In particular it
is not call by value because mutations of arguments per-
formed by the called routine will be visible to the caller.
And it is not call by reference because access is not given
to the variables of the caller, but merely to certain objects."

In Python sind die Variablen in der formalen Argumentliste an die gebunden tatsächliche Argumentobjekte. Die Objekte werden von Anrufern gemeinsam genutzt und Angerufene; Es gibt keine "frischen Standorte" oder zusätzlichen "Geschäfte".

(Aus diesem Grund nannten die CLU-Leute diesen Mechanismus "Call-by-Sharing".)

Übrigens werden Python-Funktionen auch nicht in einer erweiterten Umgebung ausgeführt. Funktionskörper haben nur sehr eingeschränkten Zugang zur Umgebung.

10
Rahul Gautam 29 Apr. 2013 im 12:49