Python schreibgeschützte Eigenschaft

92

Ich weiß nicht, wann das Attribut privat sein soll und ob ich die Eigenschaft verwenden soll.

Ich habe kürzlich gelesen, dass Setter und Getter nicht pythonisch sind und ich sollte Property Decorator verwenden. Es ist in Ordnung.

Aber was ist, wenn ich ein Attribut habe, das nicht von außerhalb der Klasse festgelegt werden darf, sondern gelesen werden kann (schreibgeschütztes Attribut)? Sollte dieses Attribut privat sein, und mit privat meine ich so einen Unterstrich self._x? Wenn ja, wie kann ich es dann lesen, ohne Getter zu verwenden? Die einzige Methode, die ich derzeit kenne, ist das Schreiben

@property
def x(self):
    return self._x

Auf diese Weise kann ich Attribute lesen, obj.xaber ich kann sie nicht festlegenobj.x = 1 dass es in Ordnung ist.

Aber sollte es mir wirklich etwas ausmachen, ein Objekt festzulegen, das nicht festgelegt werden darf? Vielleicht sollte ich es einfach verlassen. Andererseits kann ich keinen Unterstrich verwenden, da das Lesen obj._xfür den Benutzer ungerade ist. Daher sollte ich ihn verwenden, obj.xund der Benutzer weiß nicht, dass er dieses Attribut nicht festlegen darf.

Was ist Ihre Meinung und Praxis?

Rafał Łużyński
quelle
1
Die Idee einer Eigenschaft ist, dass sie sich wie ein Attribut verhält, aber zusätzlichen Code enthalten kann. Wenn Sie nur einen Wert erhalten möchten, würde ich mich nicht einmal darum kümmern: Verwenden Sie einfach self.xund vertrauen Sie darauf, dass sich niemand ändern wird x. Wenn xes wichtig ist, sicherzustellen, dass dies nicht geändert werden kann, verwenden Sie eine Eigenschaft.
li.davidm
Auch _xist überhaupt nicht seltsam: Konventionell bedeutet es etwas "Privates".
li.davidm
1
Ich meinte, dass das Lesen von _x seltsam ist. Nicht _x Name selbst. Wenn der Benutzer direkt aus _x liest, ist er nicht verantwortlich.
Rafał Łużyński
3
Wichtig! Ihre Klasse muss eine Klasse neuen Stils sein, dh von erben object, damit dies Ihre Einstellung tatsächlich stoppt obj.x. In einer Klasse im alten Stil können Sie tatsächlich noch festlegen obj.x, mit ziemlich unerwarteten Ergebnissen.
Ian H
Es gibt mehrere gültige Gründe für schreibgeschützte Eigenschaften. Einer ist, wenn Sie einen Wert haben, der aus dem Zusammenführen von zwei anderen (Lese- / Schreib-) Werten besteht. Sie können dies in einer Methode tun, aber Sie können es auch in einer schreibgeschützten Eigenschaft tun.
Philologon

Antworten:

66

Im Allgemeinen sollten Python-Programme unter der Annahme geschrieben werden, dass alle Benutzer Erwachsenen zustimmen und daher dafür verantwortlich sind, die Dinge selbst korrekt zu verwenden. In den seltenen Fällen, in denen es einfach nicht sinnvoll ist, ein Attribut einstellbar zu machen (z. B. einen abgeleiteten Wert oder einen aus einer statischen Datenquelle gelesenen Wert), ist die Eigenschaft "Nur Getter" im Allgemeinen das bevorzugte Muster.

