2011-12-15 33 views
0
def ensure_type(parameter_name, the_type, type_converter_fn=None): 
    def decorator(fn): 
     def wrapped(self, *args, **kwargs): 

     if not type_converter_fn: # fails here 
      type_converter_fn = the_type 

     return fn(self, *args, **kwargs) 
     return wrapped 
    return decorator 

當通過wrapped功能parameter_namethe_type封閉變量正確綁定,但type_converter_fn步進不是。無論ensure_type是使用或不使用該可選參數調用,都會發生這種情況,如果使該參數成爲必需參數,也會發生這種情況。建設裝飾用三個參數 - 關閉不使用Python 2.x的工作

爲什麼前兩個參數可以工作,而第三個參數無法分配?

僅供參考,我在此行上發現異常 - if not type_converter_fn表示它在分配前已被引用。

+2

請修復您的縮進。至少最後三行是關閉的。 – 2011-12-15 10:46:53

回答

3

Python中的範圍確定是靜態確定的。函數內的賦值使該函數的局部變量成爲可能。在首次分配之前,您不能訪問局部變量。

if not type_converter_fn: 
    type_converter_fn = the_type 

第二行使得type_converter_fn本地wrapped(),所以在這個代碼段的第一行的訪問產生一個UnboundLocalError。 (順便說一句,如果你能告訴我們你得到了什麼錯誤信息以及在哪一行中,它會容易得多,總是將錯誤信息的完整回溯複製到你的問題中 - 這樣可以節省很多時間。)

+0

非常好 - 謝謝。將來會抓住這個痕跡。 – 2011-12-15 11:31:24

2

你的「行動水平」太複雜了。只要你有權訪問它,就處理這些東西。

由於

if not isinstance(parameter_name, str): 
    raise Exception("parameter_name must be a string") 

if not type_converter_fn: 
    type_converter_fn = the_type 

與 「外」 的參數只涉及,你應該做它。

這同樣適用於

arg_position = list(fn.func_code.co_varnames).index(parameter_name) - 1 

的更深一層。

一般情況下,我會做(未經測試!):

def ensure_type(parameter_name, the_type, type_converter_fn=None): 
    if not isinstance(parameter_name, str): 
     raise Exception("parameter_name must be a string") 

    if not type_converter_fn: 
     type_converter_fn = the_type 

    def decorator(fn): 
     arg_position = list(fn.func_code.co_varnames).index(parameter_name) - 1 #minus once because of self 
     def wrapped(self, *args, **kwargs): 
      if arg_position > -1: 
       the_arg = args[arg_position] 
       if the_arg is not None and not isinstance(the_arg, the_type): 
        all_the_args = list(args) 
        all_the_args[arg_position] = type_converter_fn(the_arg) 
        args = tuple(all_the_args) 
      return fn(self, *args, **kwargs) 
     return wrapped 
    return decorator 

編輯:正如我剛纔看到:它仍然是太複雜了。

只有在需要時才能進行包裝。 if arg_position < 0,你可以提出異常(因爲整個東西是毫無意義的,所以使用.find()而不是.index()),或者你可以返回原始fn

def ensure_type(parameter_name, the_type, type_converter_fn=None): 
    if not isinstance(parameter_name, str): 
     raise Exception("parameter_name must be a string") 
    if not type_converter_fn: 
     type_converter_fn = the_type 

    def decorator(fn): 
     arg_position = list(fn.func_code.co_varnames).index(parameter_name) - 1 #minus once because of self 
     # Either use .find() here, or live with -1 and return the original function then: 
     if arg_position < 0: # only needed with .index() 
      return fn 
     def wrapped(self, *args, **kwargs): 
      the_arg = args[arg_position] 
      if the_arg is not None and not isinstance(the_arg, the_type): 
       all_the_args = list(args) 
       all_the_args[arg_position] = type_converter_fn(the_arg) 
       args = tuple(all_the_args) 
      return fn(self, *args, **kwargs) 
     return wrapped 
    return decorator 
+0

好的建議 - 謝謝:) – 2011-12-15 11:28:41