Documentation for set.intersection and others?

Viewed 428

Recently, I needed to find the intersection of a collection of sets, i.e. S1 ∩ S2 ∩ ... ∩ Sn in Python. I found a very interesting answer at Best way to find the intersection of multiple sets? so that my code solution was:

s1, s2, s3 = set("abc"), set("cde"), set("acf")
list_of_sets = [s1, s2, s3]
result = set.intersection(*list_of_sets)   # {'c'}

I was surprised to see .intersection() called on set the class rather than on a set object.

My question is where can I see documentation for this?

The above linked StackOverflow answer says

Note that set.intersection is not a static method, but this uses the functional notation to apply intersection of the first set with the rest of the list. So if the argument list is empty this will fail.

but the official docs do not seem to describe this: intersection(*others). Searching "python set functional notation" doesn't seem to help either. The official docs describe what I was expecting, which was to use .intersection() on a set object, e.g.

result = s1.intersection(*[s2, s3])

I'm also interested to know what other methods from other data structures can be used in this same way.

2 Answers

I think it's just an (undocumented) artifact from the way set.itersection(*others) is implemented and therefore should apply to any other classes with methods that support a variable number of other objects.

To illustrate and prove my point, here's a custom class that supports this so-called "functional notation" via its own intersection() method.

class Class:
    def __init__(self, value=''):
        self.value = str(value)
        self.type = type(value)
        pass

    def __repr__(self):
        return f'Class({self.type(self.value)!r})'

    def intersection(self, *others):
        result = self
        for other in others:
            result &= other  # Perform via __and__() method.
        return result

    def __and__(self, other):
        return type(self)('|'.join((self.value, other.value)))


c1 = Class('foo')
c2 = Class('bar')
c3 = Class(42)
print(f'c1: {c1}')
print(f'c2: {c2}')
print(f'c3: {c3}')

# Both produce the same result.
print(c1 & c2 & c3)
print(Class.intersection(c1, c2, c3))

Output

c1: Class('foo')
c2: Class('bar')
c3: Class(42)
Class('foo|bar|42')
Class('foo|bar|42')

In addition to martineau answer, a method called through a class instance like obj1.method1(...) can also be called with obj1_class.method1(obj1, ...), and in the C implementation code I only see the assumption of the first argument to be a set. That is what happens too when you unpack others, this being a list of sets.

Maybe it is undocumented in the method help, but should be expected anyway.

So I also agree with

and therefore should apply to any other classes with methods that support a variable number of other objects.

Related