One of Python object methods which don't return the modified object is the .add()
method of Python set()
. This prevents chaining multiple calls to the method:
S = set()
S = S.add('item1').add('item2').add('item3')
giving:
AttributeError:
'NoneType' object has no attribute 'add'
Why I tend to prefer usage of chaining .add()
s over usage of .update()
or union()
or the |
operator?
Because it is a clear self-explaining code which mimics spoken language and therefore best suited for private use by occasional programmers where readability of own code from the time perspective is the main issue to cope with.
A known to me work-around to make above chaining possible is to overwrite set methods. I have coded for this purpose the class chainOfSets. With this class I can write:
S = set()
S = chainOfSets(S).add('item1').add('item2').add('item3').get()
print(S) # gives: {'item1', 'item3', 'item2'}
My question is:
Is there a better approach to allow chaining of object methods which don't return the object they manipulate as using an own class (e.g. chainOfSets, chainOfLists, chainOfPandas, etc)?
Below the chainOfSets class with implemented +
operator:
class chainOfSets:
"""
Allows chaining (by dot syntax) else not chainable set() methods
and addition/subtraction of other sets.
Is doesn't support interaction of objects of this class itself as
this is considered to be out of scope of the purpose for which this
class was created.
"""
def __init__(s, sv=set()):
s.sv = sv
# ---
def add(s, itm):
s.sv.add(itm)
return s
def update(s, *itm):
s.sv.update(itm)
return s
def remove(s, itm): # key error if not in set
s.sv.remove(itm)
return s
def discard(s, itm): # remove if present, but no error if not
s.sv.discard(itm)
return s
def clear(s):
s.sv.clear()
return s
# ---
def intersection(s, p):
s.sv = s.sv.intersection(p)
return s
def union(s, p):
s.sv = s.sv.union(p)
return s
def __add__(s, itm):
if isinstance(itm, set):
s.sv = s.sv.union(itm)
else:
s.sv.update(itm)
return s
def difference(s,p):
s.sv = s.sv.difference(p)
return s
def __sub__(s, itm):
if isinstance(itm, set):
s.sv = s.sv - itm
else:
s.sv.difference(set(itm))
return s
def symmetric_difference(s,p):
# equivalent to: union - intersection
s.sv = s.sv.symmetric_difference(p)
return s
# ---
def len(s):
return len(s.sv)
def isdisjoint(s,p):
return s.sv.isdisjoint(p)
def issubset(s,p):
return s.sv.issubset(p)
def issuperset(s,p):
return s.sv.issuperset(p)
# ---
def get(s):
return s.sv
#:class chainOfSets(set)
print((chainOfSets(set([1,2,3]))+{5,6}-{1}).intersection({1,2,5}).get())
# gives {2,5}