Подтвердить что ты не робот

Полностью оберните объект в Python

Я хочу полностью обернуть объект, чтобы все запросы атрибутов и методов были перенаправлены на объект, который он обертывал, но также переопределял любые методы или переменные, которые я хочу, а также предоставлял некоторые из моих собственных методов. Этот класс-оболочка должен появиться на 100%, поскольку существующий класс (isinstance должен действовать так, как если бы это был класс), однако подклассы сами по себе не собираются его обрезать, так как я хочу обернуть существующий объект. Есть ли какое-то решение в Python для этого? Я думал что-то вроде:

class ObjectWrapper(BaseClass):
    def __init__(self, baseObject):
        self.baseObject = baseObject

    def overriddenMethod(self):
        ...

    def myOwnMethod1(self):
        ...

    ...

    def __getattr__(self, attr):
        if attr in ['overriddenMethod', 'myOwnMethod1', 'myOwnMethod2', ...]
            # return the requested method
        else:
            return getattr(self.baseObject, attr)

Но я не знаком с переопределением __getattr__, __setattr__ и __hasattr__, поэтому я не уверен, как правильно это сделать.

4b9b3361

Ответ 1

Простейший способ в большинстве случаев, вероятно:

class ObjectWrapper(BaseClass):
    def __init__(self, baseObject):
        self.__class__ = type(baseObject.__class__.__name__,
                              (self.__class__, baseObject.__class__),
                              {})
        self.__dict__ = baseObject.__dict__

    def overriddenMethod(self):
        ...

Работая таким образом, т.е. путем переназначения self __class__ и __dict__ таким образом, вам нужно только предоставить свои переопределения. Механизмы получения и настройки обычного атрибута Python сделают все остальное... в основном.

У вас будут проблемы только в том случае, если baseObject.__class__ определяет __slots__, и в этом случае метод множественного наследования не работает, и вам нужен громоздкий __getattr__ (как говорили другие, по крайней мере, вы не нужно беспокоиться о том, что он будет вызван с атрибутами, которые вы переопределяете, так как это не будет! -), __setattr__ (боль в больнице, так как она вызывается для каждого атрибута) и т.д.; и создание isinstance и специальных методов работы требует кропотливой и громоздкой детальной работы.

По существу, __slots__ означает, что класс является специальным, каждый экземпляр - легким "объектом значения", НЕ подлежащим дальнейшим сложным манипуляциям, обертке и т.д., поскольку необходимо сохранить несколько байтов на экземпляр этого класса переопределяет все обычные проблемы гибкости и т.д.; поэтому неудивительно, что иметь дело с такими экстремальными, редкими классами одним и тем же гладким и гибким способом, так как вы можете иметь дело с 99% + объектов Python, это действительно боль. Поэтому вам нужно иметь дело с __slots__ (до написания, тестирования, отладки и поддержки сотен строк кода только для этих угловых случаев), или будет достаточно 99% -ного решения в полдюжины строк? -)

Следует также отметить, что это может привести к утечке памяти, так как создание подкласса добавляет подкласс в список подклассов базового класса и не удаляется, когда все экземпляры подкласса являются GC'd.

Ответ 2

__getattr__ имеет то преимущество, что он вызывается только тогда, когда атрибут не существует, поэтому вам не нужен явный список - все, что вы не определяете, автоматически проксируется.

__setattr__ сложнее, потому что он всегда вызывал. Убедитесь, что вы используете вызов суперкласса или object.__setattr__ при настройке собственных атрибутов; использование setattr() внутри __setattr__ приведет к бесконечной рекурсии.

Последний бит, затрагивающий isinstance, очень сложно. Вы можете сделать это с привязкой к вашему экземпляру обертки. __class__ variable (но это также отменяет порядок разрешения словаря класса) или путем динамического построения вашего типа-оболочки с использованием метакласса. Поскольку isinstance настолько редко встречается в коде Python, кажется, что он на самом деле пытается обмануть его.

Дополнительная информация о специальных методах доступа к атрибутам.

Еще больше информации о них, а также некоторая помощь с метаклассами.

Ответ 3

Начните с этого и возитесь с материалом в цикле в соответствии с вашими потребностями:

import inspect
class Delegate(object):
    def __init__(self, implementation):
        self.__class__ = implementation.__class__
        for n, m in inspect.getmembers(implementation, callable):
            if not n.startswith('_'):
                setattr(self, n, m)

Возможность обтекания объектов не-нового стиля остается в качестве упражнения для читателя:)

Ответ 4

Следующий подход похож на на Alex Martelli, но более читаем для меня:

Вместо создания нового класса я создаю класс-функцию, которая создает экземпляр базового объекта и перезаписывает необходимые функции следующим образом:

class C1:
    def __init__(self, p):
        self.p = p
    def f(self):
        print("This is C1, p=%s" % self.p)

def C2(p):
    c = C1(p)
    def new_f():
        print("This is C2, p is %s" % c.p)
    c.f = new_f
    def g(param):
        print("This is C2.g(%s), p is %s" % (param, c.p))
    c.g = g
    return c

c1 = C1('abc')
c1.f()

c2 = C2('cde')
c2.f()
c2.g('xyz')

Выход:

This is C1, p=abc

This is C2, p is cde
This is C2.g(xyz), p is cde

Ответ 5

Пожалуйста, посмотрите http://code.activestate.com/recipes/577555-object-wrapper-class/ для полного кода, включая важные комментарии. Это сводится к:

class Wrapper(object):
    def __init__(self, obj):
        self._wrapped_obj = obj
    def __getattr__(self, attr):
        if attr in self.__dict__:
            return getattr(self, attr)
        return getattr(self._wrapped_obj, attr)

Ответ 6

Под "существующим объектом" вы подразумеваете экземпляр другого класса? Звучит для меня так, как будто вам просто нужно наследовать базовый класс. Когда вы создаете новый объект, передаете детали базового объекта или добавляете метод в свой новый класс, который копирует данные экземпляра базового класса в себя.