site - framework django para python
¿Por qué las clases y funciones vacías de Python funcionan como contenedores de datos arbitrarios, pero no como otros objetos? (4)
He visto dos objetos Python diferentes utilizados para agrupar datos arbitrarios: clases y funciones vacías.
def struct():
pass
record = struct
record.number = 3
record.name = "Zoe"
class Struct:
pass
record = Struct()
record.number = 3
record.name = "Zoe"
Incluso si la clase no está vacía, parece funcionar siempre y cuando esté definida en tiempo de ejecución.
Pero cuando me puse arrogante y traté de hacer esto con funciones o clases integradas, no funcionó.
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''
¿Existe una diferencia fundamental entre las clases y funciones incorporadas y "personalizadas" que explican este comportamiento?
Algunos elementos incorporados pueden ser más restrictivos. Además, las clases implementadas con ranuras tampoco aceptarán atributos arbitrarios.
La diferencia es que tanto los objetos de función como su objeto Struct tienen un atributo __dict__
, pero las instancias de set
y las funciones integradas no:
>>> 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__''
Puede emular el comportamiento utilizando ranuras (aunque solo en clases de nuevo estilo):
>>> 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__''
Los tipos incorporados están escritos en C y no se pueden modificar de esa manera. Pero después de la unificación de tipo / clase introducida en py2.2, ahora puede heredar de un tipo integrado y anular o agregar sus propios atributos a esa subclase.
Puede usar el paquete forbiddenfood para agregar atributos a los tipos incorporados:
El objetivo de este proyecto es ofrecerle el camino para encontrar el cielo en las pruebas, pero podría llevarlo al infierno si lo usa en el código de producción.
>>> 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 "
Y, por supuesto, si eres lo suficientemente arrogante, puedes crear tus propios tipos en C y agregarle esas características.
Si desea cierta protección simular en su propia clase, puede usar el __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
Ahora puedes hacer algo como esto:
t = TestClass()
t.noattr = "test" # AttributeError: TestClass has no attribute ''noattr''
Pero todavía se pueden establecer "atributos válidos":
t = TestClass()
t.myattr1 = "test"
print(t.myattr1) # test