我正在使用自定义值扩展HTTPStatus
:
from http import HTTPStatus
HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT = 573
我想知道为什么在检查HTTPStatus
时我没有看到这个值:
>>> dir(HTTPStatus)
['ACCEPTED', 'ALREADY_REPORTED', 'BAD_GATEWAY', 'BAD_REQUEST', 'CONFLICT', 'CONTINUE', 'CREATED', 'EXPECTATION_FAILED', 'FAILED_DEPENDENCY', 'FORBIDDEN', 'FOUND', 'GATEWAY_TIMEOUT', 'GONE', 'HTTP_VERSION_NOT_SUPPORTED', 'IM_USED', 'INSUFFICIENT_STORAGE', 'INTERNAL_SERVER_ERROR', 'LENGTH_REQUIRED', 'LOCKED', 'LOOP_DETECTED', 'METHOD_NOT_ALLOWED', 'MOVED_PERMANENTLY', 'MULTIPLE_CHOICES', 'MULTI_STATUS', 'NETWORK_AUTHENTICATION_REQUIRED', 'NON_AUTHORITATIVE_INFORMATION', 'NOT_ACCEPTABLE', 'NOT_EXTENDED', 'NOT_FOUND', 'NOT_IMPLEMENTED', 'NOT_MODIFIED', 'NO_CONTENT', 'OK', 'PARTIAL_CONTENT', 'PAYMENT_REQUIRED', 'PERMANENT_REDIRECT', 'PRECONDITION_FAILED', 'PRECONDITION_REQUIRED', 'PROCESSING', 'PROXY_AUTHENTICATION_REQUIRED', 'REQUESTED_RANGE_NOT_SATISFIABLE', 'REQUEST_ENTITY_TOO_LARGE', 'REQUEST_HEADER_FIELDS_TOO_LARGE', 'REQUEST_TIMEOUT', 'REQUEST_URI_TOO_LONG', 'RESET_CONTENT', 'SEE_OTHER', 'SERVICE_UNAVAILABLE', 'SWITCHING_PROTOCOLS', 'TEMPORARY_REDIRECT', 'TOO_MANY_REQUESTS', 'UNAUTHORIZED', 'UNPROCESSABLE_ENTITY', 'UNSUPPORTED_MEDIA_TYPE', 'UPGRADE_REQUIRED', 'USE_PROXY', 'VARIANT_ALSO_NEGOTIATES', '__class__', '__doc__', '__members__', '__module__']
价值本身可用:
>>> HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT
573
有什么奇怪的事吗?我应该采用不同的方法吗?
答案 0 :(得分:2)
那是因为http.HTTPStatus
是Enum
而Python并不是真的,因为Enum
是一个通用类型(这就是为什么你可以做你正在做的事情 - 那些语言实际上认为Enum
是特殊的东西不会让你像这样一般地混淆它。当然,Python尽最大努力使Enums
表现得如此(不可变,可迭代,可映射......)。
在您创建新的collections.OrderedDict
类型时,实际上会创建一个Enum._member_map_
(Enum
) - 它会读取成员,为重复项添加别名并添加额外的value -> enum member
Enum._value2member_map_
1}} map为enum.EnumMeta
(所有这些都由dir()
元类完成)。当你Enum._member_names_
枚举时 - 你得到那个地图(或者更确切地说,HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT = 573
列表中可用的枚举名称列表),你在运行时可能已经应用的任何更改都不计算在内(否则它不会不可变的。换句话说,当您执行Enum
时,您没有扩展Enum
,而只是向有问题的Enum
对象添加动态属性。
如果你想添加自定义成员,你应该以常规的OOP方式扩展你的# HERE BE DRAGONS!
# DO NOT do this unless you absolutely have to.
from http import HTTPStatus
def add_http_status(name, value, phrase, description=''):
# call our new member factory, it's essentially the `HTTPStatus.__new__` method
new_status = HTTPStatus.__new_member__(HTTPStatus, value, phrase, description)
new_status._name_ = name # store the enum's member internal name
new_status.__objclass__ = HTTPStatus.__class__ # store the enum's member parent class
setattr(HTTPStatus, name, new_status) # add it to the global HTTPStatus namespace
HTTPStatus._member_map_[name] = new_status # add it to the name=>member map
HTTPStatus._member_names_.append(name) # append the names so it appears in __members__
HTTPStatus._value2member_map_[value] = new_status # add it to the value=>member map
类型......除了Python也不允许你这样做。因此,如果你真的坚持做它的运行时你可以,有点,破解内部结构,让Python相信你的枚举值一直存在:
HTTPStatus
现在你可以'真正'在运行时扩展try:
print(HTTPStatus(573))
except ValueError as e:
print(e)
print("MY_CUSTOM_SERVICE_TIMEOUT" in dir(HTTPStatus))
add_http_status("MY_CUSTOM_SERVICE_TIMEOUT", 573, "Custom service timeout")
print("MY_CUSTOM_SERVICE_TIMEOUT" in dir(HTTPStatus))
print(HTTPStatus(573))
print(HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT.value)
print(HTTPStatus(573).phrase)
:
Enum
哪个会给你:
573 is not a valid HTTPStatus False True HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT 573 Custom service timeout
请记住,如果您想任意扩展enum.EnumMeta.__new__()
,请不要使用重复或无效值,否则此代码不会处理别名,重复数据删除和其他您应该做的好事。你会打破它(从某种意义上说,它将无法按预期工作)。检查@CliCommand
中采取的其他步骤,以确保其有效性。
答案 1 :(得分:1)
使用aenum library
1 中的extend_enum
函数:
import aenum
import http
aenum.extend_enum(http.HTTPStatus, 'CustomTimeout', 537, 'more helpful phrase here')
结果是:
>>> list(http.HTTPStatus)
[<HTTPStatus.CONTINUE: 100>,
...,
<HTTPStatus.CustomTimeout: 537>]
1 披露:我是Python stdlib Enum
,enum34
backport和Advanced Enumeration (aenum
)图书馆的作者。