patternpythonMinor
Function caller (to multiple receivers) interface
Viewed 0 times
receiverscallerfunctioninterfacemultiple
Problem
This class is designed to call a similar function of multiple objects of multiple classes using a single interface (it is not for calling functions that return a value).
A potential usage will be sending same data to multiple writers (log writer, HTML generator and styled printer to console).
Tested with Python 3.4.2
I've also included a test/example in the below code:
```
"""
caller module:
Contains Caller class - call multiple other classes or modules with
similar function definitions, (it is not for calling functions that
returns a value)
Author : Bhathiya Perera
"""
class Caller():
"""call other classes or modules with
similar function definitions"""
def __init__(self, *receivers):
"""Initialize
Parameters :
receivers - va-arg receivers (can be objects, modules,
another caller , ....)
"""
self._names = []
self._receivers = receivers
def __getattr__(self, name):
"""Get attribute of a given name
This will return 'self' therefore it can be called later
"""
self._names.append(name)
return self
def __call__(self, *args, **kw):
"""This class is callable with any arguments or key-value arguments
It will then be posted to all receivers
"""
if len(self._names) == 0:
raise Exception("Cannot call")
method_name = self._names.pop()
for receiver in self._receivers:
method = getattr(receiver, method_name)
method(*args, **kw)
return self
if __name__ == "__main__":
# -------------------------------------------------
# Test its usage
class Receiver1():
def a(self, arg):
print ("Receiver1 - a", arg)
def b(self, arg):
print ("Receiver1 - b", arg)
class Receiver2():
def a(self, arg):
print ("Receiver2 - a", arg)
def b(self,
A potential usage will be sending same data to multiple writers (log writer, HTML generator and styled printer to console).
Tested with Python 3.4.2
I've also included a test/example in the below code:
```
"""
caller module:
Contains Caller class - call multiple other classes or modules with
similar function definitions, (it is not for calling functions that
returns a value)
Author : Bhathiya Perera
"""
class Caller():
"""call other classes or modules with
similar function definitions"""
def __init__(self, *receivers):
"""Initialize
Parameters :
receivers - va-arg receivers (can be objects, modules,
another caller , ....)
"""
self._names = []
self._receivers = receivers
def __getattr__(self, name):
"""Get attribute of a given name
This will return 'self' therefore it can be called later
"""
self._names.append(name)
return self
def __call__(self, *args, **kw):
"""This class is callable with any arguments or key-value arguments
It will then be posted to all receivers
"""
if len(self._names) == 0:
raise Exception("Cannot call")
method_name = self._names.pop()
for receiver in self._receivers:
method = getattr(receiver, method_name)
method(*args, **kw)
return self
if __name__ == "__main__":
# -------------------------------------------------
# Test its usage
class Receiver1():
def a(self, arg):
print ("Receiver1 - a", arg)
def b(self, arg):
print ("Receiver1 - b", arg)
class Receiver2():
def a(self, arg):
print ("Receiver2 - a", arg)
def b(self,
Solution
If I understand correctly,
the purpose of
It works like this:
Right? The docstring doesn't explain this very well.
My impression is that you're discovering powerful features of Python,
and you're trying to use them because you can,
not because you have a concrete purpose.
Sort of like an exercise, not for real-life use.
It seems to me that
I don't have experience overriding this method,
but I would guess it's designed to implement getters dynamically.
First of all,
adding getters dynamically seems like a hack that should be used with extreme care,
including a good justification that it's the best option.
Secondly,
instead of behaving as a getter,
this implementation mutates the object and returns
I see you did that to make chaining and currying possible,
it's interesting, but it seems a misuse of the language.
Another thing I don't like about the approach is the heavy dependence on duck typing.
The objects that can be used with
they can be anything.
The user just has to make sure that when they call a
all the objects inside have a
It's great that Python let's us do this kind of thing,
but it doesn't mean that we should.
I prefer to have well-defined and well-documented interfaces,
with a list of legitimate methods that I'm allowed to call.
Python conventions
Instead of:
The class should be declared as:
Instead of:
The Pythonic way:
Instead of:
There should be no space before the opening paren:
the purpose of
Caller seems to be to call a function on multiple objects.It works like this:
- Construct a
Caller xby passing 1 or more objects in the constructor
- Call any method
monx, and it will be dispatched to methodmof all objects
Right? The docstring doesn't explain this very well.
CallDispatcher might be a better name.My impression is that you're discovering powerful features of Python,
and you're trying to use them because you can,
not because you have a concrete purpose.
Sort of like an exercise, not for real-life use.
It seems to me that
__getattr__ is seriously abused.I don't have experience overriding this method,
but I would guess it's designed to implement getters dynamically.
First of all,
adding getters dynamically seems like a hack that should be used with extreme care,
including a good justification that it's the best option.
Secondly,
instead of behaving as a getter,
this implementation mutates the object and returns
self.I see you did that to make chaining and currying possible,
it's interesting, but it seems a misuse of the language.
Another thing I don't like about the approach is the heavy dependence on duck typing.
The objects that can be used with
Caller don't have to follow a well-defined interface,they can be anything.
The user just has to make sure that when they call a
.hello function on a Caller object,all the objects inside have a
.hello function defined.It's great that Python let's us do this kind of thing,
but it doesn't mean that we should.
I prefer to have well-defined and well-documented interfaces,
with a list of legitimate methods that I'm allowed to call.
Python conventions
Instead of:
class Caller():The class should be declared as:
class Caller:Instead of:
if len(self._names) == 0:The Pythonic way:
if not self._names:Instead of:
print ("Receiver3 - b", arg)There should be no space before the opening paren:
print("Receiver3 - b", arg)Code Snippets
class Caller():class Caller:if len(self._names) == 0:if not self._names:print ("Receiver3 - b", arg)Context
StackExchange Code Review Q#74394, answer score: 2
Revisions (0)
No revisions yet.