2011-07-05 9 views
5

デコレータについては、関数の特定の名前付きパラメータを操作したいと思います。以下のデコレータを考えてみましょう:Python 2で位置引数をキーワード引数として扱うにはどうすればいいですか?

次の機能に適用さ
def square_param(param): 
    def func_decorator(func): 
     def func_caller(*args,**kwargs): 
      kwargs[param] = kwargs[param] * kwargs[param] 
      return func(*args,**kwargs) 
    return func_caller 
return func_decorator 

@square_param('dividend') 
def quotient(divisor=1,dividend=0): 
    return dividend/divisor 

配当がキーワード引数の例として呼び出された場合、これは動作します:として与えられたとき、しかし

>>> quotient(dividend=2) 
4 

位置の引数は失敗します。

@square_param('dividend') 
def quotient(divisor=1,*,dividend=0): 
    return dividend/divisor 

が、私は、Python 2をサポートしたいと思いますし、また、私は機能上、わずかな制限をつけたいと思います:私は、パラメータを強制することでこの問題を解決できたのPython 3では

>>> quotient(3,4) 
TypeError: quotient() got multiple values for keyword argument 'dividend' 

always given as a keywordされるように。

デコレータでこの動作を修正できる方法はありますか?

+3

のparams機能を得ることができます。それはあなたがこれに必要なものを持っているかもしれません。 getargspecを見てください。 – Dirk

答えて

3

最初に、square_paramデコレータは機能を返さないため、機能しません。それはする必要があります:

def square_param(param): 
    def func_decorator(func): 
     def func_caller(*args,**kwargs): 
      kwargs[param] = kwargs[param] * kwargs[param] 
      return func(*args,**kwargs) 
     return func_caller 
    return func_decorator 

は今、私はディルクのアドバイス@取り、inspectモジュールに見えました。パラメータが関数の位置引数の1つであるかどうか最初にチェックし、その位置引数が指定されている場合は2番目にチェックし、次にその引数位置を変更することで行うことができます。また、パラメータがキーワード引数として指定されている場合は、kwargsのみを変更するようにする必要があります。

import inspect 

def square_param(param): 
    def func_decorator(func): 
     def func_caller(*args,**kwargs): 
      funparams = inspect.getargspec(func).args 
      if param in funparams: 
       i = funparams.index(param) 
       if len(args) > i: 
        args = list(args) # Make mutable 
        args[i] = args[i] * args[i] 
      if param in kwargs: 
       kwargs[param] = kwargs[param] * kwargs[param] 
      return func(*args,**kwargs) 
     return func_caller 
    return func_decorator 
+0

ありがとう、これは私が探していたものです!関数の返却については、実際にはこの例では小さな愚かなエラーでした。 –

2

でも点検して使用しなくても、私たちは検査モジュールを点検

>>> func = lambda x, y, args: (x, y, {}) 
>>> func.func_code.co_argcount 
3 
>>> func.func_code.co_varnames 
('x', 'y', 'args') 
+0

ありがとう! func_codeのメンバーが文書化されているリンクを追加することができますか?私はそれを見つけることができません。 –

+0

あなたは大歓迎です! "func_codeは、コンパイルされた関数本体を表すコードオブジェクトです。" 詳細については、[link](http://docs.python.org/reference/datamodel.html)の[Callable types]セクションを参照してください。 – Yajushi