继一个伟大的系统使用类似枚举的替换Django选择(http://musings.tinbrain.net/blog/2017/may/15/alternative-enum-choices/)我有一个项目,使用一个自定义元类的类,允许我做list(MyChoices)
(在类本身)获得所有枚举的列表选择。代码的相关部分看起来像这样:
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
没有被标记为可迭代,尽管它是从一个类派生的元类将cls
定义为具有__iter__
方法。有没有人知道一个解决方案,我可以证明Choices
类本身是一个可迭代的?
我修改了代码以使MyPy更正(通过Pytype更容易检查,首先添加注释文件* .pyi)。
打字问题在__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')]
我报告了原始文章的另一个小错误。该bug与此问题无关。