How to pass an empty parameter to a python function? - python

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)

Related

Using Python decorators to take modulus of results

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.

How to create a function that returns a function of the product of a list of functions

I would like to create a function that returns a function of the product of a list of functions. The list of functions should be of variable length and the functions should have different parameters.
E.g.:
def f(a, b, **kwargs):
return a + b
def g(c, d, **kwargs):
return c + d
def product_function(function_list, **kwargs):
...
<create function that returns product function of functions in
function_list>
...
return <productfunction>
In the example above this would be something like:
my_function = product_function([f,g])
This should return a function that can be used as if it was defined as:
def my_function(a, b, c, d):
return f(a, b) * g(c, d)
I would like to use this for iterating over a list of combinations of factors and optimising parameters for these combinations to select the most predictive one in a data science project.
You can do this with some help from the introspection utilities in the inspect module.
Specifically, I used inspect.signature to find each function's positional and keyword arguments, and Signature.bind_partial to prevent clashes between positional and keyword arguments. The following is a generic implementation of a function that combines other functions:
import inspect
def generic_operator_function(operator_function, default_value,
function_list, **kwargs):
POSITIONALS = {inspect.Parameter.POSITIONAL_ONLY,
inspect.Parameter.POSITIONAL_OR_KEYWORD}
KEYWORDS = {inspect.Parameter.POSITIONAL_OR_KEYWORD,
inspect.Parameter.KEYWORD_ONLY}
# if no functions were given, return the default value
if not function_list:
return lambda: default_value
# for each function in the list, find out how many positional
# arguments it accepts. Also find out which keyword arguments
# it accepts.
arg_maps = []
kwarg_names = []
for func in function_list:
sig = inspect.signature(func)
params = sig.parameters.values()
# count the positional arguments and map them to
# parameter names
bound_args = sig.bind_partial(**kwargs).arguments
arg_map = [param.name for param in params if param.kind in POSITIONALS
and param.name not in bound_args]
arg_maps.append(arg_map)
# find the names of all keyword arguments
if any(param.kind == inspect.Parameter.VAR_KEYWORD for param in params):
kwnames = True
else:
kwnames = {param.name for param in params if param.kind in KEYWORDS}
kwarg_names.append(kwnames)
# return a function that iterates through the function_list and
# multiplies all results
def combined_func(*args, **inner_kwargs):
value = default_value
i = 0
for func, arg_map, kwnames in zip(function_list, arg_maps, kwarg_names):
# if the function takes **kwargs, pass all kwargs. Otherwise, pass
# only those that it supports.
kw_arguments = kwargs.copy()
kw_arguments.update(inner_kwargs)
if kwnames is not True:
kw_arguments = {k: v for k, v in kw_arguments.items() if k in kwnames}
# take the next batch of arguments, but only those that aren't already
# provided as keyword arguments
arg_map = [arg for arg in arg_map if arg not in kw_arguments]
numparams = len(arg_map)
arguments = args[i:i+numparams]
kw_arguments.update({arg: value for arg, value in zip(arg_map, arguments)})
# call the function
retval = func(**kw_arguments)
value = operator_function(value, retval)
i += numparams
return value
return combined_func
With this, you can easily define a bunch of functions similar to your product_function:
import operator
def product_function(*args, **kwargs):
return generic_operator_function(operator.mul, 1, *args, **kwargs)
def sum_function(*args, **kwargs):
return generic_operator_function(operator.add, 0, *args, **kwargs)
def append_function(*args, **kwargs):
return generic_operator_function(lambda x, y: x+[y], [], *args, **kwargs)
>>> my_function = product_function([f,g])
>>> my_function(1,2, 3,4)
21
>>> sum_function([f,g])(1,2, 3,4)
10
>>> append_function([f,g])(1,2, 3,4)
[3, 7]
And it correctly passes on only those keyword arguments that each function supports:
>>> p = product_function([f,g], a=1, c=2)
>>> p(3, 4)
24

Python 3.5.1 - variable returns none

My question is regarding some code that is part of an Udacity assignment. The following code is not returning any value. I assume that I'm not calling the "scalar" function properly from my "normalized" function. The line norm = self.scalar(scale) returns type none. Can someone give me a pointer?
Code:
import math
from decimal import Decimal, getcontext
getcontext().prec = 10
class Vector(object):
def __init__(self, coordinates):
try:
if not coordinates:
raise ValueError
self.coordinates = tuple([Decimal(x) for x in coordinates])
self.dimension = len(self.coordinates)
except ValueError:
raise ValueError('The coordinates must be nonempty')
except TypeError:
raise TypeError('The coordinates must be an iterable')
def __eq__(self, v):
return self.coordinates == v.coordinates
def scalar(self, c):
new_coordinates = [Decimal(c)*x for x in self.coordinates]
#new_coordinates = []
#n = len(self.coordinates)
#for i in range(n):
# new_coordinates.append(self.coordinates[i] * c)
#print(Vector(new_coordinates))
def magnitude(self):
new_sq = [x**2 for x in self.coordinates]
new_mag = math.sqrt(sum(new_sq))
return (new_mag)
def normalized(self):
magnitude = self.magnitude()
scale = 1/magnitude
print(scale)
norm = self.scalar(scale)
#print(type(norm))
print(norm)
return (norm)
my_vector = Vector([1,2])
Vector.normalized(my_vector)
Python has this cool little trick where it will always return None if not specified. So if you write a function hello world that doesn't return anything you will get None.
for example:
def hello_world():
print('hello world')
result = hello_world()
print(result) # prints nothing cause result==None
You dont have a return statement in your scalar method, so it will always return None.
My guess is that you want to return the object you create in scalar
def scalar(self, c):
new_coordinates = [Decimal(c)*x for x in self.coordinates]
return new_coordinates
Or for brevity
def scalar(self, c):
return [Decimal(c)*x for x in self.coordinates]
The problem is that you're trying to get a value from scalar even though it doesn't return anything. I'm not entirely sure what you're trying to return so you'll have to deal with that yourself.
One notable issue is with your method calling the attribute of the my_vector instance. It's not technically the issue, but it should probably be changed. Your code should be the following.
my_vector = Vector([1,2])
my_vector.normalized()

