for the purposes of a completely contrived programming exercise, I want to be able to access all objects that I have created of a specific class, using decorators, and by subscripting the class itself.
In the below example, I have created an instance of my A class, using the argument 1
, and stored the result in a dictionary, using the key '1'
.
So, by attempting to index the A class directly, it should return the instance of the object that was created, however, it only returns the parameter passed to __get__item, and raises a TypeError if I attempt to index it directly with A['1'].
import types
def remember(cl):
seen = dict()
def _remember(*args, **kwargs):
key = ','.join(map(str, args))
if key not in seen:
seen[key] = cl(*args, **kwargs)
return seen[key]
_remember.__getitem__ = types.MethodType(seen.get, _remember)
return _remember
@remember
class A(object):
def __init__(self, x):
pass
a = A(1)
b = A.__getitem__('1')
print(a == b) #should print true, but instead, A.__get__item returns '1'. Ideally, A['1'] would work.
Here's what I came up with.
We can't have A[<something>]
as possible usage because that would mean changing the attributes of type
. I've created A.get(<something>)
instead for that use case.
This differs from your attempt in that rather than returning a function from the decorator, we return a new class that extends from the original. That class stores all instances of instantiations of its parent class, and indexes them by a key which is created from the args
and kwargs
.
The __getitem__
has been overridden on the class, and then in the parent class, and it takes any of:
Anyway, on with the code:
from typing import Any, Dict
def remember(klass):
def get_key(args, kwargs):
return hash((args, tuple(sorted(kwargs.items()))))
class RememberedClass(klass):
_instances: Dict[int, Any] = {}
def __new__(cls, *args, **kwargs):
c = klass(*args, **kwargs)
klass.__getitem__ = cls.__getitem__
key = get_key(args, kwargs)
cls._instances[key] = c
return c
@staticmethod
def get(*args, **kwargs) -> klass:
key = get_key(args, kwargs)
return RememberedClass._instances[key]
def __getitem__(self, item) -> klass:
if isinstance(item, tuple) and len(item) == 2:
# (args, kwargs)
args, kwargs = item
key = get_key(args, kwargs)
elif isinstance(item, tuple):
# (arg, arg, ...)
key = get_key(item, {})
else:
# arg
key = get_key((item,), {})
return RememberedClass._instances.get(key)
return RememberedClass
@remember
class Foo:
def __init__(self, val):
pass
I believe this is what you wanted?
>>> f = Foo(1)
>>> g = Foo(2)
>>> f is g
False
>>> f is f[1]
True
>>> g is g[2]
True
>>> g is f[2]
True
>>> f is Foo.get(1)
True
>>> type(f)
__main__.Foo
>>> Foo
__main__.remember.<locals>.RememberedClass