Właściwość Python tylko do odczytu

97

Nie wiem, kiedy atrybut powinien być prywatny i czy powinienem używać właściwości.

Czytałem ostatnio, że setery i gettery nie są pythonowe i powinienem użyć dekoratora właściwości. W porządku.

Ale co, jeśli mam atrybut, którego nie można ustawić spoza klasy, ale można go odczytać (atrybut tylko do odczytu). Czy ten atrybut powinien być prywatny, a przez prywatny mam na myśli podkreślenie, w ten sposób self._x? Jeśli tak, to jak mogę to przeczytać bez użycia gettera? Jedyną metodą, jaką znam w tej chwili, jest pisanie

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

W ten sposób mogę odczytać atrybut według, obj.xale nie mogę go ustawić, obj.x = 1więc jest w porządku.

Ale czy naprawdę powinno mnie obchodzić ustawienie obiektu, którego nie wolno ustawiać? Może powinienem to po prostu zostawić. Ale z drugiej strony nie mogę użyć podkreślenia, ponieważ czytanie obj._xjest dziwne dla użytkownika, więc powinienem użyć, obj.xa potem znowu użytkownik nie wie, że nie może ustawić tego atrybutu.

Jaka jest Twoja opinia i praktyka?

Rafał Łużyński
źródło
1
Ideą właściwości jest to, że zachowuje się jak atrybut, ale może mieć dodatkowy kod. Jeśli wszystko, czego chcesz, to uzyskać wartość, nawet bym się nie przejmował: po prostu użyj self.xi ufaj, że nikt się nie zmieni x. Jeśli xważne jest, aby upewnić się, że nie można tego zmienić, użyj właściwości.
li.davidm
Poza tym _xwcale nie jest dziwne: zgodnie z konwencją oznacza coś „prywatnego”.
li.davidm
1
Miałem na myśli, że czytanie z _x jest dziwne. Nie jest to nazwa _x. Jeśli użytkownik czyta bezpośrednio z _x, jest nieodpowiedzialny.
Rafał Łużyński
3
Ważny! Twoja klasa musi być klasą w nowym stylu, tj. Dziedziczy po object, aby faktycznie uniemożliwiło to ustawienie obj.x. W klasach w starym stylu możesz nadal ustawić obj.x, z całkiem nieoczekiwanymi wynikami.
Ian H
Istnieje kilka ważnych powodów, dla których warto mieć właściwości tylko do odczytu. Pierwsza dotyczy wartości, która składa się z połączenia dwóch innych wartości (do odczytu / zapisu). Możesz to zrobić w metodzie, ale możesz to również zrobić we właściwości tylko do odczytu.
filologon

Odpowiedzi:

73

Ogólnie rzecz biorąc, programy w Pythonie powinny być pisane z założeniem, że wszyscy użytkownicy są pełnoletni, a zatem sami są odpowiedzialni za prawidłowe używanie rzeczy. Jednak w rzadkich przypadkach, w których ustawienie atrybutu po prostu nie ma sensu (na przykład wartość pochodna lub wartość odczytana z jakiegoś statycznego źródła danych), właściwość tylko pobierająca jest na ogół preferowanym wzorcem.

