Last active
September 17, 2022 14:40
-
-
Save Keyacom/17c72746fe47d21114612a93ac6f0420 to your computer and use it in GitHub Desktop.
None-aware functions (for PEP 505 being deferred)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
A way the proposed ``??``, ``?.`` and ``?[`` operators could be implemented. | |
For now, this lacks an implementation for ``??=``. | |
""" | |
from __future__ import annotations | |
from types import NoneType | |
from typing import Hashable, NamedTuple | |
def none_coalesce(*args): | |
""" | |
An alias for the ``??`` operator, for PEP 505 being currently deferred. | |
:param args: The arguments to coalesce. Returns the first one that isn't ``None``. | |
""" | |
for a in args: | |
if a is not None: | |
return a | |
def safe_navigate(entity, *chain): | |
""" | |
An alias for the ``?.`` and ``?[`` operators, for PEP 505 being currently deferred. | |
Usage: | |
```py | |
class Demo: | |
person = { | |
'name': 'Kira', | |
'mother': { | |
'name': 'Irina' | |
} | |
} | |
# Proposed syntax: | |
print(Demo?.person?['father']?['name']) | |
# The actual function: | |
print(safe_navigate(Demo, "person", ["father"], ["name"])) | |
``` | |
:param entity: The object to safely navigate through. | |
:param chain: The combination of ``str``s, ``int``s, and ``list``s. They're pseudo-evaled like this: | |
- ``str``s are pseudo-evaled as attributes | |
- ``int``s are pseudo-evaled as list indices | |
- ``list``s are pseudo-evaled as subscriptive indexing (e.g. of dicts). | |
""" | |
ret = entity | |
for c in chain: | |
try: | |
match type(c).__name__: | |
case "str": | |
if hasattr(ret, "_asdict"): | |
ret = ret._asdict()[c] | |
elif hasattr(ret, "__dict__"): | |
ret = ret.__dict__[c] | |
else: | |
ret = ret[c] | |
case "int": | |
ret = ret[c] | |
case "list": | |
assert issubclass(type(c[0]), Hashable) | |
if isinstance(c[0], str): | |
ret = ret[c[0]] | |
elif [isinstance(i, (int, NoneType)) for i in c] == [True] * 3: | |
d = [ | |
c[0] if c[0] is not None else 0, | |
c[1] if c[1] is not None else -1, | |
c[2] if c[2] is not None else 1, | |
] | |
ret = ret[d[0] : d[1] : d[2]] | |
else: | |
ret = ret[c[0]] | |
if ret is None: | |
return None | |
except (AttributeError, IndexError, KeyError): | |
return None | |
return ret | |
class Person(NamedTuple): # Test class | |
name: str | |
mother: Person = None | |
father: Person = None | |
person = Person(name="Kira", mother=Person(name="Irina")) | |
print(safe_navigate(person, "father", "name")) # None | |
print(safe_navigate(person, "mother", "name")) # Irina |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment