2016-04-28 21 views
3

テストデコレータを作成しようとすると、私は奇妙なインスタンスが見つかりました。このようなコード:pythonのパラメータのデフォルト値はargsかkwargsで受け付けられますか?

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') 

返された値は、関数内のパラメータZ追加のデフォルト値を意味するではなく24です。は、内部の装飾機能では受け入れられません。どうして?

+0

'print add(1,1、 'teststring')'は** 12 **を返します!それは正しいです!あなたは** 14 **を受け取りましたか?なぜあなたは24を期待していますか? – EbraHim

+0

@EbraHim私のコンピュータ(Python 2.7、Ubuntu Kylin)で14を返します。あなたはPython 3を使用していますか? –

+0

申し訳ありませんが、私のせいです。それは14を返します。 – EbraHim

答えて

3

これはトリッキーですが、あなたは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 

の一部を使用してunpassedデフォルト引数を取得し、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が言ったように、これは重複した質問です。デコレータの内側のワーパは、ワーピングされた機能にアクセスできません。バンザにもありがとう! –

+0

@HenryJohnしかしデコレータの内側ラッパーは*私は実証したようにアクセスします。私のコードはあなたが望むことをします。 –

2

この理由は、デコレータ関数がinnを探すのではなくラッパー関数の属性(この場合はデフォルトのkwargs)です。それでもやりたいのなら、辞書で遊ぶだけです。

その@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)) 

結果に従うためので、何getfullargspec方法がないためのPython 2では動作しません:

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

Python 2ではprintステートメントでかっこを使うことができます(そして、そうする必要があります)ので、コードは両方で動作します。小規模の辞書の場合は、両方で '.items() 'を使うだけでよく、それは2に、3には怠惰になります。大きな辞書の場合は、通常、' iteritems'を 'six'ライブラリからインポートします。 – Paul

+1

'getfullargspec'はPython 2には存在しません。 –

+0

ありがとうございます、それではPython 3の解決策です。@Nolen Royalty –

関連する問題