使用自定义Metaclass指定类的类型

问题描述 投票:1回答:1

继一个伟大的系统使用类似枚举的替换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类本身是一个可迭代的?

pycharm metaclass mypy python-typing
1个回答
1
投票

我修改了代码以使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与此问题无关。

© www.soinside.com 2019 - 2024. All rights reserved.