使 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 实例化的所有实例,并通过从 argskwargs.

创建的键对它们进行索引

__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