In Python, is it possible to redefine the default parameters of a function at runtime?
I defined a function with 3 parameters here:
def multiplyNumbers(x,y,z):
return x*y*z
print(multiplyNumbers(x=2,y=3,z=3))
Next, I tried (unsuccessfully) to set the default parameter value for y, and then I tried calling the function without the parameter y:
multiplyNumbers.y = 2;
print(multiplyNumbers(x=3, z=3))
But the following error was produced, since the default value of y was not set correctly:
TypeError: multiplyNumbers() missing 1 required positional argument: 'y'
Is it possible to redefine the default parameters of a function at runtime, as I'm attempting to do here?
Just use functools.partial
multiplyNumbers = functools.partial(multiplyNumbers, y = 42)
One problem here: you will not be able to call it as multiplyNumbers(5, 7, 9); you should manually say y=7
If you need to remove default arguments I see two ways:
Store original function somewhere
oldF = f
f = functools.partial(f, y = 42)
//work with changed f
f = oldF //restore
use partial.func
f = f.func //go to previous version.
Technically, it is possible to do what you ask… but it's not a good idea. RiaD's answer is the Pythonic way to do this.
In Python 3:
>>> def f(x=1, y=2, z=3):
... print(x, y, z)
>>> f()
1 2 3
>>> f.__defaults__ = (4, 5, 6)
4 5 6
As with everything else that's under the covers and hard to find in the docs, the inspect module chart is the best place to look for function attributes.
The details are slightly different in Python 2, but the idea is the same. (Just change the pulldown at the top left of the docs page from 3.3 to 2.7.)
If you're wondering how Python knows which defaults go with which arguments when it's just got a tuple… it just counts backward from the end (or the first of *, *args, **kwargs—anything after that goes into the __kwdefaults__ dict instead). f.__defaults = (4, 5) will set the defaults to y and z to 4 and 5, and with default for x. That works because you can't have non-defaulted parameters after defaulted parameters.
There are some cases where this won't work, but even then, you can immutably copy it to a new function with different defaults:
>>> f2 = types.FunctionType(f.__code__, f.__globals__, f.__name__,
... (4, 5, 6), f.__closure__)
Here, the types module documentation doesn't really explain anything, but help(types.FunctionType) in the interactive interpreter shows the params you need.
The only case you can't handle is a builtin function. But they generally don't have actual defaults anyway; instead, they fake something similar in the C API.
yes, you can accomplish this by modifying the function's func.__defaults__ tuple
that attribute is a tuple of the default values for each argument of the function.
for example, to make pandas.read_csv always use sep='\t', you could do:
import inspect
import pandas as pd
default_args = inspect.getfullargspec(pd.read_csv).args
default_arg_values = list(pd.read_csv.__defaults__)
default_arg_values[default_args.index("sep")] = '\t'
pd.read_csv.__defaults__ = tuple(default_arg_values)
use func_defaults as in
def myfun(a=3):
return a
myfun.func_defaults = (4,)
b = myfun()
assert b == 4
check the docs for func_defaults here
UPDATE: looking at RiaD's response I think I was too literal with mine. I don't know the context from where you're asking this question but in general (and following the Zen of Python) I believe working with partial applications is a better option than redefining a function's defaults arguments
Related
Suppose I have a Python function foo which takes a default argument, where that default is set to some global variable. If I now change that global variable before calling the function, the default argument is still set to the original value of that global variable.
For example:
x = 1
def foo(a=x):
print a
x = 2
foo()
This prints 1, instead of 2.
How should I be writing my code, so that I can change this global variable, and have it update this default argument?
A default variable is only evaluated and set once. So Python makes a copy of the reference and from then on, it always passes that reference as default value. No re-evaluation is done.
You can however solve this by using another object as default object, and then use an if statement to substitute it accordingly. Something like:
the_default = object()
x = 1
def foo(a = the_default):
if a is the_default:
a = x
print a
x = 2
foo()
Note that we use is to perform reference equality. So we check if it is indeed the default_object. You should not use the the_default object somewhere else in your code.
In many Python code, they use None as a default (and thus reduce the number of objects the construct). For instance:
def foo(a = None):
if a is None:
a = x
print a
Note however that if you do that, your program cannot make a distinction between a user calling foo() and foo(None) whereas using something as the_object makes it harder for a user to obtain a reference to that object. This can be useful if None would be a valid candidate as well: if you want foo(None) to print 'None' and not x.
Sorry if this has already been asked, I'm not sure exactly how to describe my question in a sentence anyway. I'm doing some work for my bioinformatics course, and I've begun to see variables declared when functions are called, instead of just passing the argument (see line 5 below).
def sobelFilter(pixmap):
matrix = array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]])
grey = pixmap.mean(axis=2) #<---here
edgeX = convolveMatrix2D(grey, matrix)
edgeY = convolveMatrix2D(grey, matrix.T)
pixmap2 = sqrt(edgeX * edgeX + edgeY * edgeY)
normalisePixmap(pixmap2)
return pixmap2
What is the purpose of grey = pixmap.mean(axis=2) when axis is never used again? Why not just say grey = pixmap.mean(2)?
If it's necessary for my question, this is just code we were given to use, not written by myself. Pixmap refers to this code:
def imageToPixmapRGB(img):
img2 = img.convert("RGB")
w,h = img2.size
data = img2.getdata()
pixmap = array(data, float)
pixmap = pixmap.reshape((h,w,3))
return pixmap
There is not a local variable called axis being initialized, you are just explicitly stating which parameter of the function you are passing.
You might use this if there are multiple optional parameters in a function:
Example:
In [9]: def foo(x=2, y=2):
...: print(x, y)
...:
In [10]: foo(y=3)
2 3
If I want to change y, but leave x as default, I must specify when I call the function.
Even when you aren't setting a particular optional parameter however, there is nothing wrong with passing an argument by its keyword.
pixmap is a numpy array with dimension >= 2. You can test this by printing pixmap.ndim.
The line you refer to is calculating the mean of an array along an axis. In other words, axis is an argument of the mean method. See numpy.mean documentation for more details.
You are right in that the axis parameter may be omitted, i.e. arr.mean(2) is equivalent to arr.mean(axis=2). Most likely it was explicitly stated for clarity.
This looks a bit like the variable assignment syntax because of the =, but it is actually a very different feature:
When you call functions, you can specify which variables to set to what using the "keyword argument" syntax. This allows you to set specific parameters by name:
some_func(arg1, arg2, arg5=None)
This is very useful with default arguments:
def x(a, b, c=None, d=None):
pass
If you wanted to set d with the normal syntax, you would have to specify an argument for c too.
x(1, 2, None, "hi")
Using the keyword argument syntax, you can omit this:
x(1, 2, d="hi")
Keyword arguments are often, like in this case, also used to make the purpose of a parameter clear to the reader. If you see:
pixmap.mean(2)
It is not immediately clear what the 2 meas. However, if you see
pixmap.mean(axis=2)
you immediately know what 2 refers to and why it's there
Also see this section of the python docs
I need to obtain a list of functions, where my function is defined as follows:
import theano.tensor as tt
def tilted_loss(y,f,q):
e = (y-f)
return q*tt.sum(e)-tt.sum(e[e<0])
I attempted to do
qs = np.arange(0.05,1,0.05)
q_loss_f = [tilted_loss(q=q) for q in qs]
however, get the error TypeError: tilted_loss() missing 2 required positional arguments: 'y' and 'f'. I attempted the simpler a = tilted_loss(q=0.05) with the same result.
How do you go about creating this list of functions when parameters are required? Similar questions on SO consider the case where parameters are not involved.
You can use functools.partial:
q_loss_f = [functools.partial(tilted_loss, q=q) for q in qs]
There are 2 ways you can solve this problem. Both ways require you know the default values for y and f.
With the current function, there's simply no way for the Python interpreter to know the value of y and f when you call tilted_loss(q=0.05). y and f are simply undefined & unknown.
Solution (1): Add default values
We can fix this by adding default values for the function, for example, if default values are: y = 0, f = 1:
def tilted_loss(q, y=0, f=1):
# original code goes here
Note that arguments with default values have to come AFTER non-default arguments (i.e q).
Solution (2): Specify default values during function call
Alternatively, just specify the default values every time you call that function. (Solution 1 is better)
How can I assign the results of a function call to multiple variables when the results are stored by name (not index-able), in python.
For example (tested in Python 3),
import random
# foo, as defined somewhere else where we can't or don't want to change it
def foo():
t = random.randint(1,100)
# put in a dummy class instead of just "return t,t+1"
# because otherwise we could subscript or just A,B = foo()
class Cat(object):
x = t
y = t + 1
return Cat()
# METHOD 1
# clearly wrong; A should be 1 more than B; they point to fields of different objects
A,B = foo().x, foo().y
print(A,B)
# METHOD 2
# correct, but requires two lines and an implicit variable
t = foo()
A,B = t.x, t.y
del t # don't really want t lying around
print(A,B)
# METHOD 3
# correct and one line, but an obfuscated mess
A,B = [ (t.x,t.y) for t in (foo(),) ][0]
print(A,B)
print(t) # this will raise an exception, but unless you know your python cold it might not be obvious before running
# METHOD 4
# Conforms to the suggestions in the links below without modifying the initial function foo or class Cat.
# But while all subsequent calls are pretty, but we have to use an otherwise meaningless shell function
def get_foo():
t = foo()
return t.x, t.y
A,B = get_foo()
What we don't want to do
If the results were indexable ( Cat extended tuple/list, we had used a namedtuple, etc.), we could simply write A,B = foo() as indicated in the comment above the Cat class. That's what's recommended here , for example.
Let's assume we have a good reason not to allow that. Maybe we like the clarity of assigning from the variable names (if they're more meaningful than x and y) or maybe the object is not primarily a container. Maybe the fields are properties, so access actually involves a method call. We don't have to assume any of those to answer this question though; the Cat class can be taken at face value.
This question already deals with how to design functions/classes the best way possible; if the function's expected return value are already well defined and does not involve tuple-like access, what is the best way to accept multiple values when returning?
I would strongly recommend either using multiple statements, or just keeping the result object without unpacking its attributes. That said, you can use operator.attrgetter for this:
from operator import attrgetter
a, b, c = attrgetter('a', 'b', 'c')(foo())
This question already has answers here:
Is Python's order of evaluation of function arguments and operands deterministic (+ where is it documented)?
(2 answers)
Closed 4 years ago.
Is it safe to assume that function arguments are evaluated from left to right in Python?
Reference states that it happens that way but perhaps there is some way to change this order which may break my code.
What I want to do is to add time stamp for function call:
l = []
l.append(f(), time.time())
I understand that I can evaluate the arguments sequentially:
l = []
res = f()
t = time.time()
l.append(res, t)
But it looks less elegant so I'd prefer the first way if I can rely on it.
Quoting from the reference documentation:
Python evaluates expressions from left to right.
So yes, you can count on that (with one exception, see below).
A call (the (...) part after a primary, such as a function name) is just another expression primary, and the arguments for the call are just more expressions.
Note: There is one exception to this rule. When using *expression in a call (to expand an iterable to form additional positional arguments), then this expression is evaluated before any keyword argument expressions:
>>> from itertools import count
>>> def bar(n, r=(), c=count()): print(f'{next(c)}: bar({n!r})'); return r
...
>>> def foo(*args, **kwargs): pass
...
>>> foo(bar('a1'), spam=bar('a2'), *bar('varargs'), **bar('kwargs', {}))
0: bar('a1')
1: bar('varargs')
2: bar('a2')
3: bar('kwargs')
The linked documentation states:
A consequence of this is that although the *expression syntax may appear after explicit keyword arguments, it is processed before the keyword arguments[.]
Yes, Python always evaluates function arguments from left to right.
This goes for any comma seperated list as far as I know:
>>> from __future__ import print_function
>>> def f(x, y): pass
...
>>> f(print(1), print(2))
1
2
>>> [print(1), print(2)]
1
2
[None, None]
>>> {1:print(1), 2:print(2)}
1
2
{1: None, 2: None}
>>> def f(x=print(1), y=print(2)): pass
...
1
2