【问题标题】:Basic method chaining基本方法链
【发布时间】:2017-01-23 23:33:02
【问题描述】:

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

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

下到问题定义。

我想要一个有两种方法的类:一种设置对象的参数 = '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__ 方法的方式有问题。提前感谢您的帮助。

【问题讨论】:

    标签: python method-chaining


    【解决方案1】:

    方法链只是能够将.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'
    

    【讨论】:

    • 感谢您提供非常详尽的回答!
    • @Rob 谢谢!因为它总是返回 self ,我们如何在最后一个链返回数据。在这种情况下return .kind
    • @Akhilesh 也许喜欢:b.line.my_print().bar.my_print().kind
    【解决方案2】:

    使用属性(描述符)。

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

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

    【讨论】:

      【解决方案3】:

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

      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()),它将返回 circle。

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

      【讨论】:

        猜你喜欢
        • 2020-08-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-05
        • 2010-12-15
        • 1970-01-01
        • 2015-06-30
        • 1970-01-01
        相关资源
        最近更新 更多