If it's a dictionary you can use get(keyname, value)
{'foo': {'bar': 'baz'}}.get('foo', {}).get('bar')
Answer from Aliaksandr Sushkevich on Stack OverflowI'm trying to implement the optional chaining operator (?.) from JS in Python. The idea of this implementation is to create an Optional class that wraps a type T and allows getting attributes. When getting an attribute from the wrapped object, the type of result should be the type of the attribute or None. For example:
## 1. None
my_obj = Optional(None)
result = (
my_obj # Optional[None]
.attr1 # Optional[None]
.attr2 # Optional[None]
.attr3 # Optional[None]
.value # None
) # None
## 2. Nested Objects
@dataclass
class A:
attr3: int
@dataclass
class B:
attr2: A
@dataclass
class C:
attr1: B
my_obj = Optional(C(B(A(1))))
result = (
my_obj # # Optional[C]
.attr1 # Optional[B | None]
.attr2 # Optional[A | None]
.attr3 # Optional[int | None]
.value # int | None
) # 5
## 3. Nested with None values
@dataclass
class X:
attr1: int
@dataclass
class Y:
attr2: X | None
@dataclass
class Z:
attr1: Y
my_obj = Optional(Z(Y(None)))
result = (
my_obj # Optional[Z]
.attr1 # Optional[Y | None]
.attr2 # Optional[X | None]
.attr3 # Optional[None]
.value # None
) # NoneMy first implementation is:
from dataclasses import dataclass
@dataclass
class Optional[T]:
value: T | None
def __getattr__[V](self, name: str) -> "Optional[V | None]":
return Optional(getattr(self.value, name, None))But Pyright and Ty don't recognize the subtypes. What would be the best way to implement this?
Videos
It would hurt simplicity but it is beyond that point. Python feels like Scratch compared to other languages at the moment. Lacking this basic feature hurts productivity, I don't want to write 50 lines of "if not product.name" etc.
The most straightforward way is to wrap in a try...except block.
try:
title = soup.head.title.string
except AttributeError:
print "Title doesn't exist!"
There's really no reason to test at each level when removing each test would raise the same exception in the failure case. I would consider this idiomatic in Python.
You might be able to use reduce for this:
>>> class Foo(object): pass
...
>>> a = Foo()
>>> a.foo = Foo()
>>> a.foo.bar = Foo()
>>> a.foo.bar.baz = Foo()
>>> a.foo.bar.baz.qux = Foo()
>>>
>>> reduce(lambda x,y:getattr(x,y,''),['foo','bar','baz','qux'],a)
<__main__.Foo object at 0xec2f0>
>>> reduce(lambda x,y:getattr(x,y,''),['foo','bar','baz','qux','quince'],a)
''
In python3.x, I think that reduce is moved to functools though :(
I suppose you could also do this with a simpler function:
def attr_getter(item,attributes)
for a in attributes:
try:
item = getattr(item,a)
except AttributeError:
return None #or whatever on error
return item
Finally, I suppose the nicest way to do this is something like:
try:
title = foo.bar.baz.qux
except AttributeError:
title = None