如何通过自定义行为将模块的属性添加到类中

时间:2018-12-27 21:22:29

标签: python setattr

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

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

1 个答案:

答案 0 :(得分:0)

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

还请注意,Python中的重新加载模块有很多警告,更多的是在Python shell中玩,而不是在生产代码中。

根据您在评论中所说的内容,我对您的代码做了一些更改。我使用importlib是因为不推荐使用模块imp。还要注意,“猴子补丁”(这就是所谓的这种技术,您想在代码中创建运行时补丁)始终与目标代码紧密结合。有了更改,您的补丁代码很容易破解。

我写了两个文件module.pytest_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