Using Python decorators to take modulus of results - python

I'm learning decorators in Python, and I came across some trouble with a decorator I'm using:
#curry
def modulo(mod,f):
if mod:
#fun.wraps(f)
def wrapper(*args, **kw):
result = f(*args, **kw)
return tuple(r % mod for r in result)
else:
return f
return wrapper
The curry decorator is a simple currying, so I can call mod as an argument in the following:
def _fib(p=1,q=-1,mod=None):
''' Fibonacci sequence '''
#modulo(mod)
def next_fib(pair):
x,y = pair
return y, p*x - q*y
yield from ( y for x,y in iterate(next_fib,(1,0)) )
which works and looks nice and clean. However, say I wanted another [closely related] generator for Lucas sequences:
def _luc(p=1,q=-1,mod=None):
''' Lucas sequence '''
#modulo(mod)
def next_luc(pair):
x,y = pair
return y, p*x - q*y
yield from ( y for x,y in iterate(next_luc,(p-2,2)) )
If I call them together, I get some sort of collision:
>>> F = _fib()
>>> print(take(10,F))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
>>> L = _luc()
>>> print(take(10,L))
" ... TypeError: cannot unpack non-iterable function object"
Called individually they work as expected, with the correct modular terms returned.
My question twofold:
is this a namespace collision where they are both referring to modulo()?
How would you go about doing something like this?
helper functions:
import itertools as it
import functools as fun
def curry(f):
argc = f.__code__.co_argcount
f_args = []
f_kwargs = {}
#fun.wraps(f)
def wrapper(*args, **kwargs):
nonlocal f_args, f_kwargs
f_args += args
f_kwargs.update(kwargs)
if len(f_args)+len(f_kwargs) == argc:
return f(*f_args, **f_kwargs)
else:
return wrapper
return wrapper
def iterate(f,x):
''' x, f(x), f(f(x)), f(f(f(x))), ... '''
return it.accumulate(it.repeat(x), lambda fx, _: f(fx))
def take(n,iterable):
return [x for x in it.islice(iterable,n)]

I found that extending the original modulo wrapper does the trick!
With thanks to Thomas_Breydo for suggesting to change up the curried function
def modulo(mod):
def wrapper(f):
#fun.wraps(f)
def deco(*args,**kwargs):
if mod:
return tuple(n%mod for n in f(*args,**kwargs) )
else:
return f(*args,**kwargs)
return deco
return wrapper
which resolves my issue.

Related

how can I write a decorator that changes with arguments

I have to write a decorator def that takes a validator def as argument. If the validator returned true it should decorate main to execute some code and if it returned false it should print an error.
I have tried to write two def in decorator with an if statement to return two different defs but it is not working.
the functionality and the logic MUST be exactly like i said because of online judging (validation must be done outside of decorator)
Here's an example:
#define decorator...
def validator(x):
return x>=0
#decorator(validator)
def f(x):
return x**0.5
print(f(4)) #should print 2
print(f(-4)) #should print error
Here is what you can do
#define decorator...
def validator(x):
return x>=0
def deco(validator):
def decorator(func):
def wrapper_decorator(*args, **kwargs):
if validator(*args, **kwargs):
return func(*args, **kwargs)
else:
print("error")
return
return wrapper_decorator
return decorator
#deco(validator)
def f(x):
return x**0.5
print(f(4)) #should print 2
print(f(-4)) #should print error
The answers everyone has answered are basically correct. However for your case, you require an additional function that acts as a validator. Hence you can add in another outer def to take in the function of the validator and check if it returns True/False.
Decorators can be written as example
def hello_decorator(func):
def inner1(*args, **kwargs):
print("before Execution")
# getting the returned value
returned_value = func(*args, **kwargs)
print("after Execution")
# returning the value to the original frame
return returned_value
return inner1
# adding decorator to the function
#hello_decorator
def sum_two_numbers(a, b):
print("Inside the function")
return a + b
a, b = 1, 2
# getting the value through return of the function
print("Sum =", sum_two_numbers(a, b))
You can rewrite this code as
def limit_decorator(func):
def internal(arg):
if (arg >= 0):
return func(arg)
else:
raise Exception("false input")
return internal
#limit_decorator
def square_root(a):
return a * 0.5
a = -5
print("Sum =", square_root(a))
I would suggest to do the validation of x, using one layer on nested functions (basically merge the validator function into the decorator)
def deco(f):
def wrapper(x):
if x<=0:
return False
else:
return f(x)
return wrapper
#deco
def f(x):
return x**0.
f(1) #returns false
f(4) #returns 2.0
Try this:
def decorator(validator):
def subdecorator(function):
def actual_function(arg):
if not validator(arg):
raise ValueError(f"Bad data: {arg}")
return function(arg)
return actual_function
return subdecorator

