Использование __get__-дескриптора Python для не классовых атрибутов - PullRequest
0 голосов
/ 28 декабря 2018

Используя дескриптор __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 Ответ

0 голосов
/ 31 декабря 2018

Если вы хотите, чтобы ваш класс 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, если вызывающий код полагается на duck-typing вместо (не пифонической) явной проверки типов.

Перехват магических методов, таких как __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
...