【问题标题】:Can named arguments be used with Python enums?命名参数可以与 Python 枚举一起使用吗?
【发布时间】:2014-12-28 18:48:26
【问题描述】:

例子:

class Planet(Enum):

    MERCURY = (mass: 3.303e+23, radius: 2.4397e6)

    def __init__(self, mass, radius):
        self.mass = mass       # in kilograms
        self.radius = radius   # in meters

参考:https://docs.python.org/3/library/enum.html#planet

我为什么要这样做?如果构造函数列表中有一些基本类型(int、bool),最好使用命名参数。

【问题讨论】:

    标签: python python-3.x enums named-parameters


    【解决方案1】:

    虽然您不能像使用枚举描述的那样使用命名参数,但您可以使用 namedtuple mixin 获得类似的效果:

    from collections import namedtuple
    from enum import Enum
    
    Body = namedtuple("Body", ["mass", "radius"])
    
    class Planet(Body, Enum):
    
        MERCURY = Body(mass=3.303e+23, radius=2.4397e6)
        VENUS   = Body(mass=4.869e+24, radius=6.0518e6)
        EARTH   = Body(mass=5.976e+24, radius=3.3972e6)
        # ... etc.
    

    ...在我看来更简洁,因为您不必编写 __init__ 方法。

    使用示例:

    >>> Planet.MERCURY
    <Planet.MERCURY: Body(mass=3.303e+23, radius=2439700.0)>
    >>> Planet.EARTH.mass
    5.976e+24
    >>> Planet.VENUS.radius
    6051800.0
    

    请注意,根据the docs,“在碱基序列中,混合类型必须出现在Enum 本身之前”。

    【讨论】:

    • 非常酷。我从来没有考虑过用 mixin 来解决这个问题。
    • @ZeroPiraeus:我添加了一个答案,但不是为了赏金——只是希望得到一些支持(我的 [python-3.x] 金徽章还有很长的路要走!;)。
    【解决方案2】:

    @zero-piraeus 接受的答案可以稍微扩展以允许默认参数。当您有一个大型枚举并且大多数条目具有相同的元素值时,这非常方便。

    class Body(namedtuple('Body', "mass radius moons")):
        def __new__(cls, mass, radius, moons=0):
            return super().__new__(cls, mass, radius, moons)
        def __getnewargs__(self):
            return (self.mass, self.radius, self.moons)
    
    class Planet(Body, Enum):
    
        MERCURY = Body(mass=3.303e+23, radius=2.4397e6)
        VENUS   = Body(mass=4.869e+24, radius=6.0518e6)
        EARTH   = Body(5.976e+24, 3.3972e6, moons=1)
    

    注意如果没有__getnewargs__,酸洗将无法工作。

    class Foo:
        def __init__(self):
            self.planet = Planet.EARTH  # pickle error in deepcopy
    
    from copy import deepcopy
    
    f1 = Foo()
    f2 = deepcopy(f1)  # pickle error here
    

    【讨论】:

    • @zero-piraeus 谢谢先生!
    【解决方案3】:

    如果超出了namedtuple 的混入,请查看aenum1。除了为Enum 提供一些额外的花里胡哨外,它还支持NamedConstant 和基于元类的NamedTuple

    使用aenum.Enum,上面的代码可能如下所示:

    from aenum import Enum, enum, _reduce_ex_by_name
    
    class Planet(Enum, init='mass radius'):
        MERCURY = enum(mass=3.303e+23, radius=2.4397e6)
        VENUS   = enum(mass=4.869e+24, radius=6.0518e6)
        EARTH   = enum(mass=5.976e+24, radius=3.3972e6)
        # replace __reduce_ex__ so pickling works
        __reduce_ex__ = _reduce_ex_by_name
    

    并在使用中:

    --> for p in Planet:
    ...     print(repr(p))
    <Planet.MERCURY: enum(radius=2439700.0, mass=3.3030000000000001e+23)>
    <Planet.EARTH: enum(radius=3397200.0, mass=5.9760000000000004e+24)>
    <Planet.VENUS: enum(radius=6051800.0, mass=4.8690000000000001e+24)>
    
    --> print(Planet.VENUS.mass)
    4.869e+24
    

    1 披露:我是Python stdlib Enumenum34 backportAdvanced Enumeration (aenum) 库的作者。

    【讨论】:

      【解决方案4】:

      对于 Python 3.6.1+,可以使用 typing.NamedTuple,它还允许设置默认值,从而产生更漂亮的代码。 @shao.lo 的示例如下所示:

      from enum import Enum
      from typing import NamedTuple
      
      
      class Body(NamedTuple):
          mass: float
          radius: float
          moons: int=0
      
      
      class Planet(Body, Enum):
          MERCURY = Body(mass=3.303e+23, radius=2.4397e6)
          VENUS   = Body(mass=4.869e+24, radius=6.0518e6)
          EARTH   = Body(5.976e+24, 3.3972e6, moons=1)
      

      这也支持酸洗。如果您不想指定类型,可以使用 typing.Any。

      感谢@monk-time,谁的回答here 启发了这个解决方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-11
        • 2021-12-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多