2013-04-29 2 views
6

Возьмите следующий классКак ограничить Свойства, доступные на питоне класса

class Person(object): 

def __init__(self, first_name, last_name): 
    self.first_name = first_name 
    self.last_name = last_name 

Как я могу предотвратить следующее использование?

p1 = Person('Foo', 'Bar') 
p1.firstname='Fooooooo' 

Код выше будет успешно выполняться в Python, однако, была допущена ошибка с именем свойства, то есть его отсутствие _ между first и name

UPDATE: Это звучит как «обезьяна Patching ", почему я хочу это сделать?

Мое намерение состоит в том, чтобы просто помочь пользователю установить неправильное свойство, выполнить код и увидеть неожиданное поведение и незамедлительно не знать об ошибке.

Что здесь рекомендует Pythonic?

+0

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

+1

Если вы действительно должны, '__slots__' может достичь того, что вы хотите сделать. Тем не менее, это действительно не должно быть использовано для этого ... Я бы подумал, что это «__slots__». В конечном счете, именно поэтому вам необходимо предоставить согласованный API и хорошую документацию. Если пользователь получит ошибку, они поймут, что они неправильно использовали ваш API. – mgilson

+2

@BenDundee: monkeypatching в основном о добавлении/изменении поведения во время выполнения. Опираясь на те же механизмы, приведенный выше пример определенно является опечаткой, а не monkeypatch;) –

ответ

7

Прежде всего, это почти всегда плохая идея делать такую ​​вещь. Если только причина, почему вы хотите, чтобы убедиться, что вы не делаете опечатки - для этого есть лучшие инструменты (думаю, IDE или pylint). Если вы на 100% уверены, что вам нужна такая вещь, вот два способа сделать это:

Первый способ - вы можете сделать это с использованием метода __setattr__. См python __setattr__ documentation

class Person(object): 

    def __init__(self, first_name, last_name): 
     self.__dict__['first_name'] = first_name 
     self.__dict__['last_name'] = last_name 
    def __setattr__(self, name, value): 
     if name in self.__dict__: 
      super(Person, self).__setattr__(name, value) 
     else: 
      raise AttributeError("%s has no attribute %s" %(self.__class__.__name__, name)) 

и выход:

In [49]: a = Person(1, 2) 

In [50]: a.a = 2 
--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
/usr/local/lib/python2.7/dist-packages/django/core/management/commands/shell.pyc in <module>() 
----> 1 a.a = 2 

/usr/local/lib/python2.7/dist-packages/django/core/management/commands/shell.pyc in __setattr__(self, name, value) 
     8    super(Person, self).__setattr__(name, value) 
     9   else: 
---> 10    raise AttributeError("%s has no attribute %s" %(self.__class__.__name__, name)) 

AttributeError: Person has no attribute a 

В качестве альтернативы, вы можете сделать это с помощью __slots__ (python __slots__ documentation):

class Person(object): 
    __slots__ = ("first_name", "last_name") 

    def __init__(self, first_name, last_name): 
     self.first_name = first_name 
     self.last_name = last_name 

выход:

In [32]: a = Person("a", "b") 

In [33]: a.first_name 
Out[33]: 'a' 

In [34]: a.a = 1 
--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
/usr/local/lib/python2.7/dist-packages/django/core/management/commands/shell.pyc in <module>() 
----> 1 a.a = 1 

AttributeError: 'Person' object has no attribute 'a' 

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

+0

В то время как (почти - это должно вызывать 'super .__ setattr__', а не' super .__ setitem__') технически корректное («худший вид правильного»), это прекрасный пример того, что НЕ делать в Python. Борьба с языком - всего лишь пустая трата времени и крупная ПИТА. –

+0

извините за setitem, исправил это. Я согласен, что большую часть времени это неправильно, но в одном случае я использовал его - и нашел его полезным - это создание ORM (вы все равно сражаетесь с языком). И в конечном итоге это была не пустая трата времени. –

+0

+1 для '__slots__' – Aprillion

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