使 Python 函数可订阅
Make a Python function subscriptable
出于完全人为设计的编程练习,我希望能够使用装饰器访问我为特定 class 创建的所有对象,并通过下标 class 本身。
在下面的示例中,我使用参数 1
创建了 A class 的实例,并使用键 '1'
将结果存储在字典中。
因此,通过尝试直接索引 A class,它应该 return 创建的对象的实例,但是,它只是 return 传递给的参数__get__item,如果我尝试直接用 A['1'].
对其进行索引,则会引发 TypeError
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.
这是我想出的。
我们不能将 A[<something>]
作为可能的用法,因为那将意味着更改 type
的属性。我已经为该用例创建了 A.get(<something>)
。
这与您的尝试不同,我们 return 不是 return 从装饰器中创建一个函数,而是一个从原始函数扩展而来的新 class。 class 存储其父 class 实例化的所有实例,并通过从 args
和 kwargs
.
创建的键对它们进行索引
__getitem__
已在 class 上被覆盖,然后在父 class 中被覆盖,它采用以下任何一项:
- args(元组)和 kwargs(dict)的元组
- 作为元组的参数
- 一个参数
无论如何,使用代码:
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
我相信这就是你想要的?
>>> 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
出于完全人为设计的编程练习,我希望能够使用装饰器访问我为特定 class 创建的所有对象,并通过下标 class 本身。
在下面的示例中,我使用参数 1
创建了 A class 的实例,并使用键 '1'
将结果存储在字典中。
因此,通过尝试直接索引 A class,它应该 return 创建的对象的实例,但是,它只是 return 传递给的参数__get__item,如果我尝试直接用 A['1'].
对其进行索引,则会引发 TypeErrorimport 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.
这是我想出的。
我们不能将 A[<something>]
作为可能的用法,因为那将意味着更改 type
的属性。我已经为该用例创建了 A.get(<something>)
。
这与您的尝试不同,我们 return 不是 return 从装饰器中创建一个函数,而是一个从原始函数扩展而来的新 class。 class 存储其父 class 实例化的所有实例,并通过从 args
和 kwargs
.
__getitem__
已在 class 上被覆盖,然后在父 class 中被覆盖,它采用以下任何一项:
- args(元组)和 kwargs(dict)的元组
- 作为元组的参数
- 一个参数
无论如何,使用代码:
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
我相信这就是你想要的?
>>> 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