Function decorator that logs the value of specified function arguments in an accessible python object

I am trying to create a function decorator that logs the value specified function arguments in an accessible python object. I have already working code but I am missing a piece to finish this up.
First, I have the object log where I will save stuff correctly set up:
class Borg:
_shared_state = {}
def __init__(self):
self.__dict__ = self._shared_state
class Log(Borg):
def __init__(self):
Borg.__init__(self)
if not hasattr(self, 'tape'):
self.tape = []
def add(self, this):
self.tape.append(this)
def __str__(self):
return '\n'.join([str(line) for line in self.tape])
Then I have a generic call object and the decorator implementation (with missing code):
import inspect
import functools
class Call:
def __init__(self, name, **saved_arguments):
self.name = name
self.saved_arguments = saved_arguments
def __str__(self):
return f'Call(name={self.name}, saved_arguments={self.saved_arguments})'
def record(func, save_args_names=None):
if save_args_names is None:
save_args_names = {}
name = func.__name__
args = inspect.getfullargspec(func).args
if save_args_names and not set(save_args_names).issubset(set(args)):
raise ValueError(f'Arguments not present in function: {set(save_args_names) - set(args)}')
log = Log()
#functools.wraps(func)
def wrapper(*func_args, **func_kwargs):
# **here** I am missing something to replace 0 with the correct values!
saved_arguments = {a: 0 for a in save_args_names}
log.add(Call(name, **saved_arguments))
return_value = func(*func_args, **func_kwargs)
return return_value
return wrapper
To test this, I have the following functions set up:
def inner(x, add=0):
return sum(x) + add
def outer(number, add=0):
x = range(number)
return inner(x, add)
and the basic use case (no saving of arguments) works:
inner = record(inner)
print(outer(1), outer(2), outer(3))
print(Log())
It outputs, correctly:
0 1 3
Call(name=inner, saved_arguments={})
Call(name=inner, saved_arguments={})
Call(name=inner, saved_arguments={})
What I am missing is a way to have this use case:
inner = record(inner, save_args_names=['x'])
print(outer(1), outer(2), outer(3))
print(Log())
to output:
0 1 3
Call(name=inner, saved_arguments={'x': range(0, 1)})
Call(name=inner, saved_arguments={'x': range(0, 2)})
Call(name=inner, saved_arguments={'x': range(0, 3)})
This, should also work for keyword arguments, e.g.:
inner = record(inner, save_args_names=['x', 'add'])
print(outer(1, 2), outer(2, 3), outer(3, 4))
print(Log())
should output:
2 4 7
Call(name=inner, saved_arguments={'x': range(0, 1), 'add': 2})
Call(name=inner, saved_arguments={'x': range(0, 2), 'add': 3})
Call(name=inner, saved_arguments={'x': range(0, 3), 'add': 4})
I feel like I am close and that the inspect library should help me close this, but a little help would be much appreciated!
The function you're looking for is Signature.bind. Define your wrapperfunction like so:
#functools.wraps(func)
def wrapper(*func_args, **func_kwargs):
signature = inspect.signature(func)
bound_args = signature.bind(*func_args, **func_kwargs)
saved_arguments = {a: bound_args.arguments[a] for a in save_args_names}
log.add(Call(name, **saved_arguments))
return_value = func(*func_args, **func_kwargs)
return return_value

How to return the value after decoration?

