He leído que es posible agregar un método a un objeto existente (es decir, no en la definición de clase) en Python.
Entiendo que no siempre es bueno hacerlo. Pero, ¿cómo podría uno hacer esto?
fuente
He leído que es posible agregar un método a un objeto existente (es decir, no en la definición de clase) en Python.
Entiendo que no siempre es bueno hacerlo. Pero, ¿cómo podría uno hacer esto?
En Python, hay una diferencia entre funciones y métodos enlazados.
>>> def foo():
... print "foo"
...
>>> class A:
... def bar( self ):
... print "bar"
...
>>> a = A()
>>> foo
<function foo at 0x00A98D70>
>>> a.bar
<bound method A.bar of <__main__.A instance at 0x00A9BC88>>
>>>
Los métodos enlazados se han "enlazado" (cuán descriptivo) a una instancia, y esa instancia se pasará como primer argumento cada vez que se llame al método.
Sin embargo, las llamadas que son atributos de una clase (a diferencia de una instancia) aún no están vinculadas, por lo que puede modificar la definición de la clase cuando lo desee:
>>> def fooFighters( self ):
... print "fooFighters"
...
>>> A.fooFighters = fooFighters
>>> a2 = A()
>>> a2.fooFighters
<bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
>>> a2.fooFighters()
fooFighters
Las instancias previamente definidas también se actualizan (siempre que no hayan anulado el atributo):
>>> a.fooFighters()
fooFighters
El problema surge cuando desea adjuntar un método a una sola instancia:
>>> def barFighters( self ):
... print "barFighters"
...
>>> a.barFighters = barFighters
>>> a.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: barFighters() takes exactly 1 argument (0 given)
La función no se vincula automáticamente cuando se adjunta directamente a una instancia:
>>> a.barFighters
<function barFighters at 0x00A98EF0>
Para vincularlo, podemos usar la función MethodType en el módulo de tipos :
>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters
Esta vez, otras instancias de la clase no se han visto afectadas:
>>> a2.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'
Puede encontrar más información leyendo descriptores y programación de metaclases .
MethodType
, invoque el protocolo descriptor manualmente y haga que la función produzca su instancia:barFighters.__get__(a)
produce un métodobarFighters
enlazado para enlazadoa
.descriptor protocol
vs creando unMethodType
lado de quizás ser un poco más legible.classmethod
ystaticmethod
y otros descriptores también. Evita abarrotar el espacio de nombres con otra importación más.a.barFighters = barFighters.__get__(a)
Módulo nuevo en desuso desde python 2.6 y eliminado en 3.0, use tipos
ver http://docs.python.org/library/new.html
En el siguiente ejemplo, he eliminado deliberadamente el valor de retorno de la
patch_me()
función. Creo que dar un valor de retorno puede hacernos creer que el parche devuelve un nuevo objeto, lo cual no es cierto: modifica el entrante. Probablemente esto pueda facilitar un uso más disciplinado de parches de mono.fuente
Prefacio - una nota sobre compatibilidad: otras respuestas solo pueden funcionar en Python 2 - esta respuesta debería funcionar perfectamente en Python 2 y 3. Si solo escribe Python 3, puede dejar de lado explícitamente la herencia
object
, pero de lo contrario el código debería permanecer igual .Sí, es posible, pero no recomendado
No recomiendo esto Esta es una mala idea. No lo hagas
Aquí hay un par de razones:
Por lo tanto, le sugiero que no haga esto a menos que tenga una buena razón. Es mucho mejor definir el método correcto en la definición de la clase o menos preferiblemente parchear la clase directamente, de esta manera:
Sin embargo, dado que es instructivo, te mostraré algunas formas de hacerlo.
Cómo se puede hacer
Aquí hay un código de configuración. Necesitamos una definición de clase. Podría importarse, pero realmente no importa.
Crea una instancia:
Cree un método para agregarle:
Método cero (0): utilice el método del descriptor,
__get__
Las búsquedas punteadas en funciones llaman al
__get__
método de la función con la instancia, vinculando el objeto al método y creando así un "método vinculado".y ahora:
Método uno - tipos.MetodoTipo
Primero, importe los tipos, de los cuales obtendremos el constructor del método:
Ahora agregamos el método a la instancia. Para hacer esto, requerimos el constructor MethodType del
types
módulo (que importamos anteriormente).La firma del argumento para types.MethodType es
(function, instance, class)
:y uso:
Método dos: unión léxica
Primero, creamos una función de contenedor que une el método a la instancia:
uso:
Método tres: functools.partial
Una función parcial aplica el (los) primer (s) argumento (s) a una función (y opcionalmente argumentos de palabras clave), y luego se puede invocar con los argumentos restantes (y anulando los argumentos de palabras clave). Así:
Esto tiene sentido cuando considera que los métodos enlazados son funciones parciales de la instancia.
Función independiente como atributo de objeto: por qué esto no funciona:
Si intentamos agregar sample_method de la misma manera que podríamos agregarlo a la clase, no está vinculado a la instancia y no toma el self implícito como primer argumento.
Podemos hacer que la función independiente funcione explícitamente pasando la instancia (o cualquier cosa, ya que este método en realidad no usa la
self
variable de argumento), pero no sería coherente con la firma esperada de otras instancias (si estamos parcheando mono) esta instancia):Conclusión
Ahora ya sabe varias formas en las que se podía hacer esto, pero con toda seriedad - no lo hace.
fuente
__get__
método también necesita la clase que el siguiente parámetro:sample_method.__get__(foo, Foo)
.Creo que las respuestas anteriores perdieron el punto clave.
Tengamos una clase con un método:
Ahora, juguemos con él en ipython:
Ok, por lo que m () de alguna manera se convierte en un método no unido de A . ¿Pero es realmente así?
Resulta que m () es solo una función, referencia a la cual se agrega al diccionario de clase A : no hay magia. Entonces, ¿ por qué Am nos da un método independiente? Es porque el punto no se traduce a una simple búsqueda en el diccionario. Es de facto una llamada de A .__ clase __.__ getattribute __ (A, 'm'):
Ahora, no estoy seguro de por qué la última línea se imprime dos veces, pero aún está claro lo que está sucediendo allí.
Ahora, lo que hace el __getattribute__ predeterminado es verificar si el atributo es un llamado descriptor o no, es decir, si implementa un método __get__ especial. Si implementa ese método, lo que se devuelve es el resultado de llamar a ese método __get__. Volviendo a la primera versión de nuestra clase A , esto es lo que tenemos:
Y debido a que las funciones de Python implementan el protocolo descriptor, si se llaman en nombre de un objeto, se unen a ese objeto en su método __get__.
Ok, entonces, ¿cómo agregar un método a un objeto existente? Asumiendo que no te importa parchear la clase, es tan simple como:
Entonces Bm "se convierte" en un método independiente, gracias a la magia del descriptor.
Y si desea agregar un método solo a un solo objeto, entonces debe emular la maquinaria usted mismo, mediante el uso de types.MethodType:
Por cierto:
fuente
En Python, el parcheo de mono generalmente funciona sobrescribiendo una firma de clase o funciones con la suya. A continuación se muestra un ejemplo de Zope Wiki :
Ese código sobrescribirá / creará un método llamado speak en la clase. En la reciente publicación de Jeff Atwood sobre parches de monos . Muestra un ejemplo en C # 3.0, que es el lenguaje actual que uso para el trabajo.
fuente
Puede usar lambda para vincular un método a una instancia:
Salida:
fuente
Hay al menos dos formas de adjuntar un método a una instancia sin
types.MethodType
:1:
2:
Enlaces útiles:
Modelo de datos: invocación de descriptores Guía descriptiva de
procedimientos: invocación de descriptores
fuente
Lo que estás buscando es
setattr
, creo. Use esto para establecer un atributo en un objeto.fuente
A
, no la instanciaa
.setattr(A,'printme',printme)
lugar de simplementeA.printme = printme
?Como esta pregunta solicitaba versiones que no fueran de Python, aquí está JavaScript:
fuente
Consolidando las respuestas del wiki de Jason Pratt y de la comunidad, con un vistazo a los resultados de diferentes métodos de enlace:
Especialmente en cuenta cómo la adición de la función de unión como un método de clase de obras , pero el alcance referencia es incorrecta.
Personalmente, prefiero la ruta externa de la función ADDMETHOD, ya que me permite asignar dinámicamente nuevos nombres de métodos dentro de un iterador también.
fuente
addmethod
reescrito de la siguiente maneradef addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )
resuelve el problemaEsto es realmente un complemento de la respuesta de "Jason Pratt"
Aunque la respuesta de Jasons funciona, solo funciona si uno quiere agregar una función a una clase. No funcionó para mí cuando intenté volver a cargar un método ya existente desde el archivo de código fuente .py.
Me llevó mucho tiempo encontrar una solución, pero el truco parece simple ... 1. importar el código del archivo de código fuente 2. y forzar una recarga 3. 3. usar tipos. FunctionType (...) para convertir el código el método importado y vinculado a una función también puede pasar las variables globales actuales, ya que el método recargado estaría en un espacio de nombres diferente 4. Ahora puede continuar como lo sugiere "Jason Pratt" usando los tipos. Método (... )
Ejemplo:
fuente
Si puede ser de alguna ayuda, recientemente lancé una biblioteca de Python llamada Gorilla para hacer que el proceso de parchear mono sea más conveniente.
El uso de una función
needle()
para parchear un módulo llamadoguineapig
es el siguiente:Pero también se ocupa de casos de uso más interesantes como se muestra en las preguntas frecuentes de la documentación .
El código está disponible en GitHub .
fuente
Esta pregunta se abrió hace años, pero bueno, hay una manera fácil de simular el enlace de una función a una instancia de clase usando decoradores:
Allí, cuando pase la función y la instancia al decorador de carpetas, creará una nueva función, con el mismo objeto de código que la primera. Luego, la instancia dada de la clase se almacena en un atributo de la función recién creada. El decorador devuelve una (tercera) función que llama automáticamente a la función copiada, dando la instancia como primer parámetro.
En conclusión, obtienes una función que simula que está vinculada a la instancia de clase. Dejar la función original sin cambios.
fuente
Lo que Jason Pratt publicó es correcto.
Como puede ver, Python no considera que b () sea diferente a a (). En Python, todos los métodos son solo variables que resultan ser funciones.
fuente
Test
, no una instancia de ella.Me resulta extraño que nadie haya mencionado que todos los métodos enumerados anteriormente crean una referencia de ciclo entre el método agregado y la instancia, lo que hace que el objeto sea persistente hasta la recolección de basura. Había un viejo truco que agregaba un descriptor al extender la clase del objeto:
fuente
Con esto, puede usar el puntero automático
fuente