HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Function caller (to multiple receivers) interface

Submitted by: @import:stackexchange-codereview··
0
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,

Solution

If I understand correctly,
the purpose of Caller seems to be to call a function on multiple objects.
It works like this:

  • Construct a Caller x by passing 1 or more objects in the constructor



  • Call any method m on x, and it will be dispatched to method m of 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.