Установка свойства внутри метода Python - PullRequest
4 голосов
/ 22 февраля 2011

Я хочу установить атрибут только для чтения внутри метода класса.
Я уже пробовал это:

class Foo(object):
    def __init__(self, v):
        self._set('_v', v)

    def _set(self, attr, v):
        setattr(self, attr, v)
        setattr(Foo, attr[1:], property(lambda self: getattr(self, attr)))

, но это ужасно.Есть ли другой способ?Что мне нужно сделать, это установить свойство:

class Foo(object):
    def __init__(self, v):
        self._v = v

    @ property
    def v(self):
        return self._v    

>>> f = Foo(42)
>>> f.v
42
>>> f.v = 41
AttributeError: can't set attribute ## This is what I want: a read-only attribute

, но мне нужно сделать это внутри метода.Есть ли другой способ?

Спасибо,
rubik

PS Я уже проверил этот пост, но он не решает мою проблему: Использование свойства Python () внутри метода

РЕДАКТИРОВАТЬ: я не могу использовать property, потому что я хочу установить его внутри метода.Я могу использовать property только извне:

class Foo(object):
    def __init__(self, v):
        self._v = v
    @ property
    def v(self):
        return self._v
    ## ...OR
    def getv(self):
        return self._v
    v = property(getv)

И я не могу этого сделать, потому что я не знаю имени свойства и мне нужно установить его динамически.Как то так:

class Foo(object):
    def __init__(self, v):
        self._set_property_from_inside('v', v)
>>> f = Foo(42)
>>> f.v
42

Ответы [ 4 ]

3 голосов
/ 22 февраля 2011

I думаю вы ищете Python дескрипторы .

class MyDescriptor(object):
    def __init__(self, protected_attr_name):
        self.attr = protected_attr_name

    def __get__(self, obj, objtype):
        return getattr(obj, self.attr)

    def __set__(self, obj, value):
        #setattr(obj, self.attr, value)
        raise AttributeError("Can't set attribute")

class Foo(object):
    def __init__(self, k, v):
        setattr(self.__class__, k, MyDescriptor("_" + k))
        setattr(self, "_" + k, v)

f = Foo("v", 42)
print f.v   # Prints 42
try:
    f.v = 32
except AttributeError:
    pass
print f.v  # Prints 42

Здесь вы можете делать все, что хотите, чтобы контролировать доступ в __get__ и__set__ методы.Если вы вызываете obj.get_v в __get__ и obj.set_v в __set__, это очень близко к фактической реализации свойства, как вы можете видеть в приведенной выше ссылке.

Редактировать: исправлено.Я должен был прочитать эту страницу лучше сам.Цитата:

Для объектов механизм находится в object.__getattribute__, который преобразует b.x в type(b).__dict__['x'].__get__(b, type(b))

Так что если вы поместите дескрипторы в __dict__ изэкземпляр, они просто перезаписываются, когда вы устанавливаете для этого атрибута новое значение.

2 голосов
/ 22 февраля 2011
class Foo(object):
    def __getattr__(self, name):
        return getattr(self, "_" + name)
    def __setattr__(self, name, value):
        if name.startswith('_'):
            self.__dict__[name] = value
        else:
            raise ValueError("%s is read only" % name)

Тогда:

>>> f = Foo()
>>> f.x = 5
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 8, in __setattr__
ValueError: x is read only
>>> f._x = 5
>>> f.x
5
1 голос
/ 23 февраля 2011

Я думал о том, что я считаю более чистым решением для реализации атрибута только для чтения, если это все, что вы хотите.Это вариант решения, которое дал тангентсторм, но полностью исключает необходимость использования метода __getattr__.

class Foo(object):
    def __init__(self):
        self.readonly = set()

    def set_readonly(self, attr, value):
        setattr(self, attr, value)
        self.readonly.add(attr)

    def __setattr__(self, attr, value):
        if hasattr(self, "readonly") and attr in self.readonly:
            raise AttributeError("Read only attribute: %s" % (attr,))
        object.__setattr__(self, attr, value)

Он работает так:

>>> f = Foo()
>>> f.x = 5
>>> f.set_readonly("y", 9)
>>> f.x, f.y
(5, 9)
>>> f.x = 7
>>> f.y = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "ro.py", line 13, in __setattr__
    raise AttributeError("Read only attribute: %s" % (name,))
AttributeError: Read only attribute: y

Создание только для чтенияСчитать и записать атрибут снова легко:

    def unset_readonly(self, attr):
        self.readonly.remove(attr)

В моей первой попытке написать эту идею я использовал self.__readonly вместо self.readonly, но это приводит к проблеме с установкой атрибута __readonly, поскольку мне нужно было бы выполнить un-munge атрибут "private", чтобы проверить его наличие (hasattr(self, "_Foo__readonly")), и это не рекомендуется.

1 голос
/ 22 февраля 2011

property () - это именно то решение, которое здесь.Почему бы не решить вашу проблему?При переопределении метода установки метод получения позволяет вам получить именно то, что вам нужно и нужно: полный контроль над свойством.

Пожалуйста, проверьте с официальной документацией, например,

http://docs.python.org/library/functions.html#property

чтобы понять всю историю.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...