游戏开发后端【免费下载链接】kbengineA MMOG engine of server.项目地址https://gitcode.com/gh_mirrors/kb/kbengine点击查看免费下载numbers是 Python 标准库中依据 [PEP 3141] 定义的数值抽象基类Abstract Base Class模块它通过Number → Complex → Real → Rational → Integral的“数值塔”numeric tower层级为所有数值类型提供统一的类型检查与混型运算协议。本文基于 KBEngine 仓库内置的 Python 3.7.3 源码官方文档 与 实现源码展开帮助读者掌握这套抽象基类的设计意图、各层类的抽象接口与默认实现并深入理解“类型实现者”编写自定义数值类型时必须遵循的散列一致性与__add__/__radd__回退约定从而在引擎脚本开发中写出行为正确、可无缝参与数值运算的自定义类型。模块定位与核心价值numbers模块对应 [PEP 3141]定义了一个数值抽象基类ABC层级逐层增加运算能力。模块中定义的所有类型都不能被实例化它们只作为协议与判型工具存在。源码入口位于 kbe/src/lib/python/Lib/numbers.py模块导出的全部符号为__all__ [Number, Complex, Real, Rational, Integral]它最典型的用途是“只要判断一个参数是不是数而不关心具体是哪种数”isinstance(x, Number)这条判断对内置的int、float、complex以及注册过的fractions.Fraction、decimal.Decimal等全部成立。例如 KBEngine 的 Python 标准库中lib2to3 的修复脚本 就明确把旧的operator.isNumberType(obj)等价改写为isinstance(obj, numbers.Number)。注意当前仓库内置的是 Python 3.7.3见 Include/patchlevel.h本文代码均以该版本源码为准。数值塔五个抽象基类的层级关系PEP 3141 定义了如下继承链Number └── Complex └── Real └── Rational └── Integral“塔”的含义在于越往下的类越具体、提供的运算越多越往上越通用。在 numbers.py 中这一层级通过 ABCMeta 元类实现from abc import ABCMeta, abstractmethod。Number数值层级的根Number是所有数值类型的根类。它的全部职责就是“被 isinstance 检查”本体除__hash__ None要求具体数值类型自行提供散列实现外几乎不定义任何运算。官方文档明确建议只想判断x是否为数就用isinstance(x, Number)。Complex复数协议Complex是Number的子类描述复数包含作用于内置complex类型的全部运算到complex和bool的转换、real、imag属性、、-、*、/、abs、conjugate、、!。除-和!之外其余都是抽象方法由子类实现real抽象属性取数的实部。imag抽象属性取数的虚部。conjugate()抽象方法返回复共轭如(13j).conjugate() (1-3j)。从源码看Complex抽象出的协议是 一组成对出现的运算符__add__/__radd__、__mul__/__rmul__、__truediv__/__rtruediv__、__pow__/__rpow__以及__neg__、__pos__、__abs__、__eq__。其中__sub__与__rsub__被实现为“用取负加加法推导出来”的默认版本def __sub__(self, other): return self -other def __rsub__(self, other): return -self other内置complex类型通过Complex.register(complex)注册为Complex的虚拟子类。Real实数协议Real在Complex之上叠加了实数的运算到float的转换、math.trunc、round、math.floor、math.ceil、divmod、//、%、、、、。同时Real为complex、real、imag、conjugate提供了默认实现numbers.pydef __complex__(self): return complex(float(self)) # complex(self) complex(float(self), 0) property def real(self): return self # 实数就是它自身的实部 property def imag(self): return 0 # 实数没有虚部 def conjugate(self): return self # 实数的共轭就是自身它抽象出的新接口包括__float__、__trunc__、__floor__、__ceil__、__round__、__floordiv__/__rfloordiv__、__mod__/__rmod__、__lt__、__le____divmod__/__rdivmod__则给出基于//与%的默认实现。内置float通过Real.register(float)注册。Rational有理数协议Rational是Real的子类新增numerator分子与denominator分母两个抽象属性并要求它们保持最简形式lowest terms。基于分子分母Rational给出了float的默认实现numbers.py其巧妙之处在于使用整数的“真除法”/而非先把某一边转成 float从而避免超大整数比例在转换时溢出def __float__(self): return self.numerator / self.denominatorIntegral整数协议Integral是Rational的子类新增到int的转换并为float、numerator、denominator提供默认实现整数即自身分子、分母恒为 1。它额外抽象出幂运算与位串运算**、、、、^、|、~对应源码中的__pow__、__lshift__/__rlshift__、__rshift__/__rrshift__、__and__/__rand__、__xor__/__rxor__、__or__/__ror__、__invert__numbers.py。值得注意的细节是Integral.__index__numbers.py它把自身转为int从而支持切片索引场景以及__pow__支持三参数pow(x, y, mod)形式并在指数为负或参数非整数时抛出TypeError。内置int通过Integral.register(int)注册。内置类型与第三方类型的注册关系综合文档与源码可以整理出以下注册事实抽象基类已注册的内置/标准库类型ComplexcomplexRealfloatIntegralintNumberdecimal.Decimal见 _pydecimal.py 的_numbers.Number.register(Decimal)Rationalfractions.Fraction直接继承fractions.pyclass Fraction(numbers.Rational)这里有一个刻意的设计例外Decimal实现了Real的全部方法却故意不注册为Real。原因注释在 numbers.py十进制与二进制浮点不能互操作Decimal(3.14) 2.71828是未定义的而抽象Real之间必须保证互操作两个Real相加应总能成立。这正是“抽象类定义协议、注册表控制判定”两种机制分工的典型示范。类型实现者注意事项相等的数必须散列一致官方文档专门用一节提醒自定义数值类型的实现者相等的数必须相等比较、并且散列到相同值。当同一种数值存在两种扩展形式时例如Fraction与float可能数值相等这一点很容易出问题。Fraction是标准范例其散列逻辑要求“与数值相等的整数、浮点、Decimal 散列一致”。文档中给出的 Fraction.hash参考实现 策略如下def __hash__(self): if self.denominator 1: # Get integers right. return hash(self.numerator) # Expensive check, but definitely correct. if self float(self): return hash(float(self)) else: # Use tuples hash to avoid a high collision rate on # simple fractions. return hash((self.numerator, self.denominator))作为对比仓库内 Python 3.7.3 的 fractions.py 实际实现 走的是另一条更高效的路线利用pow(denominator, _PyHASH_MODULUS - 2, _PyHASH_MODULUS)求模逆再对_PyHASH_MODULUS取模得到与int/float数值散列一致的哈希并将-1修正为-2避免与-1这个错误哨兵值冲突。两种实现殊途同归都验证了 PEP 3141 的核心要求a b蕴含hash(a) hash(b)。如何扩展数值塔在 Complex 与 Real 之间插入新 ABC该层级设计并不排斥后续扩展。如果你想把自定义的MyFoo插入到Complex与Real之间官方文档给出了两步操作class MyFoo(Complex): ... MyFoo.register(Real)即让MyFoo作为Complex的子类实现复数协议再把Real注册为MyFoo的虚拟子类。借助 ABC 的register机制isinstance(real_instance, MyFoo)将成立而无需改动Real的源码——这也是整个数值塔可以持续演进、却不破坏既有注册关系的关键。实现混型算术运算五种情形与NotImplemented回退数值类型最难的部分是混型运算mixed-mode operations。官方文档给出的目标很明确要么调用一个“作者同时了解两个参数类型”的实现要么把两个参数都转换成最近的内置类型后再运算。推荐的__add__/__radd__写法对Integral的子类文档推荐这样定义正反向加法官方文档class MyIntegral(Integral): def __add__(self, other): if isinstance(other, MyIntegral): return do_my_adding_stuff(self, other) elif isinstance(other, OtherTypeIKnowAbout): return do_my_other_adding_stuff(self, other) else: return NotImplemented def __radd__(self, other): if isinstance(other, MyIntegral): return do_my_adding_stuff(other, self) elif isinstance(other, OtherTypeIKnowAbout): return do_my_other_adding_stuff(other, self) elif isinstance(other, Integral): return int(other) int(self) elif isinstance(other, Real): return float(other) float(self) elif isinstance(other, Complex): return complex(other) complex(self) else: return NotImplemented核心纪律是__add__的兜底分支必须返回NotImplemented而不是自作主张返回一个值。因为如果A.__add__在“不认识b”时返回了结果Python 就不会再去尝试B.__radd__从而错过B作者可能写下的更聪明的实现。混型运算的 5 种情形设a是A的实例A : Complexb是B的实例B : Complex考察a bA定义了能接受b的__add__直接成功。A落入兜底代码——此时__add__必须返回NotImplemented或A根本不实现__add__把机会让给B。B的__radd__获得机会若它接受a成功。若B.__radd__也落入兜底则再无其他方法可试这里才是默认实现应该待的地方即把双方转成最近的内置类型运算。若B : AB是A的子类Python 会先尝试B.__radd__再尝试A.__add__这是安全的因为B的实现者了解A可以在委托给Complex之前正确处理A的实例。当A : Complex且B : Real、彼此没有额外知识时双方共享的合适运算落在内置complex上两条__radd__都落到那里于是a b b a得以保持。用辅助函数批量生成运算符由于同一类型上大量运算符的模式高度相似文档推荐用辅助函数一次性生成正反向运算符。fractions.Fraction正是范例官方文档def _operator_fallbacks(monomorphic_operator, fallback_operator): def forward(a, b): if isinstance(b, (int, Fraction)): return monomorphic_operator(a, b) elif isinstance(b, float): return fallback_operator(float(a), b) elif isinstance(b, complex): return fallback_operator(complex(a), b) else: return NotImplemented forward.__name__ __ fallback_operator.__name__ __ forward.__doc__ monomorphic_operator.__doc__ def reverse(b, a): if isinstance(a, Rational): # Includes ints. return monomorphic_operator(a, b) elif isinstance(a, numbers.Real): return fallback_operator(float(a), float(b)) elif isinstance(a, numbers.Complex): return fallback_operator(complex(a), complex(b)) else: return NotImplemented reverse.__name__ __r fallback_operator.__name__ __ reverse.__doc__ monomorphic_operator.__doc__ return forward, reverse def _add(a, b): a b return Fraction(a.numerator * b.denominator b.numerator * a.denominator, a.denominator * b.denominator) __add__, __radd__ _operator_fallbacks(_add, operator.add)可以看到reverse内部正是按“Rational含int→ 原生运算、Real→ 转float、Complex→ 转complex、其余 →NotImplemented”的阶梯降级与文档“5 种情形”的原则完全对应。仓库内的 fractions.py 沿用了这一模式并对其、-、*、/四个运算符都做了同样的包装。numbers 在标准库与引擎脚本中的实际应用除了判型numbers还是标准库实现多态数值逻辑的公共底座在 KBEngine 内置 Python 3.7.3 中随处可见fractions.pyFraction(numbers.Rational)继承数值塔并在__eq__、_operator_fallbacks中按Rational、Real、Complex分级处理L571-L575。_pydecimal.py比较运算时用isinstance(other, _numbers.Rational)与isinstance(other, _numbers.Complex) and other.imag 0判定对方是否为“实值数”从而决定能否直接参与比较。statistics.py用isinstance(x, (numbers.Real, Decimal))判定输入是否为可统计的数值。lib2to3把废弃的operator.isNumberType自动改写为isinstance(obj, numbers.Number)。对 KBEngine 的脚本开发者而言这套机制同样直接可用无论是给引擎实体属性自定义一种带单位或精度的数值类型还是为角色属性血量、坐标等做统一的多态运算只要让类型继承numbers.Real/numbers.Integral并遵守本文的散列一致性与NotImplemented回退约定就能与内置数值、Fraction、Decimal自由混用同时保持isinstance判型的语义正确。小结numbers模块用五个抽象基类把“数”的协议逐层固化Number只管判型Complex定义复数运算Real补上实数运算与默认复数转换Rational通过分子分母给出无损float转换Integral补齐整数幂与位运算。配合register虚拟注册机制内置的int/float/complex以及标准库的Fraction、Decimal都接入同一张数值塔而“相等的数散列一致”与“__add__兜底返回NotImplemented、让__radd__依次接棒”两条铁律则保证了混型运算在任意扩展类型组合下仍然正确、可交换。理解这些约定是编写可被 Python 生态完整接纳的自定义数值类型的前提也是读懂Fraction、Decimal等复杂数值实现的一把钥匙。赞分享游戏开发后端【免费下载链接】kbengineA MMOG engine of server.项目地址https://gitcode.com/gh_mirrors/kb/kbengine点击查看免费下载相关推荐深入 PEP 3141 数字抽象基类CPython numbers 模块权威指南深入 PEP 3141 数字抽象基类CPython numbers 模块权威指南 本文围绕 CPython 标准库中的 numbers 模块展开系统讲解其依编程语言语言运行时标准库解释器kbengine 内嵌 Python 的 abc 模块解析抽象基类机制与源码级实践指南kbengine 内嵌 Python 的 abc 模块解析抽象基类机制与源码级实践指南 导读 abc Abstract Base Classes抽象基类游戏开发后端SpiceDB 遥测体系深度解析遥测指标定义、Remote Write 上报实现与禁用配置SpiceDB 遥测体系深度解析遥测指标定义、Remote Write 上报实现与禁用配置 SpiceDB 内置了一套基于 Prometheus Remote游戏开发后端上一篇终极B站会员购抢票神器告别手速焦虑轻松抢到热门门票下一篇如何让经典Flash内容重获新生CefFlashBrowser实用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?