2010-07-01 12 views
42

Ich habe eine Klasse wie:__getattr__ für statisches/Klassenvariablen in Python

class MyClass: 
    Foo = 1 
    Bar = 2 

Wenn MyClass.Foo oder MyClass.Bar aufgerufen wird, ich brauche eine benutzerdefinierte Methode aufgerufen werden, bevor der Wert zurückgegeben wird. Ist es in Python möglich? Ich weiß, dass es möglich ist, wenn ich eine Instanz der Klasse erstelle und ich meine eigene __getattr__ Methode definieren kann. Aber mein scnenario beinhaltet die Verwendung dieser Klasse als solche, ohne irgendeine Instanz davon zu erzeugen.

Auch ich brauche eine benutzerdefinierte __str__ Methode aufgerufen werden, wenn str(MyClass.Foo) aufgerufen wird. Bietet Python eine solche Option?

Antwort

48

__getattr__() und __str__() für ein Objekt sind auf seiner Klasse gefunden, also wenn Sie diese Dinge für eine Klasse anpassen möchten, benötigen Sie die Klasse einer Klasse. Eine Metaklasse.

class FooType(type): 
    def _foo_func(cls): 
     return 'foo!' 

    def _bar_func(cls): 
     return 'bar!' 

    def __getattr__(cls, key): 
     if key == 'Foo': 
      return cls._foo_func() 
     elif key == 'Bar': 
      return cls._bar_func() 
     raise AttributeError(key) 

    def __str__(cls): 
     return 'custom str for %s' % (cls.__name__,) 

class MyClass: 
    __metaclass__ = FooType 


print MyClass.Foo 
print MyClass.Bar 
print str(MyClass) 

Druck:

foo! 
bar! 
custom str for MyClass 

Und nein, ein Objekt nicht eine Anforderung für eine stringifying eines seiner Attribute abfangen. Das für das Attribut zurückgegebene Objekt muss sein eigenes __str__() Verhalten definieren.

+0

Dank Ignacio und Matt. Ich denke jetzt verstehe ich, wie Klasse (nicht Instanz) Schöpfung funktioniert und auch ich dachte, dass das erforderliche __str__ Verhalten schwierig zu erreichen war. Ich denke, Metaklassen sollten für meine Anforderung gut genug sein. :) – Tuxdude

+0

Wenn Sie immer 'AttributeError' bekommen, sehen Sie sich die andere Antwort an, die die Syntax für python3 hat (dieses Beispiel scheint python2 zu sein) – Chris

8

Für die erste müssen Sie eine Metaklasse erstellen und __getattr__() auf diesem definieren.

class MyMetaclass(type): 
    def __getattr__(self, name): 
    return '%s result' % name 

class MyClass(object): 
    __metaclass__ = MyMetaclass 

print MyClass.Foo 

Für die zweite, nein. Der Aufruf von str(MyClass.Foo) ruft MyClass.Foo.__str__() auf, daher müssen Sie einen geeigneten Typ für MyClass.Foo zurückgeben.

+0

Getestet dieses Beispiel und es funktioniert nur für mich in Python 2.x.Nachdem ich den Druck auf eine Funktion geändert habe, gibt Python 3.2 mir ein "AttributeError: type-Objekt 'MyClass' hat kein Attribut 'Foo'" -Fehler. Irgendwelche Vorschläge? – CyberFonic

+1

@ CyberED: http://docs.python.org/py3k/reference/datamodel.html#metaclasses: "Wenn die Klassendefinition gelesen wird, wenn ein aufrufbares' metaclass' Schlüsselwort-Argument nach den Basen in der Klassendefinition übergeben wird, Die aufrufbare Angabe wird anstelle von 'type()' aufgerufen. " –

+3

Konnte nicht warten .... nach ein bisschen Googeln und Testen: Das Beispiel ist für Python 2.x. In Python 3.x benötigen Sie: Klasse MyClass (metaclass = MyMetaclass): pass – CyberFonic

5

überrascht niemand zeigte dieses heraus:

class FooType(type): 
    @property 
    def Foo(cls): 
     return "foo!" 

    @property 
    def Bar(cls): 
     return "bar!" 

class MyClass(metaclass=FooType): 
    pass 

Works:

>>> MyClass.Foo 
'foo!' 
>>> MyClass.Bar 
'bar!' 

(für Python 2.x, ändern Definition von MyClass zu:

class MyClass(object): 
    __metaclass__ = FooType 

)

Was die anderen Antworten über str sagen, gilt für diese Lösung: Sie muss auf dem tatsächlich zurückgegebenen Typ implementiert werden.

+2

Während ich normalerweise versuche, "Danke" zu veröffentlichen, muss ich dringend "Danke" im Moment posten: Danke! – Chris

11

(ich weiß, das ist eine alte Frage, aber da alle anderen Antworten verwenden, um eine Metaklasse ...)

Sie können die folgenden einfachen classproperty Descriptor verwenden:

class classproperty(object): 
    """ @[email protected] """ 
    def __init__(self, f): 
     self.f = classmethod(f) 
    def __get__(self, *a): 
     return self.f.__get__(*a)() 

verwenden Sie es wie:

class MyClass(object): 

    @classproperty 
    def Foo(cls): 
     do_something() 
     return 1 

    @classproperty 
    def Bar(cls): 
     do_something_else() 
     return 2 
+0

Dies hilft nur, wenn Sie die Namen Ihrer Eigenschaften im Voraus kennen. Wenn Sie sie aus einer Datei lesen und dynamisch reagieren müssen, funktioniert das nicht. – Chris

Verwandte Themen