Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access nested attributes or return a default value if any of the attributes doesn't exist?

Tags:

python

In JavaScript (and Node.js and TS), when we have an object A which has an attribute B, which has an attribute C, which has an attribute D and so on, we may use it like that:

const x = A?.B?.C?.D

Is there something similar in Python? I want x to be None if any of A, B, C, or D is None. Actually, I need x = A.B.C.D or DEFAULT_VALUE

What I do today is very verbose:

x = None
if A is not None and A.B is not None and A.B.C is not None:
    x = A.B.C.D

Is there a one-line solution in Python?

I prefer an approach like this, but it is still very verbose and I repeat myself. Looking for DRY.

x = A and A.B and A.B.C and A.B.C.D or DEFAULT_VALUE
like image 380
Costin Avatar asked Oct 29 '25 06:10

Costin


2 Answers

There is probably no such concise solution in Python. The most "pythonic" way would probably be to "ask for forgiveness rather than permission" (see the "EAFP" entry in the Python Glossary), i.e. to use a try-except statement that directly tries to access D (no need for intermediate checks) and that fails gracefully by providing the required DEFAULT_VALUE in the error case:

DEFAULT_VALUE = ...  # TODO: Provide default

try:
    x = A.B.C.D
except (NameError, AttributeError):
    x = DEFAULT_VALUE

You only need the NameError for the case that A might not exist at all. If you are sure that A exists (i.e. A has been defined and holds any value including None) you can drop it from the except clause.

like image 77
simon Avatar answered Oct 31 '25 09:10

simon


I would define a function that allows you get the desired behavior in just one line:

def f(x, lst, default_val=None):
    for att in lst:
        if hasattr(x, attr):
            x = getattr(x, attr)
        else:
            return default_val
    return x

Now you can do things like this:

X = f(A, ['B', 'C', 'D'])

As suggested in the comments, if you don't need a default value, then you can just define the function as follows:

def f(x, lst):
    for att in lst:
        x = getattr(x, attr, None)
    return x
like image 29
Riccardo Bucco Avatar answered Oct 31 '25 09:10

Riccardo Bucco



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!