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
Есть ли какой-нибудь мудрый программист, который может немного почистить свое время и рассказать мне о ошибках "только для чтения"?
Ответы
Ответ 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__
.