Programming
python string python-3.x if-statement conditional-operator
Updated Tue, 17 May 2022 08:47:17 GMT

Does python have a shorthand to check if an object has an attribute?


Background is I'm getting data from a JSON API where lots of fields are optional and I want most of the fields in a database. When a specific field isn't available I want an empty string ("") written into the database.

Currently I've been doing:

if jsonobject.what_i_look_for:
  dbstring = jsonobject.what_i_look_for
else:
  dbstring = ""

And then inserted dbstring into the database. However I'm getting a lot more of these fields now and I want a much cleaner code rather than a function which consists about 80% of if-statements.

I've found if-shorthands and this shorthand to check if a variable is empty, but both don't seem to work directly as a string. I've tested this using print() in an interactive python 3.5.2 shell:

>>> print(testvar or "")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'testvar' is not defined
>>> print(testvar if testvar else "")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'testvar' is not defined

This: echo (isset($testvar) ? $testvar : ""); is the PHP equivalent of what I seek.

Edit: Since it seems relevant: The object I am trying to process is coming from Telegram's JSON API. I'm using python-telegram-bot as library and this is an example object.




Solution

The Pythonic way is to look out for NameError exception that would be raised when the variable is not defined, the name is not bound to any object to be precise.

So, for example:

try:
    foobar
except NameError:
    # Do stuffs
    print('foobar is not defined')
    raise  # raise the original exception again, if you want

Names reside in namespaces e.g. local names reside in locals() (dict) namespace, global names reside in globals() (dict) namespace. You can define a function that takes name string and namespace as an argument to check for the existence, here is a hint passing namespace as a dict and catching KeyError:

In [1213]: def is_defined(name, namespace):
      ...:     try:
      ...:         namespace[name]
      ...:     except KeyError:
      ...:         return False
      ...:     return True
      ...: 
In [1214]: is_defined('spamegg', globals())
Out[1214]: False
In [1215]: spamegg = 10
In [1216]: is_defined('spamegg', globals())
Out[1216]: True

On the other hand, if you are looking to get the value of an atrribute string of an object, getattr is the way to go:

getattr(obj, attr)

For example, the following two are equivalent:

obj.foobar
getattr(obj, 'foobar')

Even you can add a default when the object attribute is missing:

getattr(obj, 'foobar', 'spamegg')

The above will output the value obj.foobar, if foobar is missing it would output spamegg.

You might also be interested in hasattr that returns True/False for an attribute existence check, instead of needing to manually handle AttributeError.





Comments (5)

  • +0 – Is there a way I can write a function or anything for that? My goal is to get a short and clean code, having a try block for every variable I need to check doesn't exactly achieve that. — Aug 16, 2018 at 16:15  
  • +0 – @confetti Names reside in namespaces e.g. local names reside in locals() namespace, global names reside in globals() namespace. You can define a function that takes namespace as an argument to check for the existence. — Aug 16, 2018 at 16:20  
  • +0except NameError is pretty major code smell to me, unless you are in to some pretty arcane meta-programming black-magic, maybe. In any event, reading the op it isn't clear to me if this is even what they need, since they are talking about attributes on objects. — Aug 16, 2018 at 16:20  
  • +0 – @confetti why have you found yourself in a situation where you don't know if a name is in scope or not? This doesn't seem related to your question at all, which is about attributes on objects, not whether or not variables are defined.... — Aug 16, 2018 at 16:22  
  • +0 – @juanpa.arrivillaga I have edited my OP. I think I worded it wrong at first. I added an example of the exact object I am working with. — Aug 16, 2018 at 16:24