New to python and have situation like this:
myfun.py:
def fun(x, ...):
par1 = ...
par2 = ...
... # many pars
parn = ...
return #*long and complicated function of all the pars and x inputs*
Sometimes I'd like to modify one or more of the pars without modifying myfun.py itself. I know I could:
Define every par as an argument to fun and give it a default value
Make a dictionary par_dict = {'par1': ..., ...} then wherever I have par1 in my return code replace it with par_dict['par1']. Then use par_dict.update() to take a dictionary argument and update my pars.
Any clean and compact alternatives to these options? I'd like something like this:
fun(x_in, par10=5)
# output uses 5 where par10 occurs in the function, in other words the argument to the function overrides the values set inside the function.
Thank you.
The most pythonic way I find is something like this:
First you define the function, such as
def fun(x, *, par1=1, par2=2, ..., parn=999):
return # function using all params
The function has its default settings, with the predefined values for the parameters. The * as second argument is to prevent the use of positional arguments for further than the x variable.
Then you may use configurable dictionaries to alter the params:
params = {
'par1': 10,
'par2': 20,
...
'parn': 0}
fun(X, **params)
The **params distributes the variables declared in the dictionary to the input parameters in the function.
EDIT It is also possible to use nested functions, like this:
def outer(par1=1, par2=2, ..., parn=999):
def inner(x):
return # function using x and pars...
return inner
Notice that the params of the outer function don't need to have default values. Then you "instance" the function, using it with any set of new params.
params = {...} # like the previous example
fun = outer(**params)
fun(X)
You can use outer to create different functions, that behaves as the params you input, for instance:
params1 = {...}
fun1 = outer(**params1)
params2 = {...}
fun2 = outer(**params2)
a = fun1(X)
b = fun2(X)
In this case a and b are different.
Related
I am writing a function that takes a lot of keywords.
I have a dictionary which is very lengthy that contains many of these keywords that already exists in my code and is being used elsewhere. E.g.
{'setting1':None, 'setting2': None....}
I am wondering is there a way, when I define my function, for me to set all of these as keywords, rather than having to type them out again like this:
def my_function(setting1=None, setting2=None, **kwargs)
To be clear, essentially I want to set all of the contents of the dictionary to be keywords with default value None, and when I call the function I should be able to change their values. So I am not looking to provide the dictionary as kwargs upon calling the function.
While not exactly the same, I ususally prefer to save the arguments in **kwargs and use .get() to get the value or None:
def my_function(**kwargs):
do_something(kwargs.get("alpha"), kwargs.get("beta"))
.get() on a dictionary returns the value if a key exists, or None of it does not. You can optionally specify a different default value as a second argument if you like.
When creating a function, you will need to implement how your arguments are used. By automatically creating arguments you end up adding arguments and forgetting to implement a behaviour for them.
# Manually defined.
def func(a, b, c, d):
return a + b / c * d
# Auto-defined - Human error.
def func(""" auto define a,b,c,d,e,f,g,h """):
return a + b / c * d # <- you only use half of the arguments. Confusing at best.
# Auto-defined - Inputs unclear, code is not explicit.
def func(defind_my_args):
return a + b / c * d
If you need to reuse "code behaviour" to the point that you can "inherit" parameters, maybe you should be using an object instead.
I have a class. This class has a list of functions that are to be evaluated by a different program.
class SomeClass(object):
def __init__(self, context):
self.functions_to_evaluate = []
There is a function that adds functions to an instance of SomeClass, via something like:
new_function = check_number(5)
SomeClassInstance.functions_to_evaluate.append(new_function)
Where check_number is a function that will check if number is greater than 10, let's say.
If I take SomeClassInstance.functions_to_evaluate and print it, I get a bunch of python objects, like so:
<some_library.check_number object at 0x07B35B90>
I am wondering if it is possible for me to extract the input given to check_number, so something like:
SomeClassInstance.functions_to_evaluate[0].python_feature() that will return "5" or whatever the input to check_number was to me.
You can use the standard library functools.partial, which creates a new partially applied function *.
>>> from functools import partial
>>> def check_number(input):
... return input > 10
>>> fn = partial(check_number, 5)
>>> fn.args # this attribute gives you back the bound arguments, as a tuple.
(5,)
>>> fn() # calls the function with the bound arguments.
False
*: actually the partial object is not a function instance, but it is a callable, and from a duck-type perspective it's a function.
If new_function = check_number(5) is a closure, then you can extract this value using __closure__[0].cell_contents:
Example:
def foo(x):
def inn(y):
return x
return inn
s = foo(5)
print(s.__closure__[0].cell_contents)
Output:
5
I understand your confusion, but:
new_function = check_number(5)
Is calling the function, and the new_function variable gets assigned the return value of the function.
If you have this check_number function:
def check_number(input):
return input > 10
Then it will return False, and new_function will be False. Never <some_library.check_number object at 0x07B35B90>.
If you're getting <some_library.check_number object at 0x07B35B90> then your check_number() function is returning something else.
There are probably several ways to skin this cat. But I'd observe first and foremost that you're not adding python function objects to the functions_to_evaluate list, you're adding the evaluations of functions.
You could simply add a tuple of function, args to the list:
SomeClassInstace.functions_to_evaluate.append((check_number, 5))
And then you can:
for f, args in SomeClassInstance.functions_to_evaluate:
print(args)
all.
I was wondering if it was possible to set multiple keywords at once (via list?) in a function call.
For example, if you do:
foo, bar = 1, 2
print(foo, bar)
The output is (1,2).
For the function
def printer(foo, bar)
print(foo,bar)
Is it possible to do something like:
printer([foo, bar] = [1,2])
where both keywords are being set with a list?
In particular, the reason why I ask is because I have a function that returns two variables, scale and offset:
def scaleOffset(...):
'stuff happens here
return [scale, offset]
I would like to pass both of these variables to a different function that accepts them as keywords, perhaps as a nested call.
def secondFunction(scale=None, offset=None):
'more stuff
So far I haven't found a way of doing a call like this:
secondFunction([scale,offset] = scaleOffset())
To pass args as a list
arg_list = ["foo", "bar"]
my_func(*arg_list)
To pass kwargs, use a dictionary
kwarg_dict = {"keyword": "value"}
my_func(**kwarg_dict)
I'm trying to create a method in python, which accepts 1-n number of parameters, calls the same method on each and returns the result. For example;
(Note this is pseudo code, I'm just typing these methods/syntax on the fly - new to python )
def get_pid(*params):
pidlist = []
for param in params:
pidlist.add(os.getpid(param))
return pidlist
Ideally I would like to do something like
x, y = get_pid("process1", "process2")
Where I can add as many parameters as I want - and the method to be as 'pythonic' and compact as possible. I think there may be a better way than looping over the parameters and appending to a list?
Any suggestions/tips?
Your code already works. Your function accepts 0 or more arguments, and returns the results for each function call. There is but a minor mistake in it; you should use list.append(); there is no list.add() method.
You could use a list comprehension here to do the same work in one line:
def get_pid(*params):
return [os.getpid(param) for param in params]
You could just inline this; make it a generator expression perhaps:
x, y = (os.getpid(param) for param in ("process1", "process2"))
You could also use the map() function for this:
x, y = map(os.getpid, ("process1", "process2"))
You can use yield to create a generator:
def get_pid(*params):
for param in params:
yield os.getpid(param)
x, y = get_pid("process1", "process2")
print(x, y)
So I have created a function that applies an action (in this case point wise multiplication of an array with a sinusoid, but that does not matter for my question) to an array.
Now I have created another function with which I want to create a string of python code to apply the first function multiple times later-on. The input of the second function can be either a string or an array, so that I can use the second function on its own output as well, if need be. My method of getting the variable name in a string works outside of the function.
Input :
var = np.array([[1,3],[2,4]]) # or sometimes var = 'a string'
if type(var)==str:
var_name = var
else:
var_name = [ k for k,v in locals().items() if v is var][0]
var_name
Output :
'var'
So here var is the variable (either array or string) supplied to the function, in this case an array. The if statement nicely returns me its name.
However when I use this inside my function, no matter what input I give it, it actually seems to look for var in locals(). Somehow it does not take var from the function input.
Definition :
def functionTWO(var, listoflistsofargs=None):
if type(var)==str:
var_name = var
else:
var_name = [ k for k,v in locals().items() if v is var][0]
if listoflistsofargs==None:
return var_name
command = []
for i in range(len(listoflistsofargs)):
if i==0:
command.append('functionONE(')
command.append(var_name)
command.append(',%.17f, %.17f)' % tuple(listoflistsofargs[i]))
else:
command.insert(0,'functionONE(')
command.append(',%.17f, %.17f)' % tuple(listoflistsofargs[i]))
''.join(command)
command[0] = var_name + ' + ' + command[0]
return ''.join(command)
Input :
somearray = np.array([[1,2,3],[1,2,3],[1,2,3]])
args = [[1,3],[6,5]]
command = functionTWO(somearray, args)
command
Output :
NameError: name 'var' is not defined
Wanted output :
'functionONE(functionONE(somearray, 1, 3), 6, 5)'
Why is listoflistsofargs taken from the function input and var not? I specify var in the listcomprehension in the definition of functionTWO. Normally when I use list comprehensions with function inputs it works fine. Does anybody know why this isnt the case here? Thank you in advance!
EDIT : So I guess the answer is dont. The implementation of classes by Marcin looks much cleaner and about the same order of amount of code. Too bad I couldnt get this to work inside a function. For other donts (actually other ideas) about using variable names as strings there is this question, where I got the above list comprehension for variable names.
You cannot pass a variable as a string*, and you should not do so.
If you want to pass a value between functions, the normal way is to pass it in as a parameter, and out as a return value.
If that is inconvenient, the usual solution is an object: define a class which carries both the shared variable, and methods which act on the variable.
If you need to create command objects, it is much better to do so in a structured way. For example, if you want to pass a function, and parameters, you can literally just pass the function object and the parameters in a tuple:
def foo():
return (functionONE,somearray,1,3)
command = foo()
command[0](*command[1:])
If you want to embed such commands within commands, you'll likely want to wrap that up with a class, so you can recursively evaluate the parameters. In fact, here's a little evaluator:
def evaluator(object):
def __init__(self,func=None,params=None):
self.func = func
self.params = params
def eval(self,alternativeparams=None):
if alternativeparams is not None:
params = alternativeparams
else:
params = self.params
if params is not None:
evaluatedparams = (item() if callable(item) else item for item in params)
else: evaluatedparams = None
if func is not None:
return self.func(*(evaluatedparams or ()))
else: return evaluatedparams
def __call__(self, *params):
return self.eval(params if params else None)
Although there are hacks by which you can pass references to local variables out of a function, these are not a great idea, because you end up creating your own half-baked object system which is more difficult to understand.
* This is because a variable has a name (which is a string) and a context, which maps names to strings. So, you need, at least to pass a tuple to truly pass a variable.
presenter's answer works in python 3
def get_variable_name(*variable):
return [ k for k,v in globals().items() if v is variable[0]][0]
example:
>> test = 'sample string'
>> get_variable_name(test)
'test'
>>
The only issue is it is a little messy.
The reason you get the error NameError: name 'var' is not defined when you wrap it all into a function is the following: locals().items() refers to the locally defined variables. as a matter of fact, the locally defined variables in your functions are only the variables defined inside the function and those passed as arguments to the function.
As the name says, locals().items() is local, and will consist of different variables depending on where it is called in your script.
On the contrary globals().items() consists of all the global variables, so try using it instead in your functions, it should do the trick.
For more info, look up global and local variables as well as the scope of variables in Python.