我试图回答阅读它的问题:“我怎样才能使“集合”的运算符的返回值成为我的集合子类的类型。忽略给定类的详细信息以及示例是否一开始就坏了。如果我的阅读正确,我是从我自己的问题来到这里的,这将是重复的。
此答案与其他一些答案不同,如下所示:
- 给定的类(子类)只能通过添加装饰器来改变
- 因此足够笼统,不关心给定类的细节(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
要将它与集合一起使用,我们需要返回一个新实例的方法列表:
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 想到的好处,或者“一些方法”是什么,它“免费”为您提供。该解决方案旨在使用基类来完成工作并返回子类的实例。一个使用成员对象来完成工作的解决方案可能会以类似的方式生成。