I have the following code:
help1 = 14
help2 = "nice"
help3 = "gate"
try:
print('''
help1 %d
help2 %s
help3 %s
help4 %s
''' % (help1, help2, help3, help4))
except (NameError):
print("")
I would like my print to reference multiple variables, some of which aren't defined (such as help4). How can I amend the print statement to skip any undefined variables? I tried with a nameError exception - but couldn't get it to work.
You're not even getting to the print here. You can't reference undefined variables. Doing so raises a NameError. So you get to the NameError before the tuple of four values can even be created.
(Well, I suppose you could do something horrible with a chain of 10 except NameError: blocks that go through all the possible permutations of what could be wrong, but… eww…)
If you really need to do something like this, you have to manually look the names up indirectly in whichever namespace you think they should be in. For example, if this code is inside a function, and all four variables are supposed to be locals, you can look them up by name in the local namespace:
[locals().get(name, 'not found') for name in ('help1', 'help2', 'help3', 'help4')]
And likewise for globals, or any other namespace.
But this is almost certainly a bad idea. You should probably be doing something like:
Assign default values to all of these variables at the top of the function/module/whatever, so they aren't undefined variables.
Put these values in a list or a dictionary instead of in a bunch of separate variables that may or may not exist. (Notice that the hacky solution means you're already effectively doing this, except that you're hiding the dictionary away from yourself.)
Keep track of the state more carefully so you know which variables you can use at any given point.
If you want to check if a var exists, you can do
if "var_name" in locals():#or globals()
print(var_name)
But this is usually not a good approach.
Related
For instance, let's say I have a variable named D with a value of 3.
D = 3
Is it possible to get the name of the variable D, with just knowing the value of it, which is 3?
with(3) # get name of variable (pseudo code of what I need to do)
Does that make sense? Is that possible?
Assume that this is a global variable, don't worry about the scope.
I tried to see if anyone else has asked this and no one else has.
Yes it is possible.
Should you do it?
Definitely not.
All variables in Python are kept in namespaces, that in easier or tougher ways, can be viewed as dictionaries (mappings, more accuratlly).
Once you get to the proper dictionary, just iterate through it and compare the values with the one you desire.
For a global variable in the current module, it is simple - for arbitrarily placed references, one can make use of the garbage collector module (gc) and retrieve all references to a given object - it can get a lot of edge-cases, but it is feasible. (in this case you need to have a reference to the instance you want to locate, not just an equivalent value that would compare equal).
Getting back to a global variable in the current module:
looking_for = 3
for name, value in globals().items():
if value == looking_for:
print(f"{value} is stored in the variable {name!r}")
again: there is no reason to do that in "real world" code except if you are writing a debugger - it can be a nice toy to learn more about the language, though.
You can use dir() without any parameters to have it return every single property and method and then iterate looking for anything that evals to 3. This just feels like a bad idea though, so I would reconsider whatever problem you encountered that led you to thinking this is a solution.
a = "foo"
b = "bar"
c = [1,2,3]
d = 3
e = {1:"a",2:"b"}
#grab a list of variables in play
all_variables = dir()
#iterate over variables
for name in all_variables:
#eval the variable to get the value
myvalue = eval(name)
if myvalue == 3:
print(f'{name} is a variable equal to 3')
context: I have global variables ids1, ids2, ids3, ids4 and ids5.
When I attempt to execute this function
def collect_id_sols(id_no):
ids = vars()["ids" + str(id_no)]
sols = vars()["sols" + str(id_no)]
for line in range(0,len(ids)):
#rest irrelevant...
The interpreter throws a:
File "sols_from_ids.py", line 112, in <module>
collect_id_sols(1)
File "sols_from_ids.py", line 78, in collect_id_sols
ids = vars()["ids" + str(id_no)]
KeyError: 'ids1'
i.e. what it's telling me is that there is no such key "ids1".
However, the variable is CLEARLY existing and completely accessible.
Right after this error is thrown, if I do a >>>ids1 or >>>vars()["ids1"] within the interpreter, everything shows and works just as it should be.
What's going on? :(
P.S. And, of course, the global variables are declared and assigned before the function definition and call.
vars returns a dictionary giving the local scope, so it wouldn't know about these non-local variables. While there are ways (e.g. with globals()) to do what you want to do, a much better solution is to use a proper data structure such as a list or dictionary for your ids. Anytime you find yourself trying to iterate over a collection of variables which differ only by a number tacked onto the end, there is a data structure waiting to be born.
According to Python docs, vars() used without an argument acts like locals(), i.e. :
Update and return a dictionary representing the current local symbol table. Free variables are returned by locals() when it is called in function blocks, but not in class blocks.
So when you use 'vars()' in your function block, it will return the symbol table of within the function block, which will be empty in your case. So you get a Key Error.
Using vars()["something"] to access the variable something only works at module-level, not inside functions or classes. Plus it is not a viable thing to do in my opinion.
What you probably want to do here is to use an array to store your variables idsX, where ids[1] will be ids1 etc. (or a dictionary)
Or you can have another function like :
def getIds(number) :
if number == 1 :
return ids1
elif number == 2 :
return ids2
etc, and then in collect_id_sols you just do ids = getIds(id_no).
We discussed in my job about the following piece of Python code (maybe an anti-pattern):
if conditional_variable_:
a = "Some value"
print a
Supose conditional_variable was defined but a variable didn't.
The question is about using a variable without declaring it. The variable a is created inside a piece of code that maybe never will be executed but it is used.
Maybe that fix may repair the anti-pattern:
a = "default value"
if conditional_variable:
a = "changed_value"
print a
In that case, a variable was defined before use it. Consider print a like a ussage of the a variable.
It is not an anti-pattern. It is a bug.
Python has no 'declarations', only binding operations; a name is either bound, or it is not. Trying to access a name that hasn't been bound to yet results in an exception.
Unless your code specifically handles the exception and expected it, running into a NameError or UnboundLocalError exception should be considered a bug.
In other words, code that tries to reference a name should always be subject to the same conditions that bind the name, or be prepared to handle the exception that'll be raised if those conditions don't always hold. Giving your variable a default value outside the if statement means it is bound under all circumstances, so you can also reference it always.
I have the following piece of code inside a function:
try:
PLACES.append(self.name)
except NameError:
global PLACES
PLACES = [self.name]
Which causes from <file containing that code> import * to return
SyntaxWarning: name 'PLACES' is used prior to global declaration
global PLACES
So I was wondering if it is considered bad practice to do such a thing, and if so, what is the correct way of doing it? I'm a noob btw.
The first problem is you shouldn't do from foo import *, this is just bad practice and will cause namespace collisions (without any warnings, by the way), and will cause you headaches later on.
If you need to share a global storage space between two modules; consider pickling the object and unpickling it where required; or a k/v store, cache or other external store. If you need to store rich data, a database might be ideal.
Checking if a name points to a object is usually a sign of bad design somewhere. You also shouldn't assume to pollute the global namespace if a name doesn't exist - how do you know PLACES wasn't deleted intentionally?
Yes, it is considered a bad practice. Just make sure the variable is defined. Virtually always, this is as simple as as module-level assignment with a reasonable default value:
Places = []
When the default value should not be instantiated at import time (e.g. if it is very costy, or has some side effect), you can at least initialize None and check whether the_thing is None when it's needed, initializing it if it's still None.
I only suggest you move global PLACES out of except block:
global PLACES
try:
PLACES.append(self.name)
except NameError:
PLACES = [self.name]
Just define:
PLACES = []
before anything else.
Than later:
PLACE.append(self.name)
If checked with
if PLACES:
an empty list yields false. This way you can tell if there are any places there already. Of course, you don't need to check anymore before you append.
This is something that I've been questioning for some time. How would I create a variable at runtime as named by the value of another variable. So, for example, the code would ask the user to input a string. A variable would then be created named after that string with a default value of "default". Is this even possible?
It is possible, but it's certainly not advised. You can access the global namespace as a dict (it's a dict internally) and add entries to it.
If you were doing an interactive interpreter, say, for doing maths, or something. You would actually pass a dict to each eval() or exec that you could then re-use as it's local namespace.
As a quick, bad, example, don't do this at home:
g = globals() # get a reference to the globals dict
g[raw_input("Name Please")] = raw_input("Value Please")
print foo
Run that, it'll traceback unless you provide 'foo' to the first prompt.