【问题标题】:How to add a module's attributes to a class with custom behavior如何将模块的属性添加到具有自定义行为的类
【发布时间】:2018-12-27 21:22:29
【问题描述】:

我在某个路径中有一个模块。我想创建一个与模块具有相同属性的类(以便可以与模块相同的方式使用它)但在访问属性之前执行一些自定义操作 - 例如重新加载模块。

def get_method_with_extra(method_name, module):
    def method_with_extra(self, *args):
        imp.reload(module)
        func_to_call = getattr(module, method_name)
        func_to_call(*args)

    return method_with_extra

class tester():
    def __init__(self, module_path):
        self.module = imp.load_source('module', module_path)
        method_list = [func for func in dir(self.module) if 
callable(getattr(self.module, func))]
        for method_name in method_list:
            method_with_extra = get_method_with_extra(method_name, 
self.module)
            setattr(type(self), method_name, method_with_extra)

因此,例如,如果模块有一个名为“Parse”的方法,我希望测试器的实例 - tess - 也有它,并且我能够调用 tess.parse() 来重新加载内部模块,然后调用模块的 parse()。相反,我收到此错误:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "<string>", line 4, in __init__
AttributeError: attribute '__call__' of 'namespace#' object is read-only

【问题讨论】:

    标签: python setattr


    【解决方案1】:

    如果允许更改目标模块的源,并且它足够小,我认为这里最干净的解决方案是将其重写为一个类。然后导入类,继承并自定义。

    另外请注意,在 Python 中重新加载模块有很多注意事项,它更适合在 Python shell 中玩,而不是生产代码。

    根据您在评论中所说的,我稍微更改了您的代码。我使用了importlib,因为模块 imp 已被弃用。另请注意,“猴子补丁”(这就是这种技术的名称,您想在代码中制作运行时补丁)始终与目标代码紧密耦合。如果有变化,你的补丁代码很容易被破解。

    我写了两个文件module.py和test_module.py:

    #-----------
    # module.py
    
    a = 100
    b = 200
    
    # returns something
    def sum3(x,y):
        return x + y + 3
    
    # does something
    def print_a_b():
        global a
        print(a,b)
        a = a + 1   # test module reloads. If ok, "a" remains 100
    
    #----------------
    # test_module.py
    
    import module
    import importlib as imp
    
    def get_method_with_extra(method_name, module):
        def method_with_extra(self, *args):
            imp.reload(module) # comment to see that "a" increases
            func_to_call = getattr(module, method_name)
            if args: # function may not have args
                return func_to_call(*args)
            else: # function may not have args
                return func_to_call()
    
        return method_with_extra
    
    class tester():
        def __init__(self, module_path):
            self.module = imp.import_module('module', module_path)
            method_list = [func for func in dir(self.module)
                if callable(getattr(self.module, func))]
            for method_name in method_list:
                #print(method_name)
                method_with_extra = \
                    get_method_with_extra(method_name, self.module)
                setattr(type(self), method_name, method_with_extra)
    
    t = tester('.')
    print(t.sum3(1,2))
    t.print_a_b()
    t.print_a_b() # checking for the reload, "a" should be 100
    

    【讨论】:

    • 我无法触及目标模块,我只想动态创建一个公开模块属性的类,并在使用该类调用它们时,首先重新加载模块,然后调用原始方法。
    • @felisimo 好吧,如果您有其他依赖于客户端的代码,就会发生这种情况。请参阅我编辑的答案。它有效。
    猜你喜欢
    • 1970-01-01
    • 2014-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多