我的函数需要接受一个对象,从中可以通过索引提取数据,即。 __getitem__
或具有已定义class IndexableContainer(Generic[int, ReturnType]):
def __getitem__(self, key: int) -> ReturnType:
...
方法的实例。
我可以使用哪种类型来提示这个论点?
更新: 据我所知目前没有这种类型,我试着自己制作一个:
File "indexable_container.py", line 22, in IndexableContainer
class IndexableContainer(Generic[int, ReturnType]):
File ".../lib/python3.6/typing.py", line 682, in inner
return func(*args, **kwds)
File ".../lib/python3.6/typing.py", line 1112, in __getitem__
"Parameters to Generic[...] must all be type variables")
TypeError: Parameters to Generic[...] must all be type variables
但是我收到以下错误:
from django.db.models import fields
from django.utils.six import string_types
import recurrence
from recurrence import forms
from recurrence.compat import Creator
try:
from south.modelsinspector import add_introspection_rules
add_introspection_rules([], [
"^recurrence\.fields\.RecurrenceField",
])
except ImportError:
pass
# Do not use SubfieldBase meta class because is removed in Django 1.10
class RecurrenceField(fields.Field):
"""Field that stores a `recurrence.base.Recurrence` to the database."""
def __init__(self, include_dtstart=True, **kwargs):
self.include_dtstart = include_dtstart
super(RecurrenceField, self).__init__(**kwargs)
def get_internal_type(self):
return 'TextField'
def to_python(self, value):
if value is None or isinstance(value, recurrence.Recurrence):
return value
value = super(RecurrenceField, self).to_python(value) or u''
return recurrence.deserialize(value, self.include_dtstart)
def from_db_value(self, value, *args, **kwargs):
return self.to_python(value)
def get_prep_value(self, value):
if not isinstance(value, string_types):
value = recurrence.serialize(value)
return value
def contribute_to_class(self, cls, *args, **kwargs):
super(RecurrenceField, self).contribute_to_class(cls, *args, **kwargs)
setattr(cls, self.name, Creator(self))
def value_to_string(self, obj):
return self.get_prep_value(self._get_val_from_obj(obj))
def formfield(self, **kwargs):
defaults = {
'form_class': forms.RecurrenceField,
'widget': forms.RecurrenceWidget,
}
defaults.update(kwargs)
return super(RecurrenceField, self).formfield(**defaults)
我该怎么做?
答案 0 :(得分:2)
有几种不同的方法可以做到这一点。
如果您只使用自定义类(可以编写)作为可索引容器,那么您需要做的就是调整代码并删除' int'类型参数:
class IndexableContainer(Generic[ReturnType]):
def __getitem__(self, key: int) -> ReturnType:
...
class MyCustomContainer(IndexableContainer[ReturnType]):
def __getitem__(self, key: int) -> ReturnType:
# Implementation here
def requires_indexable_container(container: IndexableContainer[ReturnType]) -> ReturnType:
# Code using container here
当然,问题是,如果你想将一个普通的旧列表传入函数,那么你就无法这样做,因为列表不会对你的自定义类型进行子类化。
我们可以通过巧妙地使用@overload
装饰器和联合来特殊情况下输入某些输入,但这是第二种,尽管是实验性的,这种方式称为Protocols。
协议基本上让你表达" duck typing"以一种理智的方式使用类型提示:基本思想是我们可以调整IndexableContainer成为一个协议。现在,任何使用适当签名实现__getitem__
方法的对象都被计为有效的IndexableContainer,无论它们是否为该类型的子类。
唯一需要注意的是,协议目前是实验性的,并且(afaik)仅由mypy支持。计划是最终为一般的Python生态系统添加协议 - 请参阅PEP 544了解具体提案 - 但我没有跟踪讨论/不知道该状态是什么是
在任何情况下,要使用协议,请使用pip安装typing_extensions
模块。然后,您可以执行以下操作:
from typing_extensions import Protocol
# ...snip...
class IndexableContainer(Protocol, Generic[ReturnType]):
def __getitem__(self, key: int) -> ReturnType:
...
def requires_indexable_container_of_str(container: IndexableContainer[str]) -> None:
print(container[0] + "a")
a = ["a", "b", "c"]
b = {0: "foo", 2: "bar"}
c = "abc"
d = [1, 2, 3]
# Type-checks
requires_indexable_container_of_str(a)
requires_indexable_container_of_str(b)
requires_indexable_container_of_str(c)
# Doesn't type-check
requires_indexable_container_of_str(d)
答案 1 :(得分:1)
This answer to a related question 建议 typing.Sequence
。
此类型同时支持 __getitem__
和 __len__
。
鉴于目前已弃用,我认为最好使用 collections.abc.Sequence
。
正如作者后来在评论中提到的,他/她实际上也需要一些带有 __delitem__
的东西,在这种情况下 collections.abc.MutableSequence
可能是最合适的(@@尤瓦尔在评论中)。它支持所有 __getitem__
、__setitem__
、__delitem__
、__len__
和 insert
。
最终类型的示例用法(改编自 the reference answer):
from collections.abc import MutableSequence
def foo(bar: MutableSequence[Any]):
# bar is a mutable sequence of any objects
答案 2 :(得分:0)
我们最接近的似乎是:
Mapping[int, Any]
虽然它不是我想要的,但它足够接近。