web-dev-qa-db-fra.com

Chaînage de méthode de base

J'ai trouvé ceci chaînage de méthodes en python , mais même avec cela, je ne pouvais pas comprendre le chaînage de méthodes en Python.

Ici, les objectifs sont deux: résoudre le problème de codage et comprendre le chaînage des méthodes (étant donné que je ne suis toujours pas à 100% confiant avec les callables).

Jusqu'à la définition du problème.

Je veux une classe qui a deux méthodes: l'une définit un paramètre de l'objet = 'ligne' et l'autre remplace par 'bar'.

Voici ce que j'ai obtenu jusqu'à présent:

class foo():
    def __init__(self, kind=None):
        self.kind = kind

    def __call__(self, kind=None):
        return foo(kind=kind)

    def my_print(self):
        print (self.kind)

    def line(self):
        return self(kind='line')
    def bar(self):
        return self(kind='bar')

Malheureusement, avec ce code, je peux atteindre mon objectif en faisant cela

a = foo()
a.bar().line().bar().bar().line().my_print()

Mais je voudrais obtenir le même résultat en écrivant ce code

a = foo()
a.bar.line.bar.bar.line.my_print()

Comment puis-je y parvenir? Je suppose que quelque chose ne va pas dans la façon dont j'ai défini le __call__ méthode. Merci d'avance pour votre aide.

22
Pezze

Le chaînage de méthode consiste simplement à ajouter .second_func() à tout ce que .first_func() renvoie. Il est assez facilement implémenté en s'assurant que toutes les méthodes chaînables retournent self. (Notez que cela n'a rien à voir avec __call()__).

class foo():
    def __init__(self, kind=None):
        self.kind = kind
    def my_print(self):
        print (self.kind)
        return self
    def line(self):
        self.kind = 'line'
        return self
    def bar(self):
        self.kind='bar'
        return self

Vous pouvez utiliser les objets foo de manière non chaînée en ignorant leurs valeurs renvoyées:

a = foo()
a.line()
a.my_print()
a.bar()
a.my_print()

assert a.kind == 'bar'

Ou, puisque chaque fonction retourne maintenant l'objet lui-même, vous pouvez opérer directement sur la valeur retournée. Vous pouvez utiliser le chaînage de méthodes avec ce code équivalent:

b = foo()
b.line().my_print().bar().my_print()
assert b.kind == 'bar'

Ou même:

c = foo().line().my_print().bar().my_print()
assert c.kind == 'bar'

La question de se débarrasser de la syntaxe d'appel () Est un concept complètement séparé du chaînage de méthode. Si vous voulez des propriétés de chaîne et que ces propriétés mutent leur objet, utilisez le décorateur @property. (Mais la mutation d'objets via une propriété semble dangereuse. Mieux vaut utiliser une méthode et la nommer avec un verbe: .set_line() au lieu de .line, Par exemple.)

class foo():
    def __init__(self, kind=None):
        self.kind = kind
    def my_print(self):
        print (self.kind)
        return self
    @property
    def line(self):
        self.kind = 'line'
        return self
    @property
    def bar(self):
        self.kind='bar'
        return self

a = foo()
a.line
a.my_print()
a.bar
a.my_print()

assert a.kind == 'bar'

b = foo()
b.line.my_print().bar.my_print()
assert b.kind == 'bar'

c = foo().line.my_print().bar.my_print()
assert c.kind == 'bar'
44
Robᵩ

Utilisez des propriétés (descripteurs).

class foo:
    def __init__(self, kind=None):
        self.kind = kind

    def __call__(self, kind=None):
        return foo(kind=kind)

    def my_print(self):
        print (self.kind)

    @property
    def line(self):
        return self(kind='line')

    @property
    def bar(self):
        return self(kind='bar')

Notez, cependant, que vous n'écrasez rien, la modification ne fonctionne pas sur place (ce qui est sans doute bon, au fait). Quoi qu'il en soit, cela ne semble pas être un bon choix de conception pour la plupart des cas réels, car à un moment donné, vos méthodes nécessiteront des arguments.

4
Eli Korvigo