首页 > 解决方案 > 使 Python 函数可下标

问题描述

出于完全人为的编程练习的目的,我希望能够访问我为特定类创建的所有对象,使用装饰器并通过下标类本身。

在下面的示例中,我使用参数创建了 A 类的实例1,并使用键将结果存储在字典中'1'

因此,通过尝试直接索引 A 类,它应该返回创建的对象的实例,但是,它只返回传递给 __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.

标签: pythondecorator

解决方案


这就是我想出的。

我们不能有A[<something>]尽可能多的用法,因为这意味着改变type. 我已经A.get(<something>)为那个用例创建了。

这与您的尝试不同,我们不是从装饰器返回一个函数,而是返回一个从原始类扩展的新类。该类存储其父类的所有实例化实例,并通过从argsand创建的键对它们进行索引kwargs

__getitem__在类上被覆盖,然后在父类中被覆盖,它采用以下任何一种:

  • args (tuple) 和 kwargs (dict) 的元组
  • Args 作为一个元组
  • 单个参数

无论如何,使用代码:

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

推荐阅读