Использование __get__-дескриптора Python для не классовых атрибутов
С помощью __get__
дескриптор я хотел бы добиться чего-то вроде этого:
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')
Оказалось, что __get__
дескриптор вызывается только если Wrapper
Экземпляр является атрибутом класса другого класса, он не вызывается, когда Wrapper
Экземпляр - это отдельный объект (не привязанный ни к какому атрибуту класса).
Есть ли способ сделать __get__
работа дескриптора в неклассовых атрибутах?
Основная цель - реализовать обертку, которая при использовании действует как значение, которое она оборачивает (я знаю, что на первый взгляд это не звучит полезно, но есть некоторые варианты использования, в которых это было бы полезно). Так что, возможно, есть другой способ достичь этого без использования __get__
дескриптор?
1 ответ
Если вы хотите, чтобы ваш класс Wrapper контролировал доступ к атрибутам упакованных классов, вы можете использовать __getattr__
магический метод.
Скажем, у нас есть класс Foo
:
class Foo(object):
def bar(self):
return 'bar'
def baz(self):
return 'baz'
Допустим, мы взаимодействуем с некоторым кодом, который мы не контролируем и который требует вывода Foo.bar()
в верхнем регистре, и мы не хотим явно вызывать .upper()
в нашем коде.
Мы можем создать класс-оболочку, который перехватывает вызовы Foo.bar()
, но прозрачно разрешает доступ к Foo
другие методы (это по сути шаблон адаптера).
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
Этот класс Wrapper не будет сообщаться как Foo
от type
или же isinstance
проверяет, но в противном случае его можно использовать вместо Foo
до тех пор, пока вызывающий код полагается на типизацию утилит, а не на (непитонную) явную проверку типов.
Перехватывать магические методы, такие как __str__
должно быть сделано явно. Это потому, что эти методы всегда ищутся непосредственно в классе экземпляра, поэтому __getattr__
а также __getattribute__
обойдены.
Так что переопределить Foo.__str__
вам нужно будет сделать:
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