我正在编写一些支持代码,以加快通过RequestFactory调用Django视图(在其他地方声明的函数)。我将大多数测试属性直接存储在类中,而不是存储在它们的实例上。
我要做的一件事就是在课堂上存储我感兴趣的功能,这样我以后就可以调用它(使用 inspect 来为它提供正确的参数)。
这是我的一般意图:
def to_test(var1, var2, var3):
"this function has nothing to do with MyTest"
assert isinstance(var1, basestring), "not an instance"
class MyTest(unittest.TestCase):
#only using this attribute to store the function to
#test, not to bind that function
func_under_test = to_test
def test_with_abc(self):
self.func_under_test("a","b", "c")
def test_with_def(self):
self.func_under_test("d","e", "f")
但是只要我将一个函数分配给一个类,它就会绑定到该类。
99%的时间都很棒。只是不在这里,因为它在调用时会得到错误的参数。相反,在每个类上我都重新声明某些东西,以便我可以将函数分配给它,而不是直接在类上。即使是元类也无济于事。
一些示例代码
我想要的是FunctionStore1 / 2的语法。我最接近的是FunctionStore3 / 4/6,但它们要求你记住copy&每次粘贴小_
声明。没什么大不了的,只是hacky。
def regular_function(*args, **kwds):
print (" regular_function(%s)" % (locals()))
def regular_function2(*args, **kwds):
print (" regular_function2(%s)" % (locals()))
class FunctionStore1(object):
"this fails, expecting an instance"
func_to_check = regular_function
class FunctionStore2(object):
"ditto"
func_to_check = regular_function2
class FunctionStore3Works(object):
"this works"
def _(): pass
_.func_to_check = regular_function
class FunctionStore4Works(object):
"""this too, but I have to redeclare the `_` each time
can I use MetaClass?
"""
def _(): pass
_.func_to_check = regular_function2
class BaseTsupporter(object):
"this doesnt help..."
def _(): pass
class FunctionStore5(BaseTsupporter):
"because there is no `_` here"
try:
_.func_to_check = regular_function
except Exception, e:
print ("\nno `_` on FunctionStore5:e:%s" % (e))
class FunctionStore6Works(object):
"trying a dict"
_ = dict(func_to_check=regular_function)
class MyMeta(type):
def __new__(meta, name, bases, dct):
res = super(MyMeta, meta).__new__(meta, name, bases, dct)
#this works...
res._ = dict()
return res
def __init__(cls, name, bases, dct):
super(MyMeta, cls).__init__(name, bases, dct)
try:
class FunctionStore7Meta(object):
"using meta"
__metaclass__ = MyMeta
try:
_.update(func_to_check=regular_function)
except Exception, e:
print ("\nno `_` dict on FunctionStore7:e:%s" % (e))
except Exception, e:
print ("\nno luck creating FunctionStore7 class :( exception:\n %s" % (e))
#never mind the locals() + globals() hack, that's because this code is actually in a function to
#allow SO's indenting...
li_to_call = [(k,v) for k, v in (locals().items() + globals().items()) if k.startswith("FunctionStore")]
li_to_call.sort()
for name, cls_ in li_to_call:
print ("\n calling %s" % (name))
try:
if getattr(cls_, "func_to_check", None):
cls_.func_to_check(name)
elif hasattr(cls_, "_") and hasattr(cls_._, "func_to_check"):
cls_._.func_to_check(name)
elif hasattr(cls_, "_") and isinstance(cls_._, dict) and cls_._.get("func_to_check"):
cls_._["func_to_check"](name)
else:
print (" %s: no func_to_check" % (name))
if "Meta" in name:
print(" even if %s does have a `_`, now:%s" % (name, cls_._))
except Exception, e:
print (" %s: exception:%s" % (name, e))
输出:
no `_` on FunctionStore5:e:name '_' is not defined
no `_` dict on FunctionStore7:e:name '_' is not defined
calling FunctionStore1
FunctionStore1: exception:unbound method regular_function() must be called with FunctionStore1 instance as first argument (got str instance instead)
calling FunctionStore2
FunctionStore2: exception:unbound method regular_function2() must be called with FunctionStore2 instance as first argument (got str instance instead)
calling FunctionStore3Works
regular_function({'args': ('FunctionStore3Works',), 'kwds': {}})
calling FunctionStore4Works
regular_function2({'args': ('FunctionStore4Works',), 'kwds': {}})
calling FunctionStore5
FunctionStore5: no func_to_check
calling FunctionStore6Works
regular_function({'args': ('FunctionStore6Works',), 'kwds': {}})
calling FunctionStore7Meta
FunctionStore7Meta: no func_to_check
even if FunctionStore7Meta does have a `_`, now:{}
答案 0 :(得分:3)
您可以将功能包装在staticmethod
:
class FunctionStore1(object):
"this fails, expecting an instance"
func_to_check = staticmethod(regular_function)
答案 1 :(得分:1)
函数可以通过三种不同的方式属于一个类:
def _instance_method(self, *args):
print('self:', self)
print('instance_method args:', args, '\n')
def _class_method(cls, *args):
print('cls:', cls)
print('class_method args:', args, '\n')
def _static_method(*args):
print('static_method args:', args, '\n')
class TestClass:
instance_method = _instance_method
class_method = classmethod(_class_method)
static_method = staticmethod(_static_method)
实例方法隐式传递对实例的引用。类方法隐式传递对类的引用。静态方法不会传递给实例或类。以下用法仅作为示例提供:
tester = TestClass()
tester.instance_method('args')
# self: <__main__.TestClass object at 0x0000000002232390>
# instance_method args: ('args',)
tester.class_method('args')
# cls: <class '__main__.TestClass'>
# class_method args: ('args',)
tester.static_method('args')
# static_method args: ('args',)
请注意,当在类本身中定义函数时,通常使用装饰器实现相同的结构。
class TestClass:
def instance_method(self, *args): pass
@classmethod
def class_method(cls, *args): pass
@staticmethod
def static_method(*args): pass
请注意,这只是一种控制隐式传递给函数的参数的机制。这与您的情况相关,因为您有一个独立于类定义定义的函数,因此将实例或类传递给函数是没有意义的。
还应该注意,在类定义完成后,可以直接将该函数分配给类。
class FunctionStore1: pass
FunctionStore1.func_to_check = func_to_check
我个人认为这可能是您案件的最佳模式。它清楚地表明您将有问题的函数作为成员数据附加到类中。这种模式也允许舒适的“staticmethod
”视角没有用例。
这也可以使用装饰器来完成。
def bind_function(func):
def wrapper(cls):
setattr(cls, func.__name__, func)
return cls
return wrapper
def func_to_check(*args):
print('args:', args, '\n')
@bind_function(func_to_check)
class TestClass: pass
TestClass.func_to_check('args')
# args: ('args',)
这是一个很好的模式,因为你可以在类定义之前声明要绑定的函数,而不是在它很容易被遗漏之后。如果你想改变它,它还提供了很大的灵活性。例如,您可以这样做,以便不使用func.__name__
作为类属性动态使用,而是可以使用静态名称。或者,您可以允许将多个函数传递给装饰器(甚至可以委托确定参数)。