使用自定义值扩展HTTPStatus的最佳方法 [英] Best way to extend HTTPStatus with custom value

查看:991
本文介绍了使用自定义值扩展HTTPStatus的最佳方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用自定义值扩展 HTTPStatus

I am extending HTTPStatus with a custom value:

from http import HTTPStatus

HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT = 573

我想知道为什么我在检查 HTTPStatus 时看不到该值:

I am wondering why I do not see that value when inspecting 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

是否发生了奇怪的事情?我应该采取不同的方法吗?

Is there something strange going on? Should I approach this differently?

推荐答案

这是因为 http.HTTPStatus Enum 和Python并没有真正地将 Enum 作为泛型类型(这就是为什么您可以做些什么您正在执行的操作-实际上将 Enum 识别为特殊内容的语言不会让您像这样一般地弄乱它)。当然,Python会尽最大努力使枚举表现出应有的表现(不可变,可迭代,可映射...)。

That's because http.HTTPStatus is an Enum and Python doesn't really, trully have Enum as a generic type (which is why you can do what you're doing - languages that actually recognize Enum as something special wouldn't let you mess with it like this in general). Of course, Python does its best to make Enums behave as they should (immutable, iterable, mappable...).

实际上在创建新内容时( Enum._member_map _ )下面有一个 collections.OrderedDict 枚举类型-读取成员,为重复项添加别名,并添加其他 value->枚举成员映射为 Enum._value2member_map _ (所有操作均由 enum.EnumMeta 元类)。当您 dir()枚举时-您会得到该映射(或更确切地说, Enum中可用的枚举名称列表。_member_names_ 列表),并且您在运行时可能已应用的任何更改均不计入(否则,它不会是不变的)。换句话说,当您执行 HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT = 573 时,您并没有扩展 Enum ,您只是向有问题的 Enum 对象中添加动态属性。

There is actually a collections.OrderedDict underneath (Enum._member_map_) that gets created when you create a new Enum type - it reads in the members, aliases the duplicates and adds an additional value -> enum member map as Enum._value2member_map_ (all of that is done by the enum.EnumMeta metaclass). When you dir() an enum - you get that map (or more precisely, the enum names list available in the Enum._member_names_ list) and any changes you may have applied at runtime doesn't count (otherwise it wouldn't be immutable). In other words, when you do HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT = 573 you're not extending the Enum, you're just adding a dynamic property to the Enum object in question.

您应扩展 Enum 如果您想添加自定义成员,则键入常规的OOP方式,但是Python也不允许您这样做。因此,如果您真的坚持要在运行时进行操作,则可以修改内部结构,使Python相信您的枚举值始终存在:

You should extend your Enum types the regular, OOP way if you want to add custom members... except Python won't let you do this either. So if you really insist doing it run-time you can, kind of, hack the internal structure to make Python believe your enum value was there all along:

# 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

现在您可以在运行时真正扩展 HTTPStatus

And now you can 'really' extend the HTTPStatus at runtime:

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)

哪个会给你:

573 is not a valid HTTPStatus
False
True
HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT
573
Custom service timeout

请记住,如果您想任意扩展 Enum ,因此请勿使用重复或无效的值,否则您将其破坏(在某种意义上,此后它将无法正常工作)。检查 enum.EnumMeta .__ new __()中采取的其他步骤,以确保其有效性。

Keep in mind that this code doesn't handle aliasing, de-duplication and other nice things that you should absolutely be doing if you want to arbitrary extend an Enum so don't use duplicate or invalid values or you'll break it (in a sense it won't work as expected afterwards). Check the additional steps taken in enum.EnumMeta.__new__() to ensure its validity.

这篇关于使用自定义值扩展HTTPStatus的最佳方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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