可以在枚举中覆盖__new__来将字符串解析为实例? [英] Is it possible to override __new__ in an enum to parse strings to an instance?

查看:132
本文介绍了可以在枚举中覆盖__new__来将字符串解析为实例?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将字符串解析成python枚举。通常情况下,会执行解析方法。几天前,我发现了 __ new __ 方法,它可以根据给定的参数返回不同的实例。



这里我的代码,不工作:

  import枚举
类类型(enum.Enum):
未知= 0
Source = 1
NetList = 2

def __new __(cls,value):
if(value ==src):return Types.Source
#elif(value ==nl):return Types.NetList
#else:raise异常()

def __str __(self):
if(self == Types .Unknown):返回??
elif(self == Types.Source):returnsrc
elif(self == Types.NetList):returnnl

当我执行我的Python脚本时,我收到以下消息:

  [...] 
类类型(enum.Enum):
文件C:\Program Files\Python\Python 3.4.0\lib\enum.py,行154,in __new__
enum_member._value_ = member_type(* args)
TypeError:object()不需要参数

如何返回正确的枚举值实例?



编辑1:



此枚举用于URI解析,特别是用于解析模式。所以我的URI看起来像这样

  nl:PoC.common.config 
< schema>:< namespace> ; [。< subnamespace> *]。entity

所以在一个简单的string.split操作之后,将URI的第一部分传递给枚举创建。

  type = Types(splitList [0])

类型现在应包含具有3个可能值的枚举类型的值(未知,源,NetList)



如果我允许枚举的成员列表中的别名,则不可能将枚举的值别名迭代。

解决方案

是的,您可以覆盖枚举子类的 __ new __()要实现一个解析方法,如果你小心,但是为了避免在两个地方指定整数编码,你需要在类之后单独定义方法,因此你可以参考由enu定义的符号名称



这是我的意思:

  import enum 

class Types(enum.Enum):
未知= 0
源= 1
NetList = 2

def __str __(self):
if(self == Types.Unknown):return??
elif(self == Types.Source):returnsrc
elif(self == Types.NetList):returnnl
else:raise TypeError(self)

def _Types_parser(cls,value):
如果不是isinstance(value,str):
#转发到Types的超类(enum.Enum)
return super ,cls).__ new __(cls,value)
else:
#将字符串映射到枚举值,默认为未知
return {'nl':Types.NetList,
'ntl ':Types.NetList,#alias
'src':Types.Source,} get(value,Types.Unknown)

setattr(Types,'__new__',_Types_parser)

print(types('nl') - >,Types('nl'))#Types('nl') - > nl
print(types('ntl') - >,Types('ntl'))#Types('ntl') - > nl
print(types('wtf') - >,Types('wtf'))#Types('wtf') - > ??
print(Types(1) - >,Types(1))#Types(1) - > src

更新



这是一个更多的表驱动版本,有助于消除一些否则将涉及的重复编码:

 从集合导入OrderedDict 
import枚举

类类型(enum.Enum):
未知= 0
源= 1
NetList = 2
__str__ = lambda self:Types._value_to_str.get(self)