I used the following script for evaluating whether the values in function test are within the limits:
x=[-5,5]
def test(x):
return x
def check(min,max):
def decorator(func):
def wrapper(*args,**kargs):
y=func(*args,**kargs)
for index in range(len(y)):
if y[index]>max:
y[index]=max
elif y[index]<min:
y[index]=min
return func(*args,**kargs)
return wrapper
return decorator
In this test, the minimum is -1 and maximum is 1, so I used check(-1,1)(test(x)) for decorating test(x)in order to get the expecting output value as [-1,1]. However, the output is:
<function __main__.check.<locals>.decorator.<locals>.wrapper>
which is not the expected [-1,1].
You're not wrapping the function correctly. The correct syntactic form is:
check(-1,1)(test)(x)
# check(-1,1) -> returns func decorator
# (test) -> returns func wrapper
# (x) -> calls wrapper with one argument
Better to use the decorator syntax on the function directly tho:
#check(-1, -1)
def test(x):
return x
And you should return y, the modified container, and not call func a second time in your wrapper function:
def wrapper(*args,**kargs):
y = func(*args,**kargs)
...
return y
Your wrapper should return y, the result of calling the undecorated function, rather than making a second call to it:
x=[-5,5]
def test(x):
return x
def check(min, max):
def decorator(func):
def wrapper(*args, **kargs):
y=func(*args, **kargs)
for index in range(len(y)):
if y[index] > max:
y[index] = max
elif y[index] < min:
y[index] = min
return y # <- change to this
return wrapper
return decorator
test = check(-1, 1)(test) # decorate test function
print(test(x)) # -> [-1, 1]
If you don't want to permanently decorate test, you could use this instead:
print(check(-1, 1)(test)(x)) # -> [-1, 1]

How to pass an empty parameter to a python function?

