2016-04-28 48 views
3

當我嘗試製作測試裝飾器時,發現了一個奇怪的實例。像這樣的代碼:是由args或kwargs接受的python參數的默認值嗎?

def doublefunc(fn): 
    def warpped(*arg, **kwargs): 
     arg = (i if type(i) is not int else i*2 for i in arg) 
     for k, v in kwargs.iteritems(): 
      kwargs[k] = v *2 if type(v) is int else v 
     return fn(*arg, **kwargs) 
    return warpped 

@doublefunc 
def add(x, y, m, z=10, v='bbaa'): 
    print 'x=%s, y=%s, m=%s, z=%s, v=%s' % (x, y, m, z, v) 
    return x + y + z 

當我打電話功能是這樣的:

print add(1, 1, 'teststring') 

返回的值是但不是24.這意味着參數ž的功能添加默認值,在裝飾器功能內不接受。爲什麼?

+0

'print add(1,'teststring')'返回** 12 **!這是正確的!你確定你收到** 14 **嗎?你爲什麼期望24? – EbraHim

+0

@EbraHim它在我的電腦上返回14(Python 2.7,Ubuntu Kylin)。你在使用Python 3嗎? –

+0

對不起,我的錯。它返回14. – EbraHim

回答

3

這是棘手的,但你可以得到unpassed默認參數與一些使用inspect模塊的

import inspect 

def get_unpassed_defaults(fn, args, kwargs): 
     args_len = len(args) + len(kwargs) 
     argspec = inspect.getargspec(fn) 

     defaults = argspec.defaults 
     default_names = argspec.args[-len(defaults):] 

     number_of_args = len(argspec.args) 
     number_of_positional_args = number_of_args - len(argspec.defaults) 
     number_of_kwargs = number_of_args - number_of_positional_args 
     number_of_kwargs_passed_as_positional = len(args) - number_of_positional_args 

     default_values = defaults[number_of_kwargs_passed_as_positional:] 
     default_names = default_names[number_of_kwargs_passed_as_positional:] 
     defaults_dict = dict(zip(default_names, default_values)) 

     for kwarg_name in kwargs: 
      if kwarg_name in defaults_dict: 
       defaults_dict.pop(kwarg_name) 

     return defaults_dict 

然後在doublefunc利用這一點:

def doublefunc(fn): 
    def wrapped(*arg, **kwargs): 
     unpassed_defaults = get_unpassed_defaults(fn, arg, kwargs) 
     kwargs.update(unpassed_defaults) 

     arg = (i if type(i) is not int else i*2 for i in arg) 
     for k, v in kwargs.iteritems(): 
      kwargs[k] = v *2 if type(v) is int else v 

     return fn(*arg, **kwargs) 
    return wrapped 

@doublefunc 
def add(x, y, m, z=10, v='bbaa'): 
    print 'x=%s, y=%s, m=%s, z=%s, v=%s' % (x, y, m, z, v) 
    return x + y + z 

print add(1, 1, "teststring", v=3) 

給出:

x=2, y=2, m=teststring, z=20, v=6 
24 
+0

感謝您的幫助,但正如vanza所說,這是一個重複的問題。裝飾者的內部warpper不能訪問warpped函數。感謝vanza! –

+0

@HenryJohn,但裝飾者*的內部包裝* *具有訪問權限,就像我演示的那樣。我的代碼做你想要的。 –

2

原因是裝飾者函數不尋找旅店呃包裝函數的屬性(在這種情況下是默認的kwargs)。如果你仍然想這樣做,那麼你只需要玩字典。

因爲沒有getfullargspec方法,所以對於後面@Nolen版稅的回答

對於Python 3

>>> import inspect 
>>> def doublefunc(fn): 
     def wrapped(*arg, **kwargs): 
      defaults = (inspect.getfullargspec(fn).kwonlydefaults) 
      defaults.update(kwargs) 
      kwargs = defaults 
      arg = (i if type(i) is not int else i*2 for i in arg) 
      for k, v in kwargs.items(): 
       kwargs[k] = v *2 if type(v) is int else v 
      return fn(*arg, **kwargs) 
     return wrapped 

>>> @doublefunc 
def add(x, y, m, *args, z=10, v='bbaa'): 
    print('x={0}, y={1}, m= {2}, z={3}, v={4}'.format(x,y,m,z,v)) 
    return x + y + z 

>>> print(add(1, 1, 'teststring', v=1)) 

結果不會與Python 2的工作:

x=2, y=2, m= teststring, z=20, v=2 
24 
+1

你可以(也應該)在Python 2中用print語句使用圓括號,所以代碼可以同時工作。對於小型字典,你可以在兩者中都使用'.items()',它只會急於2而懶惰在3中。對於大型字典,我通常只從'six'庫導入'iteritems'。 – Paul

+1

'getfullargspec'在Python 2中不存在。 –

+0

謝謝,所以這只是Python 3的解決方案。@Nolen Royalty –

相關問題