Почему пустые классы и функции 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'
Существует ли принципиальное различие между встроенными и "настраиваемыми" классами и функциями, которые учитывают это поведение?
Ответы
Ответ 1
Отличие состоит в том, что оба объекта функции и ваш объект 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__'
Вы можете эмулировать поведение, используя слоты (хотя и только в классах нового стиля):
>>> 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
Встроенные типы записываются на C и не могут быть изменены таким образом. Но после типа/объединения классов, введенного в 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 и добавлять к ним такие функции.
Ответ 3
Некоторые встроенные модули могут быть более ограничительными. Кроме того, классы, реализованные с помощью слотов, также не будут принимать произвольные атрибуты.
Ответ 4
Если вам нужна какая-то симулятивная защита в вашем собственном классе, вы можете использовать метод __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