Silas Ray
quelle
23
Es sieht so aus, als ob Ihre Antwort sich selbst widerspricht. Sie sagen, dass Benutzer verantwortlich sein und die Dinge richtig verwenden sollten, dann sagen Sie, dass es manchmal nicht sinnvoll ist, ein Attribut einstellbar zu machen, und Getter-Eigenschaften sind ein bevorzugter Weg. Meiner Meinung nach können Sie Attribute setzen oder nicht. Die Frage ist nur, ob ich dieses Attribut schützen oder verlassen soll. Dazwischen sollte es keine Antworten geben.
Rafał Łużyński
19
Nein, ich sagte, wenn Sie buchstäblich keinen Wert festlegen können, ist es nicht sinnvoll, einen Setter zu haben. Wenn Sie beispielsweise ein Kreisobjekt mit einem Radiuselement und einem Umfangsattribut haben, das vom Radius abgeleitet ist, oder wenn Sie ein Objekt haben, das eine schreibgeschützte Echtzeit-API mit einer Reihe von Nur-Getter-Eigenschaften umschließt. Nichts widerspricht irgendetwas.
Silas Ray
9
Der verantwortliche Benutzer wird jedoch nicht versuchen, attr festzulegen, das buchstäblich nicht festgelegt werden kann. Und wieder würde ein nicht verantwortlicher Benutzer attr einstellen, das buchstäblich eingestellt werden kann, und aufgrund seines Satzes irgendwo anders einen Fehler im Code auslösen. Am Ende können also beide attr nicht gesetzt werden. Sollte ich die Eigenschaft für beide verwenden oder nicht für eine?
Rafał Łużyński
8
Der verantwortliche Benutzer sollte jedoch nicht versuchen, attr einzustellen, das buchstäblich nicht eingestellt werden kann. Wenn bei der Programmierung etwas streng ein nicht einstellbarer Wert ist, ist es verantwortlich oder sinnvoll, sicherzustellen, dass dies nicht möglich ist. Diese kleinen Dinge tragen alle zu zuverlässigen Programmen bei.
Robin Smith
6
Das ist eine Position, die viele Menschen und Sprachen einnehmen. Wenn es sich um eine Position handelt, die Sie nicht verhandelbar finden, sollten Sie Python wahrscheinlich nicht verwenden.
Silas Ray
69

Nur meine zwei Cent, Silas Ray ist auf dem richtigen Weg, aber ich wollte ein Beispiel hinzufügen. ;-);

Python ist eine typsichere Sprache und daher müssen Sie den Benutzern Ihres Codes immer vertrauen, um den Code wie eine vernünftige (vernünftige) Person zu verwenden.

Gemäß PEP 8 :

Verwenden Sie einen führenden Unterstrich nur für nicht öffentliche Methoden und Instanzvariablen.

Um eine schreibgeschützte Eigenschaft in einer Klasse zu haben, die Sie verwenden können, müssen Sie von der @propertyDekoration erben, objectwenn Sie dies tun, um die Klassen im neuen Stil zu verwenden.

Beispiel:

>>> class A(object):
...     def __init__(self, a):
...         self._a = a
...
...     @property
...     def a(self):
...         return self._a
... 
>>> a = A('test')
>>> a.a
'test'
>>> a.a = 'pleh'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
siebz0r
quelle
9
Python ist nicht unsicher, sondern dynamisch typisiert. Und das Mangeln von Namen dient nicht dazu, das Betrügen zu erschweren, sondern um Namenskonflikte in Szenarien zu verhindern, in denen die Vererbung problematisch sein könnte (wenn Sie nicht im großen Stil programmieren, sollte es Sie nicht einmal interessieren).
Memeplex
3
Sie sollten sich jedoch daran erinnern, dass veränderbare Objekte mit dieser Methode ohnehin geändert werden können. Wenn self.__a = []Sie dies beispielsweise noch tun können, funktioniert a.a.append('anything')es.
Igor
3
Mir ist nicht klar, welche Bedeutung "eine vernünftige (vernünftige) Person" für diese Antwort hat. Können Sie genauer sagen, was Ihrer Meinung nach eine vernünftige Person tun würde und was nicht?
Winni2k
2
Wenn ich die @ Eigenschaft-Dekoration verwende, müssen Sie von Objekt erben, wenn Sie dies tun. Das war der springende Punkt bei dieser Antwort. Vielen Dank.
Akki
1
@kkm Die einzige Möglichkeit, einem Fehler niemals zu erlauben, sich in Code einzuschleichen, besteht darin, niemals Code zu schreiben.
Alechan
54

