How to encapsulate handlers in an efficient manner in Python? - python

I am making up a handler to handle different types of data. Here is my current solution:
def get_handler_by_type(type):
def handler_for_type_A:
...
#code for processing data type A
def handler_for_type_B:
...
#code for processing data type B
def handler_for_type_C:
...
#code for processing data type C
handler_map = {type_A: handler_for_type_A,
type_B: handler_for_type_B,
type_C: handler_for_type_C,
}
return handler_map(type)
However, this seems quite inefficient as I will call get_handler_by_type frequently and every time it gets called, the dictionary will be constructed again.
I know I could do this instead:
def handler_for_type_A:
...
#code for processing data type A
def handler_for_type_B:
...
#code for processing data type B
def handler_for_type_C:
...
#code for processing data type C
handler_map = {type_A: handler_for_type_A,
type_B: handler_for_type_B,
type_C: handler_for_type_C,
}
def get_handler_by_type(type, handler_map = handler_map):
return handler_map(type)
But this is pretty ugly in my opinion. Because I have handler_for_type_Xs and handler_map that are polluting the global space. Is there a way of doing this both efficiently and elegantly?
Thanks for any inputs.

One way is to look the handler up dynamically (if you have a consistent naming convention)
return vars()['handler_for_'+type]
Another way is to store the map as an attribute of the function
def get_handler_by_type(type):
def handler_for_type_A:
...
#code for processing data type A
def handler_for_type_B:
...
#code for processing data type B
def handler_for_type_C:
...
#code for processing data type C
if not hasattr(get_handler_by_type, 'handler_map'):
get_handler_by_type.handler_map = {'type_A': handler_for_type_A,
'type_B': handler_for_type_B,
'type_C': handler_for_type_C,
}
return get_handler_by_type.handler_map[type]

This way will encapsulate it:
def _handler_helper():
def fna():
print "a"
pass
def fnb():
print "b"
pass
m = {"a":fna,"b":fnb}
return lambda x:m[x]
get_handler_by_type = _handler_helper()
You may want to use def if you want to have a docstring, but this works.
Another option might be to have a more OOP approach:
class _HandlerHelper:
def fna(self):
print 'a'
def fnb(self):
print 'b'
# __call__ is a magic method which lets you treat the object as a function
def __call__(self, fn):
return getattr(self, 'fn' + fn)
get_handler_by_type = _HandlerHelper()

Related

How can I override global variables just for the scope of callees of a function in Python?

I'm writing a decorator which needs to pass data to other utility functions; something like:
STORE = []
def utility(message):
STORE.append(message)
def decorator(func):
def decorator_wrap(*args, **kwargs):
global STORE
saved_STORE = STORE
STORE = list()
func(*args, **kwargs)
for line in STORE:
print(line)
STORE = saved_STORE
return decorator_wrap
#decorator
def foo(x):
# ...
utility(x)
# ...
But that's kind of yuck, and not thread safe. Is there a way to override utility()'s view of STORE for the duration of decorator_wrap()? Or some other way to signal to utility() that there's an alternate STORE it should use?
Alternatively, to present an different utility() to foo() and all its callees; but that seems like exactly the same problem.
From this answer I find that I can implement it this way:
import inspect
STORE = []
def utility(message):
global STORE
store = STORE
frame = inspect.currentframe()
while frame:
if 'LOCAL_STORE' in frame.f_locals:
store = frame.f_locals['LOCAL_STORE']
break;
frame = frame.f_back
store.append(message)
def decorator(func):
def decorator_wrap(*args, **kwargs):
LOCAL_STORE = []
func(*args, **kwargs)
for line in LOCAL_STORE:
print(line)
return decorator_wrap
Buuuut while reading the documentation I see f_globals is present in every stack frame. I think the more efficient method would be to inject my local into my callee's f_globals. This would be similar to setting an environment variable before executing another command, but I don't know if it's legal.

Declaring a var usable by another function using a import in a secondary script

