105 Stimmen

Die Erstellung neuer Attribute außerhalb von __init__ verhindern

Ich möchte in der Lage sein, eine Klasse (in Python) zu erstellen, die nach der Initialisierung mit __init__ akzeptiert keine neuen Attribute, sondern nur Änderungen an bestehenden Attributen. Es gibt mehrere Hack-ish Möglichkeiten, die ich sehen kann, um dies zu tun, zum Beispiel mit einer __setattr__ Methode wie z.B.

def __setattr__(self, attribute, value):
    if not attribute in self.__dict__:
        print "Cannot set %s" % attribute
    else:
        self.__dict__[attribute] = value

und dann die Bearbeitung __dict__ direkt im Inneren __init__ aber ich habe mich gefragt, ob es einen "richtigen" Weg gibt, dies zu tun?

1voto

eddygeek Punkte 3725

In keiner der Antworten wird die Auswirkung auf die Leistung erwähnt, die das Überschreiben von __setattr__ was bei der Erstellung vieler kleiner Objekte ein Problem darstellen kann. (Und __slots__ wäre die performante Lösung, schränkt aber Pickle/Vererbung ein).

Also habe ich mir diese Variante ausgedacht, die unser langsameres settatr nach init installiert:

class FrozenClass:

    def freeze(self):
        def frozen_setattr(self, key, value):
            if not hasattr(self, key):
                raise TypeError("Cannot set {}: {} is a frozen class".format(key, self))
            object.__setattr__(self, key, value)
        self.__setattr__ = frozen_setattr

class Foo(FrozenClass): ...

Wenn Sie nicht anrufen möchten freeze am Ende von __init__ wenn die Vererbung ein Problem darstellt oder wenn Sie es nicht in vars() kann es auch angepasst werden: Hier ist zum Beispiel eine Dekorator-Version, die auf dem pystrict Antwort:

import functools
def strict(cls):
    cls._x_setter = getattr(cls, "__setattr__", object.__setattr__)
    cls._x_init = cls.__init__
    @functools.wraps(cls.__init__)
    def wrapper(self, *args, **kwargs):
        cls._x_init(self, *args, **kwargs)
        def frozen_setattr(self, key, value):
            if not hasattr(self, key):
                raise TypeError("Class %s is frozen. Cannot set '%s'." % (cls.__name__, key))
            cls._x_setter(self, key, value)
        cls.__setattr__ = frozen_setattr
    cls.__init__ = wrapper
    return cls

@strict
class Foo: ...

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X