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?

6voto

Eran Friedman Punkte 278

Mir gefällt die Lösung, die einen Dekorator verwendet, sehr gut, weil es einfach ist, ihn für viele Klassen in einem Projekt zu verwenden, mit minimalen Ergänzungen für jede Klasse. Aber es funktioniert nicht gut mit Vererbung. Hier ist also meine Version: Sie überschreibt nur die Funktion __setattr__ - wenn das Attribut nicht existiert und die aufrufende Funktion nicht __init__ ist, gibt sie eine Fehlermeldung aus.

import inspect                                                                                                                             

def froze_it(cls):                                                                                                                      

    def frozensetattr(self, key, value):                                                                                                   
        if not hasattr(self, key) and inspect.stack()[1][3] != "__init__":                                                                 
            print("Class {} is frozen. Cannot set {} = {}"                                                                                 
                  .format(cls.__name__, key, value))                                                                                       
        else:                                                                                                                              
            self.__dict__[key] = value                                                                                                     

    cls.__setattr__ = frozensetattr                                                                                                        
    return cls                                                                                                                             

@froze_it                                                                                                                                  
class A:                                                                                                                                   
    def __init__(self):                                                                                                                    
        self._a = 0                                                                                                                        

a = A()                                                                                                                                    
a._a = 1                                                                                                                                   
a._b = 2 # error

4voto

Clementerf Punkte 49

Wie wäre es damit:

class A():
    __allowed_attr=('_x', '_y')

    def __init__(self,x=0,y=0):
        self._x=x
        self._y=y

    def __setattr__(self,attribute,value):
        if not attribute in self.__class__.__allowed_attr:
            raise AttributeError
        else:
            super().__setattr__(attribute,value)

2voto

gswilcox01 Punkte 51

Hier ist der Ansatz, den ich mir ausgedacht habe, der kein _frozen-Attribut oder eine Methode zum Einfrieren() in init benötigt.

Während init Ich füge einfach alle Klassenattribute zur Instanz hinzu.

Das gefällt mir, denn es gibt kein _frozen, freeze(), und _frozen taucht auch nicht in der vars(instance)-Ausgabe auf.

class MetaModel(type):
    def __setattr__(self, name, value):
        raise AttributeError("Model classes do not accept arbitrary attributes")

class Model(object):
    __metaclass__ = MetaModel

    # init will take all CLASS attributes, and add them as SELF/INSTANCE attributes
    def __init__(self):
        for k, v in self.__class__.__dict__.iteritems():
            if not k.startswith("_"):
                self.__setattr__(k, v)

    # setattr, won't allow any attributes to be set on the SELF/INSTANCE that don't already exist
    def __setattr__(self, name, value):
        if not hasattr(self, name):
            raise AttributeError("Model instances do not accept arbitrary attributes")
        else:
            object.__setattr__(self, name, value)

# Example using            
class Dog(Model):
    name = ''
    kind = 'canine'

d, e = Dog(), Dog()
print vars(d)
print vars(e)
e.junk = 'stuff' # fails

1voto

Arthur Bauville Punkte 161

Ich mag den "Frozen" von Jochen Ritzel. Das Unangenehme ist, dass die Die Variable isfrozen erscheint dann beim Drucken einer Class.__dict Ich habe dieses Problem auf diese Weise umgangen, indem ich eine Liste mit autorisierten Attributen erstellt habe (ähnlich wie bei Steckplätze ):

class Frozen(object):
    __List = []
    def __setattr__(self, key, value):
        setIsOK = False
        for item in self.__List:
            if key == item:
                setIsOK = True

        if setIsOK == True:
            object.__setattr__(self, key, value)
        else:
            raise TypeError( "%r has no attributes %r" % (self, key) )

class Test(Frozen):
    _Frozen__List = ["attr1","attr2"]
    def __init__(self):
        self.attr1   =  1
        self.attr2   =  1

1voto

Endle_Zhenbo Punkte 538

El FrozenClass von Jochen Ritzel ist cool, aber der Aufruf _frozen() wenn es nicht so cool ist, eine Klasse jedes Mal zu initialisieren (und man das Risiko eingehen muss, es zu vergessen). Ich habe eine __init_slots__ Funktion:

class FrozenClass(object):
    __isfrozen = False
    def _freeze(self):
        self.__isfrozen = True
    def __init_slots__(self, slots):
        for key in slots:
            object.__setattr__(self, key, None)
        self._freeze()
    def __setattr__(self, key, value):
        if self.__isfrozen and not hasattr(self, key):
            raise TypeError( "%r is a frozen class" % self )
        object.__setattr__(self, key, value)
class Test(FrozenClass):
    def __init__(self):
        self.__init_slots__(["x", "y"])
        self.x = 42#
        self.y = 2**3

a,b = Test(), Test()
a.x = 10
b.z = 10 # fails

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