Подтвердить что ты не робот

Python, __slots__ и "атрибут доступен только для чтения"

Я хочу создать объект в python, который имеет несколько атрибутов, и я хочу защитить себя от случайного использования неправильного имени атрибута. Код выглядит следующим образом:

class MyClass( object ) :
    m = None # my attribute
    __slots__ = ( "m" ) # ensure that object has no _m etc

a = MyClass() # create one
a.m = "?"  # here is a PROBLEM

Но после запуска этого простого кода я получаю очень странную ошибку:

Traceback (most recent call last):
  File "test.py", line 8, in <module>
    a.m = "?"
AttributeError: 'test' object attribute 'm' is read-only

Есть ли какой-нибудь мудрый программист, который может немного почистить свое время и рассказать мне о ошибках "только для чтения"?

4b9b3361

Ответ 1

Когда вы объявляете переменные экземпляра с помощью __slots__, Python создает объект дескриптора как переменную класса с тем же именем. В вашем случае этот дескриптор перезаписывается переменной класса m которую вы определяете в следующей строке:

  m = None # my attribute

Вот что вам нужно сделать: не определяйте переменную класса с именем m и инициализируйте переменную экземпляра m в методе __init__.

class MyClass(object):
  __slots__ = ("m",)
  def __init__(self):
    self.m = None

a = MyClass()
a.m = "?"

Как примечание, кортежам с одиночными элементами нужна запятая после элемента. Оба работают в вашем коде, потому что __slots__ принимает одну строку или итерируемую/последовательность строк. В общем, для определения кортежа, содержащего элемент 1, используйте (1,) или 1, а не (1).

Ответ 2

Вы полностью злоупотребляете __slots__. Это предотвращает создание __dict__ для экземпляров. Это имеет смысл, если вы сталкиваетесь с проблемами памяти со многими маленькими объектами, потому что избавление от __dict__ может уменьшить след. Это хардкорная оптимизация, которая не нужна в 99,9% всех случаев.

Если вам нужна такая безопасность, которую вы описали, то Python действительно является неправильным языком. Лучше использовать что-то строгое, как Java (вместо того, чтобы писать Java на Python).

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

Просто для полноты, здесь ссылка для документации для слотов.

Ответ 3

__slots__ работает с переменными экземпляра, тогда как у вас есть переменная класса. Вот как вы должны это делать:

class MyClass( object ) :
  __slots__ = ( "m", )
  def __init__(self):
    self.m = None

a = MyClass()
a.m = "?"       # No error

Ответ 4

Рассмотрим это.

class SuperSafe( object ):
    allowed= ( "this", "that" )
    def __init__( self ):
        self.this= None
        self.that= None
    def __setattr__( self, attr, value ):
        if attr not in self.allowed:
            raise Exception( "No such attribute: %s" % (attr,) )
        super( SuperSafe, self ).__setattr__( attr, value )

Лучший подход - использовать модульные тесты для такого рода проверки. Это достаточное количество накладных расходов во время выполнения.

Ответ 5

class MyClass( object ) :
    m = None # my attribute

m здесь атрибуты класса, а не атрибут экземпляра. Вам нужно связать его с вашим экземпляром самостоятельно в __init__.