Hier ist ein Weg, um die Annahme zu vermeiden, dass

Alle Benutzer stimmen Erwachsenen zu und sind daher dafür verantwortlich, die Dinge selbst richtig zu verwenden.

Bitte beachten Sie mein Update unten

Verwenden @property, ist sehr ausführlich, zB:

   class AClassWithManyAttributes:
        '''refactored to properties'''
        def __init__(a, b, c, d, e ...)
             self._a = a
             self._b = b
             self._c = c
             self.d = d
             self.e = e

        @property
        def a(self):
            return self._a
        @property
        def b(self):
            return self._b
        @property
        def c(self):
            return self._c
        # you get this ... it's long

Verwenden von

Kein Unterstrich: Es ist eine öffentliche Variable.
Ein Unterstrich: Es ist eine geschützte Variable.
Zwei Unterstriche: Es ist eine private Variable.

Mit Ausnahme des letzten ist es eine Konvention. Wenn Sie sich wirklich anstrengen, können Sie dennoch auf Variablen mit doppeltem Unterstrich zugreifen.

Also, was machen wir? Geben wir es auf, schreibgeschützte Eigenschaften in Python zu haben?

Erblicken! read_only_propertiesDekorateur zur Rettung!

@read_only_properties('readonly', 'forbidden')
class MyClass(object):
    def __init__(self, a, b, c):
        self.readonly = a
        self.forbidden = b
        self.ok = c

m = MyClass(1, 2, 3)
m.ok = 4
# we can re-assign a value to m.ok
# read only access to m.readonly is OK 
print(m.ok, m.readonly) 
print("This worked...")
# this will explode, and raise AttributeError
m.forbidden = 4

Du fragst:

Woher kommt read_only_propertiesdas?

Ich bin froh, dass Sie gefragt haben, hier ist die Quelle für read_only_properties :

def read_only_properties(*attrs):

    def class_rebuilder(cls):
        "The class decorator"

        class NewClass(cls):
            "This is the overwritten class"
            def __setattr__(self, name, value):
                if name not in attrs:
                    pass
                elif name not in self.__dict__:
                    pass
                else:
                    raise AttributeError("Can't modify {}".format(name))

                super().__setattr__(name, value)
        return NewClass
    return class_rebuilder

aktualisieren

Ich hätte nie gedacht, dass diese Antwort so viel Aufmerksamkeit erhält. Überraschenderweise schon. Dies ermutigte mich, ein Paket zu erstellen, das Sie verwenden können.

$ pip install read-only-properties

in Ihrer Python-Shell:

In [1]: from rop import read_only_properties

In [2]: @read_only_properties('a')
   ...: class Foo:
   ...:     def __init__(self, a, b):
   ...:         self.a = a
   ...:         self.b = b
   ...:         

In [3]: f=Foo('explodes', 'ok-to-overwrite')

In [4]: f.b = 5

In [5]: f.a = 'boom'
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-a5226072b3b4> in <module>()
----> 1 f.a = 'boom'

/home/oznt/.virtualenvs/tracker/lib/python3.5/site-packages/rop.py in __setattr__(self, name, value)
    116                     pass
    117                 else:
--> 118                     raise AttributeError("Can't touch {}".format(name))
    119 
    120                 super().__setattr__(name, value)

