Рекурсивный прокси-сервер Python

#python #object #proxy

#python #объект #прокси

Вопрос:

Надеюсь, это должно объяснить, чего я пытаюсь достичь

 class A(object):
    def hello(self):
        print "hello"

a = A()
a.b = A()
a.b.c = A()
a.b.d = []
a.b.d.append(A())


p = Proxy(a)

print p.b
"b accessed"
<__main__.A object at 0xb73f3bec>

print p.b.c
"b accessed"
"c accessed"
<__main__.A object at 0xb73f3dec>

print p.b.d
"b accessed"
"d accessed"
[<__main__.A object at 0xb73f3bcc>]

print p.b.d[0]
"b accessed"
"d accessed"
<__main__.A object at 0xb73e766c>

print p.b.d[0].hello()
"b accessed"
"d accessed"
"hello called"
"hello"
 

По сути, насколько я понимаю, структура продолжает возвращать прокси-объекты. Было бы лучше, если бы он создавал эти прокси-объекты только при первом обращении к атрибуту и сохранял его в «родительском». Цель этого состоит в том, чтобы я мог выполнить некоторую логику непосредственно перед вызовом любого метода для (вложенного) объекта

Я посмотрел на: http://code.activestate.com/recipes/496741-object-proxying / Но он выполняет только первый слой, и я не мог понять, как манипулировать им, чтобы он работал так, как мне нужно.

Ответ №1:

используйте глобальный кэш и создайте фабричный метод прокси, который сначала проверяет глобальный кэш перед созданием новых прокси.

Обратите внимание, что это решение не хранит дочерние прокси-объекты в «родительском». вместо этого он использует id(obj) трюк для извлечения объекта из кэша, независимо от того, насколько он глубок.

 NOT_FOUND = object()

# global proxy cache
_id2obj_dict = {}

def remember(oid, proxy):
    _id2obj_dict[oid] = proxy
    return oid

def id2obj(oid):
    print("accessing cahed object")
    return _id2obj_dict[oid]

class A(object):
    def hello(self):
        print "hello"

class Proxy(object):
    @classmethod
    def create(cls, obj):
        ''' object factory '''
        oid = id(obj)
        if oid in _id2obj_dict:
            return id2obj(oid)
        proxy = cls()
        proxy.obj = obj
        remember(oid, proxy)
        return proxy
    def __getattr__(self, attr):
        prop = getattr(self.obj, attr, NOT_FOUND)
        if prop is NOT_FOUND:
            raise AttributeError()
        print("{} accessed".format(attr))
        proxy = Proxy.create(prop)
        return proxy
    def __getitem__(self, index):
        prop = self.obj.__getitem__(index)
        proxy = Proxy.create(prop)
        return proxy
    def __call__(self):
        print("{} called".format(self.obj.__name__))
        return self.obj()



a = A()
a.b = A()
a.b.c = A()
a.b.d = []
a.b.d.append(A())


p = Proxy.create(a)
 

Ответ №2:

Существует несколько вариантов использования рекурсивных прокси-серверов с различными компромиссами:

  1. Переопределить класс

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

    Например:

     @ProxyWhenFlag('__turn_on_proxy__')
    class A:
        def hello(self):
            print "hello"
    
    a = A()
    a.b = A()
    a.b.c = A()
    a.b.d = []
    a.b.d.append(A())
     

    Использование:

     >>> a.b
    <__main__.A object at 0xb73f3bec>
    >>> a.__turn_on_proxy__ = True
    >>> a.b
    "b accessed"
    <__main__.A object at 0xb73f3bec>
     
  2. Измените экземпляр

    Вы можете перехватить большинство обращений к атрибутам для объектов, переопределив __getattribute__

    Простым методом было бы создать imprint метод для вашего прокси-сервера, который удаляет все другие методы и разрешает доступ к базовому объекту только через прокси-методы.

    Примечание: __ базовые методы должны быть переопределены отдельно (они не перехватываются __getattribute__ ). Tuples , объекты с __slots__ и структуры данных ( List , Dict , Set , и т.д.) все оптимизированы и __getattribute__ там тоже не будут работать

    Более сложным методом было бы пометить объект как прокси-сервер и позаботиться о проксировании, переопределив Object , Tuple , List , Dict , и т.д. Set чтобы проверить наличие флага. Вы можете использовать forbiddenfruit модуль для переопределения встроенных методов. Будьте особенно осторожны при выполнении этого. Это очень нетривиально и повлияет на производительность всего вашего приложения. Это все равно может стоить того, если вы собираетесь проксировать почти все (анализ программ / трассировка / AOP framework и т. Д.)

  3. Используйте глобальный кэш

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