Python: how to find wrapped function parameter in decorator? - python-3.x

I wrote a decorated along these lines:
from functools import wraps
def mark_something(f):
#wraps(f)
def decorated(*args, **kwargs):
# How do I find the pd.DataFrame parameter of the decorated function?
# df = args[<pos_1>] or kwargs['df']
df = df.apply(lambda x: ...)
return f(*args, **kwargs)
return decorated
The wrapped method receives a DatFrame parameter as the 2nd parameter.
The problem is that the caller can do either foo(something, my_df) and then I need to look in args or foo(something, df=my_df) and I need to look in kwargs.
Is there a "nice" way to find a parameter in the wrapped function without having to explicitly check both dictionary and list?
EDIT
I tried kwargs.get('df', args[1]) but that throws an error since args has a single item ... I thought that optional part is evaluated only if get fails ...

Since your decorator is very specific, ie it has a dataframe and a single non-keyword argument, you could use the correct argument signature in your decorator method.
def wrapper(fun):
def working(a, kwd=None):
print("arg: ", a)
print("kwarg: ", kwd)
return fun(a, kwd)
return working
#wrapper
def testme(a, kwd=None):
print("running: ", a, kwd)
testme(1, 2)
testme(1, kwd=3)
The output is then:
arg: 1
kwarg: 2
running: 1 2
arg: 1
kwarg: 3
running: 1 3
Of course this could be improved by add #wraps from functools, but I think the idea is there.

Related

how to use python decorator with argument?