AttributeError: Can't touch a
Oz123
quelle
1
Das ist wirklich hilfreich und macht genau das, was ich tun wollte. Danke dir. Es ist jedoch für diejenigen, die Python 3 installiert haben. Ich verwende Python 2.7.8, daher muss ich zwei kleinere Änderungen an Ihrer Lösung vornehmen: "class NewClass (cls, <b> object <\ b>):" ... "<b> super (NewClass, self) <\ b> .__ setattr __ (Name, Wert) ".
Ying Zhang
1
Außerdem sollte man darauf achten, dass Variablen von Klassenmitgliedern Listen und Wörterbücher sind. Sie können sie nicht wirklich davon abhalten, auf diese Weise aktualisiert zu werden.
Ying Zhang
1
Eine Verbesserung und drei Probleme hier. Verbesserung: Der if..elif..elseBlock könnte einfach if name in attrs and name in self.__dict__: raise Attr...ohne passerforderlich sein. Problem 1: Auf diese Weise dekorierte Klassen erhalten alle eine identische __name__, und die Zeichenfolgendarstellung ihres Typs wird ebenfalls homogenisiert. Problem 2: Diese Dekoration überschreibt jeden Brauch __setattr__. Problem 3: Benutzer können dies mit besiegen del MyClass.__setattr__.
TigerhawkT3
Nur eine Sprachsache. "Ach ..." bedeutet "Traurig zu sagen, ...", was ich nicht will, denke ich.
Thomas Andrews
Nichts wird mich davon abhalten object.__setattr__(f, 'forbidden', 42). Ich sehe nicht, welche read_only_propertiesAdds nicht durch den doppelten Unterstrichnamen Mangling behandelt werden.
L3viathan
3

Hier ist ein etwas anderer Ansatz für schreibgeschützte Eigenschaften, die vielleicht als einmal beschreibbare Eigenschaften bezeichnet werden sollten, da sie initialisiert werden müssen, nicht wahr? Für die Paranoiden unter uns, die sich Sorgen machen, Eigenschaften durch direkten Zugriff auf das Wörterbuch des Objekts ändern zu können, habe ich "extreme" Namensverknüpfung eingeführt:

from uuid import uuid4

class Read_Only_Property:
    def __init__(self, name):
        self.name = name
        self.dict_name = uuid4().hex
        self.initialized = False

    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            return instance.__dict__[self.dict_name]

    def __set__(self, instance, value):
        if self.initialized:
            raise AttributeError("Attempt to modify read-only property '%s'." % self.name)
        instance.__dict__[self.dict_name] = value
        self.initialized = True

class Point:
    x = Read_Only_Property('x')
    y = Read_Only_Property('y')
    def __init__(self, x, y):
        self.x = x
        self.y = y

if __name__ == '__main__':
    try:
        p = Point(2, 3)
        print(p.x, p.y)
        p.x = 9
    except Exception as e:
        print(e)
Booboo
quelle
Nett. Wenn Sie dict_namestattdessen entstellen , wird beispielsweise dict_name = "_spam_" + namedie Abhängigkeit von entfernt uuid4und das Debuggen wird erheblich vereinfacht.
cz
Aber dann kann ich sagen p.__dict__['_spam_x'] = 5, den Wert von zu ändern p.x, so dass dies nicht ausreicht, um Namen zu entstellen.
Booboo
1

Ich bin mit den beiden vorherigen Antworten zum Erstellen schreibgeschützter Eigenschaften unzufrieden, da mit der ersten Lösung das schreibgeschützte Attribut gelöscht und dann festgelegt werden kann und das __dict__ nicht blockiert wird. Die zweite Lösung könnte mit Tests umgangen werden: Finden Sie den Wert, der dem entspricht, den Sie auf zwei festgelegt haben, und ändern Sie ihn schließlich.

Nun zum Code.

def final(cls):
    clss = cls
    @classmethod
    def __init_subclass__(cls, **kwargs):
        raise TypeError("type '{}' is not an acceptable base type".format(clss.__name__))
    cls.__init_subclass__ = __init_subclass__
    return cls


def methoddefiner(cls, method_name):
    for clss in cls.mro():
        try:
            getattr(clss, method_name)
            return clss
        except(AttributeError):
            pass
    return None


