Last active
August 17, 2020 13:27
-
-
Save lemon24/d1f00f5f3e02f879e2c280fcdb438f87 to your computer and use it in GitHub Desktop.
None-like singleton in Python (mypy-compatible)
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
""" | |
None-like singleton in Python (mypy-compatible). | |
https://github.com/lemon24/reader/issues/177#issuecomment-674786498 | |
""" | |
from typing import Union, cast | |
class NoValueType: | |
def __new__(cls) -> 'NoValueType': | |
return NoValue | |
def __repr__(self) -> str: | |
return 'NoValue' | |
def __bool__(self) -> bool: | |
# to prevent "if thing" when "if thing is False" was meant; | |
raise TypeError("cannot use NoValue in boolean context") | |
NoValue = cast(NoValueType, object.__new__(NoValueType)) | |
assert NoValueType() is NoValue | |
""" | |
# alternative way to make a singleton based on https://stackoverflow.com/a/6798042 | |
from typing import Dict, Any | |
class Singleton(type): | |
_instances: Dict['Singleton', 'Singleton'] = {} | |
def __call__(cls, *args: Any, **kwargs: Any) -> 'Singleton': | |
if cls not in cls._instances: | |
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) | |
return cls._instances[cls] | |
class NoValueType(metaclass=Singleton): | |
... # repr etc. | |
""" | |
# we could define a MyOptional[T] == Union[NoValueType, T], but it's too complicated | |
one: Union[NoValueType, str] = 'aaa' | |
two: Union[NoValueType, str] = NoValue | |
for maybe_str in one, two: | |
# sadly, mypy can't understand "maybe_str is NoValue", like it does for None | |
if isinstance(maybe_str, str): | |
print(maybe_str.lower()) | |
else: | |
print(repr(maybe_str)) | |
# error: Item "NoValueType" of "Union[NoValueType, str]" has no attribute "lower" | |
maybe_str.lower() | |
aaa: Union[NoValueType, bool] = True | |
bbb: Union[NoValueType, bool] = NoValue | |
def negate(a: bool) -> bool: | |
return not a | |
for maybe_bool in aaa, bbb: | |
if isinstance(maybe_bool, bool): | |
print(negate(maybe_bool)) | |
else: | |
print(repr(maybe_bool)) | |
# error: Argument 1 to "negate" has incompatible type "Union[NoValueType, bool]"; expected "bool" | |
negate(maybe_bool) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment