Использование __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
Другие вопросы по тегам