Manipulating variables using globals() in python - 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}

Related

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())]

Python: List static variables used inside a function

I want to define a function random_func with some arguments like arg_1 and arg_2 and some variables inside this function such as arg_3, arg_4.
How can I get a list of these arguments and parameters inside this function that starts with arg_ and return their corresponding values.
For example:
I list all variables by using dir() and then join all values by calling eval() function.
def random_func(arg_1, arg_2):
arg_3 = "a"
arg_4 = "b"
list = [name for name in dir() if name.startswith("arg_")]
return "-".join(eval(x) for x in list)
However, If I run, I get this error:
random_func(arg_1="one", arg_2="two")
>>> NameError: name 'arg_1' is not defined
Ideally I would get:
one-two-a-b
What can I do?
Note: This is a terrible idea, and I suspect an XY problem. If you keep reading, understand you're wandering off into the weeds, and probably not solving your real problem.
You can use locals() to get a read only view of the function's locals as a dict; it will give you access to both the name and the value without relying on eval (which is slow and more dangerous):
def random_func(arg_1, arg_2):
arg_3 = "a"
arg_4 = "b"
return "-".join([val for name, val in locals().items() if name.startswith("arg_")])
print(random_func("one", "two"))
Try it online!
As you desired, this prints one-two-a-b, and it does it without relying on eval at all.

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

Dynamically add keyword arguments

If I have a function:
def add(**kwargs):
for key, value in kwargs.iteritems():
print "%s = %s" % (key, value)
How can I dynamically add keyworded arguments to this function? I am building an HTML Generator in Python, so I need to be able to add keyworded arguments depending on which attributes the user wants to enable.
For instance, if the user wants to use the name attribute in a form, they should be able to declare it in a list of stored keyword arguments (don't know how to do this either). This list of stored keyword arguments should also contain the value of the variable. For instance, if the user wants the attribute name to be "Hello", it should look like and be passed to the function as so:
name = "Hello" (example)
Let me know if you guys need more information. Thanks!
You already accept a dynamic list of keywords. Simply call the function with those keywords:
add(name="Hello")
You can use the **expression call syntax to pass in a dictionary to a function instead, it'll be expanded into keyword arguments (which your **kwargs function parameter will capture again):
attributes = {'name': 'Hello'}
add(**attributes)

Setting local variable of a global function via kwargs in 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', ''))

Categories