2016-03-03 37 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測試的功能,因此它是最重要的一點 - 不是在它之後。 –

相關問題