def readonlyattributes(*attrs):
    """Method to create readonly attributes in a class

    Use as a decorator for a class. This function takes in unlimited 
    string arguments for names of readonly attributes and returns a
    function to make the readonly attributes readonly. 

    The original class's __getattribute__, __setattr__, and __delattr__ methods
    are redefined so avoid defining those methods in the decorated class

    You may create setters and deleters for readonly attributes, however
    if they are overwritten by the subclass, they lose access to the readonly
    attributes. 

    Any method which sets or deletes a readonly attribute within
    the class loses access if overwritten by the subclass besides the __new__
    or __init__ constructors.

    This decorator doesn't support subclassing of these classes
    """
    def classrebuilder(cls):
        def __getattribute__(self, name):
            if name == '__dict__':
                    from types import MappingProxyType
                    return MappingProxyType(super(cls, self).__getattribute__('__dict__'))
            return super(cls, self).__getattribute__(name)
        def __setattr__(self, name, value): 
                if name == '__dict__' or name in attrs:
                    import inspect
                    stack = inspect.stack()
                    try:
                        the_class = stack[1][0].f_locals['self'].__class__
                    except(KeyError):
                        the_class = None
                    the_method = stack[1][0].f_code.co_name
                    if the_class != cls: 
                         if methoddefiner(type(self), the_method) != cls:
                            raise AttributeError("Cannot set readonly attribute '{}'".format(name))                        
                return super(cls, self).__setattr__(name, value)
        def __delattr__(self, name):                
                if name == '__dict__' or name in attrs:
                    import inspect
                    stack = inspect.stack()
                    try:
                        the_class = stack[1][0].f_locals['self'].__class__
                    except(KeyError):
                        the_class = None
                    the_method = stack[1][0].f_code.co_name
                    if the_class != cls:
                        if methoddefiner(type(self), the_method) != cls:
                            raise AttributeError("Cannot delete readonly attribute '{}'".format(name))                        
                return super(cls, self).__delattr__(name)
        clss = cls
        cls.__getattribute__ = __getattribute__
        cls.__setattr__ = __setattr__
        cls.__delattr__ = __delattr__
        #This line will be moved when this algorithm will be compatible with inheritance
        cls = final(cls)
        return cls
    return classrebuilder

def setreadonlyattributes(cls, *readonlyattrs):
    return readonlyattributes(*readonlyattrs)(cls)


if __name__ == '__main__':
    #test readonlyattributes only as an indpendent module
    @readonlyattributes('readonlyfield')
    class ReadonlyFieldClass(object):
        def __init__(self, a, b):
            #Prevent initalization of the internal, unmodified PrivateFieldClass
            #External PrivateFieldClass can be initalized
            self.readonlyfield = a
            self.publicfield = b


    attr = None
    def main():
        global attr
        pfi = ReadonlyFieldClass('forbidden', 'changable')
        ###---test publicfield, ensure its mutable---###
        try:
            #get publicfield
            print(pfi.publicfield)
            print('__getattribute__ works')
            #set publicfield
            pfi.publicfield = 'mutable'
            print('__setattr__ seems to work')
            #get previously set publicfield
            print(pfi.publicfield)
            print('__setattr__ definitely works')
            #delete publicfield
            del pfi.publicfield 
            print('__delattr__ seems to work')
            #get publicfield which was supposed to be deleted therefore should raise AttributeError
            print(pfi.publlicfield)
            #publicfield wasn't deleted, raise RuntimeError
            raise RuntimeError('__delattr__ doesn\'t work')
        except(AttributeError):
            print('__delattr__ works')


        try:
            ###---test readonly, make sure its readonly---###
            #get readonlyfield
            print(pfi.readonlyfield)
            print('__getattribute__ works')
            #set readonlyfield, should raise AttributeError
            pfi.readonlyfield = 'readonly'
            #apparently readonlyfield was set, notify user
            raise RuntimeError('__setattr__ doesn\'t work')
        except(AttributeError):
            print('__setattr__ seems to work')
            try:
                #ensure readonlyfield wasn't set
                print(pfi.readonlyfield)
                print('__setattr__ works')
                #delete readonlyfield
                del pfi.readonlyfield
                #readonlyfield was deleted, raise RuntimeError
                raise RuntimeError('__delattr__ doesn\'t work')
            except(AttributeError):
                print('__delattr__ works')
        try:
            print("Dict testing")
            print(pfi.__dict__, type(pfi.__dict__))
            attr = pfi.readonlyfield
            print(attr)
            print("__getattribute__ works")
            if pfi.readonlyfield != 'forbidden':
                print(pfi.readonlyfield)
                raise RuntimeError("__getattr__ doesn't work")
            try:
                pfi.__dict__ = {}
                raise RuntimeError("__setattr__ doesn't work")
            except(AttributeError):
                print("__setattr__ works")
            del pfi.__dict__
            raise RuntimeError("__delattr__ doesn't work")
        except(AttributeError):
            print(pfi.__dict__)
            print("__delattr__ works")
            print("Basic things work")