Silas Ray
źródło
26
Wygląda na to, że Twoja odpowiedź jest sprzeczna z samą sobą. Mówisz, że użytkownicy powinni być odpowiedzialni i używać rzeczy poprawnie, a następnie mówisz, że czasami ustawianie atrybutu nie ma sensu, a właściwość getter jest preferowanym sposobem. Moim zdaniem możesz lub nie możesz ustawić atrybutu. Pytanie tylko, czy powinienem chronić ten atrybut, czy go zostawić. Pomiędzy nimi nie powinno być odpowiedzi.
Rafał Łużyński
20
Nie, powiedziałem, że jeśli dosłownie nie możesz ustawić wartości, to nie ma sensu mieć setera. Na przykład, jeśli masz obiekt typu circle z elementem promienia i atrybutem obwodu, który pochodzi z promienia, lub jeśli masz obiekt, który otacza niektóre tylko do odczytu interfejsów API w czasie rzeczywistym z kilkoma właściwościami tylko do pobierania. Nie ma nic sprzecznego.
Silas Ray
9
Ale odpowiedzialny użytkownik nie będzie próbował ustawić atr, którego dosłownie nie można ustawić. I znowu nie odpowiedzialny użytkownik ustawiłby atr, który dosłownie można ustawić, i zgłosi błąd w innym miejscu w kodzie z powodu swojego zestawu. Więc ostatecznie nie można ustawić obu atr. Powinienem używać właściwości na obu, czy nie używać jej na żadnym?
Rafał Łużyński
8
Ale odpowiedzialny użytkownik nie powinien próbować ustawiać atr, którego dosłownie nie można ustawić. W programowaniu, jeśli coś jest wartością ściśle niemożliwą do ustawienia, odpowiedzialną lub rozsądną rzeczą jest upewnienie się, że tak nie jest. Te małe rzeczy składają się na niezawodne programy.
Robin Smith
6
Takie stanowisko zajmuje wiele osób i języków. Jeśli jest to pozycja, która nie podlega negocjacjom, prawdopodobnie nie powinieneś używać Pythona.
Silas Ray
75

Tylko moje dwa centy, Silas Ray jest na dobrej drodze, ale chciałem dodać przykład. ;-)

Python jest językiem niebezpiecznym dla typów, dlatego zawsze będziesz musiał ufać użytkownikom twojego kodu, że będą używać kodu jak rozsądna (rozsądna) osoba.

Za PEP 8 :

Używaj jednego wiodącego podkreślenia tylko w przypadku metod niepublicznych i zmiennych instancji.

Aby mieć właściwość „tylko do odczytu” w klasie, której możesz użyć @propertydekoracji, musisz ją odziedziczyć object, aby skorzystać z klas w nowym stylu.

Przykład:

>>> 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
źródło
10
Python nie jest typem niebezpiecznym, jest wpisywany dynamicznie. A zniekształcanie nazw nie ma na celu utrudnienia oszukiwania, ale zapobiega konfliktom nazw w scenariuszach, w których dziedziczenie może być problematyczne (jeśli nie programujesz na dużą skalę, nie powinieneś się tym przejmować).
memeplex
3
Należy jednak pamiętać, że zmienne obiekty i tak można zmieniać tą metodą. Na przykład, jeśli self.__a = []nadal możesz to zrobić a.a.append('anything')i zadziała.
Igor
3
Nie jest dla mnie jasne, co w tej odpowiedzi ma sformułowanie „rozsądna (rozsądna) osoba”. Czy możesz bardziej szczegółowo opisać rzeczy, które Twoim zdaniem rozsądna osoba zrobiłaby, a czego nie zrobiłaby?
winni2k
3
Dla mnie użyj dekoracji @property, będziesz musiał dziedziczyć po obiekcie, kiedy to zrobisz, o to chodzi w tej odpowiedzi. Dzięki.
akki
2
@kkm jedynym sposobem, aby nigdy nie dopuścić, aby błąd wkradł się do kodu, jest nigdy nie pisać kodu.
Alechan
58

Oto sposób na uniknięcie takiego założenia

wszyscy użytkownicy są pełnoletnimi i dlatego sami są odpowiedzialni za prawidłowe używanie rzeczy.

zobacz moją aktualizację poniżej

Używanie @propertyjest bardzo rozwlekłe, np .:

   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

Za pomocą

Bez podkreślenia: to zmienna publiczna.
Jedno podkreślenie: to chroniona zmienna.
Dwa podkreślenia: to zmienna prywatna.

Poza ostatnim to konwencja. Nadal możesz, jeśli naprawdę się postarasz, uzyskać dostęp do zmiennych z podwójnym podkreśleniem.

Więc co robimy? Czy rezygnujemy z posiadania właściwości tylko do odczytu w Pythonie?

Ujrzeć! read_only_propertiesdekorator na ratunek!

@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

Ty pytasz:

Skąd się bierze read_only_properties?

Cieszę się, że zapytałeś, oto źródło 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

aktualizacja

