基本方法链接

时间:2017-01-23 23:33:03

标签: python callable method-chaining

我发现了这个method chaining in python,但即便如此,我也无法理解Python中的方法链。

这里的目标是两个:解决编码问题并理解方法链(考虑到我仍然对使用callables没有100%的信心)。

直到问题定义。

我想要一个有两个方法的类:一个设置object ='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__方法的方法有点不对劲。在此先感谢您的帮助。

3 个答案:

答案 0 :(得分:42)

方法链只是能够将.second_func()添加到任何.first_func()返回。通过确保所有可链接方法返回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

您可以通过忽略返回的值来以非链式方式使用foo个对象:

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'

摆脱()调用语法的问题是来自方法链的完全独立的概念。如果您想要链属性,并让这些属性改变其对象,请使用@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'

答案 1 :(得分:3)

使用属性(描述符)。

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

但请注意,您没有覆盖任何内容,修改不适用于现场(这可以说是好的,顺便说一句)。无论如何,对于大多数真实案例来说,这看起来不是一个好的设计选择,因为在某些时候你的方法需要参数。

答案 2 :(得分:0)

这是实现这一目标的另一种有趣方式

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类将把任何东西当作参数,例如如果我们尝试print(my_obj.bar.line.bar.bar.circle()),它将返回圆。

您还可以在调用任何函数时编辑此代码以使用args。