1

for now I use this:

print(theObject.nestedObjectOne.NestedObjectTwo.NestedObjectThree if theObject and theObject.NestedOBjectOne and theObject.NestedObjectOne.NestedObjectTwo else "n/a")

In order to support a case where NestedObjectOne is None.

Is there a more elegant way to do this? Yes, I know I can write a function to traverse the object and check attributes recursively. I'm asking if there's any construct in python that does this as elegantly as ES6 for instance:

console.log(theObj?.theFirstNested?.theSecondNested)
JasonGenX
  • 4,952
  • 27
  • 106
  • 198
  • 1
    What would you expect the call to `print()` to print when any of them isn't defined? – Grismar Feb 10 '23 at 22:19
  • No, there isn't. You'd be stuck with smart uses of getattr, or write that function. – 9769953 Feb 10 '23 at 22:20
  • @Grismar None, obv. – njzk2 Feb 10 '23 at 22:20
  • 2
    @njzk2 I don't think that's obvious at all, which is why I'm asking OP. – Grismar Feb 10 '23 at 22:20
  • @Grismar OP gave an example of how that looks in other languages, which demonstrates the expected behaviour – njzk2 Feb 10 '23 at 22:21
  • @njzk2, I'm sure you're trying to be helpful, but I suggest allowing OP to point out what is obvious in response to valid questions about their question. – Grismar Feb 10 '23 at 22:22
  • There is nothing that *easy* in Python. Elegance is a matter of opinion. Python philosophy is that "Errors should never pass silently, unless explicitly silenced." The closest you'll get is `getattr` specifying a default, which at least allows you to write the whole thing as a single "flowing" expression. I closed as a duplicate with the reference for how to use `getattr` - because otherwise, this is simply opinion based and not appropriate for Stack Overflow. – Karl Knechtel Feb 10 '23 at 22:22
  • 3
    The more elegant way to do this (IMO) is `try/except`. Access the deepest item directly, then catch the `AttributeError` if any. – Samwise Feb 10 '23 at 22:25
  • @Samwise well, sure, if you want any missing attribute on the chain to be handled the same way. But you can also do that in other languages. – Karl Knechtel Feb 10 '23 at 22:27
  • @KarlKnechtel although the linked question provides an answer to how to implement 1 way of solving this, it doesn't actually answer OP's question. `try .. except` or implementing or modifying the class to fail in an expected way are other solutions that go unmentioned there. – Grismar Feb 10 '23 at 22:27
  • so, that would look like `getattr(getattr(getattr(theObject, "nestedObjectOne", None), "nestedObjectTwo", None), "nestedObjectThree", None)`? – njzk2 Feb 10 '23 at 22:27
  • @Grismar then it needs to be edited to be more objective - i.e., to give specific criteria for what is or isn't an acceptable solution. – Karl Knechtel Feb 10 '23 at 22:27
  • @njzk2 yes; that's why I said it isn't easy. But `getattr` is the unique tool that *works the same way* that `?.` does in the ES6 example. – Karl Knechtel Feb 10 '23 at 22:28
  • 1
    I think this: https://stackoverflow.com/questions/64285182/optional-chaining-in-python is a better duplicate. It explicitly offers a couple of options, and links to the pep proposal for optional chaining: https://peps.python.org/pep-0505/ – njzk2 Feb 10 '23 at 22:29
  • @njzk2 agreed. I would have swapped that in, if the question hadn't been reopened. I'll leave it to the rest of you, then. – Karl Knechtel Feb 10 '23 at 22:31
  • Python 3.10s `match` statements get somewhat close if you're happy relying on something that recent – Sam Mason Feb 10 '23 at 22:41
  • Came up with an answer, but agree that the alternative closer covers the bases, other than extending the class - someone is welcome to go and add it there, if they like :) – Grismar Feb 10 '23 at 22:43
  • I added [an answer](https://stackoverflow.com/a/75416977/1358308) to the referred question that shows how to use `match` to solve this. – Sam Mason Feb 10 '23 at 23:17

1 Answers1

0

Whether code should fail or just return some default value is a matter of opinion or style. Where ES6 defaults to undefined, other languages might pick something like null - in Python you might expect None.

However, multiple choices are available, and for some types, a value of 0 might make sense and None could cause havoc, so Python chooses to cause your code to fail when it accesses non-existent attributes.

There's a few ways to deal with it gracefully though.

try .. except:

try:
    x = theObject.nestedObjectOne.NestedObjectTwo.NestedObjectThree
except AttributeError:
    x = None  # or whatever value makes the most sense

Without exceptions:

x = getattr(
        getattr(
            getattr(
                theObject, 'nestedObjectOne', None
            ), 
            'nestedObjectTwo', None
        ), 
        'nestedObjectThree', None
    )

(obviously, you can write it on a single line if you don't mind the lack of readability)

Or you could modify the class so that it deals with the case of a non-existent attribute gracefully itself, which can make sense in specific case without being "unpythonic".

For example:

class MyClass:
    has_this = 42

    def __getattr__(self, item):
        if item in self.__dict__:
            return self.__dict__[item]
        else:
            return None


x = MyClass()
print(x.has_this)
print(x.does_not_have_this)

Note that this is a somewhat simplistic and incomplete way to implement this, it's here to show the direction you could be looking in. Of course, you can have MyClass inherit from any other class, to add the behaviour to an existing class.

Grismar
  • 27,561
  • 4
  • 31
  • 54
  • 1
    Modifying MyClass like that will not allow for chaining; `x.missing.also_missing` will raise an exception because `None` doesn't have an `also_missing` attribute and also doesn't have this `__getattr__` logic. – Karl Knechtel Feb 10 '23 at 22:45
  • That's a good point - whatever is returned instead of None should have the same capabilities. Not going to update it, since the question is closed - it's worth remarking though. – Grismar Feb 10 '23 at 22:51
  • 1
    I added an answer at the linked duplicate to refine that technique. – Karl Knechtel Feb 10 '23 at 23:01