Métodos dynamics de clase Python.

Digamos que hay:

class A(B): ... 

donde B podría ser object y ... no es:

 @classmethod # or @staticmethod def c(cls): print 'Hello from c!' 

¿Qué tengo que hacer para que la llamada Ac() no active AttributeError ?

En otras palabras, sé que es posible agregar manualmente métodos de clase a una clase en tiempo de ejecución. Pero, ¿es posible hacerlo automáticamente, por ejemplo, cada vez que falta un método de clase se crea algún método ficticio?

En otras palabras, solo si pudiera reemplazar A.__dict__ con mi A.__dict__ que maneja __getitem__ – pero A.__dict__ parece que no se puede escribir …

Puede lograr esto utilizando un gancho __getattr__ en una metaclase .

 class DefaultClassMethods(type): def __getattr__(cls, attr): def _defaultClassMethod(cls): print 'Hi, I am the default class method!' setattr(cls, attr, classmethod(_defaultClassMethod)) return getattr(cls, attr) 

Manifestación:

 >>> class DefaultClassMethods(type): ... def __getattr__(cls, attr): ... def _defaultClassMethod(cls): ... print 'Hi, I am the default class method!' ... setattr(cls, attr, classmethod(_defaultClassMethod)) ... return getattr(cls, attr) ... >>> class A(object): ... __metaclass__ = DefaultClassMethods ... >>> A.spam > >>> A.spam() Hi, I am the default class method! 

Tenga en cuenta que establecemos el resultado de la llamada de classmethod clase directamente en la clase, y la classmethod en caché para futuras búsquedas.

Si necesita regenerar el método de clase en cada llamada, use el mismo método para vincular una función a una instancia pero con la clase y la metaclase en su lugar (usando cls.__metaclass__ para ser consistente con la subclase de metaclase):

 from types import MethodType class DefaultClassMethods(type): def __getattr__(cls, attr): def _defaultClassMethod(cls): print 'Hi, I am the default class method!' return _defaultClassMethod.__get__(cls, cls.__metaclass__) 

Para los métodos estáticos, simplemente devuelva la función directamente en todos los casos, no es necesario muck con el decorador de staticmethod o el protocolo descriptor.

Los comportamientos proporcionados a las instancias por métodos como __getattr__ y el protocolo descriptor también pueden funcionar para las clases, pero en ese caso, debe codificarlos en la metaclase de la clase.

En este caso, todo lo que hay que hacer es configurar la función metaclase __getattr__ para que genere automáticamente el atributo de clase deseado.

(El truco de setattr, getattr es dejar que Python haga la función-> método transoform sin necesidad de meterse con él)

 class AutoClassMethod(type): def __getattr__(cls, attr): default = classmethod(lambda cls: "Default class method for " + repr(cls)) setattr(cls, attr, default) return getattr(cls, attr) class A(object): __metaclass__ = AutoClassMethod @classmethod def b(cls): print cls 
 >>> class C(object): ... pass ... >>> Cm = classmethod(lambda cls: cls.__name__) >>> Cm() 'C' 

O puedes usar algo como esto:

 class Wrapper(object): def __init__(self, clz, default=lambda cls: None): self._clz = clz self._default = default def __getattr__(self, attr): # __attrs__ will be getted from Wrapper if attr.startswith('__'): return self.__getattribute__(attr) if not hasattr(self._clz, attr): setattr(self._clz, attr, classmethod(self._default)) return getattr(self._clz, attr) def __call__(self, *args, **kwargs): return self._clz(*args, **kwargs) >>> class C(object): ... pass ... >>> C = Wrapper(C, default=lambda cls: cls.__name__) >>> c = C() >>> print Cm() 'C' >>> print cm() # now instance have method "m" 'C'