So erstellen Sie eine neue Instanz aus einem Klassenobjekt in Python

75

Ich muss dynamisch eine Instanz einer Klasse in Python erstellen. Grundsätzlich verwende ich das Modul load_module und inspect, um die Klasse in ein Klassenobjekt zu importieren und zu laden, aber ich kann nicht herausfinden, wie eine Instanz dieses Klassenobjekts erstellt wird.

Bitte helfen Sie!

ycseattle
quelle
8
Was? instance = Class()...
Jochen Ritzel
1
Ich nehme an, Sie möchten dynamisch eine neue Klasse erstellen. Kein Objekt einer bestimmten Klasse.
Jsbueno
Neue Instanz bedeutet ein neues Objekt mit einem eigenen Speicherplatz. Die meisten Sprachen verwenden ein 'neues' Schlüsselwort, sodass explizit angegeben wird, dass das Objekt neu ist und die Eigenschaften der neuen Instanz (des Objekts) nicht mit einer anderen Instanz referenziert / geteilt werden.
Bill Rosmus
Da Python kein "neues" Schlüsselwort verwendet, habe ich meine Antwort unten mit einer vollständigen Erläuterung des Klasseninstanzierungsmechanismus aktualisiert.
Jsbueno

Antworten:

122

Ich fand die Antwort auf die Frage heraus, die mich auf diese Seite gebracht hatte. Da niemand die Antwort auf meine Frage vorgeschlagen hat, dachte ich, ich würde sie posten.

class k:
  pass

a = k()
k2 = a.__class__
a2 = k2()

Zu diesem Zeitpunkt sind a und a2 beide Instanzen derselben Klasse (Klasse k).

xvf17
quelle
2
Dies scheint die richtige Antwort zu sein. Funktioniert und es ist einfach.
Miguel Vazq
1
Beachten Sie, dass Sie Parameter auch an den Konstruktor übergeben können, wenn der Init sie akzeptiert a3 = k2(7)
gerardw
Was ist der Unterschied zwischen k2 = a.__class__und k2 = k?
Joshua Coady
1
k2Ist der Objekttyp, in diesem Fall de Typ von a( a.__class), k2()erstellen Sie eine neue Instanz eines Objekts dieses Typs unabhängig von a. k2 = kwürde einfach einen neuen Verweis auf dieselbe Instanz erstellen.
Pixelou
2
Sie können das classObjekt auch mit k2 = type(a)abrufen und dann das Klassenobjekt aufrufen a2 = k2(). Es ist im Grunde das gleiche, aber Sie müssen Ihren Code nicht mit diesen hässlichen doppelten Unterstrichen verunreinigen ... könnte auch portabler sein
Robert
18

Rufen Sie einfach den eingebauten "Typ" mit drei Parametern auf:

ClassName = type("ClassName", (Base1, Base2,...), classdictionary)

Update wie im Kommentar unten angegeben. Dies ist überhaupt nicht die Antwort auf diese Frage. Ich werde es nicht löschen, da es Hinweise gibt, die einige Leute hier bekommen, die versuchen, dynamisch Klassen zu erstellen - was die obige Zeile tut.

Um ein Objekt einer Klasse zu erstellen, hat man auch eine Referenz, wie in der akzeptierten Antwort angegeben, man muss nur die Klasse aufrufen:

instance = ClassObject()

Der Mechanismus für die Instanziierung ist also:

Python verwendet nicht das newSchlüsselwort, das einige Sprachen verwenden. Stattdessen erklärt das Datenmodell den Mechanismus, der zum Erstellen einer Instanz einer Klasse verwendet wird, wenn diese mit derselben Syntax wie alle anderen aufrufbaren aufgerufen wird:

Die __call__Methode der Klasse wird aufgerufen (im Fall einer Klasse ist ihre Klasse die "Metaklasse" - normalerweise die integrierte type). Das normale Verhalten dieses Aufrufs besteht darin, die (pseudo) statische __new__Methode für die zu instanziierende Klasse aufzurufen , gefolgt von ihrer __init__. Das __new__Verfahren ist verantwortlich für die Zuteilung von Speicher und so, und in der Regel wird durch die durchgeführt __new__von objectdem die Klassenhierarchie Wurzel.

So ruft ClassObject()aufruft ClassObject.__class__.call()(die in der Regel wird type.__call__) diese __call__Methode classobject selbst als ersten Parameter erhalten - eine reine Python - Implementierung würde so aussehen: (die CPython Version ist natürlich in C durchgeführt und mit vielen zusätzlichen Code für cornercases und Optimierungen)

class type:
    ...
    def __call__(cls, *args, **kw):
          constructor = getattr(cls, "__new__")
          instance = constructor(cls) if constructor is object.__new__ else constructor(cls, *args, **kw)
          instance.__init__(cls, *args, **kw)
          return instance

(Ich erinnere mich nicht, dass ich in den Dokumenten die genaue Rechtfertigung (oder den Mechanismus) für die Unterdrückung zusätzlicher Parameter an die Wurzel __new__und die Weitergabe an andere Klassen gesehen habe - aber es ist das, was "im wirklichen Leben" passiert - wenn object.__new__es mit zusätzlichen Parametern aufgerufen wird löst einen Typfehler aus - jede benutzerdefinierte Implementierung von a __new__erhält jedoch normalerweise die zusätzlichen Parameter.

jsbueno
quelle
8
Wollte das OP keine Instanz erstellen, keine Klasse?
Keith
3

Auf diese Weise können Sie dynamisch eine Childin Ihrem Code benannte Klasse erstellen, sofern diese Parentbereits vorhanden ist. Auch wenn Sie keine explizite ParentKlasse haben, können Sie object...

Der folgende Code definiert ihn __init__()und ordnet ihn dann der Klasse zu.

>>> child_name = "Child"
>>> child_parents = (Parent,)
>>> child body = """
def __init__(self, arg1):
    # Initialization for the Child class
    self.foo = do_something(arg1)
"""
>>> child_dict = {}
>>> exec(child_body, globals(), child_dict)
>>> childobj = type(child_name, child_parents, child_dict)
>>> childobj.__name__
'Child'
>>> childobj.__bases__
(<type 'object'>,)
>>> # Instantiating the new Child object...
>>> childinst = childobj()
>>> childinst
<__main__.Child object at 0x1c91710>
>>>
Mike Pennington
quelle
2

Wenn Sie ein Modul mit einer Klasse haben, die Sie importieren möchten, können Sie dies folgendermaßen tun.

module = __import__(filename)
instance = module.MyClass()

Wenn Sie nicht wissen, wie die Klasse heißt, können Sie die in einem Modul verfügbaren Klassen durchlaufen.

import inspect
module = __import__(filename)
for c in module.__dict__.values():
    if inspect.isclass(c):
        # You may need do some additional checking to ensure 
        # it's the class you want
        instance = c()
Brawner
quelle