Is there a way to make a function_a define a variable usable inside another function_b so that both are possible to import in a project ? Something like so:
Script_1
def func_a(str):
if str == 'Yes'
nb = 1
else:
nb=0
return nb
def func_b(int)
calc = (nb+int)**2
return calc
Script_2
from Script_1 import func_a, func_b
func_a('Yes')
func_b(5)
My attempt at declaring nb in Script_2 did not work as python tried to find it in Script_1. I hope this can give an idea of what I am trying to do. Also, the names of the variable are but a representation of type (strand int) I am looking for. Python is rather new to me and I am still learning. Thanks in advance.
The standard way to pass state from one function to another is for one function to return the value and for the other to take it as an argument.
# Script_1
def func_a(msg: str) -> int:
if msg == 'Yes':
return 1
else:
return 0
def func_b(na: int, nb: int) -> int:
return (na + nb)**2
# Script_2
# from Script_1 import func_a, func_b
nb = func_a('Yes')
print(func_b(5, nb))
By adding nb as an argument to func_b, we can take the return value from func_a and pass it to func_b. (Doing weird stuff with injecting data into the global namespace is technically possible, but it makes your code extraordinarily difficult to debug.)
Thanks to Amadan's suggestion, I was able to do this:
class test(object):
def __init__(self,string):
self.string = string
if string == 'Yes':
self.factor = 1
else:
self.factor = 0
def func(self, num):
calc = (num+self.factor)**2
return calc
And can be used as such in another file once saved in test.py:
from test import test
test('Yes').func(3)
test('No').func(3)

Python function asserting value over pre declared value

