背景
最初我问过这个问题是因为我必须支持一些大量使用元组的意大利面条代码库,但没有对其中的值给出任何解释。经过一些重构后,我注意到我需要从其他元组中提取一些类型化的信息,并且正在寻找一些无样板且类型安全的方法。
解决方案
您可以将命名元组定义子类化并实现自定义__new__
方法来支持它,可选地在途中执行一些数据格式化和验证。有关详细信息,请参阅此参考。
例子
from __future__ import annotations
from collections import namedtuple
from typing import Union, Tuple
Point = namedtuple('Point', 'x y')
Color = namedtuple('Color', 'red green blue')
Pixel = namedtuple('Pixel', Point._fields + Color._fields)
# Redeclare "Color" to provide custom creation method
# that can deduce values from various different types
class Color(Color):
def __new__(cls, *subject: Union[Pixel, Color, Tuple[float, float, float]]) -> Color:
# If got only one argument either of type "Pixel" or "Color"
if len(subject) == 1 and isinstance((it := subject[0]), (Pixel, Color)):
# Create from invalidated color properties
return super().__new__(cls, *cls.invalidate(it.red, it.green, it.blue))
else: # Else treat it as raw values and by-pass them after invalidation
return super().__new__(cls, *cls.invalidate(*subject))
@classmethod
def invalidate(cls, r, g, b) -> Tuple[float, float, float]:
# Convert values to float
r, g, b = (float(it) for it in (r, g, b))
# Ensure that all values are in valid range
assert all(0 <= it <= 1.0 for it in (r, g, b)), 'Some RGB values are invalid'
return r, g, b
Color
现在,您可以从任何受支持的值类型(Color
, Pixel
,数字三元组)进行实例化,而无需样板。
color = Color(0, 0.5, 1)
from_color = Color(color)
from_pixel = Color(Pixel(3.4, 5.6, 0, 0.5, 1))
您可以验证所有都是相等的值:
>>> (0.0, 0.5, 1.0) == color == from_color == from_pixel
True