2009-09-11 4 views
18

に渡されたキーワード引数を取得する:私は明示的なキーワード引数にPythonのメソッドの夢を見ています実際にPythonの方法

def func(a=None, b=None, c=None): 
    for arg, val in magic_arg_dict.items(): # Where do I get the magic? 
     print '%s: %s' % (arg, val) 

私が実際に渡されただけでこれらの引数、呼び出し元の辞書を取得したいですメソッドと同じように、**kwargsと同じですが、私は、呼び出し側が、**kwargsとは異なり、古いランダムな引数を渡すことはできません。

>>> func(b=2) 
b: 2 
>>> func(a=3, c=5) 
a: 3 
c: 5 

So:そのような呪文はありますか?私の場合は、それぞれの引数をデフォルトと比較して異なるものを見つけることができますが、9つの引数があるときには、これは控えめで、面倒です。

>>> func(a=None) 
a: None 

Tricksy:ボーナスポイントの場合、呼び出し側がキーワード引数に渡しても、そのデフォルト値が割り当てられて私に言うことができる呪文を提供!

編集:(字句)関数の署名はそのまま残す必要があります。これは公開APIの一部です。明示的なキーワード引数の主な価値は、ドキュメンタリーの価値にあります。物事を面白くするために。 :)

+0

この質問のタイトルは非常に似ていますが、重複しているかどうかはわかりません:http://stackoverflow.com/questions/196960/can-you-list-the-keyword-arguments-a-python -function-received –

+0

@AndersonGreen。あなたが言及する質問は、この問題とは関係ありません。 **表記法をすべてのキーワードを受け入れないメソッドに渡すときに辞書をフィルタリングする方法について質問します。 –

+0

これは大きな質問です。 –

答えて

23

私は失われた理論のデコレータの良さに触発され、ビットのためにそれについてと遊んだ後、この思い付いた:私は幸せです

 
I'm only passing a 
    a: a 
Here's b and c 
    b: True 
    c: c 
All defaults 
    a: None 
    b: False 
    c: 
    d: 0 
Nothin' 
Invalid kwarg 
    func() got an unexpected keyword argument 'e' 

def actual_kwargs(): 
    """ 
    Decorator that provides the wrapped function with an attribute 'actual_kwargs' 
    containing just those keyword arguments actually passed in to the function. 
    """ 
    def decorator(function): 
     def inner(*args, **kwargs): 
      inner.actual_kwargs = kwargs 
      return function(*args, **kwargs) 
     return inner 
    return decorator 


if __name__ == "__main__": 

    @actual_kwargs() 
    def func(msg, a=None, b=False, c='', d=0): 
     print msg 
     for arg, val in sorted(func.actual_kwargs.iteritems()): 
      print ' %s: %s' % (arg, val) 

    func("I'm only passing a", a='a') 
    func("Here's b and c", b=True, c='c') 
    func("All defaults", a=None, b=False, c='', d=0) 
    func("Nothin'") 
    try: 
     func("Invalid kwarg", e="bogon") 
    except TypeError, err: 
     print 'Invalid kwarg\n %s' % err 

これを印刷し、どのこれとともに。より柔軟なアプローチは、 'actual_kwargs'にハードコーディングするのではなく、デコレータに使用する属性の名前を渡すことですが、これはソリューションを示す最も簡単なアプローチです。

Mmm、Pythonはおいしいです。

def funky(a=None, b=None, c=None): 
    for name, value in [('a', a), ('b', b), ('c', c)]: 
     print name, value 
+0

Heh、かなりいいです。私はこの方法も好きです。ありがとうございました! –

5

一つの可能​​性:

def f(**kw): 
    acceptable_names = set('a', 'b', 'c') 
    if not (set(kw) <= acceptable_names): 
    raise WhateverYouWantException(whatever) 
    ...proceed... 

IOW、それが渡された名前を許容セット内にあり、そうでなければ、(Pythonが調達するTypeError例外をしたいと思うものは何でも上げ、私は推測していることを確認することは非常に簡単です;-)。デコレータ、btwに変えるのはかなり簡単です。

別の可能性:

_sentinel = object(): 
def f(a=_sentinel, b=_sentinel, c=_sentinel): 
    ...proceed with checks `is _sentinel`... 

あなたは、発信者が誤ってNoneを渡す(または他の非ユニークなデフォルト値は、発信者はおそらく渡すことができる)であるかもしれないというリスクを取り除く_sentinel独自のオブジェクトを作成することもできます。これはすべてobject()が良いです、btw:他のオブジェクトと間違って混同されることのない、非常に軽量でユニークなセンチネルです(isオペレータでチェックした場合)。

いずれかの解決策が、わずかに異なる問題のために好ましい。

2

着信kwargsを検証するためにデコレータを使用することはどうですか?

def validate_kwargs(*keys): 
    def entangle(f): 
     def inner(*args, **kwargs): 
      for key in kwargs: 
       if not key in keys: 
        raise ValueError("Received bad kwarg: '%s', expected: %s" % (key, keys)) 
      return f(*args, **kwargs) 
     return inner 
    return entangle 