Passing arguments down recursive functions

I want to pass an argument from the first call of a recursive function down to the later ones:
Example:
def function(x):
if base_case:
return 4
else:
return function(x_from_the_first_call + x_from_this_call)
Is there any better way of doing this than a closure?
E.g.
def function(outer_x):
def _inner(x)
if base_case:
return 4
else:
return function(outer_x + x)
return _inner(outer_x)
If you will change x somehow in function, then this should work i think:
def function(x, *args):
if base_case:
return 4
else:
new_x = x+1 # some change to x
if args:
# in args all previous x values
# remove if in case if you need all previous values
if not args:
args.append(x)
return function(new_x, *args)

How to pass additional parameters (besides arguments) to a function?

I need to write a function (say fun1) that has one argument, because it will be used in other function (fun2). The latter requires a function with a single argument. However, I need to pass other parameters to function fun1. How can I do this in Python without using global variables? Or this is the only way?
Addition: If it is important, fun2 is some optimization function from scipy.optimize. Below is an example of passing additional parameter c to function fun1 using global. In the first call, function fun2 takes fun1 as x+1, but in the second call, fun1 is x+2. I would like to make similar, but without using global. Hopefully, the example clarifies the question. (The example is changed).
def fun1(x) :
global c
return x + c
def fun2(f1, x) :
return f1(x)
# main program
global c
x0= 1
c= 1; y= fun2(fun1, x0); print(y) # gives 2
c= 2; y= fun2(fun1, x0); print(y) # gives 3
If I've understood your question correctly, there are quite a number of ways to do what you want and avoid using global variables. Here they are.
Given:
x0 = 1
def fun2(f1, x):
return f1(x)
All of these techniques accomplish your goal:
#### #0 -- function attributes
def fun1(x):
return x + fun1.c
fun1.c = 1; y = fun2(fun1, x0); print(y) # --> 2
fun1.c = 2; y = fun2(fun1, x0); print(y) # --> 3
#### #1 -- closure
def fun1(c):
def wrapper(x):
return x + c
return wrapper
y = fun2(fun1(c=1), x0); print(y) # --> 2
y = fun2(fun1(c=2), x0); print(y) # --> 3
#### #2 -- functools.partial object
from functools import partial
def fun1(x, c):
return x + c
y = fun2(partial(fun1, c=1), x0); print(y) # --> 2
y = fun2(partial(fun1, c=2), x0); print(y) # --> 3
#### #3 -- function object (functor)
class Fun1(object):
def __init__(self, c):
self.c = c
def __call__(self, x):
return x + self.c
y = fun2(Fun1(c=1), x0); print(y) # --> 2
y = fun2(Fun1(c=2), x0); print(y) # --> 3
#### #4 -- function decorator
def fun1(x, c):
return x + c
def decorate(c):
def wrapper(f):
def wrapped(x):
return f(x, c)
return wrapped
return wrapper
y = fun2(decorate(c=1)(fun1), x0); print(y) # --> 2
y = fun2(decorate(c=2)(fun1), x0); print(y) # --> 3
Note that writing c= arguments wasn't always strictly required in the calls -- I just put it in all of the usage examples for consistency and because it makes it clearer how it's being passed.
The fact that that function can be called even without those other parameters suggests, that they are optional and have some default value. So you should use default arguments.
def fun1(foo, bar='baz'):
# do something
This way you can call function fun1('hi') and bar will default to 'baz'. You can also call it fun1('hi', 15).
If they don't have any reasonable default, you can use None as the default value instead.
def fun1(foo, bar=None):
if bar is None:
# `bar` argument was not provided
else:
# it was provided
What you are looking for is a method in a class.
you define a class, with a method fun1 and an instance variable c. it is accessed from anywhere using the . notation:
class A:
def fun1(self, x):
return x + self.c
Let's define fun2, for the example:
def fun2(f, p):
return f(p)
We can now use a.c it like you did with the global varaible c:
>>> a = A() # create an instance and initialize it
>>> # "self.c" is undefined yet
>>>
>>> a.c = 1 # "self.c" will be 1
>>> fun2(a.fun1, 1)
2
>>> a.c = 2 # now "self.c" will be 2
>>> fun2(a.fun1, 1) # same arguments, different result
3
Here you can learn more about classes.
Just add the extra parameters with default values:
def fun1(param1, param2=None, param3=None):
...
Then you can call fun1 from fun2 like this:
def fun2():
something = fun1(42)
And from somewhere else you can call it like this:
fun1(42, param2=60)
You may use the decorators to pass it
the very decorators:
def jwt_or_redirect(fn):
#wraps(fn)
def decorator(*args, **kwargs):
...
return fn(*args, **kwargs)
return decorator
def jwt_refresh(fn):
#wraps(fn)
def decorator(*args, **kwargs):
...
new_kwargs = {'refreshed_jwt': 'xxxxx-xxxxxx'}
new_kwargs.update(kwargs)
return fn(*args, **new_kwargs)
return decorator
and the final function:
#jwt_or_redirect
#jwt_refresh
def home_page(*args, **kwargs):
return kwargs['refreched_jwt']

Categories

Resources