创建一个通用类型的 Dict 导数
Creating a generic typed Dict derivative
我正在尝试用 setdefault()
代替 get()
创建一个类似 dict 的类型,并使用预定义的默认类型。但它也不应该一直是指向同一个对象的指针。
问题是,我不明白如何在给定相应 TypeVar 的情况下实例化 var。
伪代码如下:
F = TypeVar('F')
T = TypeVar('T')
class AutoExpandingDict(Dict[F, T]):
def __getitem__(self, key: F) -> T:
if key not in self:
self[key] = new T()
return super(AutoExpandingDict, self).__getitem__(key)
如何让 new T()
工作?
预期用途是:
stats = AutoExpandingDict[str, AutoExpandingDict[str, RequestTimer]]()
其中 RequestTimer 是一个 class 记录一些统计信息,然后
def _api_request(method = 'GET', endpoint = None):
...
with stats[method][endpoint]:
...
response = self.session.request(method, f'{self.base_url}{api_path}{url_params_encoded}', ...)
...
...
除了 RequestTimer
之外还有其他用途,我不想复制粘贴很多只是名称不同的 class 或者用魔法重复 setdefault
每次参数(如果它是一个普通的dict
)。
Python 中的通用类型参数在运行时被“擦除”,因此您无法访问 T
的值。考虑一下:
A = TypeVar("A", covariant=True)
class Foo(Generic[A]):
def __init__(self, something: A) -> None:
self._something = something
def do_something(self):
[a_type] = get_params_somehow(self)
print(a_type)
class X:
pass
class Y(X):
pass
foo1: Foo[X] = Foo(Y())
foo2: Foo[Y] = Foo(Y())
x = some().com().pu().ta() + tion()
foo3 = Foo(x)
foo1
和 foo2
是以相同的方式创建的,他们无法访问有关其类型的信息(不借助 inspect
进行黑客攻击)。
使用 foo3
,x
的类型将由类型检查器 推断 ,因此 foo3
无法真正知道是什么它是在运行时,类型可能与 mypy
、pyright
、pyre
等不同。同样,类型(不是运行时类型,而是类型提示的东西)只是“在类型检查器的头部。
不过,如果您只打算以 AutoExpandingDict[str, AutoExpandingDict[str, RequestTimer]]()
方式构建 dict,则可以覆盖 __class_getitem__
或制作自定义元类。但这不是很直观,可能有点矫枉过正
你可能想要做的是提供一个工厂,类似于 collections.defaultdict
:
class AutoExpandingDict(Dict[F, T]):
def __init__(self, factory: Callable[[], T]):
self._factory = factory
def __getitem__(self, key: F) -> T:
if key not in self:
self[key] = self._factory()
return super().__getitem__(key)
例如:
stats: AutoExpandingDict[str, AutoExpandingDict[str, RequestTimer]] =
AutoExpandingDict(lambda: AutoExpandingDict(RequestTimer))
也许你只是想要一个 defaultdict
?
我正在尝试用 setdefault()
代替 get()
创建一个类似 dict 的类型,并使用预定义的默认类型。但它也不应该一直是指向同一个对象的指针。
问题是,我不明白如何在给定相应 TypeVar 的情况下实例化 var。
伪代码如下:
F = TypeVar('F')
T = TypeVar('T')
class AutoExpandingDict(Dict[F, T]):
def __getitem__(self, key: F) -> T:
if key not in self:
self[key] = new T()
return super(AutoExpandingDict, self).__getitem__(key)
如何让 new T()
工作?
预期用途是:
stats = AutoExpandingDict[str, AutoExpandingDict[str, RequestTimer]]()
其中 RequestTimer 是一个 class 记录一些统计信息,然后
def _api_request(method = 'GET', endpoint = None):
...
with stats[method][endpoint]:
...
response = self.session.request(method, f'{self.base_url}{api_path}{url_params_encoded}', ...)
...
...
除了 RequestTimer
之外还有其他用途,我不想复制粘贴很多只是名称不同的 class 或者用魔法重复 setdefault
每次参数(如果它是一个普通的dict
)。
Python 中的通用类型参数在运行时被“擦除”,因此您无法访问 T
的值。考虑一下:
A = TypeVar("A", covariant=True)
class Foo(Generic[A]):
def __init__(self, something: A) -> None:
self._something = something
def do_something(self):
[a_type] = get_params_somehow(self)
print(a_type)
class X:
pass
class Y(X):
pass
foo1: Foo[X] = Foo(Y())
foo2: Foo[Y] = Foo(Y())
x = some().com().pu().ta() + tion()
foo3 = Foo(x)
foo1
和 foo2
是以相同的方式创建的,他们无法访问有关其类型的信息(不借助 inspect
进行黑客攻击)。
使用 foo3
,x
的类型将由类型检查器 推断 ,因此 foo3
无法真正知道是什么它是在运行时,类型可能与 mypy
、pyright
、pyre
等不同。同样,类型(不是运行时类型,而是类型提示的东西)只是“在类型检查器的头部。
不过,如果您只打算以 AutoExpandingDict[str, AutoExpandingDict[str, RequestTimer]]()
方式构建 dict,则可以覆盖 __class_getitem__
或制作自定义元类。但这不是很直观,可能有点矫枉过正
你可能想要做的是提供一个工厂,类似于 collections.defaultdict
:
class AutoExpandingDict(Dict[F, T]):
def __init__(self, factory: Callable[[], T]):
self._factory = factory
def __getitem__(self, key: F) -> T:
if key not in self:
self[key] = self._factory()
return super().__getitem__(key)
例如:
stats: AutoExpandingDict[str, AutoExpandingDict[str, RequestTimer]] =
AutoExpandingDict(lambda: AutoExpandingDict(RequestTimer))
也许你只是想要一个 defaultdict
?