ChatGPT解决这个技术问题 Extra ChatGPT

Dynamically set local variable [duplicate]

This question already has answers here: How can you dynamically create variables? [duplicate] (8 answers) Closed 7 years ago.

How do you dynamically set local variable in Python (where the variable name is dynamic)?

You're doing it wrong if the variable name is dynamic.
I once programmed to read chemical species name from input file and then create objects for those species name taking what I read from text file. In this way i can say H2O.mwt, something like that. there could be a legitimate reason to do this, IMHO.
I am aware this is bad practice. But in some conditions it may be a dirty shortcut. Besides, it's interesting, it shows how dynamic the language can really be. It's also good that the feedback is here so readers won't use it readily, but that's not a valid reason for a down-vote.
The fast locals array is coupled to the code object (i.e. co_varnames, co_nlocals). It's fixed. locals() is just calling PyFrame_FastToLocals to create a dict view of the current values. You can dynamically update the fast locals based on this dict if you use ctypes to call PyFrame_LocalsToFast on the current frame (sys._getframe(0)).
@eryksun - that sounds like the start of a complete and insightful answer - if you'll compile it to one, I'll accept it

u
user2357112

Contrary to other answers already posted you cannot modify locals() directly and expect it to work.

>>> def foo():
    lcl = locals()
    lcl['xyz'] = 42
    print(xyz)


>>> foo()

Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    foo()
  File "<pyshell#5>", line 4, in foo
    print(xyz)
NameError: global name 'xyz' is not defined

Modifying locals() is undefined. Outside a function when locals() and globals() are the same it will work; inside a function it will usually not work.

Use a dictionary, or set an attribute on an object:

d = {}
d['xyz'] = 42
print(d['xyz'])

or if you prefer, use a class:

class C: pass

obj = C()
setattr(obj, 'xyz', 42)
print(obj.xyz)

Edit: Access to variables in namespaces that aren't functions (so modules, class definitions, instances) are usually done by dictionary lookups (as Sven points out in the comments there are exceptions, for example classes that define __slots__). Function locals can be optimised for speed because the compiler (usually) knows all the names in advance, so there isn't a dictionary until you call locals().

In the C implementation of Python locals() (called from inside a function) creates an ordinary dictionary initialised from the current values of the local variables. Within each function any number of calls to locals() will return the same dictionary, but every call to locals() will update it with the current values of the local variables. This can give the impression that assignment to elements of the dictionary are ignored (I originally wrote that this was the case). Modifications to existing keys within the dictionary returned from locals() therefore only last until the next call to locals() in the same scope.

In IronPython things work a bit differently. Any function that calls locals() inside it uses a dictionary for its local variables so assignments to local variables change the dictionary and assignments to the dictionary change the variables BUT that's only if you explicitly call locals() under that name. If you bind a different name to the locals function in IronPython then calling it gives you the local variables for the scope where the name was bound and there's no way to access the function locals through it:

>>> def foo():
...     abc = 123
...     lcl = zzz()
...     lcl['abc'] = 456
...     deF = 789
...     print(abc)
...     print(zzz())
...     print(lcl)
...
>>> zzz =locals
>>> foo()
123
{'__doc__': None, '__builtins__': <module '__builtin__' (built-in)>, 'zzz': <built-in function locals>, 'foo': <function foo at 0x000000000000002B>, '__name__': '__main__', 'abc': 456}
{'__doc__': None, '__builtins__': <module '__builtin__' (built-in)>, 'zzz': <built-in function locals>, 'foo': <function foo at 0x000000000000002B>, '__name__': '__main__', 'abc': 456}
>>>

This could all change at any time. The only thing guaranteed is that you cannot depend on the results of assigning to the dictionary returned by locals().


Two minor corrections: 1. The return value of locals() is a standard dictionary in CPython 2.x and 3.x, which can be changed as usual (but the changes to propagate back to the local scope). 2. Access to class and instance namespaces doesn't always involve a dictionary look-up. There are several exceptions, including instances of classes that define __slots__.
Thanks @SvenMarnach, I've updated my answer a bit on your second point. What I wrote on the first point is sufficiently specific that I'm sure I must have tested it, so either I confused myself totally or perhaps it varies by Python version. I'll check again and update the answer in a bit.
@SvenMarnach, I figured it out: multiple calls to locals() in the same function return the same dictionary and overwrite existing keys each time you call it so because I mixed multiple calls to locals() with printing the dictionary it originally returned it looked to me as though it wasn't changing. I've explained this possible pitfall in the answer. Thanks.
@CharlieParker it is better because if you don't know the name of a local in advance there is no way to access it except through the locals() dict, and no way to be sure you haven't overwritten or hidden another local or even a global name. Using a dict it is safe to store any strings as keys.
@Duncan I always thought python was a language for consenting adults. If one uses exec or overwrites locals() one takes the risks of the things you mentioned (overwriting things for example). If one knows for sure that loading variables to locals and what the names are one can just use those variables later, the compiler has an issue if they have not been pre-defined a-priori. I personally don't see the big deal, one can always re-write gobals like for = 'breaking for keyword' without the need the help of overwriting locals. I guess I don't appreciate the real issue or something...
k
kindall