main()

Es macht keinen Sinn, schreibgeschützte Attribute zu erstellen, es sei denn, Sie schreiben Bibliothekscode, Code, der als Code zur Verbesserung ihrer Programme an andere verteilt wird, und keinen Code für andere Zwecke wie die App-Entwicklung. Das __dict__ -Problem ist gelöst, da das __dict__ jetzt vom unveränderlichen Typ ist. MappingProxyType ist. , sodass Attribute nicht über __dict__ geändert werden können. Das Festlegen oder Löschen von __dict__ ist ebenfalls blockiert. Die einzige Möglichkeit, schreibgeschützte Eigenschaften zu ändern, besteht darin, die Methoden der Klasse selbst zu ändern.

Obwohl ich glaube, dass meine Lösung besser ist als die beiden vorherigen, könnte sie verbessert werden. Dies sind die Schwächen dieses Codes:

a) Erlaubt nicht das Hinzufügen zu einer Methode in einer Unterklasse, die ein schreibgeschütztes Attribut setzt oder löscht. Eine in einer Unterklasse definierte Methode kann automatisch nicht auf ein schreibgeschütztes Attribut zugreifen, selbst wenn die Version der Methode der Oberklasse aufgerufen wird.

b) Die schreibgeschützten Methoden der Klasse können geändert werden, um die schreibgeschützten Einschränkungen aufzuheben.

Ohne die Bearbeitung der Klasse ist es jedoch nicht möglich, ein schreibgeschütztes Attribut festzulegen oder zu löschen. Dies hängt nicht von Namenskonventionen ab, was gut ist, da Python nicht so konsistent mit Namenskonventionen ist. Auf diese Weise können schreibgeschützte Attribute erstellt werden, die nicht mit ausgeblendeten Lücken geändert werden können, ohne die Klasse selbst zu bearbeiten. Listen Sie einfach die Attribute auf, die schreibgeschützt sein sollen, wenn Sie den Dekorateur als Argumente aufrufen, und sie werden schreibgeschützt.

Dank an Brices Antwort in Wie erhalte ich den Namen der Anruferklasse in einer Funktion einer anderen Klasse in Python? zum Abrufen der Aufruferklassen und -methoden.

Michael
quelle
object.__setattr__(pfi, 'readonly', 'foobar')bricht diese Lösung, ohne die Klasse selbst zu bearbeiten.
L3viathan
0

Beachten Sie, dass Instanzmethoden auch Attribute (der Klasse) sind und dass Sie sie auf Klassen- oder Instanzebene festlegen können, wenn Sie wirklich ein Badass sein möchten. Oder dass Sie eine Klassenvariable festlegen (die auch ein Attribut der Klasse ist), bei der praktische schreibgeschützte Eigenschaften nicht sofort funktionieren. Ich versuche zu sagen, dass das Problem des "schreibgeschützten Attributs" tatsächlich allgemeiner ist, als es normalerweise angenommen wird. Glücklicherweise gibt es konventionelle Erwartungen bei der Arbeit, die so stark sind, dass sie uns für diese anderen Fälle blind machen (schließlich ist fast alles eine Art Attribut in Python).

Aufbauend auf diesen Erwartungen denke ich, dass der allgemeinste und leichteste Ansatz darin besteht, die Konvention zu übernehmen, dass "öffentliche" Attribute (kein führender Unterstrich) schreibgeschützt sind, es sei denn, dies wird ausdrücklich als beschreibbar dokumentiert. Dies setzt die übliche Erwartung voraus, dass Methoden nicht gepatcht werden und Klassenvariablen, die Instanzstandards angeben, besser sind, geschweige denn. Wenn Sie sich in Bezug auf ein bestimmtes Attribut wirklich paranoid fühlen, verwenden Sie einen schreibgeschützten Deskriptor als letzte Ressourcenmaßnahme.

