Какой самый простой и краткий способ сделать выбранные атрибуты в экземпляре доступными только для чтения? - PullRequest
8 голосов
/ 24 сентября 2008

В Python я хочу, чтобы выбранные атрибуты экземпляра класса были доступны для чтения только для кода вне класса. Я хочу, чтобы никакой внешний код не мог изменить атрибут, кроме как косвенным путем вызова методов в экземпляре. Я хочу, чтобы синтаксис был кратким. Какой самый лучший способ? (Я даю свой лучший ответ ниже ...)

Ответы [ 6 ]

7 голосов
/ 24 сентября 2008

Вы должны использовать декоратор @property.

>>> class a(object):
...     def __init__(self, x):
...             self.x = x
...     @property
...     def xval(self):
...             return self.x
... 
>>> b = a(5)
>>> b.xval
5
>>> b.xval = 6
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
2 голосов
/ 24 сентября 2008

Вот как:

class whatever(object):
  def __init__(self, a, b, c, ...):
    self.__foobar = 1
    self.__blahblah = 2

  foobar = property(lambda self: self.__foobar)
  blahblah = property(lambda self: self.__blahblah)

(Предполагая, что foobar и blahblah являются атрибутами, которые вы хотите использовать только для чтения.) Добавление двух подчеркиваний к имени атрибута эффективно скрывает его за пределами класса, поэтому внутренние версии выиграли не быть доступным снаружи. Это работает только для классов нового стиля, наследуемых от объекта , поскольку оно зависит от property.

С другой стороны ... это довольно глупо. Сохранение приватности переменных, похоже, навязчивая идея C ++ и Java. Ваши пользователи должны использовать общедоступный интерфейс для вашего класса, потому что он хорошо продуман, а не потому, что вы заставляете их.

Редактировать: Похоже, Кевин уже опубликовал похожую версию.

2 голосов
/ 24 сентября 2008
class C(object):

    def __init__(self):

        self.fullaccess = 0
        self.__readonly = 22 # almost invisible to outside code...

    # define a publicly visible, read-only version of '__readonly':
    readonly = property(lambda self: self.__readonly)

    def inc_readonly( self ):
        self.__readonly += 1

c=C()

# prove regular attribute is RW...
print "c.fullaccess = %s" % c.fullaccess
c.fullaccess = 1234
print "c.fullaccess = %s" % c.fullaccess

# prove 'readonly' is a read-only attribute
print "c.readonly = %s" % c.readonly
try:
    c.readonly = 3
except AttributeError:
    print "Can't change c.readonly"
print "c.readonly = %s" % c.readonly

# change 'readonly' indirectly...
c.inc_readonly()
print "c.readonly = %s" % c.readonly

Это выводит:

$ python ./p.py
c.fullaccess = 0
c.fullaccess = 1234
c.readonly = 22 * ​​1007 * Не могу изменить c.readonly
c.readonly = 22 * ​​1009 * c.readonly = 23

Мои пальцы зудят, чтобы я могла сказать

    @readonly
    self.readonly = 22

т.е. использовать декоратор для атрибута. Это было бы так чисто ...

1 голос
/ 24 сентября 2008

Нет реального способа сделать это. Есть способы сделать это более «сложным», но нет концепции полностью скрытых, недоступных атрибутов класса.

Если человеку, использующему ваш класс, нельзя доверять, чтобы он следовал документации API, то это его собственная проблема. Защита людей от глупостей просто означает, что они будут делать гораздо более сложные, сложные и разрушительные глупости, пытаясь делать то, что им не следовало делать.

0 голосов
/ 24 апреля 2010

Я знаю, что Уильям Келлер - самое чистое решение на сегодняшний день ... но вот что я придумал ..

class readonly(object):
    def __init__(self, attribute_name):
        self.attribute_name = attribute_name

    def __get__(self, instance, instance_type):
        if instance != None:
            return getattr(instance, self.attribute_name)
        else:
            raise AttributeError("class %s has no attribute %s" % 
                                 (instance_type.__name__, self.attribute_name))

    def __set__(self, instance, value):
        raise AttributeError("attribute %s is readonly" % 
                              self.attribute_name)

А вот пример использования

class a(object):
    def __init__(self, x):
        self.x = x
    xval = readonly("x")

К сожалению, это решение не может обрабатывать частные переменные (__Именованные переменные).

0 голосов
/ 24 сентября 2008

Вы можете использовать метакласс, который автоматически оборачивает методы (или атрибуты класса), которые следуют соглашению об именах, в свойства (бессовестно взятые из Объединение типов и классов в Python 2.2 :

class autoprop(type):
    def __init__(cls, name, bases, dict):
        super(autoprop, cls).__init__(name, bases, dict)
        props = {}
        for name in dict.keys():
            if name.startswith("_get_") or name.startswith("_set_"):
                props[name[5:]] = 1
        for name in props.keys():
            fget = getattr(cls, "_get_%s" % name, None)
            fset = getattr(cls, "_set_%s" % name, None)
            setattr(cls, name, property(fget, fset))

Это позволяет использовать:

class A:
    __metaclass__ = autosuprop
    def _readonly(self):
        return __x
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...