Others have suggested assigning to locals(). This won't work inside a function, where locals are accessed using the LOAD_FAST opcode, unless you have an exec statement somewhere in the function. To support this statement, which could create new variables that are not known at compile time, Python is then forced to access local variables by name within the function, so writing to locals() works. The exec can be out of the code path that is executed.

def func(varname):
    locals()[varname] = 42
    return answer           # only works if we passed in "answer" for varname
    exec ""                 # never executed

func("answer")
>>> 42

Note: This only works in Python 2.x. They did away with this foolishness in Python 3, and other implementations (Jython, IronPython, etc.) may not support it either.

This is a bad idea, though. How will you access the variables if you don't know their name? By locals()[xxx] probably. So why not just use your own dictionary rather than polluting locals() (and taking the chance of overwriting a variable your function actually needs)?


C
Community

(Just a quick note for others googlin')

Ok, so modifying locals() is not the way to go ( while modifying globals() is supposed to work). In the meantime, exec could be, but it's painfully slow, so, as with regular expressions, we may want to compile() it first:

# var0 = 0; var1 = 1; var2 = 2
code_text = '\n'.join( "var%d = %d" % (n, n) for n in xrange(3) )

filename = ''
code_chunk = compile( code_text, filename, 'exec' )

# now later we can use exec:
exec code_chunk # executes in the current context

... and why would you ever do this if you can just use a dictionary?
"I'm aware this isn't good practice, and the remarks are legit, but this doesn't make it a bad question, just a more theoretical one" (q) TS -- takes quite a while to explain. and it's a different question. let's say this answer is safe to ignore in most cases ) ps. and isn't it good that if you'll decide you need it, it would be there?
p
poke

You could modify locals() directly:

locals()['foo'] = 'bar'

But a better way would be to have some dict that holds all your dynamic variable names as dictionary keys:

d = {}
for some in thing:
    d[some] = 'whatever'

-1 for suggesting modifying locals()
@Duncan: If you read carefully, I didn’t suggest doing it. I merely said that one could do it, but that there is a better way.
Saying you could do it is what I'm objecting to. From the docs: Note The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter.
-1 modifying locals() doesn't work.
locals()['foo'] = 'bar' works in python 2.7 and 3, was this changed at some time?
R
Redsplinter

I've spent the last... couple hours, I guess, trying to hack around the lack of function closures, and I came up with this, which might help:

common_data = ...stuff...
def process(record):
    ...logic...

def op():
    for fing in op.func_dict: # Key line number 1
        exec(fing + " = op.func_dict[fing]") # Key line number 2

    while common_data.still_recieving:
        with common_data._lock:
            if common_data.record_available:
                process(common_data.oldest_record)
        time.sleep(1.0)

op.func_dict.update(locals()) # Key line number 3
threading.Thread(target = op).start()

...

It's a pretty heavy handed / contrived example, but if there are a lot of locals or you're still in the process of prototyping this pattern becomes useful. Mostly I was just bitter about all the data stores being replicated or moved in order to handle callback delegates, etc.


A
Adam Zalcman

You can use a local dictionary and put all the dynamic bindings as items into the dictionary. Then knowing the name of such a "dynamic variable" you can use the name as the key to get its value.


0
0x90

Let's say We have the below dictionary:

DictionaryA = {'No Rating': ['Hobbit', 'Movie C', 'Movie G'],
               'Forget It': ['Avenger', 'Movie B'], 
               'Must See': ['Children of Men', 'Skyfall', 'Movie F'], 
               '3': ['X-Men', 'Movie D'],
               '2': ['Captain America', 'Movie E'], 
               '4': ['Transformers', 'Movie A']} 

I want to create new dictionaries as below:

NewDictionary1 = {'No Rating': ['Hobbit', 'Movie C', 'Movie G']} 
NewDictionary2 = {'Forget It': ['Avenger', 'Movie B']} 
NewDictionary3 = {'Must See': ['Children of Men', 'Skyfall', 'Movie F']}

a oneliner:

dics = [{k:v} for k,v in DictionaryA.iteritems()]

would be outputted to:

[{'Must See': ['Children of Men', 'Skyfall', 'Movie F']}, {'Forget It': ['Avenger', 'Movie B']}, {'No Rating': ['Hobbit', 'Movie C', 'Movie G']}, {'3': ['X-Men', 'Movie D']}, {'2': ['Captain America', 'Movie E']}, {'4': ['Transformers', 'Movie A']}]

But to precisely declaring variables we could go with:

>>> i=0
>>> lcl = locals()
>>> for key,val in DictionaryA.iteritems():
        lcl["Dict" + str(i)] = {key:val}
        i += 1

As can be seen the first 3 Dict variables:

>>> Dict0
{'Must See': ['Children of Men', 'Skyfall', 'Movie F']}
>>> Dict1
{'Forget It': ['Avenger', 'Movie B']}
>>> Dict2
{'No Rating': ['Hobbit', 'Movie C', 'Movie G']}

As mentioned by others, if you want to put it in a function you should add it to the globals():

>>> glb = globals()
>>> for key,val in DictionaryA.iteritems():
        glb["Dict" + str(i)] = {key:val}
        i += 1