使用自定义 Metaclass 指定 class 的类型
Specify Typing of class with custom Metaclass
在一个伟大的系统上使用类似枚举的 Django 选择替换 (http://musings.tinbrain.net/blog/2017/may/15/alternative-enum-choices/) 我有一个项目使用 class 和自定义元 class 允许我要做 list(MyChoices)
(在 Class 本身上)以获取所有枚举选项的列表。代码的相关部分如下所示:
class MetaChoices(type):
@classmethod
def __prepare__(mcs, name, bases, **kwargs):
return OrderedDict()
def __new__(mcs, name, bases, attrs):
_choices = OrderedDict()
for attr_name, value in list(attrs.items()):
...do things...
return type.__new__(mcs, name, bases, dict(attrs))
def __iter__(cls):
return iter(cls._choices.items())
class Choices(metaclass=MetaChoices):
pass
class IceCreamFlavor(Choices):
STRAWBERRY = ('strawberry', 'Fruity')
CHOCOLATE = 'chocolate'
list(IceCreamFlavor)
# [('strawberry', 'Fruity'), ('chocolate', Chocolate')
代码已经运行了一段时间,但现在我打开了输入(在本例中使用 PyCharm 的类型检查器,但也在寻找通用解决方案),并且 IceCreamFlavor
没有被标记为可迭代的,尽管它是从 class 派生的,其 metaclass 将 cls
定义为具有 __iter__
方法。有谁知道我可以证明 Choices
class 本身就是一个可迭代的解决方案?
我修复了代码以使其适用于 MyPy(通过首先添加注释文件 *.pyi 的 Pytype 检查更容易)。
方法 __iter__()
中存在打字问题,属性 _choices
对于检查器而言似乎未定义,因为它不是透明分配的,仅由 attrs['_choices'] = ...
.
可以加一行注释:
class MetaChoices(type):
_choices = None # type: dict # written as comment for Python >= 3.5
# _choices: dict # this line can be uncommented if Python >= 3.6
它对 Pytype 完全有效,并且它的注释当然也被 MyPY 检查有效。
也许 __iter__()
中的输入问题可能导致元类方法在检查器中被忽略。
如果修复没有帮助,则可以使用以下简化示例报告问题:
class MetaChoices(type):
_choices = {0: 'a'}
def __iter__(cls):
return iter(cls._choices.items())
class Choices(metaclass=MetaChoices):
pass
assert list(Choices) == [(0, 'a')]
我向原始文章报告了另一个小错误。该错误与此问题无关。
在一个伟大的系统上使用类似枚举的 Django 选择替换 (http://musings.tinbrain.net/blog/2017/may/15/alternative-enum-choices/) 我有一个项目使用 class 和自定义元 class 允许我要做 list(MyChoices)
(在 Class 本身上)以获取所有枚举选项的列表。代码的相关部分如下所示:
class MetaChoices(type):
@classmethod
def __prepare__(mcs, name, bases, **kwargs):
return OrderedDict()
def __new__(mcs, name, bases, attrs):
_choices = OrderedDict()
for attr_name, value in list(attrs.items()):
...do things...
return type.__new__(mcs, name, bases, dict(attrs))
def __iter__(cls):
return iter(cls._choices.items())
class Choices(metaclass=MetaChoices):
pass
class IceCreamFlavor(Choices):
STRAWBERRY = ('strawberry', 'Fruity')
CHOCOLATE = 'chocolate'
list(IceCreamFlavor)
# [('strawberry', 'Fruity'), ('chocolate', Chocolate')
代码已经运行了一段时间,但现在我打开了输入(在本例中使用 PyCharm 的类型检查器,但也在寻找通用解决方案),并且 IceCreamFlavor
没有被标记为可迭代的,尽管它是从 class 派生的,其 metaclass 将 cls
定义为具有 __iter__
方法。有谁知道我可以证明 Choices
class 本身就是一个可迭代的解决方案?
我修复了代码以使其适用于 MyPy(通过首先添加注释文件 *.pyi 的 Pytype 检查更容易)。
方法 __iter__()
中存在打字问题,属性 _choices
对于检查器而言似乎未定义,因为它不是透明分配的,仅由 attrs['_choices'] = ...
.
可以加一行注释:
class MetaChoices(type):
_choices = None # type: dict # written as comment for Python >= 3.5
# _choices: dict # this line can be uncommented if Python >= 3.6
它对 Pytype 完全有效,并且它的注释当然也被 MyPY 检查有效。
也许 __iter__()
中的输入问题可能导致元类方法在检查器中被忽略。
如果修复没有帮助,则可以使用以下简化示例报告问题:
class MetaChoices(type):
_choices = {0: 'a'}
def __iter__(cls):
return iter(cls._choices.items())
class Choices(metaclass=MetaChoices):
pass
assert list(Choices) == [(0, 'a')]
我向原始文章报告了另一个小错误。该错误与此问题无关。