【发布时间】:2019-02-19 19:27:16
【问题描述】:
在数学意义上,如果操作总是返回集合本身的成员,则集合(或类型)在操作下是 closed。
这个问题是关于创建一个在从其超类继承的所有操作下关闭的类。
考虑以下类。
class MyInt(int):
pass
由于__add__没有被覆盖,所以在加法下没有关闭。
x = MyInt(6)
print(type(x + x)) # <class 'int'>
使类型关闭的一种非常繁琐的方法是手动将每个返回int 的操作的结果转换回MyInt。
在这里,我使用元类自动化了该过程,但这似乎是一个过于复杂的解决方案。
import functools
class ClosedMeta(type):
_register = {}
def __new__(cls, name, bases, namespace):
# A unique id for the class
uid = max(cls._register) + 1 if cls._register else 0
def tail_cast(f):
@functools.wraps(f)
def wrapper(*args, **kwargs):
out = f(*args, **kwargs)
if type(out) in bases:
# Since the class does not exist yet, we will recover it later
return cls._register[uid](out)
return out
return wrapper
for base in reversed(bases):
for name, attr in base.__dict__.items():
if callable(attr) and name not in namespace:
namespace[name] = tail_cast(attr)
subcls = super().__new__(cls, name, bases, namespace)
cls._register[uid] = subcls
return subcls
class ClosedInt(int, metaclass=ClosedMeta):
pass
这在某些极端情况下失败,例如 property 和通过 __getattribute__ 恢复的方法。当基础不仅由基础类型组成时,它也会失败。
例如,这会失败:
class MyInt(int):
pass
class ClosedInt(MyInt, metaclass=ClosedMeta):
pass
ClosedInt(1) + ClosedInt(1) # returns the int 2
我试图解决这个问题,但它似乎在兔子洞里越来越深。
这似乎是一个可能有一些简单的pythonic解决方案的问题。还有什么其他更简洁的方法可以实现这种封闭类型?
【问题讨论】:
-
所以,这有点吹毛求疵,但
class BasicIntContainer(int)不是一个 int 容器,它是一个 int。 -
@juanpa.arrivillaga 我的术语可能有点不对劲,你怎么称呼这样的结构?
-
它只是继承,用 OOP 的说法是一种“is-a”关系。如果将鼠标悬停在容器标签上,您会看到:“容器是一个类、数据结构或抽象数据类型,其实例是其他对象的集合。”。所以就像
list或dict。这里,BasicIntContainer实例不是其他对象的集合,它是一种特殊的int对象。我不想用更多的术语吹毛求疵来转移一个有趣的问题。无论如何,我只想改写为“如何创建在继承操作下关闭的类型” -
如果您只想将返回值转换为类型本身,您可以轻松编写一个简单的装饰器,但这样做的问题是可能有一些方法无法返回值如果是这种情况,则强制转换为正确的类型;你需要以某种方式区分它们,否则你可以用装饰器装饰类来装饰每个方法。
-
@IşıkKaplan 为了简单起见,我们假设
__new__知道如何从它的所有超类中转换。此外,这个装饰器解决方案基本上是我用元类实现的。我目前正在修复它,但它变得非常复杂,我肯定会忘记很多角落。
标签: python python-3.x oop containers metaclass