Python proxy object

I am looking for a way to pass method calls from an object (wrapper) to a member variable of an object (wrappee). There are potentially many methods that need to be externalized, so it would be useful to do this without changing the wrapper interface when adding a method to wrappee.

class Wrapper(object)
  def __init__(self, wrappee):
    self.wrappee = wrappee

  def foo(self):
    return 42

class Wrappee(object):
  def bar(self):
    return 12

o2 = Wrappee()
o1 = Wrapper(o2)

o1.foo() # -> 42
o1.bar() # -> 12
o1.<any new function in Wrappee>() # call directed to this new function 

It would be great if the redirection of this call is "fast" (relative to the direct call, ie does not add too much overhead).

+3
source share
2 answers

A somewhat elegant solution is to create an “attribute proxy” in the wrapper class:

class Wrapper(object):
    def __init__(self, wrappee):
        self.wrappee = wrappee

    def foo(self):
        print 'foo'

    def __getattr__(self, attr):
        return getattr(self.wrappee, attr)


class Wrappee(object):
    def bar(self):
        print 'bar'

o2 = Wrappee()
o1 = Wrapper(o2)

o1.foo()
o1.bar()

__getattr__ Wrapper, Wrapper, , .

, , :

o2.not_valid
Traceback (most recent call last):
  File "so.py", line 26, in <module>
    o2.not_valid
  File "so.py", line 15, in __getattr__
    raise e
AttributeError: 'Wrappee' object has no attribute 'not_valid'
+5

, , monkeypatch :

def __init__(self, wrappee):
    for name, value in inspect.getmembers(wrappee, callable):
        if not hasattr(self, name):
            setattr(self, name, value)

Wrapper , Wrappee. . ?

class WrapperA(object):
    def __init__(self, wrappee):
        self.wrappee = wrappee
        for name, value in inspect.getmembers(wrappee, callable):
            if not hasattr(self, name):
                setattr(self, name, value)

class WrapperB(object):
    def __init__(self, wrappee):
        self.wrappee = wrappee
    def __getattr__(self, name):
        return getattr(self.wrappee, name)

In [1]: %run wrapper
In [2]: o2 = Wrappee()
In [3]: o1a = WrapperA(o2)
In [4]: o1b = WrapperB(o2)
In [5]: %timeit o2.bar()
10000000 loops, best of 3: 154 ns per loop
In [6]: %timeit o1a.bar()
10000000 loops, best of 3: 159 ns per loop
In [7]: %timeit o1b.bar()
1000000 loops, best of 3: 879 ns per loop
In [8]: %timeit o1b.wrapper.bar()
1000000 loops, best of 3: 220 ns per loop

, 3% ( , ...). - , , self.wrapper, 66% . __getattr__ 471% ( ).

, bound-, ?

. 471% - 700 . ? , , - .

. " ". , dict. o2, , , o1 ( - ...). , . Python 2.x 3.x( 2.x 3.x, inspect), __getattr__ 2.3 ( Python). .

, : __getattr__, . : -, , ; , .

+5

Source: https://habr.com/ru/post/1686223/


All Articles