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






Реального способа сделать это нет. Есть способы сделать его более «сложным», но нет концепции полностью скрытых, недоступных атрибутов класса.
Если человеку, использующему ваш класс, нельзя доверять, чтобы он следил за документами API, то это его собственная проблема. Защита людей от глупых поступков означает лишь то, что они будут делать гораздо более сложные, сложные и разрушительные глупые поступки, пытаясь делать то, что им изначально не следовало делать.
Вы можете установить атрибут attr. to property (), то НИЧЕГО, даже сам экземпляр вообще не сможет получить к нему доступ!
(Хотя я также хотел бы отметить, что ваш второй пункт верен!)
Специальный модуль расширения может значительно усложнить изменение выбранных атрибутов
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
Не могу изменить c.readonly
c.readonly = 22
c.readonly = 23
Мои пальцы чешутся, чтобы сказать
@readonly
self.readonly = 22
то есть использовать декоратор для атрибута. Было бы так чисто ...
Ты можешь! @property будет работать, но вы должны использовать def readonly (self): return readonly Тем не менее, он по-прежнему избегает лямбда-шума.
Это не атрибут только для чтения, потому что другой код все еще может выполнять "c._C__readonly = 4"
Да, я знал, что это «искажение» используется для префикса «__». Это обман В самом деле со стороны внешнего кода, и если они так отчаялись, я уступлю! :)
Вам следует использовать декоратор @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
Разве это не эквивалент того, что я сделал, «readonly = property (lambda self: self .__ readonly)»?
Вы можете использовать метакласс, который автоматически обертывает методы (или атрибуты класса), которые следуют соглашению об именах, в свойства (бесстыдно взятые из Унификация типов и классов в 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
Вот как:
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. Ваши пользователи должны использовать общедоступный интерфейс вашего класса, потому что он хорошо спроектирован, а не потому, что вы их заставляете.
Обновлено: похоже, Кевин уже опубликовал аналогичную версию.
Я знаю, что Уильям Келлер - однозначно самое чистое решение ... но вот кое-что, что я придумал ...
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")
К сожалению, это решение не может обрабатывать частные переменные (__ именованные переменные).
что ж, способ есть, просто он не такой четкий и лаконичный, как хотелось бы ... -k