基本方法链接

Basic method chaining

我找到了这个 method chaining in python,但即使有了它我还是无法理解 Python 中的方法链接。

这里的目标有两个:解决编码问题和理解方法链(考虑到我对可调用对象仍然不是 100% 有信心)。

下到问题定义。

我想要一个有两种方法的 class:一种设置对象的参数 = 'line',另一种覆盖 'bar'.

这是我目前得到的:

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')

遗憾的是,使用这段代码我可以实现我的目标

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

但我想通过编写这段代码获得相同的结果

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

我该如何实现?我想我定义 __call__ 方法的方式有问题。预先感谢您的帮助。

使用属性(描述符)。

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')

但是请注意,您没有覆盖任何内容,修改不会就地生效(顺便说一句,这可以说是好的)。无论如何,对于大多数 real-world 情况,这看起来不是一个好的设计选择,因为在某些时候您的方法将需要参数。

方法链接只是能够将 .second_func() 添加到任何 .first_func() return 中。通过确保所有可链接的方法 return self,它很容易实现。 (注意这与__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

您可以通过忽略 returned 值以 non-chained 方式使用 foo 对象:

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

assert a.kind == 'bar'

或者,由于现在每个函数 return 都是对象本身,您可以操作 直接在 returned 值上。您可以使用具有以下等效代码的方法链接:

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'

摆脱 () 调用语法的问题是一个完全独立于方法链的概念。如果您想要链属性,并让这些属性改变它们的对象,请使用 @property 装饰器。 (但是通过 属性 改变对象似乎很危险。最好使用一个方法并用动词命名它:例如 .set_line() 而不是 .line。)

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'

还有另一种有趣的实现方式

class Foo:
    def __init__(self, kind=[]):
        self.kind = kind

    def __getattr__(self, attrs):
        self.attrs = attrs
        return Foo(self.kind + [attrs]) 

    def __call__(self):
        return self.kind[::-1][0]


my_obj = Foo()
print(my_obj.bar.line.bar.bar.line())

使用此代码你不必传递 .my_print() 但这里要注意的一件事是 Foo class 将把任何东西作为参数,就像我们尝试 print(my_obj.bar.line.bar.bar.circle()) 它会return圈子。

您还可以编辑此代码以在调用任何函数时获取参数。