Search code examples
pythonlist-comprehensionjirapython-jira

How do I get attribute3 of attribute2 of attribute1, if attribute2 may or may not exist?


I'm trying to construct a list comprehension to create a list of the value of issues.fields.parent.id, or None if there is no parent. Is there a simple way of getting attribute2 of attribute1, and returning a default if attribute1 doesn't exist?

>> type(issue)
<class 'jira.resources.Issue'>

>> type(issue.fields)
<class 'jira.resources.PropertyHolder'>

>> type(issue.fields.parent)
<class 'jira.resources.Issue'>

>> issues[0].fields.parent.id
'12345'

>> issues[1].fields.parent.id
AttributeError: 'PropertyHolder' object has no attribute 'parent'

I want to return the list ['12345', None]

Trying to pretend the problem doesn't exist obviously returns an error :)

>> [i.fields.parent.id for i in issues]
AttributeError: 'PropertyHolder' object has no attribute 'parent'

Adding an if statement to the comprehension misses out the None value:

>> [i.fields.parent.id for i in issues if hasattr(i.fields, 'parent')]
['12345']

Using getattr with a default only returns the value of parent, not parent.id

>> [getattr(i.fields, 'parent', None) for i in issues]
[<JIRA Issue: key='PRJ-1', id='12345'>, None]

I can do it by creating a function, but six lines of code for one list comprehension seems clunky, and I have quite a few of these to do

>>> def parent_id(issue):
...    if hasattr(issue.fields, 'parent'):
...        return issue.fields.parent.id
...    else:
...        return None
>>> [parent_id(i) for i in issues]
['12345', None]

Is there a simpler / more elegant way of doing this?


Solution

  • You can define something that has an id attribute of None using types.SimpleNamespace, and return that from getattr instead of None.

    from types import SimpleNamespace
    p = SimpleNamespace(id=None)
    
    [getattr(i.fields, 'parent', p).id for i in issues]
    

    (p is factored out to avoid creating a new instance for every call to getattr.)


    Were PEP 505 to be accepted, you could write something like

    [getattr(i.fields, 'parent')?.id for i in issues]