我试图回答阅读它的问题:“我怎样才能使“set”的运算符的返回值成为我的 set 子类的类型。忽略给定类的详细信息以及是否不是这个例子一开始就坏了。我是从我自己的问题来到这里的,如果我的阅读是正确的,这将是重复的。
此答案与其他一些答案的不同之处如下:
- 给定的类(子类)只能通过添加装饰器来改变
- 因此足够笼统,无需关心给定类的细节 (hasattr(s, 'foo'))
- 每个班级(装饰时)支付一次额外费用,而不是每个实例。
- 给定示例的唯一问题是特定于“集合”的方法列表,可以轻松定义。
- 假设基类不是抽象的,可以自己复制构造(否则需要实现 __init__ 方法,从基类的实例复制)
库代码,可以放在项目或模块的任何地方:
class Wrapfuncs:
def __init__(self, *funcs):
self._funcs = funcs
def __call__(self, cls):
def _wrap_method(method_name):
def method(*args, **kwargs):
result = getattr(cls.__base__, method_name)(*args, **kwargs)
return cls(result)
return method
for func in self._funcs:
setattr(cls, func, _wrap_method(func))
return cls
要将它与集合一起使用,我们需要返回一个 new 实例的方法列表:
returning_ops_funcs = ['difference', 'symmetric_difference', '__rsub__', '__or__', '__ior__', '__rxor__', '__iand__', '__ror__', '__xor__', '__sub__', 'intersection', 'union', '__ixor__', '__and__', '__isub__', 'copy']
我们可以将它与 我们的 类一起使用:
@Wrapfuncs(*returning_ops_funcs)
class MySet(set):
pass
我不会详细说明这门课的特别之处。
我已经用以下几行测试了代码:
s1 = MySet([1, 2, 3])
s2 = MySet([2, 3, 4])
s3 = MySet([3, 4, 5])
print(s1&s2)
print(s1.intersection(s2))
print(s1 and s2)
print(s1|s2)
print(s1.union(s2))
print(s1|s2|s3)
print(s1.union(s2, s3))
print(s1 or s2)
print(s1-s2)
print(s1.difference(s2))
print(s1^s2)
print(s1.symmetric_difference(s2))
print(s1 & set(s2))
print(set(s1) & s2)
print(s1.copy())
哪个打印:
MySet({2, 3})
MySet({2, 3})
MySet({2, 3, 4})
MySet({1, 2, 3, 4})
MySet({1, 2, 3, 4})
MySet({1, 2, 3, 4, 5})
MySet({1, 2, 3, 4, 5})
MySet({1, 2, 3})
MySet({1})
MySet({1})
MySet({1, 4})
MySet({1, 4})
MySet({2, 3})
{2, 3}
MySet({1, 2, 3})
有一种情况,结果不是最优的。也就是说,运算符与类的实例一起用作右手操作数,内置“set”的实例作为第一个操作数。我不喜欢这样,但我相信这个问题对于我所见过的所有提议的解决方案都很常见。
我还想过提供一个示例,其中使用了 collections.abc.Set。
虽然可以这样做:
from collections.abc import Set, Hashable
@Wrapfuncs(*returning_ops_funcs)
class MySet(set, Set):
pass
我不确定它是否会带来@bjmc 想到的好处,或者它“免费”为您提供的“某些方法”是什么。
该解决方案旨在使用基类来完成工作并返回子类的实例。可能会以类似的方式生成使用成员对象完成工作的解决方案。