Skip to content

Instantly share code, notes, and snippets.

@yashshah1
Last active July 26, 2022 16:47
Show Gist options
  • Select an option

  • Save yashshah1/4cf30c19597e744b6d05a8eb572320cf to your computer and use it in GitHub Desktop.

Select an option

Save yashshah1/4cf30c19597e744b6d05a8eb572320cf to your computer and use it in GitHub Desktop.
Safe operator in python
# Attempt to create something like a safe operator (?. in js) for Python
#
# Solution 1: Overload objects in Python (pollute global name space)
# Turns out this is not actually possible: https://stackoverflow.com/a/4698550
# and you have to subclass the main class, and create instances.
# This is not a great idea since we would have to stop instantiating inbuilt classes
# and would have weird looking code
#
# Solution 2: Possibly create a wrapper class that wraps instances
# Something like:
# >>> class A():
# ... def __init__(self):
# ... self.x = 1
# >>> A().y
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# AttributeError: 'A' object has no attribute 'y'
# >>>
# >>> safe(A()).y == None
# True
class CustomNone:
def __bool__(self):
return False
def __str__(self):
return "None"
def __eq__(self, obj):
return (obj is None or isinstance(obj, CustomNone))
def __getattr__(self, _):
return self
def __getitem__(self, _):
return self
def __call__(self):
return self
none = CustomNone()
class SafeWrapper():
def __init__(self, obj):
if obj is None:
self.__obj = none
else:
self.__obj = obj
def __getitem__(self, key: str):
# https://docs.python.org/3/reference/datamodel.html#object.__getitem__
try:
value = self.__obj.__getitem__(key)
# There is a chance that value is None, which doesn't
# implement __getitem__, so returning None isn't okay here
# which is why we should return a custom value which mimics
# None, but implements some basic stuff
if value == None:
return none
except (KeyError, AttributeError):
return none
return SafeWrapper(value)
def __getattr__(self, key):
# https://docs.python.org/3/library/functions.html#getattr
return SafeWrapper(getattr(self.__obj, key, none))
def __setattr__(self, name, value):
if name == "_SafeWrapper__obj":
# This is called when setting self.__obj = obj
# so we let python do this!
return super().__setattr__(name, value)
else:
return self.__obj.__setattr__(name, value)
def __repr__(self):
module_name = self.__class__.__module__
class_name = self.__class__.__name__
built_in_repr = repr(self.__obj)
return f"{module_name}:{class_name}: {built_in_repr}"
def __str__(self):
return str(self.__obj)
def safe(obj):
# in the case None is passed in, we return a CustomNone object
if obj is None:
return none
return SafeWrapper(obj)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment