【问题标题】:Iterate over a type without instantiating it迭代一个类型而不实例化它
【发布时间】:2020-10-26 22:53:46
【问题描述】:

问题

我希望能够迭代一个类型而不实例化它,类似于枚举。

class Foo:
    """Class I want to iterate over without instantiating."""
    ALLOWED_VALUES = (1, 2, 3)

# I want iterating over `Foo` to be equivalent to iterating over `Foo.ALLOWED_VALUES`
for val_from_tuple, val_from_foo in zip(Foo.ALLOWED_VALUES, Foo):
    assert val_from_tuple == val_from_foo

这种行为可以通过枚举实现,但前提是 ALLOWED_VALUES 是有效的 Python 名称。我想在没有这个限制的情况下拥有相同的迭代行为。

我尝试了什么

我尝试在Foo 上将__iter__() 实现为staticmethod,这样就不需要Foo 的实例来获取Iterator。这允许我迭代 Foo.__iter__(),但 iter(Foo) 会引发错误。这似乎是因为iter(Foo)type 上寻找__iter__ 方法,而不是在Foo 上(因为Footype 对象)。

class Foo:
    """Class I want to iterate over without instantiating."""
    ALLOWED_VALUES = (1, 2, 3)

    @staticmethod
    def __iter__():
        return Foo.ALLOWED_VALUES

# This works, but isn't what I want because it involves calling `__iter__()` explicitly.
for val in Foo.__iter__():
    print(val)

# This raises an error:
# `TypeError: 'type' object is not iterable`
for val in Foo:
    print(val)

【问题讨论】:

    标签: python class iterator static-methods magic-methods


    【解决方案1】:

    Enum 是可迭代的,因为它使用不同的元类(EnumMeta 而不是type)来创建它。您可以定义自己的元类来提供__iter__ 的定义,而type 本身没有。

    class IterableClass(type):
        def __iter__(self):
            yield from self.ALLOWED_VALUES
    
    class Foo(metaclass=IterableClass):
        ALLOWED_VALUES = (1,2,3)
    
    for x, y in zip(Foo.ALLOWED_VALUES, Foo):
        assert x == y
    

    【讨论】:

    • 一个小问题:考虑在元类方法中命名self 参数cls,以提醒读​​者它正在处理一个类对象。
    • 我来回讨论这个问题。仍然可以在元类中定义类方法,所以我通常觉得简单地记住比元类的实例是一个类要好。
    • (不过,可以使用cls/metacls 代替self/cls。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-31
    • 2015-11-28
    • 2016-01-21
    • 2020-11-25
    • 2016-01-05
    • 1970-01-01
    相关资源
    最近更新 更多