### 

@validate_kwargs('a', 'b', 'c') 
def func(**kwargs): 
    for arg,val in kwargs.items(): 
     print arg, "->", val 

func(b=2) 
print '----' 
func(a=3, c=5) 
print '----' 
func(d='not gonna work') 

は、この出力を与える:

b -> 2 
---- 
a -> 3 
c -> 5 
---- 
Traceback (most recent call last): 
    File "kwargs.py", line 20, in <module> 
    func(d='not gonna work') 
    File "kwargs.py", line 6, in inner 
    raise ValueError("Received bad kwarg: '%s', expected: %s" % (key, keys)) 
ValueError: Received bad kwarg: 'd', expected: ('a', 'b', 'c') 
0

はこれを行うには良い方法はおそらくありますが、ここで私のテイクがあります:

def CompareArgs(argdict, **kwargs): 
    if not set(argdict.keys()) <= set(kwargs.keys()): 
     # not <= may seem weird, but comparing sets sometimes gives weird results. 
     # set1 <= set2 means that all items in set 1 are present in set 2 
     raise ValueError("invalid args") 

def foo(**kwargs): 
    # we declare foo's "standard" args to be a, b, c 
    CompareArgs(kwargs, a=None, b=None, c=None) 
    print "Inside foo" 


if __name__ == "__main__": 
    foo(a=1) 
    foo(a=1, b=3) 
    foo(a=1, b=3, c=5) 
    foo(c=10) 
    foo(bar=6) 

その出力:

 
Inside foo 
Inside foo 
Inside foo 
Inside foo 
Traceback (most recent call last): 
    File "a.py", line 18, in 
    foo(bar=6) 
    File "a.py", line 9, in foo 
    CompareArgs(kwargs, a=None, b=None, c=None) 
    File "a.py", line 5, in CompareArgs 
    raise ValueError("invalid args") 
ValueError: invalid args 

このおそらくデコレータに変換できますが、m yのデコレータは作業が必要です。読者の練習として残しておきます:P

0

おそらく、* argsを渡すとエラーが発生しますか?

def func(*args, **kwargs): 
    if args: 
    raise TypeError("no positional args allowed") 
    arg1 = kwargs.pop("arg1", "default") 
    if kwargs: 
    raise TypeError("unknown args " + str(kwargs.keys())) 

これは、varnamesのリストまたは一般的な解析機能を使用して考慮することは簡単です。あまりにも、デコレータ(のpython 3.1)にこれを行うためにあまりにもハードではないでしょう。

def OnlyKwargs(func): 
    allowed = func.__code__.co_varnames 
    def wrap(*args, **kwargs): 
    assert not args 
    # or whatever logic you need wrt required args 
    assert sorted(allowed) == sorted(kwargs) 
    return func(**kwargs) 

注:私は、これは*argsまたは**kwargsを持って、すでにラップ機能や機能を回避する方法をうまくわかりません既に。 {'a': 2, 'c': None, 'b': 'egg'}:これは、出力を与える

def func(a=None, b=None, c=None): 
    args = locals().copy() 
    print args 

func(2, "egg") 

:ここ

9

は、最も簡単で最も簡単な方法です。 はlocals辞書のコピーである必要があります。辞書は変更可能であるため、この関数内にローカル変数を作成した場合、argsには引数だけでなくすべてのローカル変数とその値が含まれます。

組み込みlocals関数hereに関するその他のドキュメント。

+0

+1私はPythonについてとても多くのことを知っています。 –

+3

ニース - 近く - でも、そこにはいけません。それはあなたが渡したものを私に教えてくれません。 –

0

マジックが答えではない

# Top of module, does not need to be exposed in __all__ 
missing = {} 

# Function prototype 
def myFunc(a = missing, b = missing, c = missing): 
    if a is not missing: 
     # User specified argument a 
    if b is missing: 
     # User did not specify argument b 

このアプローチの良いところは、私たちがしていることから、ということです"is"演算子を使用すると、呼び出し元は引数の値として空のdictを渡すことができ、引き続き引き渡すことを意味するものではありません。私たちはまたこのように厄介なデコレータを避け、コードを少しきれいに保ちます。

+1

...あなたは何を言っているのですか? a.k.a.男、私はそれを取得しない – n611x007

+0

何も魔法をしないでください。それはちょうど混乱している。 –

1

これは、最も簡単な歩哨オブジェクトの単一のインスタンスで達成される:

+0

欠落は実際にはdictではないので、なぜ "object()"を使わないのですか?これは、オブジェクトの一意の(ポイントである)インスタンスです。何にも使えない(細かいことはありません)。 安全のために、モジュールの最後にはあまりにもdelがありませんので、エクスポートすることはできません。 –

+1

削除することはできません。関数はそこからそれを参照しているので、関数はグローバルスコープにある必要があります。あなたが本当に心配している場合は、アンダースコアで接頭辞を付けることができます。オブジェクトまたはdictが動作します。私はいずれかの厳しい恩恵に気づいていませんが、{}以外はobject()よりも簡潔です。 –

関連する問題