I need to check one or another environment variable.
With casual variables - I can use:
if var1:
print('Var1 found')
elif var2:
print('Var2 found')
else:
print('Neither Var1 nor Var2 not found')
How can I do same for environment variables?
I can't use if/else, because of if variable not found - os.environ will raize KeyError:
File "C:\Python27\lib\os.py", line 425, in __getitem__
return self.data[key.upper()]
KeyError: 'BAMBOO_WORKING_DIRECTORY'
If I'll do two try/except, like:
try:
r = os.environ['Var1']
except KeyError as error:
print('Var1 not found')
try:
r = os.environ['Var2']
except KeyError as error:
print('Var2 not found')
So, it will check both of them. But I need Var1 or Var2.
Add if/else after first try/except, to check if r: an call second try/except if not? Will looks disgusting...
os.environ is a dict, so you can use the .get call on it with default value.
If you use the two .get calls in conjunction, then this will get you the first variable, if it is present (because of python short circuiting), else it will get you the second one.
So essentially, the code is simplified to:
r = os.environ.get('Var1', "") or os.environ.get('Var2', "")
In this case, there is no need of a try - except block.
Chain this slightly longer, and you can get the expression which will give the default value as well:
>>> r = os.environ.get('Var1', "") or os.environ.get('Var2', "") or (
"Neither Var1 nor Var2 not found")
The exact equivalent of your if/elif for known variables (but for environment variables) would be:
from os import environ
if environ.get("VAR1"):
print('VAR1 found')
elif environ.get("VAR2"):
print('VAR2 found')
else:
print('Neither VAR1 nor VAR2 not found')
Since os.environ is a dict and dict.get has a signature of dict.get(key, [default]) where default defaults to None you can take advantage of this and get None back for key(s) that don't exist (which evaluate Falsely).
How about using a for loop?
for varname in ['Var1', 'Var2']:
try:
r = os.environ['Var1']
break
except KeyError as error:
print('{} not found'.format(varname))
You can nest your try statements
try:
r = os.environ['Var1']
try:
r = os.environ['Var2']
except KeyError as error:
print('Var2 not found')
except KeyError as error:
print('Var1 not found')
Related
I would like to assign value to a variable if a certain statement does not raise exceptions. If exceptions occur, None should be assigned. What is the pythonic way to achieve this?
Here is a concrete example of what I'm trying to do:
try:
bar = foo(data['buzz']) # data is a dict
except KeyError:
bar = None
Points to note:
KeyError may result from buzz not existing in data
foo can still throw an exception (of any kind, depends on it's implementation) even if buzz exists in data
While your approach is readable, you might want to consider the following.
Caveat?
bar will be set to None if a KeyError occurs inside function also:
data = {'buzz': 1}
def foo(x):
abc_value = data['abc'] # this raises a `KeyError` inside function `foo`.
try:
bar = foo(data['buzz']) # data is a dict
except KeyError:
bar = None
print(bar)
# None
Solution:
Better way you can avoid this is to avoid using try-except as it's scope is nested as well:
if "buzz" in data:
bar = foo(data["buzz"])
else:
bar = None
Even more pythonically in one line:
bar = foo(data["buzz"]) if "buzz" in data else None
For completeness, another way to avoid the problem raised by Austin, with other KeyErrors in foo (not caused by "buzz" being missing from data) triggering the except clause, would be to use the else clause:
try:
val = data["buzz"]
except KeyError:
bar = None
else:
bar = foo(val)
This else clause would be executed in the event that the statements within try did not raise KeyError.
Of course, in this case you would be better explicitly testing whether the "buzz" key exists in the dictionary, as others have suggested.
I think a better way to do this in your example is:
if "buzz" in data:
bar = foo(data["buzz"])
else:
bar = None
If you're specifically asking about a dictionary, you can use myDic.get(key) instead of myDict(key), why? because get() provides a fallback value, what's a fallback ? in case key is not in myDict, use the fallback value, in your case:
bar = foo(data.get('buzz', None) # if buzz is in data, return data["buzz"] else None
bar = [f if 'buzz' in data else None for f in data][0]
The [0] part isn't as pythonic as I would like...
Assuming foo doesn't raise a KeyError:
bar = foo(data['buzz']) if 'buzz' in data else None
However, I think your approach is cleaner and more correct, based on the pythonic pattern of "ask for forgiveness rather than permission" described here: https://docs.quantifiedcode.com/python-anti-patterns/readability/asking_for_permission_instead_of_forgiveness_when_working_with_files.html
Is there a deleting method for deleting an element of a set that takes a parameter to return if there is no element to delete that matches the parameter you gave it to delete?
So it would be something like set.discard(a,b) where a is the parameter that you want to delete and b is the parameter that gets returned if a is not found.
Something like this?
def _discard(s, key, ret):
try:
s.remove(key)
except KeyError:
return ret
return key
s = set([1,2,3])
ret = "not found"
key = 4
print _discard(s, key, ret), s
key = 3
print _discard(s, key, ret), s
Not built-in.
remove(elem)
remove(elem)
Remove element elem from the set. Raises KeyError if elem is not contained in the set.
Try to catch the exception in your own function maybe?
When exception caught return your element b.
Let's say I have the following function/method, which calculates a bunch of stuff and then sets a lot a variables/attributes: calc_and_set(obj).
Now what I would like to do is to call the function several times with different objects, and if one or more fails then nothing should be set at all.
I thought I could do it like this:
try:
calc_and_set(obj1)
calc_and_set(obj2)
calc_and_set(obj3)
except:
pass
But this obviously doesn't work. If for instance the error happens in the third call to the function, then the first and second call will already have set the variables.
Can anyone think of a "clean" way of doing what I want? The only solutions I can think of are rather ugly workarounds.
I see a few options here.
A. Have a "reverse function", which is robust. So if
def calc_and_set(obj):
obj.A = 'a'
def unset(obj):
if hasattr(obj, 'A'):
del obj.A
and
try:
calc_and_set(obj1)
calc_and_set(obj2)
except:
unset(obj1)
unset(obj2)
Notice, that in this case, unset doesn't care if calc_and_set completed successfully or not.
B. Separate calc_and_set to try_calc_and_set, testing if it works, and set, which won't throw errors, and would be called only if all try_calc_and_set didn't fail.
try:
try_calc_and_set(obj1)
try_calc_and_set(obj2)
calc_and_set(obj1)
calc_and_set(obj2)
except:
pass
C. (my favorite) - have calc_and_set return a new variable, and not operate in place. If successful, replace the original reference with the new one. This could easily be done by adding copy as the first statement in calc_and_set, and then returning the variable.
try:
obj1_t = calc_and_set(obj1)
obj2_t = calc_and_set(obj2)
obj1 = obj1_t
obj2 = obj2_t
except:
pass
The mirror of that one is of course to save your objects before:
obj1_c = deepcopy(obj1)
obj2_c = deepcopy(obj2)
try:
calc_and_set(obj1)
calc_and_set(obj2)
except:
obj1 = obj1_c
obj2 = obj2_c
And as a general comment (if this is just a sample code, forgive me) - don't have excepts without specifying exception type.
You can also try cache the actions you want to take and then do them all in one go if everybody passes:
from functools import partial
def do_something (obj, val):
# magic here
def validate (obj):
if obj.is_what_you_want():
return partial(do_something, obj, val)
else:
raise ValueError ("unable to process %s" % obj)
instructions = [validate(item) for item in your_list_of_objects]
for each_partial in instructions:
each_partial()
The operations will only get fired if the list compehension collects without any exceptions. You could wrap that for exception safety:
try:
instructions = [validate(item) for item in your_list_of_objects]
for each_partial in instructions:
each_partial()
print "succeeded"
except ValueError:
print "failed"
If there is no "built-in" way of doing this, I think after all the "cleanest" solution is to divide the function in two parts. Something Like this:
try:
res1 = calc(obj1)
res2 = calc(obj2)
res3 = calc(obj3)
except:
pass
else:
set(obj1, res1)
set(obj2, res2)
set(obj3, res3)
What is the python analog of perl's // operator?
In perl, one can do something like :
$pos = $some_list[0] // 1
How do you accomplish the same in python?
In Python there is no undefined; instead, you'd get an exception if you tried to access an non-existent index in a list. As such, you can use exception handling instead:
try:
pos = some_list[0]
except IndexError:
pos = 1
For the first element of a sequence, you could explicitly test the sequence as a boolean (a python container is 'falsey' when empty):
post = some_list[0] if some_list else 1
How about using exceptions?
try:
pos = some_list[0]
except (NameError, IndexError):
pos = 1
An alternative to try/catch answers above for dictionaries is the default argument on .get():
param_value = my_dictionary.get(param_key, default_value)
The best practice for this in python is to handle exceptions explicitly with a try, except clause. One example presented here to help you visuallize
my_list = []
try:
item = my_list[1]
except IndexError:
item = 1
Here the code executes and an exception is raised because the index "1" is out of bounds. We then go on to handle that exception and set item=1 allowing the program to continue running. The reason for this explicit handling of exceptions is so we as programmers see exactly what is causing our problems. Take this for example:
my_list = [0]
try:
item = 1/my_list[0]
except IndexError:
item = 1
This will raise a zero division error (halting execution) and let us know that we need to handle some other exception explicitly beyond the original exception we expected, the IndexError. We might then do something like this to deal with that situation:
my_list = [0]
try:
item = 1/my_list[0]
except IndexError:
item = 1
except ZeroDivisionError:
item = 99999
try-except blocks also have a few other notable features we can exploit:
try:
# code which might raise error
pass
except IndexError as err:
# handling an index error and storing the traceback in err
pass
except ZeroDivisionError:
#handling some other error:
pass
else:
# code we would like to execute if the try block succeeds without any errors
pass
finally:
# code we will execute regardless of what occurs in the entire
# try,except,else block listed above (i.e we can ensure a file is closed)
pass
How do you get around this error while using .pop? I get that when it tries to return a number but there isn't one an error is raised but how do you get around it so the program keeps running?
def remove_element(self,integer):
self.integer = integer
self.members.pop()
Just check if self.members is not empty:
if self.members:
self.members.pop()
or, catch KeyError via try/except:
try:
self.members.pop()
except KeyError:
# do smth
You can use try/except to catch the KeyError raised by an_empty_set.pop(), or check the set first to make sure it's not empty:
if s:
value = s.pop()
else:
# whatever you want to do if the set is empty
Single line solution
res = self.members.pop() if self.members else None