2009-05-04 2 views
16

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

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

Это похоже на ненужный случай использования. Зачем ты это делаешь? Почему вы не используете свойства? –

+0

Я хочу получить от записи имя свойства с опечаткой, для eample, если я пишу object.my_prperty = 1 вместо object.my_property = 1 (пропущенный 'o'), python не будет показывать никаких ошибок, но у меня будет логическая ошибка в моей программе. Поэтому я хочу ограничить свойства своим предопределенным множеством, так что только они могут быть выполнены. Как я могу решить эту проблему со свойствами? – grigoryvp

+9

, как правило, в python мы принимаем возможность орфографических ошибок в присваиваниях и записываем хорошие тесты, которые также будут захватывать поверхностные и более семантические ошибки. Я смиренно предлагаю, чтобы, если вы хотите написать python, вам следует подумать о том, чтобы сделать это с помощью python, вместо того, чтобы бороться с зубами и ногтями языка. Я добавлю, что опечатки в моем коде на Python стоили мне * возможно * 20 минут времени за последние 9 лет. – llimllib

ответ

38

При объявлении переменных экземпляра с помощью __slots__, Python создает descriptor object в качестве переменной класса с таким же именем. В вашем случае, этот дескриптор перезаписывает переменной класса 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).

+0

Вы знаете, почему, если я писал: __slots__ = [«м»] м = 5 Python будет перезаписывать объект дескриптора с переменная и не будет использовать метод set() объектов дескриптора для установки значения вместо этого? – grigoryvp

+7

Поскольку m в этом контексте является переменной класса, а не переменной экземпляра. Чтобы использовать метод set() дескриптора, вы должны назначить m объекта, а не класса. Если вы хотите инициализировать переменную экземпляра объекта, выполните ее из метода __init__, как это было в моем фрагменте кода. –

+0

Ключ от документов (который перемещался btw [здесь] (https://docs.python.org/2/reference/datamodel.html#slots)) - это '__slots__ реализуются на уровне класса, создавая дескрипторы для каждой переменной имя. В результате атрибуты класса не могут использоваться для установки значений по умолчанию для переменных экземпляра, определенных __slots__; в противном случае атрибут класса будет перезаписывать присвоение дескриптора'. Так что происходит, что (независимо от порядка определения 'm' и' __slots__'?) При попытке присваивания метод set больше не привязан к чему-либо - и он не возвращается, но возвращается к MyClass.m. –

7

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

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

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

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

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) 

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

+4

+1 для не злоупотреблять __slots__ – Ravi

+3

Но это больше строк кода по сравнению с __slots__? Зачем вручную проверять, что может автоматически выполнять язык. – grigoryvp

+3

Чтобы избежать сообщений «очень странной ошибки» и избежать необходимости тратить много времени на отладку внутренних тайн __slots__. Я предпочитаю очевидные и простые решения, где секретные знания не требуются для отладки. –

14

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

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

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

Только для полноты, вот documentation link for slots.

+4

+1 для полноты. Как упоминалось повсюду, __slots__ - неплохая идея, но требует тщательного использования. Это больше, чем проверка безопасности и добавляет много сложности в реализацию, которая потребует обратить внимание на то, как вы используете объект. –

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^