2012-03-11 7 views
11

Por lo tanto, estoy leyendo un poco sobre metaclases en Python, y cómo el alter-ego de tres argumentos de type() se usa para crear clases dinámicamente. Sin embargo, el tercer argumento es generalmente un dict que inicializa la clase '__dict__' de la clase que se creará.Metaclasses y __slots__?

Si quiero crear dinámicamente clases basadas en una metaclase que use __slots__ en lugar de __dict__, ¿cómo podría hacer esto? ¿Todavía se usa type() de alguna manera junto con __new__()?

Como un FYI, soy consciente de los usos adecuados para __slots__, para ahorrar memoria al crear grandes cantidades de una clase en lugar de abusar de ella para aplicar una forma de seguridad de tipo.


ejemplo de una clase normal (de nuevo estilo) que establece __metaclass__ y utiliza un __dict__: el cuarto argumento

class Meta(type): 
    def __new__(cls, name, bases, dctn): 
     # Do something unique ... 
     return type.__new__(cls, name, bases, dctn) 

class Foo(object): 
    __metaclass__ = Meta 

    def __init__(self): 
     pass 


En lo anterior, type.__new__() se llama y (que se convierte en la tercera cuando se utiliza en realidad) crea un __dict__ en Foo. Pero si quisiera modificar Meta para incluir __slots__, entonces no tengo ningún diccionario para pasar a la función type()__new__() (hasta donde yo sé, no he probado nada de esto todavía, solo ponderando e intentando encontrar algo tipo de escenario de caso de uso).

Editar: Un cálculo rápido, pero no probado, es tomar un diccionario de los valores que se ponga en las variables de __slots__ y pasarlo a type.__new__(). A continuación, agregue __init__() a Meta que rellena las variables __slots__ del dict. Aunque, no estoy seguro de cómo que dict alcanzaría __init__(), debido a que la declaración de __slots__ impide __dict__ que se creen menos __dict__ se define en __slots__ ...

Respuesta

15

No se puede crear un tipo con un atributo __slots__ no vacío. Lo que puede hacer es insertar un atributo __slots__ en dict de la nueva clase, así:

class Meta(type): 
    def __new__(cls, name, bases, dctn): 
     dctn['__slots__'] = ('x',) 
     return type.__new__(cls, name, bases, dctn) 

class Foo(object): 
    __metaclass__ = Meta 

    def __init__(self): 
     pass 

atributos Ahora Foo ha ranurados:

foo = Foo() 
foo.y = 1 

tiros

AttributeError: 'Foo' object has no attribute 'y' 
+0

Esto parece interesante. Lo intentaré en unos minutos. – Kumba

+0

Nunca llegué a probar esto, pero creo que esto técnicamente responde la pregunta. Lo resolveré algún día. – Kumba

+0

Recién ahora inicie sesión en SO en años; esta respuesta es finalmente lo que funcionó. ¡Gracias! – Kumba

5

dctn en su ejemplo de una metaclase es el diccionario clase, no el instancia diccionario. __slots__ reemplaza el ejemplo diccionario. Si crea dos ejemplos:

class Meta(type): 
    def __new__(cls, name, bases, dctn): 
     return type.__new__(cls, name, bases, dctn) 

class Foo1(object): 
    __metaclass__ = Meta 

class Foo2(object): 
    __metaclass__ = Meta 
    __slots__ = ['a', 'b'] 

continuación:

>>> f1 = Foo1() 
>>> f2 = Foo2() 
>>> f1.__dict__ is Foo1.__dict__ 
False 
>>> f2.__dict__ 
Traceback (most recent call last): 
    ... 
AttributeError: 'Foo2' object has no attribute '__dict__' 
+1

bien, todavía estoy aprendiendo las cuerdas en Python aquí y estoy empezando a envolver mi cabeza alrededor lema de "todo es un objeto" (similar a la de UNIX "todo es un archivo") de Python. ¿No es posible definir '__slots__' en la Metaclass y hacer que se propague a las clases creadas a partir de la metaclase? ¿O es aquí donde una clase base se vuelve más útil? O, ¿me gustaría combinar ambos conceptos? – Kumba

+0

Buena pregunta. Definitivamente puedes definir '__slots__' en la metaclase simplemente agregando' dctn'. Sin embargo, la subclasificación de una clase base generalmente sería un mejor enfoque. En general, si no está seguro de si necesita una metaclase, entonces no es así. – aquavitae

+0

Es por eso que estoy tratando de aprender sobre las metaclases un poco más, por lo que puedo determinar si realmente las necesito o no. Tenga en cuenta que vengo de un entorno en .NET donde tuve que lidiar con genéricos, interfaces y herencia única, y nunca tuve entrenamiento formal en OOP (solo unas pocas clases en C bueno). Tratar de entender las diferencias únicas de Python ... está resultando ser bastante interesante a veces. Al igual que uno encuentra la manera de convertir un spork en un trebuchet que lanza brotes de Bruselas sobre la mesa mientras cena. – Kumba

0

Más sobre ranuras definido en metaclases.

si subclase type 's define no vacío __slots__, Python lanza una TypeError debido a algún complicated implementation-related stuff.

In [1]: 

class Meta(type): 
    __slots__ = ('x') 

--------------------------------------------------------------------------- 
TypeError         Traceback (most recent call last) 
<ipython-input-38-1b16edef8eca> in <module>() 
----> 1 class Meta(type): 
     2  __slots__ = ('x') 

TypeError: nonempty __slots__ not supported for subtype of 'type' 

vacío __slots__ por el contrario no producen ningún error, pero la costura no tener ningún efecto.

In [2]: 

class Meta(type): 
    __slots__ =() 

class Foo(metaclass=Meta): 
    pass 

type(Foo) 

Out [2]: 

__main__.Meta 

In [3]: 

Foo.y = 42 
Foo.y 

Out [3]: 

42 

In [4]: 

Foo.__dict__ 

Out [4]: 

mappingproxy({'y': 42, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__module__': '__main__'}) 

In [5]: 

foo = Meta('foo',(), {}) 
type(foo).__slots__ 

Out [5]: 

() 

In [6]: 

foo.x = 42 
foo.x 

Out [6]: 

42 

In [7]: 

foo.__dict__ 

Out [7]: 

mappingproxy({'__dict__': <attribute '__dict__' of 'foo' objects>, 'x': 42, '__module__': '__main__', '__doc__': None, '__weakref__': <attribute '__weakref__' of 'foo' objects>}) 

In [8]: 

# Testing on non-metaclasses. Just in case. 
class Bar: 
    __slots__ =() 

b = Bar() 
b.__dict__ 

--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
<ipython-input-54-843730c66f3f> in <module>() 
     3 
     4 b = Bar() 
----> 5 b.__dict__ 

AttributeError: 'Bar' object has no attribute '__dict__' 
Cuestiones relacionadas