#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:
Существует несколько вариантов использования рекурсивных прокси-серверов с различными компромиссами:
- Переопределить класс
Вероятно, это самый лучший и простой в реализации вариант. Создайте декоратор, который заменяет класс прокси-классом. Вы можете сделать прокси немного более интеллектуальным, чтобы класс вел себя как обычно, пока на экземпляре не будет установлен флаг поведения прокси.
Например:
@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>
- Измените экземпляр
Вы можете перехватить большинство обращений к атрибутам для объектов, переопределив
__getattribute__
Простым методом было бы создать
imprint
метод для вашего прокси-сервера, который удаляет все другие методы и разрешает доступ к базовому объекту только через прокси-методы.Примечание:
__
базовые методы должны быть переопределены отдельно (они не перехватываются__getattribute__
).Tuples
, объекты с__slots__
и структуры данных (List
,Dict
,Set
, и т.д.) все оптимизированы и__getattribute__
там тоже не будут работатьБолее сложным методом было бы пометить объект как прокси-сервер и позаботиться о проксировании, переопределив
Object
,Tuple
,List
,Dict
, и т.д.Set
чтобы проверить наличие флага. Вы можете использоватьforbiddenfruit
модуль для переопределения встроенных методов. Будьте особенно осторожны при выполнении этого. Это очень нетривиально и повлияет на производительность всего вашего приложения. Это все равно может стоить того, если вы собираетесь проксировать почти все (анализ программ / трассировка / AOP framework и т. Д.) - Используйте глобальный кэш
Вероятно, это самый экономичный вариант, как указывает Мбарси. Однако, если вы хотите внедрить прокси-сервер в другой код, вам все равно нужно будет получить другой код, используя объект прокси.