Pregunta

Estoy acostumbrado a que Python permita algunos trucos para delegar la funcionalidad a otros objetos. Un ejemplo es la delegación a objetos contenidos.

Pero parece que no tengo suerte cuando quiero delegar __contiene __:

class A(object):
    def __init__(self):
       self.mydict = {}
       self.__contains__ = self.mydict.__contains__

a = A()
1 in a

Obtengo:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: argument of type 'A' is not iterable

¿Qué estoy haciendo mal? Cuando llamo a .__ contiene __ (1), todo funciona sin problemas. Incluso intenté definir un método __iter __ en A para hacer que A pareciera más iterable, pero no sirvió de nada. ¿Qué me estoy perdiendo aquí?

¿Fue útil?

Solución

Los métodos especiales como __contains__ solo son especiales cuando se definen en la clase, no en la instancia (excepto en las clases heredadas en Python 2, que debe no utilizar de todos modos ).

Entonces, haga su delegación a nivel de clase:

class A(object):
    def __init__(self):
       self.mydict = {}

    def __contains__(self, other):
       return self.mydict.__contains__(other)

Prefiero deletrear este último como return other en self.mydict , pero ese es un problema menor de estilo.

Editar : si y cuándo " redirección totalmente dinámica por instancia de métodos especiales " (como las clases de estilo antiguo que se ofrecen) es indispensable, no es difícil implementarlo con clases de estilo nuevo: solo necesita que cada instancia que tiene una necesidad tan peculiar se incluya en su propia clase especial. Por ejemplo:

class BlackMagic(object):
    def __init__(self):
        self.mydict = {}
        self.__class__ = type(self.__class__.__name__, (self.__class__,), {})
        self.__class__.__contains__ = self.mydict.__contains__

Esencialmente, después de un poco de magia negra reasignando self .__ class__ a un nuevo objeto de clase (que se comporta igual que el anterior pero tiene un dict vacío y ninguna otra instancia excepto este self ), en cualquier lugar de una clase de estilo antiguo que asignaría a self .__ magicname__ , asigne a self .__ class __.__ magicname__ en su lugar (y asegúrese de que sea un staticmethod incorporado, no una función Python normal, a menos que, por supuesto, en algún caso diferente desee que reciba el self cuando se le llame en la instancia).

Por cierto, el operador in en una instancia de esta clase BlackMagic es más rápido , como sucede, que con cualquiera de los propuestos anteriormente soluciones, o al menos así lo estoy midiendo con mi habitual -mtimeit de confianza (yendo directamente al método incorporado , en lugar de seguir rutas de búsqueda normales que implican herencia y descriptores, afeita un poco la sobrecarga).

Una metaclase para automatizar la idea self .__ class__ por instancia no sería difícil de escribir (podría hacer el trabajo sucio en el método __new__ de la clase generada, y tal vez también establezca todos los nombres mágicos para asignar realmente en la clase si se asigna en la instancia, ya sea a través de __setattr__ o muchas, muchas propiedades). Pero eso se justificaría solo si la necesidad de esta función estuviera realmente extendida (por ejemplo, portar un enorme y antiguo proyecto de Python 1.5.2 que usa liberalmente `` métodos especiales por instancia '' a Python moderno, incluido Python 3).

¿Te recomiendo " inteligente " o "magia negra" soluciones? No, no lo hago: casi siempre es mejor hacer las cosas de manera simple y directa. Pero "casi" es una palabra importante aquí, y es bueno tener a la mano tan avanzados '' ganchos '' para las raras, pero no inexistentes, situaciones en las que su uso puede estar justificado.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top