Currently, the `in` operator (also known as `__contains__`) always uses the rightmost argument's implementation.
For example,
status = obj in "xylophone"
Is similar to:
status = "xylophone".__contains__(
obj
)
The current implementation of
`__contains__`
is similar to the way that `+` used to only look to the leftmost argument for implementation.
total = 4 + obj
total = int.__add__(4, obj)
However, these days, `__radd__` gives us the following:
try:
total = type(4).__add__(4, obj)
except NotImplementedError:
total = type(obj).__radd__(obj, 4)
We propose something similar for `__contains__`: That a new dunder/magic method `__lcontains__` be created and that the `in` operator be implemented similarly to the following:
# IMPLEMENTATION OF
# status = obj in "xylophone"`
try:
status =
"xylophone".__contains__(obj)
except NotImplementedError:
status = False
if not status:
try:
status = obj.__lcontains__(“xylophone”)
except AttributeError:
# type(obj) does not have an `__lcontains__` method
with io.StringIO() as string_stream:
print(
"unsupported operand type(s) for `in`:",
repr(type(4).__name__),
"and",
repr(type(obj).__name__),
file=string_stream
)
msg = string_stream.getvalue()
raise TypeError(msg) from None
The proposed enhancement would be backwards compatible except in the event that a user already wrote a class having an `__lcontains__` method.
With our running example of the string “xylophone”, writers of user-defined classes would be able to decide whether their objects are elements of
“xylophone” or not. Programmer would do this by writing an `__lcontains__` method.
As an example application, one might develope a tree in which each node represents a string (the strings being unique within the tree). A property of the tree might be that node `n` is a descendant of node `m` if and only if `n` is a sub-string of `m`. For example the string "yell" is a descendant of "yellow." We might want the root node of the tree to be a special object, `root` such that every string is in `root` and that `root` is in no string. That is, the code `root in "yellow"` should return `False`. If `
__lcontains__
` were implemented, then we could implement the node as follows:
class RootNode(Node):
def
__contains__(container, element):
return True
def
__lcontains__(element, container):
return False