I found this method chaining in python, but even with it I couldn\'t understand method chaining in Python.
Here the goals are two: solve the coding problem and under
Method chaining is simply being able to add .second_func()
to whatever .first_func()
returns. It is fairly easily implemented by ensuring that all chainable methods return self
. (Note that this has nothing to do with __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
You can use foo
objects in a non-chained way by ignoring their returned values:
a = foo()
a.line()
a.my_print()
a.bar()
a.my_print()
assert a.kind == 'bar'
Or, since every function now returns the object itself, you can operate directly on the returned value. You can use method chaining with this equivalent code:
b = foo()
b.line().my_print().bar().my_print()
assert b.kind == 'bar'
Or even:
c = foo().line().my_print().bar().my_print()
assert c.kind == 'bar'
The question of getting rid of the ()
calling syntax is a completely separate concept from method chaining. If you want chain properties, and have those properties mutate their object, use the @property
decorator. (But mutating objects via a property seems dangerous. Better to use a method and name it with a verb: .set_line()
instead of .line
, for example.)
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'
Use properties (descriptors).
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')
Note, though, that you overwrite nothing, the modification doesn't work inplace (which is arguably good, btw). Anyway, this doesn't look like a good design choice for most real-world cases, because at some point your methods will require arguments.
ther's an another interesting way of achieving this
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())
with this code u don't have to pass .my_print()
but one thing to note here is the Foo class will take anything as argument like if we try print(my_obj.bar.line.bar.bar.circle())
it will return circle.
You can also edit this code to take the args while calling any function.