una otra objeto metodo llamar listas instanciar ejemplo dentro crear con como clases clase python class metaprogramming static-methods setattr

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í:

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 de ModelPatcher 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 haces a.func , la llamada a setattr() 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 usar self._func o self.__class__._func
  • self será una instancia de a , si configuras un atributo en la instancia, solo estará disponible para esa instancia, no para la clase. Entonces, incluso después de llamar a setattr(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 como setattr(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()