2012-04-29 31 views
13

espero que alguien pueda responder a esta que tiene una buena comprensión profunda de Python :)métodos especiales Anulación de una instancia

Considere el siguiente código:

>>> class A(object): 
...  pass 
... 
>>> def __repr__(self): 
...  return "A" 
... 
>>> from types import MethodType 
>>> a = A() 
>>> a 
<__main__.A object at 0x00AC6990> 
>>> repr(a) 
'<__main__.A object at 0x00AC6990>' 
>>> setattr(a, "__repr__", MethodType(__repr__, a, a.__class__)) 
>>> a 
<__main__.A object at 0x00AC6990> 
>>> repr(a) 
'<__main__.A object at 0x00AC6990>' 
>>> 

Note como repr (a) no cede el resultado esperado de "A"? Quiero saber por qué este es el caso y si hay una manera de lograr esto ...

Me contraste, el siguiente ejemplo funciona sin embargo (Tal vez porque no estamos tratando de reemplazar un método especial?):

>>> class A(object): 
...  def foo(self): 
...    return "foo" 
... 
>>> def bar(self): 
...  return "bar" 
... 
>>> from types import MethodType 
>>> a = A() 
>>> a.foo() 
'foo' 
>>> setattr(a, "foo", MethodType(bar, a, a.__class__)) 
>>> a.foo() 
'bar' 
>>> 
+0

Probablemente obtendrá una respuesta mucho más esclarecedora si dice lo que finalmente está tratando de lograr. – mattmanser

+1

Para las clases de estilo nuevo, se buscan métodos especiales en la clase en lugar de la instancia. Consulte mi respuesta a continuación para obtener más detalles y algunas soluciones. –

+0

No pude describir lo que quiero hacer en detalle. Lo siento. Pero, en resumen, estoy tratando de modelar un prototipo de modelo OO donde pueda hacer operadores como: World = Object(). Clone(). Mixin (World); Where World es una clase con una colección de métodos que reemplaza/reemplaza uno en la instancia de Object.clone(). –

Respuesta

15

Python no llama a los métodos especiales, aquellos con el nombre rodeado por __ en la instancia, pero solo en la clase, aparentemente para mejorar el rendimiento. Por lo tanto, no hay manera de anular __repr__() directamente en una instancia y hacerlo funcionar. En su lugar, es necesario hacer algo así:

class A(object): 
    def __repr__(self): 
     return self._repr() 
    def _repr(self): 
     return object.__repr__(self) 

Ahora se puede anular __repr__() en una instancia mediante la sustitución de _repr().

+0

"Python no llama a los métodos especiales, aquellos con el nombre rodeado por __ en la instancia, pero solo en la clase, aparentemente para mejorar el rendimiento" <- ¿Tiene una referencia a esto? –

+1

