为Python类方法添加__getitem__访问器的实现问题
解决Python类方法添加
__getitem__实现方括号类型指定的问题 问题核心
你的实现出错的原因是没有处理Python的描述符协议:类方法在实例上访问时,默认会通过描述符的__get__方法绑定到实例,而你的TypedMethod类没有实现这个逻辑,导致原类实例(比如MyClass()的a)没有被正确传递,反而把TypedMethod自身当成了self传入原方法,所以才会出现'TypedMethod' object has no attribute 'my_other_method'的错误。
修正后的实现
我们需要让TypedMethod实现描述符协议,同时在获取方法时返回一个支持__call__和__getitem__的绑定对象,确保原实例能正确传递:
from functools import partial class TypedMethod: _REQUESTED_TYPE_ATTR = '__requested_type' def __init__(self, method): self._method = method def __get__(self, instance, owner): # 实现描述符协议,绑定原实例到方法 if instance is None: return self # 类层面访问时返回自身 # 返回一个绑定了实例的包装器,支持__call__和__getitem__ return _BoundTypedMethod(instance, self._method) @classmethod def requested_type(cls, kwargs): return kwargs.pop(cls._REQUESTED_TYPE_ATTR, None) class _BoundTypedMethod: def __init__(self, instance, method): self._instance = instance self._method = method def __call__(self, *args, **kwargs): # 常规调用,直接传递实例和参数 return self._method(self._instance, *args, **kwargs) def __getitem__(self, specified_type): if not isinstance(specified_type, type): raise TypeError("Only Type Accessors are supported - must be an instance of `type`") # 生成partial,把类型存入kwargs return partial( self._method, self._instance, **{TypedMethod._REQUESTED_TYPE_ATTR: specified_type} ) def typedmethod(method): return TypedMethod(method)
测试验证
用你提供的MyClass测试两种调用方式:
class MyClass: @typedmethod def compute_typed_value(self, value, *args, **kwargs): print(f"self is MyClass instance: {isinstance(self, MyClass)}") datatype = TypedMethod.requested_type(kwargs) or str # 默认类型设为str result = datatype(value) self.my_other_method() return result def my_other_method(self): print('Doing some other things!') return 3 # 测试方括号调用 a = MyClass() result1 = a.compute_typed_value[int]('12345') print(f"方括号调用结果: {result1}, 类型: {type(result1)}") # 测试常规调用(默认类型) result2 = a.compute_typed_value('12345') print(f"常规调用结果: {result2}, 类型: {type(result2)}")
运行输出:
self is MyClass instance: True Doing some other things! 方括号调用结果: 12345, 类型: <class 'int'> self is MyClass instance: True Doing some other things! 常规调用结果: 12345, 类型: <class 'str'>
API适配器场景适配
把修正后的装饰器用到你的API适配器例子中:
from dataclasses import dataclass import requests @dataclass class MyAPIData: property_a: int = 0 property_b: int = 0 class MyAPIAdapter: def __init__(self, token): self._session = requests.Session() self._session.headers['Authorization'] = f"Bearer {token}" @typedmethod def request_json(self, url, **kwargs): datatype = TypedMethod.requested_type(kwargs) response = self._session.get(url) response_data = response.json() if datatype: response_data = datatype(**response_data) return response_data def fetch_myapidata(self, search): # 使用方括号指定返回类型 return self.request_json[MyAPIData](f"https://example.com/myapi?q={search}")
关键逻辑说明
- 描述符协议:
TypedMethod的__get__方法在实例访问方法时,返回_BoundTypedMethod对象,该对象持有原实例和原方法,确保后续调用时能正确传递self。 _BoundTypedMethod:同时实现__call__(处理常规调用)和__getitem__(处理方括号语法),其中__getitem__通过partial把类型参数注入到kwargs中,供原方法通过TypedMethod.requested_type获取。- 参数处理:
requested_type方法使用pop移除kwargs中的类型标记,避免污染原方法的参数环境。
内容的提问来源于stack exchange,提问作者David Culbreth
相关产品推荐
相关产品推荐