Here is the working code:
def g(y=10):
return y**2
def f(x,y=10):
return x*g(y)
print(f(5)) #->500
However, let's suppose we don't want to remember and copy a default value of keyword parameter y to the definition of external function (especially if there are several layers of external functions). In the above example it means that we want to use parameter, already defined in g.
One way to do that:
def f(x,y=None):
if y==None: return x*g()
else: return x*g(y)
But is there a cleaner way to do the same?
Something like:
def f(x,y=empty()):
return x*g(y)
Interesting question! Here's another possibility, however this requires handing in the second parameter as a named parameter.
>>> def g(y=10):
... return y**2
...
>>> def f(x, **kwargs):
... return x * g(**kwargs)
...
>>> f(5)
500
>>> f(5, y=0)
0
A limitation of signatures such as def f(x, y=None) or def f(x, **kwargs) is that readers have to dig into source code or documentation to find out what's going on with y. Stick to something simple and straightforward:
DEFAULT_Y = 10
def g(y=DEFAULT_Y): ...
def f(x, y=DEFAULT_Y): ...
This is possible:
def g(y=10):
return y**2
def f(x, y=g.__defaults__[0]):
return x * g(y)
But it is arguably less clear than what you had originally (defaulting y to None).
An option which doesn't restrict the definition order of f and g, and should remain working if the function default of g gets changed dynamically:
def f(x, y=None):
kwargs = {}
if y is None:
kwargs['y'] = y
return x * g(**kwargs)
I'd like to start by saying if the arguments were keyword only this would be so easy:
def f(*, x="x", y= "y",z="z"):
print(x,y,z)
def g(*, x,y,z):
print(x,y,z,"from g!!")
if g.__kwdefaults__ is None: #completely override defaults
g.__kwdefaults__ = f.__kwdefaults__
else: #if there are already some defaults then update
g.__kwdefaults__.update(f.__kedefaults__)
g()
if you are using positional arguments it isn't quite as easy although your example is one of the specific cases that works the same way:
def g(y=10): #last argument is y
return y**2
def f(x,y): #last argument is y
return x*g(y)
f.__defaults__ = g.__defaults__ #copies the end of the defaults to f
print(f(5)) #->500
But this is a very specific case:
The arguments to inherit the defaults must be in the same order as the original.
There must not be any positional arguments after the ones with inherited defaults
There must not be any other arguments with default values (or they get overridden)
The generic solution requires quite a bit of code but allows any signature to be merged into another, for example:
def f(x,y,z=0,reverse=True):
pass
#copy_defaults(f)
def g(a,b, #arguments for g
x,y,z, #arguments to forward to f
c=None, d="test", #some optional arguments for g
*,reverse): #only take reverse as a keyword
pass
>>> inspect.signature(g)
<Signature (a, b, x, y, z=0, c=None, d='test', *, reverse=True)>
This can be achieved with the following code (I can't find a simpler way to do it that works with above case)
import inspect
def copy_defaults(original_f):
"creates wrapper for DefaultArgs(original_f).copy_defaults(dest_f)"
def wrapper(dest_f):
return DefaultArgs(original_f).copy_defaults(dest_f)
return wrapper
class DefaultArgs(dict):
def __init__(self,func):
spec = inspect.getfullargspec(func)
if spec.defaults:
dict.__init__(self,
zip(reversed(spec.args),
reversed(spec.defaults)
))
else:
dict.__init__(self) #I'm not sure this is necessary
if spec.kwonlydefaults:
self.update(spec.kwonlydefaults)
def get_kwdefaults(self,keywords):
return {k:v for k,v in self.items() if k in keywords}
def gen_pos_defaults(self,args,defaults=None):
if defaults is None:
defaults = ()
found_default = False
for i,arg in enumerate(args,start=len(defaults)-len(args)):
if arg in self:
yield self[arg]
found_default = True
elif i>=0:
yield defaults[i]
elif found_default: #if an argument does not have a default but is after one that does
raise TypeError("non-default argument %r follows default argument"%arg)
def copy_defaults(self,func):
spec = inspect.getfullargspec(func)
new_kwargs = self.get_kwdefaults(spec.kwonlyargs)
if func.__kwdefaults__ is not None:
func.__kwdefaults__.update(new_kwargs)
else:
func.__kwdefaults__ = new_kwargs
func.__defaults__ = tuple(self.gen_pos_defaults(spec.args,spec.defaults))
return func
If you can modify g, then this works:
def g(y=None):
if y is None:
y = 10
return y**2
def f(x,y=None):
return x*g(y)

Call several functions and return the collective result

I want to call some functions to a single value and return the collective result.
class Foo:
def __init__(self, i):
self.i = i
def get(self):
return self.fn1(self.fn2(self.i)) #200
def fn1(self, i):
return i + i #10+10 = 20
def fn2(self, i):
return i * i #20*20 = 200
#...
foo = Foo(10)
print(foo.get())
Is there a more elegant way or pattern?
Here is my try to improve this a little bit.
def fn1(i):
return i + i #10+10 = 20
def fn2(i):
return i * i #20*20 = 200
def get(i):
funcs = [fn2, fn1]
for f in funcs:
i = f(i)
return i
print(get(10))
In general, nesting functions as you do above is the most straightforward and readable way to compose functions in Python.
If you're composing many functions, it might be worth writing a compose function.
def compose(*funcs):
if len(funcs) == 1:
return funcs[0]
else:
def composition(*args, **kwargs):
return funcs[0](compose(*funcs[1:])(*args, **kwargs))
return composition
Or, if you prefer an iterative over a recursive solution:
def compose_pair(f1, f2):
def composition(*args, **kwargs):
return f1(f2(*args, **kwargs))
return composition
def compose_iterative(*funcs):
iterfuncs = iter(funcs)
comp = next(iterfuncs)
for f in iterfuncs:
comp = compose_pair(comp, f)
return comp
Personally, two of my favorite python functions are map and reduce.
def get(i):
return reduce(lambda acc, f: f(acc), [i,fn2,fn1] )
def fn1(i):
return i + i #10+10 = 20
def fn2(i):
return i * i #20*20 = 200
print( get(10) ) # 200
You could use a decorator-style solution:
class Base()
def __init__(self, decorated):
self.decorates = decorated
def foo(self, arg):
if self.decorates:
arg = self.decorates.foo( arg )
return self._do_foo( arg )
def _do_foo(self, arg):
return arg
Your implementations will inherit from Base and implement _do_foo().
You set it up like this:
a = Subclass(None)
b = AnotherSublcass( a )
c = YetAnotherSubclass( b )
all of the Sublcasses inherit from Base. when you call c.foo( arg ), you'll get the result passed through all three _do_foo() methods.

Categories

Resources