I have a class, which have several methods of its own, not shown here for simplicity:
class Foo:
def __init__(self, arg: str):
self.bar = arg
Let's say, aside from its own methods, I want Foo's instances to use str's methods in its bar property's (assured to be a string) stead. This is possible with the __getattr__ dunder method:
class Foo:
def __getattr__(self, item):
return getattr(self.foo, item)
The call's result should be .bar's new value. However, since Python strings are immutable, a string resulted from a method call (say, str.strip()) will need to be re-assigned, which doesn't look very nice. Also, an if is needed in case that call doesn't return a string:
result = instance_of_Foo.strip()
if isinstance(result, str):
instance_of_Foo.bar = result
else:
...
I solved this problem with a decorator:
def decorator(function, *, self):
def wrapper(*args, **kwargs):
result = function(*args, **kwargs)
if isinstance(result, str):
self.bar = result
else:
return result
return wrapper
class Foo:
def __init__(self, arg: str):
self.bar = arg
def __getattr__(self, item):
method = decorator(getattr(self.bar, item), self = self)
return method
foo = Foo(' foo ')
print(foo.bar) # ' foo '
foo.strip()
print(foo.bar) # 'foo'
...but there surely is a more "Pythonic" way, preferably using dunder methods instead of a decorator, to intercept the call, isn't there? Note that my class cannot substitute a string (Liskov principle violation), so inheritance is out of the question.
To answer my own question:
You (or I) can use a wrapper and cache the
__getattr__dunder method. However, chepner's answer should be preferred as it can handle an arbitrary given function and is better designed.Try it: