对 Python 集类进行子类化、添加新实例变量的正确(或最佳)方法是什么?

2022-01-17 00:00:00 python set subclass instance-variables

问题描述

我正在实现一个与集合几乎相同的对象,但需要一个额外的实例变量,所以我将内置集合对象子类化.在复制我的一个对象时,确保复制此变量的值的最佳方法是什么?

I'm implementing an object that is almost identical to a set, but requires an extra instance variable, so I am subclassing the built-in set object. What is the best way to make sure that the value of this variable is copied when one of my objects is copied?

使用旧的 sets 模块,以下代码完美运行:

Using the old sets module, the following code worked perfectly:

import sets
class Fooset(sets.Set):
    def __init__(self, s = []):
        sets.Set.__init__(self, s)
        if isinstance(s, Fooset):
            self.foo = s.foo
        else:
            self.foo = 'default'
f = Fooset([1,2,4])
f.foo = 'bar'
assert( (f | f).foo == 'bar')

但这不适用于内置的 set 模块.

but this does not work using the built-in set module.

我能看到的唯一解决方案是覆盖每个返回复制集合对象的单个方法......在这种情况下,我可能不会费心子类化集合对象.肯定有标准的方法可以做到这一点吗?

The only solution that I can see is to override every single method that returns a copied set object... in which case I might as well not bother subclassing the set object. Surely there is a standard way to do this?

(澄清一下,以下代码不起作用(断言失败):

(To clarify, the following code does not work (the assertion fails):

class Fooset(set):
    def __init__(self, s = []):
        set.__init__(self, s)
        if isinstance(s, Fooset):
            self.foo = s.foo
        else:
            self.foo = 'default'

f = Fooset([1,2,4])
f.foo = 'bar'
assert( (f | f).foo == 'bar')

)


解决方案

我最喜欢的封装内置集合方法的方式:

My favorite way to wrap methods of a built-in collection:

class Fooset(set):
    def __init__(self, s=(), foo=None):
        super(Fooset,self).__init__(s)
        if foo is None and hasattr(s, 'foo'):
            foo = s.foo
        self.foo = foo



    @classmethod
    def _wrap_methods(cls, names):
        def wrap_method_closure(name):
            def inner(self, *args):
                result = getattr(super(cls, self), name)(*args)
                if isinstance(result, set) and not hasattr(result, 'foo'):
                    result = cls(result, foo=self.foo)
                return result
            inner.fn_name = name
            setattr(cls, name, inner)
        for name in names:
            wrap_method_closure(name)

Fooset._wrap_methods(['__ror__', 'difference_update', '__isub__', 
    'symmetric_difference', '__rsub__', '__and__', '__rand__', 'intersection',
    'difference', '__iand__', 'union', '__ixor__', 
    'symmetric_difference_update', '__or__', 'copy', '__rxor__',
    'intersection_update', '__xor__', '__ior__', '__sub__',
])

基本上与您在自己的答案中所做的事情相同,但 loc 更少.如果您也想对列表和字典做同样的事情,也很容易放入元类.

Essentially the same thing you're doing in your own answer, but with fewer loc. It's also easy to put in a metaclass if you want to do the same thing with lists and dicts as well.

相关文章