I would like to define a decorator that will register classes by a name given as an argument of my decorator. I could read from stackoverflow and other sources many examples that show how to derive such (tricky) code but when adapted to my needs my code fails to produce the expected result. Here is the code:
import functools
READERS = {}
def register(typ):
def decorator_register(kls):
#functools.wraps(kls)
def wrapper_register(*args, **kwargs):
READERS[typ] = kls
return wrapper_register
return decorator_register
#register(".pdb")
class PDBReader:
pass
#register(".gro")
class GromacsReader:
pass
print(READERS)
This code produces an empty dictionary while I would expect a dictionary with two entries. Would you have any idea about what is wrong with my code ?
Taking arguments (via (...)) and decoration (via #) both result in calls of functions. Each "stage" of taking arguments or decoration maps to one call and thus one nested functions in the decorator definition. register is a three-stage decorator and takes as many calls to trigger its innermost code. Of these,
the first is the argument ((".pdb")),
the second is the class definition (#... class), and
the third is the class call/instantiation (PDBReader(...))
This stage is broken as it does not instantiate the class.
In order to store the class itself in the dictionary, store it at the second stage. As the instances are not to be stored, remove the third stage.
def register(typ): # first stage: file extension
"""Create a decorator to register its target for the given `typ`"""
def decorator_register(kls): # second stage: Reader class
"""Decorator to register its target `kls` for the previously given `typ`"""
READERS[typ] = kls
return kls # <<< return class to preserve it
return decorator_register
Take note that the result of a decorator replaces its target. Thus, you should generally return the target itself or an equivalent object. Since in this case the class is returned immediately, there is no need to use functools.wraps.
READERS = {}
def register(typ): # first stage: file extension
"""Create a decorator to register its target for the given `typ`"""
def decorator_register(kls): # second stage: Reader class
"""Decorator to register its target `kls` for the previously given `typ`"""
READERS[typ] = kls
return kls # <<< return class to preserve it
return decorator_register
#register(".pdb")
class PDBReader:
pass
#register(".gro")
class GromacsReader:
pass
print(READERS) # {'.pdb': <class '__main__.PDBReader'>, '.gro': <class '__main__.GromacsReader'>}
If you don't actually call the code that the decorator is "wrapping" then the "inner" function will not fire, and you will not create an entry inside of READER. However, even if you create instances of PDBReader or GromacsReader, the value inside of READER will be of the classes themselves, not an instance of them.
If you want to do the latter, you have to change wrapper_register to something like this:
def register(typ):
def decorator_register(kls):
#functools.wraps(kls)
def wrapper_register(*args, **kwargs):
READERS[typ] = kls(*args, **kwargs)
return READERS[typ]
return wrapper_register
return decorator_register
I added simple init/repr inside of the classes to visualize it better:
#register(".pdb")
class PDBReader:
def __init__(self, var):
self.var = var
def __repr__(self):
return f"PDBReader({self.var})"
#register(".gro")
class GromacsReader:
def __init__(self, var):
self.var = var
def __repr__(self):
return f"GromacsReader({self.var})"
And then we initialize some objects:
x = PDBReader("Inside of PDB")
z = GromacsReader("Inside of Gromacs")
print(x) # Output: PDBReader(Inside of PDB)
print(z) # Output: GromacsReader(Inside of Gromacs)
print(READERS) # Output: {'.pdb': PDBReader(Inside of PDB), '.gro': GromacsReader(Inside of Gromacs)}
If you don't want to store the initialized object in READER however, you will still need to return an initialized object, otherwise when you try to initialize the object, it will return None.
You can then simply change wrapper_register to:
def wrapper_register(*args, **kwargs):
READERS[typ] = kls
return kls(*args, **kwargs)

Using singledispatch with custom class(CPython 3.8.2)

Let's say I want to set functions for each classes in module Named 'MacroMethods'. So I've set up singledispatch after seeing it in 'Fluent Python' like this:
#singledispatch
def addMethod(self, obj):
print(f'Wrong Object {str(obj)} supplied.')
return obj
...
#addMethod.register(MacroMethods.Wait)
def _(self, obj):
print('adding object wait')
obj.delay = self.waitSpin.value
obj.onFail = None
obj.onSuccess = None
return obj
Desired behavior is - when instance of class 'MacroMethods.Wait' is given as argument, singledispatch runs registered function with that class type.
Instead, it runs default function rather than registered one.
>>> Wrong Object <MacroMethods.Wait object at 0x0936D1A8> supplied.
However, type() clearly shows instance is class 'MacroMethods.Wait', and dict_keys property also contains it.
>>> dict_keys([<class 'object'>, ..., <class 'MacroMethods.Wait'>])
I suspect all custom classes I made count as 'object' type and don't run desired functions in result.
Any way to solve this problem? Entire codes are here.
Update
I've managed to mimic singledispatch's actions as following:
from functools import wraps
def state_deco(func_main):
"""
Decorator that mimics singledispatch for ease of interaction expansions.
"""
# assuming no args are needed for interaction functions.
func_main.dispatch_list = {} # collect decorated functions
#wraps(func_main)
def wrapper(target):
# dispatch target to destination interaction function.
nonlocal func_main
try:
# find and run callable for target
return func_main.dispatch_list[type(target)]()
except KeyError:
# If no matching case found, main decorated function will run instead.
func_main()
def register(target):
# A decorator that register decorated function to main decorated function.
def decorate(func_sub):
nonlocal func_main
func_main.dispatch_list[target] = func_sub
def register_wrapper(*args, **kwargs):
return func_sub(*args, **kwargs)
return register_wrapper
return decorate
wrapper.register = register
return wrapper
Used like:
#state_deco
def general():
return "A's reaction to undefined others."
#general.register(StateA)
def _():
return "A's reaction of another A"
#general.register(StateB)
def _():
return "A's reaction of B"
But still it's not singledispatch, so I find this might be inappropriate to post this as answer.
I wanted to do similar and had the same trouble. Looks like we have bumped into a python bug. Found a write-up that describes this situation.
Here is the link to the Python Bug Tracker.
Python 3.7 breaks on singledispatch_function.register(pseudo_type), which Python 3.6 accepted

Calling various function based on input in python 3.x

I'm writing some code to get various data from a class (which extracts data from a '.csv' file). I was wondering if there was a way to call one of these methods based off the name of an input
I've attempted to create a function called get(), which takes in 'param_name' - the name of the method contained within the class that I want to call. I was wondering if there was a more elegant way to solve this without creating a large amount of if statements.
def get(param_name):
# Some initialisation of the .csv file goes here. This works as intended.
list_of_objects = [] # Initialised above, as a list of objects with methods function1(), function2() for getting data out of the .csv
for item in list_of_objects:
if param_name == "name of function 1":
return function1()
if param_name == "name of function 2":
return function2()
You could store your functions ina a dictionary as such:
function_dict = {
'function_1': function_1,
'function_2': function_2
}
To use these you could do:
function_to_use = function_dict.get(param_name)
function_to_use(*args, **kwargs) # *args, **kwargs are arguments to be used.
If you want to return a list after you have applied the function to all item in list_of_objects instead of the for loop you could do:
list(map(function_to_use, list_of_objects))
You could use __getattribute__:
class Alpha:
def f1(self):
print("F1")
x = Alpha()
x.__getattribute__('f1')()
You can do that using globals(), globals() returns a dict containing all methods and attributes.
def fun1():
print('this is fun1')
def fun2():
print('this is fun2')
def get(func_name):
globals()[func_name]()
get('fun1')
get('fun2')
Will Output:
this is fun1
this is fun2

Python3 having problems passing *args and **kwargs to a threading.Timer() and a function

for some reason I am having trouble passing *args and **kwargs to both a threading.Timer() and a function...here is the code I am having trouble with;
from threading import Timer
def print_me(text='foo'):
print(text)
def repeat_task(delay, action, *args, **kwargs):
Timer(delay, repeat_task, (delay, action, [*args], {**kwargs}).start()
action(*args, **kwargs)
repeat_task( 5, print_me, text='bar' )
if anyone could point me in the right direction I would be very appreciative. :)
[*args] and {**kwargs} are both invalid syntax. kwargs is already a dict, so if you want it as a dict, just use it directly. args is a tuple, so if you want its value as a tuple, just use it directly, and if you want to turn it into a list for some reason, you can use list(args).
You're passing these arguments to Timer(), whose parameters are defined as:
Timer(interval, function, args=None, kwargs=None)
Notice that there are no *s there: the args and kwargs parameters for this constructor are normal parameters.
So your third argument needs to be all your positional arguments: delay and action, then all the contents of args. One way to do this is to add lists together with +:
[delay, action] + list(args)
And your fourth argument needs to be the keyword arguments, which is just kwargs.
So this should work:
Timer(delay, repeat_task, [delay, action] + list(args), kwargs).start()

python string format suppress/silent keyerror/indexerror [duplicate]

This question already has answers here:
How to get Python to gracefully format None and non-existing fields [duplicate]
(3 answers)
Closed 8 years ago.
Is there a way to use python string.format such that no exception is thrown when an index is missing, instead an empty string is inserted.
result = "i am an {error} example string {error2}".format(hello=2,error2="success")
here,result should be :
"i am an example string success"
Right now, python throws a keyerror and stops formatting. Is it possible to change this behavior ?
Thanks
Edit:
There exists Template.safe_substitute (even that leaves the pattern intact instead of inserting an empty string) , but couldn't something similar for string.format
The desired behavior would be similar to string substitution in php.
class Formatter(string.Formatter):
def get_value(self,key,args,kwargs):
try:
if hasattr(key,"__mod__"):
return args[key]
else:
return kwargs[key]
except:
return ""
This seems to provide the desired behavior.
The official solution (Python 3 Docs) for strings in format mappings is to subclass the dict class and to define the magic-method __missing__(). This method is called whenever a key is missing, and what it returns is used for the string formatting instead:
class format_dict(dict):
def __missing__(self, key):
return "..."
d = format_dict({"foo": "name"})
print("My %(foo)s is %(bar)s" % d) # "My name is ..."
print("My {foo} is {bar}".format(**d)) # "My name is ..."
Edit: the second print() works in Python 3.5.3, but it does not in e.g. 3.7.2: KeyError: 'bar' is raised and I couldn't find a way to catch it.
After some experiments, I found a difference in Python's behavior. In v3.5.3, the calls are __getitem__(self, "foo") which succeeds and __getitem__(self, "bar") which can not find the key "bar", therefore it calls __missing__(self, "bar") to handle the missing key without throwing a KeyError. In v3.7.2, __getattribute__(self, "keys") is called internally. The built-in keys() method is used to return an iterator over the keys, which yields "foo", __getitem__("foo") succeeds, then the iterator is exhausted. For {bar} from the format string there is no key "bar". __getitem__() and hence __missing_() are not called to handle the situation. Instead, the KeyError is thrown. I don't know how one could catch it, if at all.
In Python 3.2+ you should use format_map() instead (also see Python Bug Tracker - Issue 6081):
from collections import defaultdict
d = defaultdict(lambda: "...")
d.update({"foo": "name"})
print("My {foo} is {bar}".format_map(d)) # "My name is ..."
If you want to keep the placeholders, you can do:
class Default(dict):
def __missing__(self, key):
return key.join("{}")
d = Default({"foo": "name"})
print("My {foo} is {bar}".format_map(d)) # "My name is {bar}"
As you can see, format_map() does call __missing__().
The following appears to be the most compatible solution as it also works in older Python versions including 2.x (I tested v2.7.15):
class Default(dict):
def __missing__(self, key):
return key.join("{}")
d = Default({"foo": "name"})
import string
print(string.Formatter().vformat("My {foo} is {bar}", (), d)) # "My name is {bar}"
To keep placeholders as-is including the format spec (e.g. {bar:<15}) the Formatter needs to be subclassed:
import string
class Unformatted:
def __init__(self, key):
self.key = key
def __format__(self, format_spec):
return "{{{}{}}}".format(self.key, ":" + format_spec if format_spec else "")
class Formatter(string.Formatter):
def get_value(self, key, args, kwargs):
if isinstance(key, int):
try:
return args[key]
except IndexError:
return Unformatted(key)
else:
try:
return kwargs[key]
except KeyError:
return Unformatted(key)
f = Formatter()
s1 = f.vformat("My {0} {1} {foo:<10} is {bar:<15}!", ["real"], {"foo": "name"})
s2 = f.vformat(s1, [None, "actual"], {"bar":"Geraldine"})
print(s1) # "My real {1} name is {bar:<15}!"
print(s2) # "My real actual name is Geraldine !"
Note that the placeholder indices are not changed ({1} remains in the string without a {0}), and in order to substitute {1} you need to pass an array with any odd first element and what you want to substitute the remaining placeholder with as second element (e.g. [None, "actual"]).
You can also call the format() method with positional and named arguments:
s1 = f.format("My {0} {1} {foo:<10} is {bar:<15}!", "real", foo="name")
s2 = f.format(s1, None, "actual", bar="Geraldine")
str.format() doesn't expect a mapping object. Try this:
from collections import defaultdict
d = defaultdict(str)
d['error2'] = "success"
s = "i am an {0[error]} example string {0[error2]}"
print s.format(d)
You make a defaultdict with a str() factory that returns "". Then you make one key for the defaultdict. In the format string, you access keys of the first object passed. This has the advantage of allowing you to pass other keys and values, as long as your defaultdict is the first argument to format().
Also, see http://bugs.python.org/issue6081
Unfortunately, no, there is no such way to do by default. However you can provide it defaultdict or object with overridden __getattr__, and use like this:
class SafeFormat(object):
def __init__(self, **kw):
self.__dict = kw
def __getattr__(self, name):
if not name.startswith('__'):
return self.__dict.get(name, '')
print "i am an {0.error} example string {0.error2}".format(SafeFormat(hello=2,error2="success"))
i am an example string success
I made a version that does work similarly to Daniel's method but without the {0.x} attribute access.
import string
class SafeFormat(object):
def __init__(self, **kw):
self.__dict = kw
def __getitem__(self, name):
return self.__dict.get(name, '{%s}' % name)
string.Formatter().vformat('{what} {man}', [], SafeFormat(man=2))
prints out
'{what} 2'

Resources