Setting local variable of a global function via kwargs in Python - python

Not being able to overload functions in Python brought me to look into *args and **kwargs to pass undefined amounts of arguments and variable definitions to a called function. I tried to apply that concept to a simple function that prints arguments to the screen using a seperator (and without the nasty "" around everything by the while). You can find the code snippet at the end of that post.
My problem is that I don't know the correct way to actually assign values of kwargs to their respective parameters OUTSIDE CLASSES. If I want use setattr which takes in 3 arguments (setatt(self, key, value)) it will, of course, always give me an error when I try to pass only a key and a value. Is there a proper way to assign values to parameters for global functions outside classes?
I hope you can clear the skies for me.
Here the not working code example which I wanna get to work by finding an alternative to setattr(self, key, value):
import sys
def printAll(*args, **kwargs):
sep = ""
for key, value in kwargs.items():
setattr(key, value)
for arg in args:
sys.stdout.write(str(arg))
sys.stdout.write(sep)
nr = 9876543210
printAll("How many", "arguments", " I want", nr, sep='-')
EDIT:
Ok, here is the updated and working example function after reading the answers of #Martijn Pieters and #Fabian:
def printAll(*args, **kwargs):
# use sep = 'value' to define a seperator or use the default one
sep = kwargs.get('sep')
if sep is None:
sep = ""
for arg in args:
sys.stdout.write(str(arg))
sys.stdout.write(sep)

Python 2:
def foo(*args, **kwargs):
for key, value in kwargs.iteritems():
locals()[key] = value
return foobar
exec ''
print foo(foobar=12)
This is not a good idea though. You should specifically access kwargs, as Martijn said. Generally you cannot modify locals() in a function, unless you put an exec statement in it. That forces the interpreter to access locals() again, because it's possible that the namespace has changed (and did, in this case). It's sufficient to put the exec statement right after the return so it will never be executed, as long as it's there.
Please don't do this.

You generally don't set kwargs values as local variables. It's a dictionary, just refer to the values contained:
sys.stdout.write(kwargs.get('sep', ''))

Related

Manipulating variables using globals() in python

I wanted to have the option to pass my arguments to a function in a concise manner. So instead of passing all arguments directly, I include them as parameters function definiton and pass all such arguments as Common_Args. Then I do the following
for key, value in Common_args.items():
if key in locals():
globals()[key] = value
Here is the problem. Even though the control goes inside the if condition(checked with debugger), the parameter's value do not change, but it should, right?
Guides have confirmed that this should work, and if I run single statements in my debugger, the value of the variable does change.
My function definition in more detail is
def plotLFPData1Channel(plotHandles=None, channelString="", stimulus_list=None, folderName=None, analysisType=None,
timeVals=None, plotColor=None, blRange=None, stRange=None, referenceChannelString=None,
badTrialNameStr=None, useCommonBadTrialsFlag=None ,*,Common_args=None):
# first we have to unpack common args
if Common_args is not None:
for key, value in Common_args.items():
if key in locals():
globals()[key] = value
and I call the function like so
result=plotLFPData1Channel(plotHandles=ax.flatten(), channelString=analogChannelString, stimulus_list=stimValsToUse,analysisType=analysisType,Common_args=commonArgs)
with commonArgs={"folderName":folderName , "timeVals":timeVals, "plotColor":plotColor, "blRange":blRange, "stRange":stRange, "referenceChannelString":referenceChannelString, "badTrialNameStr":badTrialNameStr, "useCommonBadTrialsFlag":useCommonBadTrialsFlag}

python dict unpacking so that kw arguments are added to locals()

