Por supuesto que las metaclases son la forma más Pythonic ir cuando se quiere modificar la forma ese pitón crea los objetos. Lo cual se puede hacer anulando el método __new__
de su clase. Sin embargo, hay algunos puntos en torno a este problema (especialmente para 3.X Python) que me gustaría mencionar:
types.FunctionType
no protege los métodos especiales de ser decorado, ya que son los tipos de función. Como una forma más general, puede decorar los objetos cuyos nombres no se inician con doble guión bajo (__
). Otra ventaja de este método es que también cubre los objetos que existen en espacio de nombres y comienza con __
pero no se funciona como __qualname__
, __module__
, etc.
El argumento namespace
en la cabecera __new__
's no contiene atributos de clase dentro del __init__
. La razón es que el __new__
se ejecuta antes del __init__
(inicialización).
No es necesario usar un classmethod
como decorador, ya que la mayoría de las veces importa el decorador desde otro módulo.
- Si la clase es contener un elemento mundial (fuera del lado de la
__init__
) por negarse a ser decorada junto comprobar si el nombre no se ha iniciado con __
se puede comprobar el tipo con types.FunctionType
estar seguro de que usted no está decorando un objeto sin función.
Aquí es una metacalss de ejemplo que puede utilizar:
class TheMeta(type):
def __new__(cls, name, bases, namespace, **kwds):
# if your decorator is a class method of the metaclass use
# `my_decorator = cls.my_decorator` in order to invoke the decorator.
namespace = {k: v if k.startswith('__') else my_decorator(v) for k, v in namespace.items()}
return type.__new__(cls, name, bases, namespace)
Demostración:
def my_decorator(func):
def wrapper(self, arg):
# You can also use *args instead of (self, arg) and pass the *args
# to the function in following call.
return "the value {} gets modified!!".format(func(self, arg))
return wrapper
class TheMeta(type):
def __new__(cls, name, bases, namespace, **kwds):
# my_decorator = cls.my_decorator (if the decorator is a classmethod)
namespace = {k: v if k.startswith('__') else my_decorator(v) for k, v in namespace.items()}
return type.__new__(cls, name, bases, namespace)
class MyClass(metaclass=TheMeta):
# a = 10
def __init__(self, *args, **kwargs):
self.item = args[0]
self.value = kwargs['value']
def __getattr__(self, attr):
return "This class hasn't provide the attribute {}.".format(attr)
def myfunction_1(self, arg):
return arg ** 2
def myfunction_2(self, arg):
return arg ** 3
myinstance = MyClass(1, 2, value=100)
print(myinstance.myfunction_1(5))
print(myinstance.myfunction_2(2))
print(myinstance.item)
print(myinstance.p)
Salida:
the value 25 gets modified!!
the value 8 gets modified!!
1
This class hasn't provide the attribute p. # special method is not decorated.
Para la comprobación de la tercera i TEM de las notas antes mencionadas, puede descomentar la línea a = 10
y hacer print(myinstance.a)
y ver el resultado a continuación, cambiar el diccionario por comprensión en __new__
como sigue entonces ver el resultado de nuevo:
namespace = {k: v if k.startswith('__') and not isinstance(v, types.FunctionType)\
else my_decorator(v) for k, v in namespace.items()}
Estoy totalmente de acuerdo, también estoy mucho más cómodo con la decoración explícita de cada función en lugar de un decorador mágico implícito. Era curioso, es todo. – dochead