It is not possible to give custom, "real" methods to individual instances of a class. In the original code, EMPTY.__bool__
is not a method, but an ordinary function. You can see this by trying to invoke it explicitly:
>>> EMPTY = types.SimpleNamespace(__bool__=lambda self: False)
>>> EMPTY.__bool__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'self'
See also Adding a Method to an Existing Object Instance. While it's possible - following the advice there - to make EMPTY.__bool__
behave like a method:
>>> EMPTY = types.SimpleNamespace()
>>> EMPTY.__bool__ = types.MethodType(lambda self: False, EMPTY)
>>> EMPTY.__bool__()
False
that will still be ignored by bool
:
>>> bool(EMPTY)
True
The implementation of bool
looks up __bool__
directly on the class, because it has no reason to expect an instance to have such an attribute.
Instead, we need to have our own class, with an actual method named __bool__
that does the right thing. Thus:
class PossiblyEmptyNamespace(types.SimpleNamespace):
"""A namespace that is falsey when it doesn't contain anything."""
def __bool__(self):
return bool(vars(self))
Now we can test that:
>>> EMPTY = PossiblyEmptyNamespace()
>>> bool(EMPTY)
False
>>> EMPTY.foo = 'bar'
>>> bool(EMPTY) # oops, not actually empty any more.
True