Question

Je suis habitué à ce que Python permette à certaines astuces de déléguer des fonctionnalités à d'autres objets. La délégation aux objets contenus en est un exemple.

Mais il me semble que je n'ai pas de chance quand je veux déléguer __contains __:

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

a = A()
1 in a

je reçois:

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

Qu'est-ce que je fais mal? Quand j'appelle un .__ contient __ (1), tout se passe bien. J'ai même essayé de définir une méthode __iter __ dans A pour que A ressemble davantage à une variable, mais cela n'a pas aidé. Qu'est-ce qui me manque ici?

Était-ce utile?

La solution

Les méthodes spéciales telles que __ contient __ ne sont spéciales que lorsqu'elles sont définies sur la classe, pas sur l'instance (sauf dans les classes héritées de Python 2, que vous ne devriez pas utiliser quand même » ).

Alors, votre délégation au niveau de la classe:

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

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

Je préférerais réellement épeler ce dernier sous la forme en renvoyer un autre dans self.mydict , mais il s'agit d'un problème de style mineur.

Modifier : si et quand "redirige totalement dynamique par instance des méthodes spéciales" (comme les anciennes classes de style offertes) est indispensable, il n'est pas difficile de l'implémenter avec des classes de nouveau style: vous avez juste besoin que chaque instance qui a un tel besoin particulier soit encapsulée dans sa propre classe spéciale. Par exemple:

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

Essentiellement, après le peu de magie noire qui a réaffecté self .__ class __ à un nouvel objet de classe (qui se comporte exactement comme le précédent mais dont le dict est vide et qui ne contient aucune autre instance sauf celle-ci self ), n'importe où dans une classe de style ancien que vous affecteriez à self .__ nom magique __ , attribuez-le à self .__ classe __.__ nom magique __ à la place (et assurez-vous qu'il une méthode intégrée ou staticmethod , pas une fonction Python normale, à moins bien sûr que dans un cas différent, vous souhaitiez qu'elle reçoive le self lorsqu'elle est appelée sur l'instance).

Incidemment, l’opérateur dans sur une instance de cette classe BlackMagic est plus rapide que ce n’est le cas avec aucune des fonctions précédemment proposées. solutions - ou du moins si je mesure avec mon -mtimeit habituel (allant directement à la méthode intégrée , au lieu de suivre les routes de recherche normales impliquant l'héritage et descripteurs, réduit un peu les frais généraux).

Une métaclasse pour automatiser l'idée de auto-instance de self .__ class __ ne serait pas difficile à écrire (elle pourrait effectuer le sale travail de la méthode __ new __ de la classe générée, et peut-être aussi définir tous les noms magiques à assigner réellement à la classe si assigné à l'instance, soit via __ setattr __ , soit par plusieurs propriétés many ). Mais cela ne serait justifié que si le besoin de cette fonctionnalité était vraiment répandu (par exemple, le portage d’un énorme projet ancien de Python 1.5.2 qui utilisait généreusement des "méthodes spéciales par instance" pour Python moderne, y compris Python 3).

Est-ce que je recommande "astucieux"? ou "magie noire" solutions? Non, je ne le fais pas: il est presque toujours préférable de faire les choses de manière simple et directe. Mais " presque " est un mot important ici, et il est agréable d’avoir sous la main de tels "crochets" avancés dans les rares cas, mais non inexistants, où leur utilisation peut réellement être justifiée.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top