0

I want to subclass set in Python so that it behaves as much as a regular set as possible, with a small modification, as shown below.

from typing import Iterable, Any
from collections.abc import MutableSet


class MSet(MutableSet):
    """ Set with a filename attribute """
    def __init__(self, fname: str, iterable: Iterable = None):
        self._fname = fname
        if iterable is None:
            self.elements = set()
        else:
            self.elements = set(iterable)

    def __repr__(self) -> str:
        return f"NAME:{self._fname}; VALUE:{self.elements}"

    def __contains__(self, value: Any) -> bool:
        return value in self.elements

    def __iter__(self) -> Iterable:
        return iter(self.elements)

    def __len__(self) -> int:
        return len(self.elements)

    def add(self, value: Any) -> None:
        self.elements.add(value)

    def discard(self, value: Any) -> None:
        self.elements.discard(value)

    @property
    def filename(self) -> str:
        return self._fname

This is fine, but I want to be able to make it so that the double-under methods

__le__, __lt__, __eq__, __ne__, __gt__, __ge__, __and__, __or__, __sub__, __xor__, 
isdisjoint, __ior__, __iand__, __ixor__, and __isub__

only work when two MSet instances share the same "self._fname". In other words, if I want to make a comparison between mset_a, and mset_b, where their file names are "a.txt" and "b.txt", I want the following:

mset_a = MSet("a.txt", [1, 2, 3])
mset_b = MSet("b.txt", [2, 3, 4])

mset_a & mset_b  # raise exception

How do I go about implementing this?

martineau
  • 119,623
  • 25
  • 170
  • 301
user3166083
  • 147
  • 8
  • At a minimum, you would only need to implement the *required* **Abstract Methods** shown in that column of the table of [Collections Abstract Base Classes](https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes) for the `MutableSet` class — which are merely `__contains__`, `__iter__`, `__len__`, `add`, and `discard` — and make each check the `_filename` attribute of their operands. Performance would be much better if you also implemented some or all the optional **Mixin Methods** shown in that column of the table. – martineau Mar 21 '22 at 23:02

1 Answers1

1

You need to implement the __and__ method:

def __and__(self, other):
    if self.filename != other.filename:
        raise Exception()
    return self.elements & other.elements
Riccardo Bucco
  • 13,980
  • 4
  • 22
  • 50
  • Do I override this behavior in all of the other double-underscore methods? I would be inserting the same behavior multiple on methods that take the same input types. Is there a better way? – user3166083 Mar 21 '22 at 22:28
  • @user3166083 You might want to use a decorator – Riccardo Bucco Mar 21 '22 at 22:29
  • I could use a decorator. Ideally I would wrap all of the methods to include this behavior in a for loop, rather than do it all by hand. Is this approach possible? – user3166083 Mar 21 '22 at 22:37
  • @user3166083 https://stackoverflow.com/questions/2237624/applying-a-decorator-to-every-method-in-a-class – Riccardo Bucco Mar 21 '22 at 22:44