I have a function (func.py). Structure of which look like this:
database = 'VENUS'
def first_function():
print("do some thing")
def second_function():
print("call third function)
third_function()
def third_function(db = database):
print("do some other thing")
I need to import this function and used the inner defined function. But, I want to use a different key for database. Basically, I want to overwrite database = 'VENUS' and use database = 'MARS' while second function call the third function. is there any way to do this?
Just provide the database name as argument
first_function("MARS")
second_function("MARS")
So the problem here, if I understood correctly, is that the default argument for func.third_function is defined at import time. It doesn't matter if you later modify the func.database variable, since the change will not reflect on the default argument of func.third_function.
One (admittedly hacky) solution is to inject a variable using a closure over the imported function. Example:
file.py:
x = 1
def print_x(xvalue = x)
print(xvalue)
Python console:
>>> import file
>>> file.print_x()
1
>>> file.x = 10
>>> file.print_x() # does not work (as you're probably aware)
1
>>> def inject_var(func_to_inject, var):
def f(*args, **kwargs):
return func_to_inject(var, *args, **kwargs)
return f
>>> file.print_x = inject_var(file.print_x, 10)
>>> file.print_x() # works
10
So using the inject_var as written above, you could probably do:
func.third_function = inject_var(func.third_function, "MARS")

Conditional execution without having to check repeatedly for a condition

I have a class with code that fits into the following template:
class aClass:
def __init__(self, switch = False):
self.switch = switch
def f(self):
done = False
while not done:
# a dozen lines of code
if self.switch:
# a single line of code
# another dozen lines of code
So the single line of code in the if statement will either never be executed, or it will be executed in all iterations. And this is actually known as soon as the object is initialized.
When self.switch is True, I would like the single line of code to be executed without having to check for self.switch at every single iteration. And when self.switch is False, I would like the single line of code to be ignored, again without having to repeatedly check for self.switch.
I have of course considered writing two versions of f and selecting the appropriate one in __init__ according to the value of the switch, but duplicating all this code except for a single line doesn't feel right.
Can anyone suggest an elegant way to solve this problem? Perhaps a way to generate the appropriate version of the f method at initialization?
That's a completely valid ask. If not for performance then for readability.
Extract the three pieces of logic (before, inside, and after your condition) in three separate methods and in f() just write two implementations of the big loop:
def first(self):
pass
def second(self):
pass
def third(self):
pass
def f(self):
if self.switch:
while ...:
self.first()
self.third()
else:
while ...:
self.first()
self.second()
self.third()
If you want it more elegant (although it depends on taste), you express the two branches of my f() into two methods first_loop and second_loop and then in __init__ assign self.f = self.first_loop or self.f = self.second_loop depending on the switch:
class SuperUnderperformingAccordingToManyYetReadable(object):
def __init__(self, switch):
if self.switch:
self.f = self._first_loop
else:
self.f = self._second_loop
def _first(self):
pass
def _second(self):
pass
def _third(self):
pass
def _first_loop(self):
while ...:
self.first()
self.third()
def _second_loop(self):
while ...:
self.first()
self.second()
self.third()
You may need to do some extra work to manage breaking out of the while loop.
If the .switch attribute is not supposed to change, try to select the loop body dynamicly in the __init__() method:
def __init__(self, switch=False):
self.switch = switch
self.__fBody = self.__fSwitchTrue if switch else self.__fSwitchFalse
def f(self):
self.__done = False
while not self.__done:
self.__fBody()
def __fSwitchTrue(self):
self.__fBodyStart()
... # a single line of code
self.__fBodyEnd()
def __fSwitchFalse(self):
self.__fBodyStart()
self.__fBodyEnd()
def __fBodyStart(self):
... # a dozen lines of code
def __fBodyEnd(self):
... # another dozen lines of code
Remember to change values used by more than one of the defined methods to attributes (like done is changed to .__done).
In a comment to my original question, JohnColeman suggested using exec and provided a link to another relevant question.
That was an excellent suggestion and the solution I was lead to is:
_template_pre = """\
def f(self):
for i in range(5):
print("Executing code before the optional segment.")
"""
_template_opt = """\
print("Executing the optional segment")
"""
_template_post = """\
print("Executing code after the optional segment.")
"""
class aClass:
def __init__(self, switch = False):
if switch:
fdef = _template_pre + _template_opt + _template_post
else:
fdef = _template_pre + _template_post
exec(fdef, globals(), self.__dict__)
# bind the function
self.f = self.f.__get__(self)
You can verify this actually works:
aClass(switch = False).f()
aClass(switch = True).f()
Before jumping to conclusions as to how "pythonic" this is, let me point out that such an approach is employed in a couple of metaclass recipes I have encountered and even in the Python Standard Library (check the implementation of namedtuple, to name one example).

How to pass a list of parameters to a function in Python

I warped a class in this way:
import Queue
import threading
class MyThread():
q = Queue.Queue()
content = []
result = {}
t_num = 0
t_func = None
def __init__ (self, t_num, content, t_func):
for item in content:
self.q.put(item)
self.t_num = t_num
self.t_func = t_func
def start(self):
for i in range(self.t_num):
t = threading.Thread(target=self.worker)
t.daemon = True
t.start()
self.q.join()
return self.result
def worker(self):
while True:
item = self.q.get()
value = self.t_func(item)
self.result[item] = value
self.q.task_done()
x = [5, 6, 7, 8, 9]
def func(i):
return i + 1
m = MyThread(4, x, func)
print m.start()
It works well. If I design the function func with 2 or more parameters, and pass these parameters in a list to the class, how can I call the func function in the function worker properly?
eg.
def __init__ (self, t_num, content, t_func, t_func_p):
for item in content:
self.q.put(item)
self.t_num = t_num
self.t_func = t_func
self.t_func_p = t_func_p
def func(i, j, k):
m = MyThread(4, x, func, [j, k])
You need to use *args and **kwargs to pass any number of parameters to a function.
Here is more info: http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
Maybe this might help:
def __init__(self, t_num, content, func, *params):
func(*params) # params is a list here [param1, param2, param3....]
def func(param1, param2, param3):
# or
def func(*params): # for arbitrary number of params
m = MyThread(4, x, func, param1, param2, param3....)
As a general rule, if you are going to be passing many parameters to a particular function, you may consider wrapping them into a simple object, the reasons are
If you ever need to add/remove parameters, you just need to modify the object, and the function itself, the method signature (and all its references) will remain untouched
When working with objects, you will always know what your function is receiving (this is specially useful if you are working on a team, where more people will use that function).
Finally, because you control the creation of the object on its constructor, you can ensure that the values associated with the object are correct (for example, in the constructor you can make sure that you have no empty values, or that the types are correct).
If still you want to go with multiple parameters, check the *args and **kwargs, although I personally do not like that, as it may end up forcing people to read the function's source in order to use it.
Good luck :)

Categories