2013-07-11 1 views
16

Я видел два разных объекта Python, используемых для группировки произвольных данных: пустые классы и функции.Почему пустые классы и функции Python работают как произвольные контейнеры данных, но не другие объекты?

def struct(): 
    pass 

record = struct 
record.number = 3 
record.name = "Zoe" 


class Struct: 
    pass 

record = Struct() 
record.number = 3 
record.name = "Zoe" 

Даже если класс не пуст, он работает, пока он определен во время выполнения.

Но когда я стал дерзким и пытался сделать это со встроенными функциями или классами, это не сработало.

record = set() 
record.number = 3 
AttributeError: 'set' object has no attribute 'number' 

record = pow 
pow.number = 3 
AttributeError: 'builtin_function_or_method' object has no attribute 'number' 

Существует ли принципиальное различие между встроенными и «настраиваемыми» классами и функциями, которые учитывают это поведение?

+0

Там ошибка в первом примере: он должен быть 'запись = struct' withouth'() ', иначе 'record' будет' None', а следующая строка выдает исключение. Кроме того, следует отметить, что использование функций работает только потому, что пользовательская функция является всего лишь еще одним объектом в python, то есть вы можете назначить произвольные атрибуты. Если функция пуста или нет, она тоже не имеет к ней никакого отношения. Но, хотя вы, вероятно, можете использовать это для какой-то напуганной магии мета-динамического программирования, я не могу придумать, почему использование функции в качестве контейнера хранения было бы предпочтительнее над классом ... – l4mpi

+0

@ l4mpi Я думаю, что это полезно, если вы хотите эмулировать замыкания, которые «пишут» в их охватывающую область в Python 2.x перед «nonlocal». Вместо локальных варов используются специальные атрибуты локального объекта функции. – millimoose

+0

Ой, вы правы в первом примере. –

ответ

6

Разница заключается в том, что оба объекта функции и ваш объект Struct имеют атрибут __dict__, но set экземпляров и встроенных функций нет:

>>> def struct(): 
...  pass 
... 
>>> record = struct 
>>> record.number = 2 
>>> struct.__dict__ 
{'number': 2} 
>>> class Struct: 
...  pass 
... 
>>> record = Struct() 
>>> record.number = 3 
>>> record.__dict__ 
{'number': 3} 
>>> record=set() 
>>> record.__dict__ 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'set' object has no attribute '__dict__' 
>>> pow.__dict__ 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'builtin_function_or_method' object has no attribute '__dict__' 

Вы можете эмулировать behavour с помощью слотов (хотя только на новых классов):

>>> class StructWithSlots(object): 
...  __slots__ = [] 
... 
>>> record = StructWithSlots() 
>>> record.number = 3 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'StructWithSlots' object has no attribute 'number' 
>>> record.__dict__ 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'StructWithSlots' object has no attribute '__dict__' 
2

Некоторые встроенные модули могут быть более ограничительными. Кроме того, классы, реализованные с помощью слотов, также не будут принимать произвольные атрибуты.

3

Встроенные типы записаны на C и не могут быть изменены таким образом. Но после type/class unification, введенного в py2.2, вы можете наследовать от встроенных типов и переопределять или добавлять свои собственные атрибуты в этот подкласс.

Вы можете использовать forbiddenfood пакет для добавления атрибутов встроенных типов:

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

>>> from forbiddenfruit import curse 
>>> def words_of_wisdom(self): 
...  return self * "blah " 
>>> curse(int, "words_of_wisdom", words_of_wisdom) 
>>> assert (2).words_of_wisdom() == "blah blah " 

И, конечно, если вы дерзкий достаточно, то вы можете создать свои собственные типы в C и добавить такие функции к нему.

+4

Конечно, можно создать тип в C, который можно использовать так? – delnan

+0

@ delnan, например. тип объекта. Возможно, было бы интересно погрузиться в источник Python, который является особым случаем - разрешить объекты expando или предотвращать их. – millimoose

+0

@delnan Но я бы не назвал его встроенным типом, потому что вы пишете свой собственный тип в C (это не из стандартной библиотеки). Речь идет о встроенных и пользовательских классах. –

1

Если вы хотите получить некоторую симуляционную защиту в своем классе, вы можете использовать метод __setattr__().

class TestClass(object): 
    # Accept the attributes in this list 
    __valid_attributes = ["myattr1", "myattr2"] 

    def __setattr__(self, name, value): 
     if not name in TestClass.__valid_attributes: 
      raise AttributeError(
       "{0} has no attribute '{1}'".format(self.__class__.__name__, name)) 
     self.__dict__[name] = value 

Теперь вы можете сделать что-то вроде этого:

t = TestClass() 
t.noattr = "test" # AttributeError: TestClass has no attribute 'noattr' 

Но «действительные атрибуты» по-прежнему могут быть установлены:

t = TestClass() 
t.myattr1 = "test" 
print(t.myattr1) # test 
+2

Поздравляем, вы только что изобрели колесо ['__slots__'] (http://docs.python.org/2/reference/datamodel.html#slots). – RoadieRich

+0

не знал о __slots__, спасибо за ваш комментарий – joente

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