【问题标题】:Python: Make a dynamically created class (as in the class itself) iterable?Python:使动态创建的类(如类本身)可迭代?
【发布时间】:2023-03-15 13:42:01
【问题描述】:

我从这段代码 sn-p 开始,据我了解,它本质上是一个类工厂,用于模拟其他语言的“枚举”类型:

def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    return type('Enum', (), enums)

(我取自这里:How can I represent an 'Enum' in Python?

我知道它是如何工作的,而且确实如此,但我想让我的动态生成的类(它们的类型为“枚举”)可迭代,以便我可以执行以下操作以进行完整性检查:

MyEnum = enum('FOO', 'BAR', 'JIMMY')
def func(my_enum_value):  # expects one of the MyEnum values
    if not my_enum_value in MyEnum:
        raise SomeSortOfException

但是,为了使健全性检查工作,我需要使 MyEnum 可迭代。我在这里读到:http://pydanny.blogspot.com/2007/10/required-methods-to-make-class-iterable.html 我需要按顺序添加 iter len containsgetitem 方法使其可迭代。我开始走这条路(重写枚举代码)但卡住了:

def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    enums['_enums'] = enums.copy() # (so I'd have them in the Class in order to use for the methods I'll be implementing to make it iterable)
    Enum = type('Enum', (), enums)

    Enum.__iter__ = #Er, how do I do this? Guess I'll ask SO
    Enum.__len__ =  # etc. 
    # . . . 
    return Enum

那么,如何使生成的 Enum 类可迭代,以便我可以对它们使用 'in' 词?这对我来说不是生死攸关的事,但我通过这样做学习了大量的 Python。

【问题讨论】:

标签: python


【解决方案1】:

使用比type 更智能的元类。

class EnumMC(type):
  def __contains__(self, val):
    return val in self.__dict__

def enum(*sequential, **named):
  enums = dict(zip(sequential, range(len(sequential))), **named)
  return EnumMC('Enum', (), enums)

MyEnum = enum('FOO', 'BAR', 'JIMMY')
def func(my_enum_value):  # expects one of the MyEnum values
    if not my_enum_value in MyEnum:
        raise ValueError()

func('FOO')
func('QUUX')

您可能希望使用实际属性来存储枚举键,而不是依赖于类字典。

【讨论】:

  • 谢谢,伊格纳西奥。加上一些补充,这让我到达了我需要的地方。元类是关键。
  • 我无法抗拒:(来自 Nacho Libre)“好吧。孤儿!听伊格纳西奥的话。我知道摔跤很有趣。一个漂亮的撞脸……或者一拳打在面对……但你不能这样做。因为,在圣经中不能与你的邻居摔跤。” (当然没有侮辱的意思)
【解决方案2】:
class Enum(dict): pass
def enum(*sequential, **named):
    return Enum(zip(sequential, range(len(sequential))), **named)

这更容易阅读,而且它支持迭代。

编辑:你仍然需要做

MyEnum = enum('FOO', 'BAR', 'JIMMY')

那你就可以了

'FOO' in MyEnum

你也可以使用

class Enum(dict):
    def __init__(self, *sequential, **named):
        self.update(zip(sequential, range(len(sequential))))
        self.update(named)
def __getattr__(self, attr):
    if attr in self:
        return self[attr]
    else:
        return super(Enum, self).__getattr__(attr)

做同样的事情。

编辑 2:另一种选择。

from collections import namedtuple

def enum(*sequential):
    return namedtuple('Enum', sequential)(*sequential)

仍然以同样的方式调用它,你可以使用MyEnum.FOO,它支持in。还编辑了我上面的类以支持属性访问。

如果您想要连续的整数值,您也可以使用range(len(sequential)) 填写namedtuple 值(不是键),或者如果您想要指定任意值,请使用**kwargs 然后kwargs.keys()kwargs.values() .

最后一个解决方案交叉发布到How can I represent an 'Enum' in Python?

【讨论】:

  • 更容易阅读:同意。支持迭代:false。如果您阅读了我最初的问题,我希望 class 支持迭代。所以我可以执行以下操作: MyEnum 中的 0
  • 试试看——MyEnum = enum('FOO', 'BAR', 'JIMMY') 然后'FOO' in MyEnum。它工作正常。
  • 是的。像你说的那样工作。它改变了我的“枚举”的工作方式。使用原始代码,我可以执行 MyEnum.FOO 。 . . (请参阅上面链接中的第二个答案 stackoverflow.com/questions/36932/… )而不是 MyEnum['FOO'] 。我不确定那是一件坏事,但我确实喜欢原始方法提供的语法糖。 (而且我已经向我的开发人员发送了一封电子邮件,说我在我们的实用程序中添加了这个很酷的新枚举):) 如果我这样做,我还不如只使用一个 dict。
  • 如果您希望它们成为属性,请查看我的上次编辑 -- dict 子类和 namedtuple 都可以轻松完成。
  • 甜蜜。我特别喜欢您的最后一个答案,并且可能最终会使用它作为我的实现。这是交易,因为我最初的问题询问如何使动态生成的类可迭代并且 Ignacio 回答了这个问题,(您的解决方案避免了对动态创建的类的需要)我将他标记为已接受。但是因为您的解决方案更加优雅和实用,所以我会投票赞成,我认为您应该将其发布在上面的链接中作为枚举问题的答案,因为它比那里提供的更好。如果你这样做,我也会在那里投票。再次感谢。
【解决方案3】:

我不知道为什么enum 需要返回class 而不是instance,这样你就可以轻松实现__contains__,我也不确定为什么我们甚至需要python 中的枚举,反正如果你需要在类级别添加__contains__,你需要一个元类,就在这里

class EnumMeta(type):
    def __contains__(self, x):
        return x in self.__dict__

class EnumBase(object):
    __metaclass__ = EnumMeta

def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    return type('Enum', (EnumBase,), enums)

MyEnum = enum('FOO', 'BAR', 'JIMMY')

print 'foobar in MyEnum','foobar' in MyEnum
print 'FOO in MyEnum', 'FOO' in MyEnum

输出:

foobar in MyEnum False
FOO in MyEnum True

【讨论】:

  • 谢谢。与 Ignacio 之前的建议非常相似。关于python中枚举的需要。我意识到它通常被认为是不需要的:python.org/dev/peps/pep-0354 但是有一个很方便,尤其是当开发人员觉得他们需要一个时。大多数情况下,我正在学习元类和动态生成的类,这似乎是学习它的好案例。这里的讨论很有帮助。
猜你喜欢
  • 2018-08-31
  • 2011-05-14
  • 2019-04-07
  • 2020-02-12
  • 1970-01-01
  • 2011-04-24
  • 1970-01-01
相关资源
最近更新 更多