otra - ¿Cómo puedo crear dinámicamente métodos de clase para una clase en python
metodo init python (6)
Esta pregunta ya tiene una respuesta aquí:
- Agregar un método a una instancia de objeto existente 17 respuestas
Si defino un pequeño programa de Python como
class a():
def _func(self):
return "asdf"
# Not sure what to resplace __init__ with so that a.func will return asdf
def __init__(self, *args, **kwargs):
setattr(self, ''func'', classmethod(self._func))
if __name__ == "__main__":
a.func
Recibo el error de rastreo
Traceback (most recent call last):
File "setattr_static.py", line 9, in <module>
a.func
AttributeError: class a has no attribute ''func''
Lo que estoy tratando de averiguar es, ¿cómo puedo establecer dinámicamente un método de clase para una clase sin instanciar un objeto?
Editar:
La respuesta para este problema es
class a():
pass
def func(cls, some_other_argument):
return some_other_argument
setattr(a, ''func'', classmethod(func))
if __name__ == "__main__":
print(a.func)
print(a.func("asdf"))
devuelve la siguiente salida
<bound method type.func of <class ''__main__.a''>>
asdf
1. La idea básica: utilizar una clase extra para mantener los métodos
Encontré una forma significativa de hacer el trabajo:
Primero, definimos una BaseClass así:
class MethodPatcher:
@classmethod
def patch(cls, target):
for k in cls.__dict__:
obj = getattr(cls, k)
if not k.startswith(''_'') and callable(obj):
setattr(target, k, obj)
Ahora que tenemos una clase original:
class MyClass(object):
def a(self):
print(''a'')
Luego definimos el nuevo método que queremos agregar en una nueva clase Patcher
:
(No haga que el nombre del método comience con un _
en este caso)
class MyPatcher(MethodPatcher):
def b(self):
print(''b'')
Luego llame:
MyPatcher.patch(MyClass)
Entonces, encontrará que el nuevo método b(self)
se agrega al MyClass
original:
obj = MyClass()
obj.a() # which prints an ''a''
obj.b() # which prints a ''b''
2. Haz que la sintaxis sea menos detallada, usamos el decorador de clases
Ahora, si tenemos MethodPatcher
decalred, tenemos que hacer dos cosas:
- define una clase infantil
ChildClass
deModelPatcher
que contiene los métodos adicionales para agregar - llame a
ChildClass.patch(TargetClass)
Así que pronto descubrimos que el segundo paso se puede simplificar mediante el uso de un decorador:
Definimos un decorador:
def patch_methods(model_class):
def do_patch(cls):
cls.patch(model_class)
return do_patch
Y podemos usarlo como:
@patch_methods(MyClass)
class MyClassPatcher(MethodPatcher):
def extra_method_a(self):
print(''a'', self)
@classmethod
def extra_class_method_b(cls):
print(''c'', cls)
# !!ATTENTION!! the effect on declaring staticmethod here may not work as expected:
# calling this method on an instance will take the self into the first argument.
# @staticmethod
# def extra_static_method_c():
# print(''c'')
3. Envolver juntos
Entonces, ahora podemos poner la definición de MethodPatcher
y patch_method
en un solo módulo:
# method_patcher.py
class MethodPatcher:
@classmethod
def patch(cls, target):
for k in cls.__dict__:
obj = getattr(cls, k)
if not k.startswith(''_'') and callable(obj):
setattr(target, k, obj)
def patch_methods(model_class):
def do_patch(cls):
cls.patch(model_class)
return do_patch
Entonces podemos usarlo libremente:
from method_patcher import ModelPatcher, patch_model
4. Solución final: declaración más simple
Pronto descubrí que la clase MethodPatcher
no es necesaria, mientras que el decorador @patch_method
puede hacer el trabajo, por lo que solo necesitamos un patch_method
:
def patch_methods(model_class):
def do_patch(cls):
for k in cls.__dict__:
obj = getattr(cls, k)
if not k.startswith(''_'') and callable(obj):
setattr(model_class, k, obj)
return do_patch
Y el uso se convierte en:
@patch_methods(MyClass)
class MyClassPatcher:
def extra_method_a(self):
print(''a'', self)
@classmethod
def extra_class_method_b(cls):
print(''c'', cls)
# !!ATTENTION!! the effect on declaring staticmethod here may not work as expected:
# calling this method on an instance will take the self into the first argument.
# @staticmethod
# def extra_static_method_c():
# print(''c'')
Estoy usando Python 2.7.5, y no pude lograr que las soluciones anteriores funcionen para mí. Esto es con lo que terminé:
# define a class object (your class may be more complicated than this...)
class A(object):
pass
def func(self):
print ''I am class {}''.format(self.name)
A.func = func
# using classmethod() here failed with:
# AttributeError: type object ''...'' has no attribute ''name''
Hay un par de problemas aquí:
-
__init__
solo se ejecuta cuando creas una instancia, por ejemplo,obj = a()
. Esto significa que cuando hacesa.func
, la llamada asetattr()
no ha sucedido - No puede acceder a los atributos de una clase directamente desde los métodos de esa clase, así que en lugar de usar simplemente
_func
dentro de__init__
, necesitaría usarself._func
oself.__class__._func
-
self
será una instancia dea
, si configuras un atributo en la instancia, solo estará disponible para esa instancia, no para la clase. Entonces, incluso después de llamar asetattr(self, ''func'', self._func)
,a.func
generará un AttributeError - El uso de
staticmethod
la forma en que usted es no hará nada,staticmethod
devolverá una función resultante, no modifica el argumento. Entonces, en cambio, querrías algo comosetattr(self, ''func'', staticmethod(self._func))
(pero teniendo en cuenta los comentarios anteriores, esto aún no funcionará)
Entonces, ahora la pregunta es, ¿qué estás tratando de hacer? Si realmente desea agregar un atributo a una clase al inicializar una instancia, puede hacer algo como lo siguiente:
class a():
def _func(self):
return "asdf"
def __init__(self, *args, **kwargs):
setattr(self.__class__, ''func'', staticmethod(self._func))
if __name__ == ''__main__'':
obj = a()
a.func
a.func()
Sin embargo, esto todavía es un poco raro. Ahora puede acceder a a.func
y llamarlo sin ningún problema, pero el self
argumento de a.func
siempre será la instancia creada más recientemente de a
. Realmente no puedo pensar en ninguna manera _func()
convertir un método de instancia como _func()
en un método estático o método de clase de la clase.
Ya que intenta agregar dinámicamente una función a la clase, ¿quizás algo como lo siguiente está más cerca de lo que realmente está tratando de hacer?
class a():
pass
def _func():
return "asdf"
a.func = staticmethod(_func) # or setattr(a, ''func'', staticmethod(_func))
if __name__ == ''__main__'':
a.func
a.func()
Necesitas setattr(self, ''func'', staticmethod(self._func))
Necesita inicializar la variable=a()
clase variable=a()
para llamar a __init__
No hay init en la clase estática
Puede agregar dinámicamente un método de clase a una clase por simple asignación al objeto de clase o por setattr en el objeto de clase. Aquí estoy usando la convención de Python que las clases comienzan con letras mayúsculas para reducir la confusión:
# define a class object (your class may be more complicated than this...)
class A(object):
pass
# a class method takes the class object as its first variable
def func(cls):
print ''I am a class method''
# you can just add it to the class if you already know the name you want to use
A.func = classmethod(func)
# or you can auto-generate the name and set it this way
the_name = ''other_func''
setattr(A, the_name, classmethod(func))
Puedes hacerlo de esta manera
class a():
def _func(self):
return "asdf"
setattr(a, ''func'', staticmethod(a._func))
if __name__ == "__main__":
a.func()