memeplex
quelle
0

Während ich den Klassendekorator von Oz123 mag, können Sie auch Folgendes tun, bei dem ein expliziter Klassenumbruch und __new__ mit einer Factory-Methode verwendet werden, die die Klasse innerhalb eines Abschlusses zurückgibt:

class B(object):
    def __new__(cls, val):
        return cls.factory(val)

@classmethod
def factory(cls, val):
    private = {'var': 'test'}

    class InnerB(object):
        def __init__(self):
            self.variable = val
            pass

        @property
        def var(self):
            return private['var']

    return InnerB()
Apollo Marquis
quelle
Sie sollten einige Tests hinzufügen, die zeigen, wie es mit mehreren Eigenschaften
funktioniert
0

Das ist meine Problemumgehung.

@property
def language(self):
    return self._language
@language.setter
def language(self, value):
    # WORKAROUND to get a "getter-only" behavior
    # set the value only if the attribute does not exist
    try:
        if self.language == value:
            pass
        print("WARNING: Cannot set attribute \'language\'.")
    except AttributeError:
        self._language = value
Rusiano
quelle
0

Jemand erwähnte die Verwendung eines Proxy-Objekts. Ich habe kein Beispiel dafür gesehen und es am Ende [schlecht] ausprobiert.

/! \ Bitte bevorzugen Sie nach Möglichkeit Klassendefinitionen und Klassenkonstruktoren

Dieser Code wird effektiv neu geschrieben class.__new__(Klassenkonstruktor), außer in jeder Hinsicht schlechter. Sparen Sie sich den Schmerz und verwenden Sie dieses Muster nicht, wenn Sie können.

def attr_proxy(obj):
    """ Use dynamic class definition to bind obj and proxy_attrs.
        If you can extend the target class constructor that is 
        cleaner, but its not always trivial to do so.
    """
    proxy_attrs = dict()

    class MyObjAttrProxy():
        def __getattr__(self, name):
            if name in proxy_attrs:
                return proxy_attrs[name]  # overloaded

            return getattr(obj, name)  # proxy

        def __setattr__(self, name, value):
            """ note, self is not bound when overloading methods
            """
            proxy_attrs[name] = value

    return MyObjAttrProxy()


myobj = attr_proxy(Object())
setattr(myobj, 'foo_str', 'foo')

def func_bind_obj_as_self(func, self):
    def _method(*args, **kwargs):
        return func(self, *args, **kwargs)
    return _method

def mymethod(self, foo_ct):
    """ self is not bound because we aren't using object __new__
        you can write the __setattr__ method to bind a self 
        argument, or declare your functions dynamically to bind in 
        a static object reference.
    """
    return self.foo_str + foo_ct

setattr(myobj, 'foo', func_bind_obj_as_self(mymethod, myobj))
ThorSummoner
quelle
-2

Ich weiß, dass ich diesen Thread von den Toten zurückbringe, aber ich habe mir überlegt, wie man eine Eigenschaft schreibgeschützt macht, und nachdem ich dieses Thema gefunden habe, war ich mit den bereits geteilten Lösungen nicht zufrieden.

Kehren Sie also zur ursprünglichen Frage zurück, wenn Sie mit diesem Code beginnen:

@property
def x(self):
    return self._x

Und wenn Sie X schreibgeschützt machen möchten, können Sie einfach hinzufügen:

@x.setter
def x(self, value):
    raise Exception("Member readonly")

Wenn Sie dann Folgendes ausführen:

print (x) # Will print whatever X value is
x = 3 # Will raise exception "Member readonly"
vincedjango
quelle
3
Aber wenn Sie einfach keinen Setter machen, wird der Versuch, ihn zuzuweisen, ebenfalls einen Fehler AttributeError('can't set attribute')
auslösen