Reputation: 58
Using __get__
descriptor I would like to achieve something like that:
class Wrapper:
def __init__(self, wrapped_value):
self._wrapped_value = wrapped_value
def __get__(self, instance, owner):
return self._wrapped_value
wrapper = Wrapper('foo')
assert type(wrapper) == type('foo')
It turned out, that __get__
descriptor gets called only if Wrapper
instance is a class attribute of some other class, it does not get called when Wrapper
instance is a standalone object (not bound to any class attribute).
Is there a way to make __get__
descriptor work in non-class attributes?
The main goal is to implement wrapper, which when used acts like value it wraps (I know it doesn't sound useful at first glance but there are some use cases in which this would be helpful). So maybe there is other way to achieve this without using __get__
descriptor?
Upvotes: 1
Views: 590
Reputation: 55619
If you want your Wrapper class to control access to the wrapped classes attributes you can use the __getattr__
magic method.
Say we have a class Foo
:
class Foo(object):
def bar(self):
return 'bar'
def baz(self):
return 'baz'
Let's say that we are interacting with some code that we do not control that requires the output of Foo.bar()
to be upper case, and we don't want to explicitly call .upper()
in our code.
We can create a wrapper class that intercepts calls to Foo.bar()
, but transparently allows access to Foo
's other methods (this is essentially the Adapter pattern).
class Wrapper(object):
def __init__(self, wrapped):
self._wrapped = wrapped
def __getattr__(self, name):
# If 'name' isn't an attribute of this class,
# get it from the wrapped instance.
return getattr(self._wrapped, name)
def bar(self):
# Intercept calls to the wrapped instance's bar() method.
return self._wrapped.bar().upper()
>>> wrapper = Wrapper(Foo())
>>> print wrapper.baz()
baz
>>> print wrapper.bar()
BAR
This Wrapper class would not be reported as a Foo
by type
or isinstance
checks, but otherwise it can be used in place of Foo
as long as the calling code relies on duck-typing rather than (unpythonic) explicit type-checking.
Intercepting magic methods such as __str__
has to be done explicitly. This is because these methods are always looked up directly on an instance's class, so __getattr__
and __getattribute__
are bypassed.
So to override Foo.__str__
you would need to do:
class Foo(object):
...
def __str__(self):
return 'I am a Foo'
class Wrapper(object):
...
def __str__(self):
return str(self._wrapped)
>>> wrapper = Wrapper(Foo())
>>> print wrapper
I am a Foo
Upvotes: 2