3

我正在尝试将一个函数分配给另一个函数,分配的左侧可以作为字符串提供给我。例如,我正在寻找的方法的主体是

def change_function_defintion(name_of_function = 'module1.function1'
       , function_object):
    # Can I do eval(name_of_function) = function_object ? will that work?
    pass

问题:

  1. 我如何实现这一目标?显然,如果我调用上述方法,然后调用 module.function1,我希望新函数能够被拾取。
  2. 我在单元测试的上下文中这样做,即模拟几个函数,运行测试然后基本上“取消模拟”它们。上述方法有什么问题吗?
4

6 回答 6

5

我认为使用像Mock这样的模拟库会更好。使用patch您可以在上下文管理器或函数的范围内更改函数的行为,然后让它恢复正常。例如:

from mock import patch

with patch('module1.function1') as function1:
    function1.side_effect = function_object
    # Do stuff

如果function1在块内被调用with,它将被替换为function_object.

同样,在函数内进行修补:

@patch('module1.function1')
def my_test(function1):
    function1.side_effect = function_object
    # Do stuff
于 2013-05-09T10:36:39.143 回答
1

我的做法:

import importlib

def change_function_defintion(name_of_function = 'module1.function1'
   , function_object):
   my_mod = importlib.import_module('module1')
   setattr(my_mod, function1, function_object)

现在更长的咆哮:

如果 module1 已经导入到 local namespace ,那么这种方法可能会奏效,例如,您可以执行以下操作:

>>> a = eval('str')
>>> a
<type 'str'>
>>> a(123)
'123'

在模拟单元测试的上下文中,可能有更好的方法。

您可以在此处查看:http: //pycheesecake.org/wiki/PythonTestingToolsTaxonomy#MockTestingTools的一些库,这些库将允许您在单元测试中对模拟对象进行更多控制。

编辑:

您可以执行以下操作来动态导入模块:

>>> import importlib
>>> my_mod = importlib.import_module('mymodule1')

然后,您可以访问模块内的可用函数,或通过 eval/getattr 获取它们:

my_function = getattr(my_mod,'somefunction')

当然,如果你想将该功能换成别的东西:

my_mod.functionName = newFunction
于 2013-05-09T10:19:44.997 回答
1

模拟存在一些问题,如果可能,您可以考虑使用不同的测试方法:

于 2013-05-09T10:38:38.030 回答
1

I think the following will do what you want (but you might want more robust parsing):

def set_named_fun(funname, fun) :
    import sys
    modname, funname =  funname.rsplit('.')
    mod = sys.modules[modname]
    setattr(mod, funname, fun)

The assumptions here are:

  • the module owning the function is already imported, and
  • you need to refer to the target function as a fully qualified name

The two assumptions are slightly in tension. There must be a great many cases where you can simply do:

import legend
legend.monkey = lambda : "great sage, equal of heaven"
于 2013-05-09T10:38:53.103 回答
1

Python函数装饰器

首先,您所说的概念是函数装饰器的概念。函数装饰器通过将函数定义放在函数定义开始之前的行(符号@)来应用于函数定义。它是一种修改函数行为或操作函数组合的工具。这是一个例子

class entryExit(object):

    def __init__(self, f):
        self.f = f

    def __call__(self):
        print "Entering", self.f.__name__
        self.f()
        print "Exited", self.f.__name__

@entryExit # decorator
def func1(): # decorated function
    print "inside func1()"

@entryExit
def func2():
    print "inside func2()"

我跑

func1()
func2()

我明白了

Entering func1
inside func1()
Exited func1
Entering func2
inside func2()
Exited func2

Python unittest.mock.patch()

patch 充当函数装饰器、类装饰器或上下文管理器。在函数体或 with 语句内部,目标被一个新对象修补。当 function/with 语句退出时,补丁被撤消。

Patch 允许您修改with语句中的函数行为。

这是一个示例,其中 patch() 用作带有with语句的上下文管理器。

>>> with patch.object(ProductionClass, 'method', return_value=None) 
  as mock_method:
...     thing = ProductionClass()
...     thing.method(1, 2, 3)
...
>>> mock_method.assert_called_once_with(1, 2, 3)
于 2013-05-09T11:27:56.187 回答
0

可以使用“getattr”通过函数的字符串名称获取函数(一个函数是一个对象)。然后您可以更改名称并在新的命名调用中调用/调用其他东西(原始命名函数)。

于 2013-05-09T10:26:47.520 回答