There is one drawback in returning self
. Mutating one of the variables can affect another one. Here is an example
Taken from @TheLazyScripter code but with modified example.
a = Send_message("Hello")
b = a
a = a._from('theLazyscripter')
b = b._from('Kracekumar').to('samba 2')
b()
a.message = 'Hello A'
a.to('samba2')()
b.to('samba 2')()
Send_message("Hello").to('samba2')._from('TheLazyScripter')()
Send_message("Hello").to('samba2')()
The a and b variable points to the same instance. Modifying one's value will affect the other ones. See the second and third line of the output.
The output
Sent 'Hello' from: Kracekumar to samba 2.
Sent 'Hello A' from: Kracekumar to samba2.
Sent 'Hello A' from: Kracekumar to samba 2.
Sent 'Hello' from: TheLazyScripter to samba2.
Sent 'Hello' from: my_address to samba2.
b=a
and modifying the a
's content affects b
's value.
How to remove this side-effect?
Rather than returning self
return a new instance to remove side-effect.
DEFAULT_SENDER = 'my_address'
#Because the sender object is optional I assume you have a default sender
class Send_message(object):
def __init__(self, message):
self.message = message
self.sender = None
self.receiver = None
self.method = None
def _clone(self):
inst = self.__class__(message=self.message)
inst.sender = self.sender
inst.receiver = self.receiver
inst.method = self.method
return inst
def to(self, receiver):
self.receiver = receiver
self.method = self.send()
return self._clone()
def _from(self, sender):
self.sender = sender
self.method = self.send()
return self._clone()
def __call__(self):
if self.method:
return self.method()
return None
def send(self):
if self.receiver:
if not self.sender:
self.sender = DEFAULT_SENDER
return lambda:actual_message_code(self.message, self.sender, self.receiver)
def actual_message_code(message, sender, receiver):
print("Sent '{}' from: {} to {}.".format(message, sender, receiver))
a = Send_message("Hello")
b = a
a = a._from('theLazyscripter')
b = b._from('Kracekumar').to('samba 2')
b()
a.message = 'Hello A'
a.to('samba2')()
b.to('samba 2')()
Send_message("Hello").to('samba2')._from('TheLazyScripter')()
Send_message("Hello").to('samba2')()
The _clone
method creates a new copy of the instance every time. Note: when one of the values is a list or dictionary, the deep copy needs to be called. Here it's string hence not required. But the idea remains the same, copy each attribute before returning.
Output
Sent 'Hello' from: Kracekumar to samba 2.
Sent 'Hello A' from: theLazyscripter to samba2.
Sent 'Hello' from: Kracekumar to samba 2.
Sent 'Hello' from: TheLazyScripter to samba2.
Sent 'Hello' from: my_address to samba2.
The output line number 2
and 3
clearly shows the absence of side-effect in the new code.
I wrote a blog post about Fluent Interface