Created
July 23, 2021 14:25
-
-
Save tchar/2f8b34377c9feee70025fab3fba84c05 to your computer and use it in GitHub Desktop.
Singleton pattern, singletonmethod decorator
This file contains hidden or 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
from typing import Any, Callable, Type | |
from functools import wraps | |
class Singleton(type): | |
_instances = {} | |
def __call__(cls: Type[Any], *args: Any, **kwargs: Any) -> Any: | |
'''Gets as input the class and args/kwargs and returns either an already | |
instantiated object or a new object from that class''' | |
if cls not in cls._instances: | |
cls._instances[cls] = super( | |
Singleton, cls).__call__(*args, **kwargs) | |
return cls._instances[cls] | |
class SingletonMethod: | |
def __init__(self, func: Callable[[Any], Any]): | |
self._func = func | |
def __get__(self, obj: Any, cls: Type[Any]): | |
'''Returns a wrapper which passes the self to the function provided | |
If obj is None (meaning it is a class method) instantiates the class | |
and passes it as the self argument''' | |
@wraps(self._func) | |
def _wrapper(*args, **kwargs): | |
if obj is None: | |
_self = cls() | |
else: | |
_self = obj | |
return self._func(_self, *args, **kwargs) | |
return _wrapper | |
def singletonmethod(func): | |
'''Returns a SingletonMethod object''' | |
return SingletonMethod(func) | |
def main(): | |
class Test(metaclass=Singleton): | |
def __init__(self, value=0): | |
self.value = value | |
@singletonmethod | |
def f(self, value: int) -> str: | |
'''Some singletonmethod docstring''' | |
return 'My value: {}, Your value: {}'.format(self.value, value) | |
# Can be called without instantiation | |
tf = Test.f(2) | |
assert tf == 'My value: 0, Your value: 2' | |
# Can be called with instantiation but still holds the previous reference | |
tf = Test(1).f(1) | |
assert tf == 'My value: 0, Your value: 1' | |
assert Test.f.__doc__ == 'Some singletonmethod docstring' | |
assert Test().f.__doc__ == 'Some singletonmethod docstring' | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment