Каков самый простой и краткий способ сделать выбранные атрибуты в экземпляре доступными только для чтения?

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

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
8
0
380
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

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

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

что ж, способ есть, просто он не такой четкий и лаконичный, как хотелось бы ... -k

Kevin Little 24.09.2008 06:23

Вы можете установить атрибут attr. to property (), то НИЧЕГО, даже сам экземпляр вообще не сможет получить к нему доступ!

William Keller 24.09.2008 06:53

(Хотя я также хотел бы отметить, что ваш второй пункт верен!)

William Keller 24.09.2008 07:01

Специальный модуль расширения может значительно усложнить изменение выбранных атрибутов

jfs 24.09.2008 07:11

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 Тем не менее, он по-прежнему избегает лямбда-шума.

William Keller 24.09.2008 06:28

Это не атрибут только для чтения, потому что другой код все еще может выполнять "c._C__readonly = 4"

John Millikin 24.09.2008 06:44

Да, я знал, что это «искажение» используется для префикса «__». Это обман В самом деле со стороны внешнего кода, и если они так отчаялись, я уступлю! :)

Kevin Little 24.09.2008 07:00
Ответ принят как подходящий

Вам следует использовать декоратор @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)»?

Kevin Little 24.09.2008 06:27

Вы можете использовать метакласс, который автоматически обертывает методы (или атрибуты класса), которые следуют соглашению об именах, в свойства (бесстыдно взятые из Унификация типов и классов в 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")

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

Другие вопросы по теме