我发现了这个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__
方法的方法有点不对劲。在此先感谢您的帮助。
答案 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。