Python “最简单的方法”;商店「;类上的函数而不绑定它?

Python “最简单的方法”;商店「;类上的函数而不绑定它?,python,metaprogramming,Python,Metaprogramming,我正在编写一些支持代码,以加快通过RequestFactory调用Django视图(在别处声明的函数)。我将大多数测试属性直接存储在类上,而不是它们的实例上 我必须做的一件事是在类中存储我感兴趣的函数,以便稍后调用它(使用inspect为其提供正确的参数) 以下是我的总体意图: def to_test(var1, var2, var3): "this function has nothing to do with MyTest" assert isinstance(var1, b

我正在编写一些支持代码,以加快通过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,但它们要求您记住每次都要复制和粘贴小的
\u
声明。没什么大不了的,只是哈奇

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:{}

您可以在
staticmethod
中包装函数:

class FunctionStore1(object):
    "this fails, expecting an instance"
    func_to_check = staticmethod(regular_function)

函数可以通过三种不同的方式归属于类:

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 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.\uu\u name\uu
作为类属性。或者,您可以允许将多个函数传递给decorator(甚至可以委托确定参数)。

是否需要将函数存储在类而不是实例上?另外,这只是Python 2中的一个问题,因为在Python 3中,未绑定方法不再要求第一个参数是类的实例。它存储测试属性,如要调用的数据库、登录凭据、url和要调用的函数。这些属性对于类来说是固定的,在实例上设置它们会起作用,但也会引入样板代码。这是Python2,尽管print()s非常棒!这正是我想要的。另外,我记得一位Python“大人物”写道,他没有看到staticmethod的用例。那会给他看;-)@JLPeyret:没有看到用例的原因是,如果它确实是一个静态方法(调用时,没有对类或实例的引用),那么它也可以不附加到类。我熟悉在类创建之后在类之上声明东西。然而,为了易读性,我所追求的解决方案需要包含在类的主体中——它毕竟是unittest测试的函数,所以它是最重要的一位——而不是在它之后。
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',)