代码之家  ›  专栏  ›  技术社区  ›  Ray Salemi

Python中的动态包装器

  •  2
  • Ray Salemi  · 技术社区  · 8 年前

    我希望创建一个动态包装器类,该类使用对象中的数据公开来自所提供对象的API调用。

    静态情况如下:

     class Concrete:
         def __init__(self, data):
           self.data = data
    
         def print_data(self):
             print(self.data)
    
    
     class Wrapper:
         '''
         One day this will wrap a variety of objects. But today
         it can only handle Concrete objects.
         '''
         def wrap_it(self, concrete):
             self.cco = concrete  # concreteobject=cco
    
         def print_data(self):
             self.cco.print_data()
    
    
     cco = Concrete(5)
     wcco = Wrapper()
     wcco.wrap_it(cco)
     wcco.print_data()
    

    生产

     5
    

    我想知道如何做同样的事情 wrap_it 动态它应该搜索具体的对象 查找函数,并创建同名函数 在具体对象中调用相同的函数。

    我想解决方案包括 inspect.signature 或 至少使用了 *args **kwargs ,但我没有看到 一个关于如何将所有这些放在一起的示例。

    2 回复  |  直到 8 年前
        1
  •  3
  •   Ray Salemi    8 年前

    您可以使用 __getattr__ 用于钩住未定义属性并将其转发到具体对象的神奇方法:

    class DynamicWrapper():
        def wrap_it(self, concrete):
            self.cco = concrete
    
        def __getattr__(self, k):
            def wrapper(*args, **kwargs):
                print(f'DynamicWrapper calling {k} with args {args} {kwargs}')
                return getattr(self.cco, k)(*args, **kwargs)
    
            if hasattr(self.cco, k):
                return wrapper
            else:
                raise AttributeError(f'No such field/method: {k}')
    
    cco = Concrete(5)
    dwcco = DynamicWrapper()
    dwcco.wrap_it(cco)
    dwcco.print_data()
    
        2
  •  1
  •   Ray Salemi    8 年前

    使用 dir() 函数获取给定对象的属性,检查它们是否可调用并将其分配给包装器,如下所示:

     class Wrapper:
         def wrap_it(self, objToWrap):
             for attr in dir(objToWrap):
                 if not attr.startswith('__') and callable(getattr(objToWrap, attr)):
                     exec('self.%s = objToWrap.%s' % (attr, attr))
    

    现在,进行测试。

    >>> cco = Concrete(5)
    >>> wcco = Wrapper()
    >>> wcco.wrap_it(cco)
    >>> wcco.print_data()
    5