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