2010-03-17 7 views
11

Ich möchte Klassenattribute dynamisch zu einer Superklasse hinzufügen. Außerdem möchte ich Klassen erstellen, die dynamisch von dieser Oberklasse erben, und der Name dieser Unterklassen sollte von Benutzereingaben abhängen.Python: Klassenfabrik verwendet Benutzereingaben als Klassennamen

Es gibt eine Oberklasse "Unit", zu der ich zur Laufzeit Attribute hinzufügen kann. Das funktioniert schon.

def add_attr (cls, name, value): 
    setattr(cls, name, value) 

class Unit(object): 
    pass 

class Archer(Unit): 
    pass 

myArcher = Archer() 
add_attr(Unit, 'strength', 5) 
print "Strenght ofmyarcher: " + str(myArcher.strength) 
Unit.strength = 2 
print "Strenght ofmyarcher: " + str(myArcher.strength) 

Dies führt zu der gewünschten Ausgabe:
Strenght ofmyarcher: 5
Strenght ofmyarcher: 2

Aber jetzt will ich nicht die Unterklasse Archer vordefinieren, aber ich würde lieber lassen die Benutzer entscheiden, wie diese Unterklasse aufgerufen wird. Ich habe etwas wie dieses versucht:

aber kein Glück. Ich glaube, ich habe nicht wirklich verstanden, was neue hier tut. Was ich als Ergebnis hier will, ist

class Soldier(Unit): 
    pass 

vom Werk geschaffen. Und wenn ich die Fabrik mit dem Argument "Ritter" anrufe, möchte ich, dass ein Klassenritter, eine Unterklasse der Einheit, erstellt wird.

Irgendwelche Ideen? Vielen Dank im Voraus!
Bye
-Sano

Antwort

14

Um eine Klasse aus einem Namen zu erstellen, verwenden Sie die Anweisung class und weisen Sie den Namen zu. Beobachten:

def meta(name): 
    class cls(Unit): 
     pass 

    cls.__name__ = name 
    return cls 

Jetzt nehme ich an, dass ich mich erklären sollte, und so weiter. Wenn Sie eine Klasse mit der Klassenanweisung erstellen, geschieht dies dynamisch. Sie entspricht dem Aufruf type().

Zum Beispiel die folgenden zwei Schnipsel das gleiche tun:

class X(object): pass 
X = type("X", (object,), {}) 

Der Name eines class-- das erste Argument type-- wird __name__ zugewiesen, und das ist im Grunde das Ende, dass (Die einzige Zeit, __name__ ist selbst verwendet wird wahrscheinlich in der Standardeinstellung __repr__() Implementierung). Um eine Klasse mit einem dynamischen Namen zu erstellen, können Sie den Typ tatsächlich wie folgt aufrufen oder den Klassennamen einfach ändern. Die class Syntax existiert aus einem Grund, obwohl - es ist bequem, und es ist einfach hinzuzufügen und Dinge später zu ändern.Wenn Sie beispielsweise Methoden hinzufügen wollten, wäre es

class X(object): 
    def foo(self): print "foo" 

def foo(self): print "foo" 
X = type("X", (object,), {'foo':foo}) 

und so weiter. Also würde ich Ihnen raten, die Klassenaussage zu verwenden - wenn Sie gewusst hätten, dass Sie das von Anfang an tun könnten, hätten Sie das wahrscheinlich getan. Der Umgang mit type und so weiter ist ein Durcheinander.

(Sie sollten nicht durch die Art und Weise, rufen type.__new__() von Hand, nur type())

+0

Danke Devin! Sowohl Ihre als auch Felix 'Lösung scheint einen Nebeneffekt zu haben, den ich nicht berücksichtigt habe: Die neue Klasse ist ein Objekt und muss an etwas angehängt werden - gespeichert in einer Variablen oder angehängt an eine Liste oder etwas. Ich kann nicht einfach die Knight-Klasse erstellen und dann den Konstruktor mit myKnight = Knight() aufrufen. Eher muss ich etwas tun: unitlist.append (meta ("Knight")) myKnight = unitlist [0]() Gibt es einen Weg um dies zu erreichen? Um es klar zu sagen: Speichern Sie die Knight-Klasse an der gleichen "allgemeinen Position" die Unit-Klasse wird gespeichert, wenn die Definition im Code erreicht ist? – Sano98

+0

@ Sano98: Ich bin mir nicht sicher, was du meinst. Du kannst 'Knight = meta (" Knight ")' machen und dann 'myKnight = Knight()'. Was genau willst du? –

+0

Danke, genau das möchte ich machen! Speichern Sie es einfach unter Knight ... Und dann ist es im globalen Namespace und ich kann die Klasse verwenden, als wäre sie fest codiert. Grosses Dankeschön. Ich dachte nur nicht, dass ich Knight() anrufen könnte, wenn ich die Klasse als Ritter rette, aber sicher, warum nicht? – Sano98

7

Werfen Sie einen Blick auf die type() eingebaute Funktion.

knight_class = type('Knight', (Unit,), {}) 
  • Erster Parameter: Name der neuen Klasse
  • Zweiter Parameter: Tuple von übergeordneten Klassen
  • Dritter Parameter: Wörterbuch der Klassenattribute.

Aber in Ihrem Fall, wenn die Unterklassen vielleicht nicht ein anderes Verhalten implementieren, ist die Unit Klasse ein name Attribut geben ausreichend.

+0

Danke, Felix, was Sie funktioniert auch vorgeschlagen! – Sano98

+0

Ich fand deine Antwort sehr hilfreich. Danke, dass du es hinzugefügt hast, obwohl die anderen schon da sind! – Profane

Verwandte Themen