Quick silly question:
def show(**args):
print(locals())
show(a = 1)
I would like the print to show
{a: 1}
and not
{args : {a: 1}}
is that possible ?
Unfortunately, you can't do what you're asking for.
Python functions use what are called "fast" locals. The compiler assigns each local variable an index into an array, and local assignments and lookups use those indexes, rather than the variable names themselves.
What this means is that you can't add new variable names at runtime. There is no way to write a function that has local variable names that are unknown at compile time, as those variables could not be assigned an index into the fast locals array.
Code like you want would not be a good idea anyway.
Variable names are intended to be used by the programmer, not by the user. If you need a namespace for user-supplied data, use a dictionary where the keys are explicitly data, not local variables.
If you just want to be able to run b=a**2, you should make a an explicitly named argument for your function, rather than using **args in the first place. If you want to require your arguments be passed as keywords (rather than positionally), you can do that with:
def show(*, a): # a is a keyword-only argument
b = a**2
print(a, b)
The same way you would add something to any dictionary.
for arg in args:
locals()[arg] = args[arg]
del args
Of course, if there is an arg in args named "args", then this needs some wonky logic to move "args" to a new unused variable not in args:
locals()[max(args) + max(locals())] = args
del args
for arg in locals()[max(locals())]:
locals()[arg] = locals()[max(locals())][arg]
if "arg" in locals()[max(locals())]:
locals()["arg"] = locals()[max(locals())]["arg"]
else:
del arg
del locals()[max(locals())]

Is there a cleaner way to test long list of parameters in python3?

I have a function which takes a **keywords parameter, and I want to loop through parameters in a list to test whether or not the function is working accurately. However, when I give the parameters with key, value pairs such as 'name=joe,' python complains of syntax only in the list. So, I hacked a solution with encapsulating the parameters in quotes and using the exec function for calling the parameters. However, this seems hacky, and is there a better way to test functions with many test cases? Here is my code:
def function(**keywords):
for key, value in keywords.items():
print(value)
joe = 'joe'
bug='bug'
parameter_list = ('name=joe, insect=bug', 'name=joe')
# test parameters my hacky way
for parameters in parameter_list:
# place to test function
exec('function('+parameters+')', locals(), globals())
If my original design for testing this way is unpreferred, I am open to any alternatives!
Thanks!
why don't you pass a tuple of dictionaries of arguments instead?
def function(**keywords):
for key, value in keywords.items():
print(value)
joe = "joe"
bug = "bug" # if you want an indirection...
parameter_list = ({'name':joe, 'insect':bug}, {'name':joe})
for parameters in parameter_list:
# place to test function
function(**parameters)

Python: call function with default arguments that come before positional arguments

For example, I'd like to do something like: greet(,'hola'), where greet is:
def greet(person='stranger', greeting='hello')
This would help greatly for testing while writing code
Upon calling a function you can use the variable names to make it even more clear what variable will assume which value. At the same time, if defaults are provided in the function definition, skipping variables when calling the function does not raise any errors. So, in short you can just do this:
def greet(person='stranger', greeting='hello')
print('{} {}'.format(greeting, person))
return
greet(greeting='hola') # same as greet(person='stranger', greeting='hola')
# returns 'hola stranger'
Note that, as I said above this would not work if for example your function definition was like this:
def greet(person, greeting)
print('{} {}'.format(greeting, person))
return
Since in this case, Python would complain saying that it does not know what to do with person; no default is supplied..
And by the way, the problem you are describing is most likely the very reason defaults are used in the first place
Without knowing the other parameters, and only knowing that the parameter you want to change is in second position you could use the inspect module to get function signature & associated default values.
Then make a copy of the default values list and change the one at the index you want:
import inspect
def greet(person='stranger', greeting='hello'):
print(person,greeting)
argspec = inspect.getargspec(greet)
defaults = list(argspec.defaults)
defaults[1] = "hola" # change second default parameter
greet(**dict(zip(argspec.args,defaults)))
Assuming that all parameters have default values (else it shifts the lists an that fails) that prints:
stranger hola

passing parameters in python function

I have written one method in python as follows :
def report(self,year,month):
print year,month
emptylist=[]
parameter month is optional. Sometimes it's NOT passed from the caller function but sometimes it is passed.How is this different from the following code ?
def func(self,*args,**kwargs):print args,kwargs I know this has simple answer but I'm new to python and programming stuff. Please help me out.
If month has a default value, you can try
def report(self, year, month='default_value'):
print year, month
emptylist = []
The value of month gets overwritten if passed
In addition to Ashoka's answer, it's normally a good idea to use None as the default value in the method's signature and assign the true default value with in function body. This makes it easier to wrap the function, pass arguments, and it also avoids problems with mutable default values.
def func(kwarg=None):
if kwarg is None:
kwarg = 'default'
print(kwarg)
func()
func(kwarg='Hello')

Categories