Ver [esto] (http://docs.python.org/reference/datamodel.html#special-method-lookup-for-new-style-classes). Las reglas son un poco relajadas en las clases de estilo antiguo, pero debes usar clases de estilo nuevo, y en Python 3 son las únicas de todos modos. – kindall

+5

No es principalmente para mejorar el rendimiento. Como dicen los documentos, "la razón de este comportamiento reside en una serie de métodos especiales, como' __hash __() 'y' __repr __() ', que implementan todos los objetos, incluidos los objetos de tipo. Si se utiliza la búsqueda implícita de estos métodos el proceso de búsqueda convencional, fallarían cuando se invocaran en el objeto de tipo mismo ... "(Sin embargo, la razón por la cual' __getattribute__' _also_ es omitible _es_ rendimiento). – abarnert

3

la razón de esto es métodos especiales (__x__()) están definidos para la clase, no es el caso.

Esto tiene sentido cuando piensa en __new__() - sería imposible llamar esto a una instancia ya que la instancia no existe cuando se la llama.

Así que usted puede hacer esto en la clase en su conjunto si se quiere:

>>> A.__repr__ = __repr__ 
>>> a 
A 

o en una instancia individual, como in kindall's answer. (Tenga en cuenta que aquí hay mucha similitud, pero pensé que mis ejemplos también se agregaron para publicar esto).

3

Para las nuevas clases de estilo, Python usa una búsqueda de método especial que omite las instancias. Aquí un extracto de the source:

1164 /* Internal routines to do a method lookup in the type 
    1165 without looking in the instance dictionary 
    1166 (so we can't use PyObject_GetAttr) but still binding 
    1167 it to the instance. The arguments are the object, 
    1168 the method name as a C string, and the address of a 
    1169 static variable used to cache the interned Python string. 
    1170 
    1171 Two variants: 
    1172 
    1173 - lookup_maybe() returns NULL without raising an exception 
    1174  when the _PyType_Lookup() call fails; 
    1175 
    1176 - lookup_method() always raises an exception upon errors. 
    1177 
    1178 - _PyObject_LookupSpecial() exported for the benefit of other places. 
    1179 */ 

Puede cambiar a una clase de estilo antiguo (no heredar de objeto) o puede agregar métodos despachador de la clase (métodos que adelante las búsquedas de nuevo a la ejemplo). Para un ejemplo de los métodos despachador ejemplo, véase la receta en http://code.activestate.com/recipes/578091

+0

AFAIK si tuviera que cambiar a usar clases de estilo antiguo, ¿esto no funcionaría para Python 3? –

7

Como se explica en Special Method Lookup:

para clases personalizadas, invocaciones implícitas de métodos especiales sólo están garantizados para funcionar correctamente si está definida en un tipo de objeto, no en el diccionario de la instancia del objeto ... Además de pasar por cualquier instancia de atributos en aras de la exactitud, implícita de búsqueda especial método generalmente también pasa por el método __getattribute__() incluso de metaclase del objeto

(La parte que he recortado explica la razón detrás de esto, si te interesa eso).)

Python no documenta exactamente cuándo una implementación debe o no buscar el método en el tipo; todos los documentos son, en efecto, que las implementaciones pueden o no tener en cuenta la instancia para búsquedas de métodos especiales, por lo que no debe contar tampoco.

Como puede adivinar a partir de los resultados de sus pruebas, en la implementación de CPython, __repr__ es una de las funciones buscadas en el tipo.


cosas son ligeramente diferentes en 2.x, sobre todo debido a la presencia de clases clásicos, pero siempre y cuando usted es sólo la creación de clases de nuevo estilo se puede pensar en ellos como el mismo.


La razón más común que la gente quiere hacer esto es mono-patch diferentes instancias de un objeto de hacer cosas diferentes. No puedes hacer eso con métodos especiales, entonces ... ¿qué puedes hacer? Hay una solución limpia y una solución hacky.

La solución limpia es implementar un método especial en la clase que solo llama a un método normal en la instancia. Entonces puedes parchear mono ese método regular en cada instancia. Por ejemplo:

class C(object): 
    def __repr__(self): 
     return getattr(self, '_repr')() 
    def _repr(self): 
     return 'Boring: {}'.format(object.__repr__(self)) 

c = C() 
def c_repr(self): 
    return "It's-a me, c_repr: {}".format(object.__repr__(self)) 
c._repr = c_repr.__get__(c) 

La solución de hacky consiste en crear una nueva subclase sobre la marcha y volver a clasificar el objeto. Sospecho que cualquier persona que realmente tenga una situación en la que esta sea una buena idea sabrá cómo implementarla a partir de esa oración, y cualquiera que no sepa cómo hacerlo no debería intentarlo, así que lo dejo así.

+0

Creo que para trabajar en 2.x, es posible que deba llamar a '__get__ (c, type (c))', pero no lo recuerdo con certeza, y no tengo un intérprete 2.x en funcionamiento en esta máquina ... ¿Tal vez sería mejor usar 'types.MethodType' en su lugar? – abarnert

Cuestiones relacionadas