Nie spodziewałem się, że ta odpowiedź przyciągnie tyle uwagi. Zaskakujące jest, że tak. To zachęciło mnie do stworzenia pakietu, którego możesz użyć.

$ pip install read-only-properties

w twojej powłoce Pythona:

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
źródło
1
To jest naprawdę pomocne i robi dokładnie to, co chciałem. Dziękuję Ci. Jednak to jest dla tych, którzy mają zainstalowany Python 3. Używam Pythona 2.7.8, więc muszę wprowadzić dwie drobne poprawki do twojego rozwiązania: "class NewClass (cls, <b> obiekt <\ b>):" ... "<b> super (NewClass, self) <\ b> .__ setattr __ (nazwa, wartość) ".
Ying Zhang
1
Ponadto należy uważać, aby zmiennymi składowymi klas były listy i słowniki. Tak naprawdę nie można ich „zablokować” przed aktualizacją w ten sposób.
Ying Zhang
1
Tutaj jedna poprawa i trzy problemy. Poprawa: the if..elif..elseblok może być tylko if name in attrs and name in self.__dict__: raise Attr...ze nie passwymagane. Problem 1: wszystkie klasy tak udekorowane kończą się identycznym __name__, a ciąg znaków reprezentujących ich typ jest również homogenizowany. Problem 2: ta dekoracja zastępuje dowolny zwyczaj __setattr__. Problem 3: użytkownicy mogą to pokonać za pomocą del MyClass.__setattr__.
TigerhawkT3
Tylko kwestia języka. „Niestety…” oznacza „Przykro to mówić…”, co chyba nie jest tym, czego chcesz.
Thomas Andrews,
Nic mi nie przeszkodzi object.__setattr__(f, 'forbidden', 42). Nie rozumiem, co read_only_propertiesdodaje, że nie jest to obsługiwane przez podwójne podkreślenie zniekształcenia nazwy.
L3viathan
4

Oto nieco inne podejście do właściwości tylko do odczytu, które być może powinny nazywać się właściwościami jednorazowego zapisu, ponieważ muszą zostać zainicjalizowane, prawda? Dla paranoików, którzy martwią się możliwością modyfikowania właściwości poprzez bezpośredni dostęp do słownika obiektu, wprowadziłem „ekstremalne” zniekształcanie nazw:

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)
Boo Boo
źródło
Ładny. Jeśli dict_namezamiast tego zmienisz kod , np dict_name = "_spam_" + name. Usunie to zależność od uuid4i znacznie ułatwi debugowanie.
cz
Ale wtedy mogę powiedzieć, p.__dict__['_spam_x'] = 5żeby zmienić wartość p.x, więc to nie wystarczy do zmiany nazwy.
Booboo
1

Nie jestem zadowolony z poprzednich dwóch odpowiedzi dotyczących tworzenia właściwości tylko do odczytu, ponieważ pierwsze rozwiązanie umożliwia usunięcie atrybutu tylko do odczytu, a następnie ustawienie i nie blokuje __dict__. Drugie rozwiązanie można obejść, testując - znajdując wartość, która jest równa temu, co ustawisz, i ostatecznie ją zmieniając.

A teraz kod.

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()

Nie ma sensu tworzyć atrybutów tylko do odczytu, z wyjątkiem sytuacji, gdy piszesz kod biblioteki, kod, który jest dystrybuowany do innych jako kod do wykorzystania w celu ulepszenia ich programów, a nie kod do innych celów, takich jak tworzenie aplikacji. Problem __dict__ został rozwiązany, ponieważ __dict__ ma teraz niezmienne typy.MappingProxyType , więc atrybutów nie można zmienić za pomocą __dict__. Ustawianie lub usuwanie __dict__ jest również zablokowane. Jedynym sposobem zmiany właściwości tylko do odczytu jest zmiana metod samej klasy.

Chociaż uważam, że moje rozwiązanie jest lepsze niż poprzednie dwa, można je ulepszyć. Oto słabości tego kodu:

a) Nie pozwala na dodawanie do metody w podklasie, która ustawia lub usuwa atrybut tylko do odczytu. Metoda zdefiniowana w podklasie jest automatycznie blokowana przed dostępem do atrybutu tylko do odczytu, nawet przez wywołanie wersji metody nadklasy.