#define after Types class
类型.__ new__ = lambda cls,value:(cls._str_to_value.get(value,Types.Unknown )
如果isinstance(value,str)else
super(Types,cls).__ new __(cls,value))
#定义查找表及其倒数
类型。 _str_to_value = OrderedDict((('??',Types.Unknown),
('src',Types.Source),
('ntl',Types.NetList),#alias
('nl',Types.NetList))
Types._value_to_str = {val:key for key,val in Types._str_to_value.items()}
Types._str_to_value = dict(Types._str_to_value )$ convert
$ b如果__name__ =='__main__':
print(types('nl') - >,Types('nl')) #types('nl') - > nl
print(types('ntl') - >,Types('ntl'))#Types('ntl') - > nl
print(types('wtf') - >,Types('wtf'))#Types('wtf') - > ??
print(Types(1) - >,Types(1))#Types(1) - > src

print()
print(list(Types))#iterate values

import pickle#demostrate picklability
print(pickle.loads(pickle .dumps(Types.NetList))== Types.NetList)# - > True


I want to parse strings into python enums. Normally one would implement a parse method to do so. A few days ago I spotted the __new__ method which is capable of returning different instances based on a given parameter.

Here my code, which will not work:

import enum
class Types(enum.Enum):
  Unknown = 0
  Source = 1
  NetList = 2

  def __new__(cls, value):
    if (value == "src"):  return Types.Source
#    elif (value == "nl"): return Types.NetList
#    else:                 raise Exception()

  def __str__(self):
    if (self == Types.Unknown):     return "??"
    elif (self == Types.Source):    return "src"
    elif (self == Types.NetList):   return "nl"

When I execute my Python script, I get this message:

[...]
  class Types(enum.Enum):
File "C:\Program Files\Python\Python 3.4.0\lib\enum.py", line 154, in __new__
  enum_member._value_ = member_type(*args)
TypeError: object() takes no parameters

How can I return a proper instance of a enum value?

Edit 1:

This Enum is used in URI parsing, in particular for parsing the schema. So my URI would look like this

nl:PoC.common.config
<schema>:<namespace>[.<subnamespace>*].entity

So after a simple string.split operation I would pass the first part of the URI to the enum creation.

type = Types(splitList[0])

type should now contain a value of the enum Types with 3 possible values (Unknown, Source, NetList)

If I would allow aliases in the enum's member list, it won't be possible to iterate the enum's values alias free.

解决方案

Yes, you can override the __new__() method of an enum subclass to implement a parse method if you're careful, but in order to avoid specifying the integer encoding in two places, you'll need to define the method separately, after the class, so you can reference the symbolic names defined by the enumeration.

Here's what I mean:

import enum

class Types(enum.Enum):
    Unknown = 0
    Source = 1
    NetList = 2

    def __str__(self):
        if (self == Types.Unknown):     return "??"
        elif (self == Types.Source):    return "src"
        elif (self == Types.NetList):   return "nl"
        else:                           raise TypeError(self)

def _Types_parser(cls, value):
    if not isinstance(value, str):
        # forward call to Types' superclass (enum.Enum)
        return super(Types, cls).__new__(cls, value)
    else:
        # map strings to enum values, default to Unknown
        return { 'nl': Types.NetList,
                'ntl': Types.NetList,  # alias
                'src': Types.Source,}.get(value, Types.Unknown)

setattr(Types, '__new__', _Types_parser)

print("Types('nl') ->",  Types('nl'))   # Types('nl') -> nl
print("Types('ntl') ->", Types('ntl'))  # Types('ntl') -> nl
print("Types('wtf') ->", Types('wtf'))  # Types('wtf') -> ??
print("Types(1) ->",     Types(1))      # Types(1) -> src

Update

Here's a more table-driven version that helps eliminates some of the repetitious coding that would otherwise be involved:

from collections import OrderedDict
import enum

class Types(enum.Enum):
    Unknown = 0
    Source = 1
    NetList = 2
    __str__ = lambda self: Types._value_to_str.get(self)

# define after Types class
Types.__new__ = lambda cls, value: (cls._str_to_value.get(value, Types.Unknown)
                                    if isinstance(value, str) else
                                    super(Types, cls).__new__(cls, value))
# define look-up table and its inverse
Types._str_to_value = OrderedDict((( '??', Types.Unknown),
                                   ('src', Types.Source),
                                   ('ntl', Types.NetList),  # alias
                                   ( 'nl', Types.NetList),))
Types._value_to_str = {val: key for key, val in Types._str_to_value.items()}
Types._str_to_value = dict(Types._str_to_value) # convert to regular dict (optional)

if __name__ == '__main__':
    print("Types('nl')  ->", Types('nl'))   # Types('nl')  -> nl
    print("Types('ntl') ->", Types('ntl'))  # Types('ntl') -> nl
    print("Types('wtf') ->", Types('wtf'))  # Types('wtf') -> ??
    print("Types(1)     ->", Types(1))      # Types(1)     -> src

    print()
    print(list(Types))  # iterate values

    import pickle  # demostrate picklability
    print(pickle.loads(pickle.dumps(Types.NetList)) == Types.NetList)  # -> True

这篇关于可以在枚举中覆盖__new__来将字符串解析为实例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