What is the correct (or best) way to subclass the Python set class, adding a new instance variable?

前端 未结 7 903
囚心锁ツ
囚心锁ツ 2021-01-01 20:45

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

7条回答
  •  礼貌的吻别
    2021-01-01 20:54

    I think that the recommended way to do this is not to subclass directly from the built-in set, but rather to make use of the Abstract Base Class Set available in collections.abc.

    Using the ABC Set gives you some methods for free as a mix-in so you can have a minimal Set class by defining only __contains__(), __len__() and __iter__(). If you want some of the nicer set methods like intersection() and difference(), you probably do have to wrap them.

    Here's my attempt (this one happens to be a frozenset-like, but you can inherit from MutableSet to get a mutable version):

    from collections.abc import Set, Hashable
    
    class CustomSet(Set, Hashable):
        """An example of a custom frozenset-like object using
        Abstract Base Classes.
        """
        __hash__ = Set._hash
    
        wrapped_methods = ('difference',
                           'intersection',
                           'symetric_difference',
                           'union',
                           'copy')
    
        def __repr__(self):
            return "CustomSet({0})".format(list(self._set))
    
        def __new__(cls, iterable=None):
            selfobj = super(CustomSet, cls).__new__(CustomSet)
            selfobj._set = frozenset() if iterable is None else frozenset(iterable)
            for method_name in cls.wrapped_methods:
                setattr(selfobj, method_name, cls._wrap_method(method_name, selfobj))
            return selfobj
    
        @classmethod
        def _wrap_method(cls, method_name, obj):
            def method(*args, **kwargs):
                result = getattr(obj._set, method_name)(*args, **kwargs)
                return CustomSet(result)
            return method
    
        def __getattr__(self, attr):
            """Make sure that we get things like issuperset() that aren't provided
            by the mix-in, but don't need to return a new set."""
            return getattr(self._set, attr)
    
        def __contains__(self, item):
            return item in self._set
    
        def __len__(self):
            return len(self._set)
    
        def __iter__(self):
            return iter(self._set)
    

提交回复
热议问题