b) Metody klasy „tylko do odczytu” można zmienić, aby obejść ograniczenia tylko do odczytu.

Jednak nie ma możliwości ustawienia lub usunięcia atrybutu tylko do odczytu bez edycji klasy. Nie jest to zależne od konwencji nazewnictwa, co jest dobre, ponieważ Python nie jest tak spójny z konwencjami nazewnictwa. Zapewnia to sposób tworzenia atrybutów tylko do odczytu, których nie można zmienić za pomocą ukrytych luk bez edytowania samej klasy. Po prostu wymień atrybuty, które mają być odczytywane tylko podczas wywoływania dekoratora jako argumenty, a staną się one tylko do odczytu.

Podziękowania dla odpowiedzi Brice'a w Jak uzyskać nazwę klasy wywołującego wewnątrz funkcji innej klasy w Pythonie? do pobierania klas i metod wywołujących.

Michał
źródło
object.__setattr__(pfi, 'readonly', 'foobar')psuje to rozwiązanie, bez edytowania samej klasy.
L3viathan
0

Zauważ, że metody instancji są również atrybutami (klasy) i że możesz ustawić je na poziomie klasy lub instancji, jeśli naprawdę chcesz być złym człowiekiem. Albo, że możesz ustawić zmienną klasy (która jest również atrybutem klasy), gdzie przydatne właściwości tylko do odczytu nie będą działać porządnie po wyjęciu z pudełka. Chcę przez to powiedzieć, że problem „atrybutu tylko do odczytu” jest w rzeczywistości bardziej ogólny, niż zwykle się wydaje. Na szczęście w pracy istnieją konwencjonalne oczekiwania, które są tak silne, że oślepiają nas na te inne przypadki (w końcu prawie wszystko jest jakąś cechą w Pythonie).

Opierając się na tych oczekiwaniach, myślę, że najbardziej ogólnym i lekkim podejściem jest przyjęcie konwencji, że atrybuty „public” (bez wiodącego podkreślenia) są tylko do odczytu, chyba że są wyraźnie udokumentowane jako zapisywalne. To obejmuje zwykłe oczekiwanie, że metody nie zostaną poprawione, a zmienne klas wskazujące wartości domyślne instancji są lepsze, nie mówiąc już o tym. Jeśli czujesz się naprawdę paranoicznie z powodu jakiejś specjalnej cechy, użyj deskryptora tylko do odczytu jako ostatniej miary zasobów.

memeplex
źródło
0

Chociaż podoba mi się dekorator klas z Oz123, możesz również wykonać następujące czynności, które używają jawnego opakowania klasy i __new__ z metodą klasy Factory zwracającą klasę w zamknięciu:

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
źródło
powinieneś dodać test pokazujący, jak to działa z wieloma właściwościami
Oz123
0

To jest moje obejście.

@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
źródło
0

ktoś wspomniał o używaniu obiektu proxy, nie widziałem tego przykładu, więc ostatecznie go wypróbowałem, [słabo].

/! \ Jeśli to możliwe, preferuj definicje klas i konstruktory klas

ten kod jest efektywnie przepisywany class.__new__(konstruktor klas) z wyjątkiem gorszych pod każdym względem. Oszczędź sobie bólu i nie używaj tego schematu, jeśli możesz.

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
źródło
-2

Wiem, że przywracam z martwych ten wątek, ale zastanawiałem się, jak sprawić, by nieruchomość była tylko do odczytu i po znalezieniu tego tematu nie byłem zadowolony z udostępnionych już rozwiązań.

Wracając do pierwszego pytania, jeśli zaczniesz od tego kodu:

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

A jeśli chcesz, aby X był tylko do odczytu, możesz po prostu dodać:

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

Następnie, jeśli uruchomisz następujące:

print (x) # Will print whatever X value is
x = 3 # Will raise exception "Member readonly"
vincedjango
źródło
3
Ale jeśli po prostu nie zrobisz setera, próba przypisania również spowoduje błąd (An AttributeError('can't set attribute'))
Artyer