2016-03-03 78 views
2

我正在写一些支持代码来加快通过RequestFactory调用Django视图(在其他地方声明的函数)。我将大多数测试属性直接存储在类上,而不是其实例上。最简单的方法来“存储”一个类没有绑定它的功能?

我需要做的一件事是在课堂上存储我感兴趣的功能,以便稍后可以调用它(使用检查为其提供正确的参数)。

这里是我的总的意图:

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,但它们要求您记住每次粘贴_声明时复制&。没什么大不了,只是哈克。

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

是否有原因需要将该函数存储在类中而不是实例上?另外,这只是Python 2中的一个问题,因为在Python 3中,未绑定的方法不再需要第一个参数作为类的一个实例。 – Blckknght

+0

嗯,因为这个类有点像在Django中看到的所有* Meta *和* Model *类。它存储测试属性,如数据库调用,登录凭据,URL和函数调用。这些属性对于类是固定的,并且在实例上设置它们会起作用,但也只是引入样板代码。这是Python 2,尽管print()。 –

回答

2

你可以用功能在staticmethod

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

太棒了!正是我在找的东西。另外,我还记得Python中的一个“大哥们”,他没有看到** staticmethod **的用例。那会告诉他;-)。 –

+1

@JLPeyret:没有看到用例的原因是,如果它确实是一个静态方法(当被调用时,没有对类或实例的引用),它可能不会被附加到类中。 – ShadowRanger

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__作为类属性,你可以使用静态名称。或者,您可以允许将多个函数传递给装饰器(甚至可能委托确定参数)。

+0

我很熟悉在*创建后在类*之上声明东西。然而,为了易读性,我之后需要将解决方案包含在课程的内部 - 毕竟这是由unittest测试的功能,因此它是最重要的一点 - 不是在它之后。 –

相关问题