Is there a more Pythonic way to combine an Else: statement and an Except:?
Question:
I have a piece of code that searches AutoCAD for text boxes that contain certain keywords (eg. "overall_weight"
in this case) and replaces it with a value from a dictionary. However, sometimes the dictionary key is assigned to an empty string and sometimes, the key doesn’t exist altogether. In these cases, the "overall_weight"
keywords should be replaced with "N/A"
. I was wondering if there was a more pythonic way to combine the KeyError
exception and the else
to both go to nObject.TextString = "N/A"
so its not typed twice.
if nObject.TextString == "overall_weight":
try:
if self.var.jobDetails["Overall Weight"]:
nObject.TextString = self.var.jobDetails["Overall Weight"]
else:
nObject.TextString = "N/A"
except KeyError:
nObject.TextString = "N/A"
Edit: For clarification for future visitors, there are only 3 cases I need to take care of and the correct answer takes care of all 3 cases without any extra padding.
-
dict[key]
exists and points to a non-empty string. TextString
replaced with the value assigned to dict[key]
.
-
dict[key]
exists and points to a empty string. TextString
replaced with "N/A"
.
-
dict[key]
doesn’t exist. TextString
replaced with "N/A"
.
Answers:
Use .get()
with a default argument of "N/A"
which will be used if the key does not exist:
nObject.TextString = self.var.jobDetails.get("Overall Weight", "N/A")
Update
If empty strings need to be handled, simply modify as follows:
nObject.TextString = self.var.jobDetails.get("Overall Weight") or "N/A"
This will set nObject.TextString
to “N/A” if a KeyError
is raised, or if the value is retrieved is empty: ''
, []
, etc.
Use get()
function for dictionaries. It will return None
if the key doesn’t exist or if you specify a second value, it will set that as the default. Then your syntax will look like:
nObject.TextString = self.var.jobDetails.get('Overall Weight', 'N/A')
I think this is a good case for setting the default value in advance
if nObject.TextString == "overall_weight":
nObject.TextString = "N/A"
try:
if self.var.jobDetails["Overall Weight"]:
nObject.TextString = self.var.jobDetails["Overall Weight"]
except KeyError:
pass
RADICAL RETHINK
Ditch that first answer (just keeping it because it got an upvote). If you really want to go pythonic, (and you always want to set a value on TextString) replace the whole thing with
nObject.TextString = (nObject.TextString == "overall_weight"
and self.var.jobDetails.get("Overall Weight")
or "N/A")
Python and
and or
operations return their last calculated value, not True/False and you can use that to walk through the combinations.
Use dict.get()
which will return the value associated with the given key if it exists otherwise None
. (Note that ''
and None
are both falsey values.) If s
is true then assign it to nObject.TextString
otherwise give it a value of "N/A"
.
if nObject.TextString == "overall_weight":
nObject.TextString = self.var.jobDetails.get("Overall Weight") or "N/A"
How about with
:
key = 'Overall Weight'
with n_object.text_string = self.var.job_details[key]:
if self.var.job_details[key] is None
or if self.var.job_details[key] is ''
or if KeyError:
n_object.text_string = 'N/A'
The Zen of Python says “Explicit is better than implicit.” I have found this to be very true in my own experience. When I write a piece of code I think to my self, “Will I understand what this means a year from now?” If the answer is “no” then it needs to be re-written or documented. The accepted answer relies on remembering the implementation of dict.get to know how it will handle the corner cases. Since the OP has 3 clear criteria, I would instead document them clearly in an if statement.
if nObject.TextString == "overall_weight" and
"Overall Weight" in self.var.jobDetails and
self.var.jobDetails["Overall Weight"] != "":
nObject.TextString = self.var.jobDetails["Overall Weight"]
else:
nObject.TextString = "N/A"
It’s certainly more verbose… but that’s a good thing. There is no question when reading this what the behavior will be.
I have a piece of code that searches AutoCAD for text boxes that contain certain keywords (eg. "overall_weight"
in this case) and replaces it with a value from a dictionary. However, sometimes the dictionary key is assigned to an empty string and sometimes, the key doesn’t exist altogether. In these cases, the "overall_weight"
keywords should be replaced with "N/A"
. I was wondering if there was a more pythonic way to combine the KeyError
exception and the else
to both go to nObject.TextString = "N/A"
so its not typed twice.
if nObject.TextString == "overall_weight":
try:
if self.var.jobDetails["Overall Weight"]:
nObject.TextString = self.var.jobDetails["Overall Weight"]
else:
nObject.TextString = "N/A"
except KeyError:
nObject.TextString = "N/A"
Edit: For clarification for future visitors, there are only 3 cases I need to take care of and the correct answer takes care of all 3 cases without any extra padding.
-
dict[key]
exists and points to a non-empty string.TextString
replaced with the value assigned todict[key]
. -
dict[key]
exists and points to a empty string.TextString
replaced with"N/A"
. -
dict[key]
doesn’t exist.TextString
replaced with"N/A"
.
Use .get()
with a default argument of "N/A"
which will be used if the key does not exist:
nObject.TextString = self.var.jobDetails.get("Overall Weight", "N/A")
Update
If empty strings need to be handled, simply modify as follows:
nObject.TextString = self.var.jobDetails.get("Overall Weight") or "N/A"
This will set nObject.TextString
to “N/A” if a KeyError
is raised, or if the value is retrieved is empty: ''
, []
, etc.
Use get()
function for dictionaries. It will return None
if the key doesn’t exist or if you specify a second value, it will set that as the default. Then your syntax will look like:
nObject.TextString = self.var.jobDetails.get('Overall Weight', 'N/A')
I think this is a good case for setting the default value in advance
if nObject.TextString == "overall_weight":
nObject.TextString = "N/A"
try:
if self.var.jobDetails["Overall Weight"]:
nObject.TextString = self.var.jobDetails["Overall Weight"]
except KeyError:
pass
RADICAL RETHINK
Ditch that first answer (just keeping it because it got an upvote). If you really want to go pythonic, (and you always want to set a value on TextString) replace the whole thing with
nObject.TextString = (nObject.TextString == "overall_weight"
and self.var.jobDetails.get("Overall Weight")
or "N/A")
Python and
and or
operations return their last calculated value, not True/False and you can use that to walk through the combinations.
Use dict.get()
which will return the value associated with the given key if it exists otherwise None
. (Note that ''
and None
are both falsey values.) If s
is true then assign it to nObject.TextString
otherwise give it a value of "N/A"
.
if nObject.TextString == "overall_weight":
nObject.TextString = self.var.jobDetails.get("Overall Weight") or "N/A"
How about with
:
key = 'Overall Weight'
with n_object.text_string = self.var.job_details[key]:
if self.var.job_details[key] is None
or if self.var.job_details[key] is ''
or if KeyError:
n_object.text_string = 'N/A'
The Zen of Python says “Explicit is better than implicit.” I have found this to be very true in my own experience. When I write a piece of code I think to my self, “Will I understand what this means a year from now?” If the answer is “no” then it needs to be re-written or documented. The accepted answer relies on remembering the implementation of dict.get to know how it will handle the corner cases. Since the OP has 3 clear criteria, I would instead document them clearly in an if statement.
if nObject.TextString == "overall_weight" and
"Overall Weight" in self.var.jobDetails and
self.var.jobDetails["Overall Weight"] != "":
nObject.TextString = self.var.jobDetails["Overall Weight"]
else:
nObject.TextString = "N/A"
It’s certainly more verbose… but that’s a good thing. There is no question when reading this what the behavior will be.