* argsおよび** kwargsを使用したデフォルトの引数


84

Python 2.xのでデフォルト引数を使用する適切な方法である(私は2.7を使用)、*argsおよび**kwargs
このトピックに関連するSOに関する質問を見つけましたが、それはPython 3です
* args、** kwargs、およびオプション/デフォルトの引数を使用してPython関数を呼び出す

そこで、彼らはこの方法が機能すると言います:

def func(arg1, arg2, *args, opt_arg='def_val', **kwargs):
    #...

2.7では、結果はSyntaxError。になります。そのような関数を定義するための推奨される方法はありますか?
私はそれをこのように機能させましたが、もっと良い解決策があると思います。

def func(arg1, arg2, *args, **kwargs):
    opt_arg ='def_val'
    if kwargs.__contains__('opt_arg'):
        opt_arg = kwargs['opt_arg']
    #...

それでも私は全体の実行した最も簡潔な説明:saltycrane.com/blog/2008/01/...
verbsintransit

4
明示的に呼び出すことはありません__contains__。常に使用してくださいin'opt_arg' in kwargs。(さらに良い:kwargs.get('opt_arg', 'def_val')mgilsonの答えのように)。
nneonneo 2013年

回答:


80

*args:の前にデフォルトの引数を置くだけです。

def foo(a, b=3, *args, **kwargs):

これでb、キーワード引数または2番目の位置引数として渡すと、明示的に設定されます。

例:

foo(x) # a=x, b=3, args=(), kwargs={}
foo(x, y) # a=x, b=y, args=(), kwargs={}
foo(x, b=y) # a=x, b=y, args=(), kwargs={}
foo(x, y, z, k) # a=x, b=y, args=(z, k), kwargs={}
foo(x, c=y, d=k) # a=x, b=3, args=(), kwargs={'c': y, 'd': k}
foo(x, c=y, b=z, d=k) # a=x, b=z, args=(), kwargs={'c': y, 'd': k}

特に、その場合は位置によって割り当てられるfoo(x, y, b=z)ため、機能しないことに注意してくださいb


このコードはPython3でも機能します。Python 3でデフォルトの引数を *argsに置くと、位置ではなく名前でのみ指定できる「キーワードのみ」の引数になります。Python 2でキーワードのみの引数が必要な場合は、@ mgilsonのソリューションを使用できます。


1
はい、それはのあいまいさによるもの*argsです。キーワードのみの引数が必要な場合は、正しい方法です。
nneonneo 2013年

上記のコメントで答えてもらいました。(別のbで関数を呼び出し、* argsを追加したい場合はどうなりますか?)

とにかく、尋ねる前に、私はこの解決策を試しましたが、opt_argを定義した後、kwargsのみを使用した場合にのみ機能することもわかりました。

@nneonneo:例を取得しましたが、リンクで説明されているように、Python 3.xで許可されているように、この方法ではデフォルトの引数と* argsを同時に指定する自由はありません。しますか?

5
*argsいいえ、入力しながらデフォルトの引数をそのままにしておくことはできません。機能は一般的に、Pythonの2のデフォルト引数が0等の明らかなものとして指定されているのPython 3に追加された理由はここにありNone、彼らが明示的に渡すことができるようにします。
nneonneo

55

他の質問の構文はpython3.xのみであり、キーワードのみの引数を指定します。python2.xでは機能しません。

python2.xの場合、popkwargsから外します。

def func(arg1, arg2, *args, **kwargs):
    opt_arg = kwargs.pop('opt_arg', 'def_val')

また、デフォルトの引数の前に* argsを指定します。

4

次のようなデコレータを使用することもできます。

import functools
def default_kwargs(**defaultKwargs):
    def actual_decorator(fn):
        @functools.wraps(fn)
        def g(*args, **kwargs):
            defaultKwargs.update(kwargs)
            return fn(*args, **defaultKwargs)
        return g
    return actual_decorator

次に、次のようにします。

@default_kwargs(defaultVar1 = defaultValue 1, ...)
def foo(*args, **kwargs):
    # Anything in here

例えば:

@default_kwargs(a=1)
def f(*args, **kwargs):
    print(kwargs['a']+ 1)

f() # Returns 2
f(3) # Returns 4

1

ソリューションアプローチをより一般的でコンパクトにしようとしている間、ソリューションアプローチに非常に近づき、次のようなことを検討することをお勧めします。

>>> def func(arg1, arg2, *args, **kwargs):
...     kwargs_with_defaults = dict({'opt_arg': 'def_val', 'opt_arg2': 'default2'}, **kwargs)
...     #...
...     return arg1, arg2, args, kwargs_with_defaults

>>> func('a1', 'a2', 'a3', 'a5', x='foo', y='bar')
('a1', 'a2', ('a3', 'a5'), {'opt_arg2': 'default2', 'opt_arg': 'def_val', 'y': 'bar', 'x': 'foo'})

>>> func('a1', 'a2', 'a3', 'a5', opt_arg='explicit_value', x='foo', y='bar')
('a1', 'a2', ('a3', 'a5'), {'opt_arg2': 'default2', 'opt_arg': 'explicit_value', 'y': 'bar', 'x': 'foo'})

0

Python 2.xで処理する別の方法:

def foo(*args, **kwargs):
    if 'kwarg-name' not in kwargs.keys():
        kwargs['kwarg-name'] = 'kwarg-name-default-value'
    return bar(*args, **kwargs)

これは*args、@ nneonneoの回答とは異なり、基になる呼び出しへの任意の受け渡しを処理します。


1
これはつまり、あなたが有効なコードを使用した場合より明確になる'opt_arg'の代わりに< kwarg-name >'def_val'の代わりに< kwarg-name-default-value >
wjandrea

0

この答えは、ダニエル・アメリカが提案したことの延長です。

このデコレータは、厳密に定義されていない場合、デフォルトのkwarg値を割り当てます。

from functools import wraps

def force_kwargs(**defaultKwargs):
    def decorator(f):
        @wraps(f)
        def g(*args, **kwargs):
            new_args = {}
            new_kwargs = defaultKwargs
            varnames = f.__code__.co_varnames
            new_kwargs.update(kwargs)
            for k, v in defaultKwargs.items():
                if k in varnames:
                    i = varnames.index(k)
                    new_args[(i, k)] = new_kwargs.pop(k)
            # Insert new_args into the correct position of the args.
            full_args = list(args)
            for i, k in sorted(new_args.keys()):
                if i <= len(full_args):
                    full_args.insert(i, new_args.pop((i, k)))
                else:
                    break
            # re-insert the value as a key-value pair
            for (i, k), val in new_args.items():
                new_kwargs[k] = val
            return f(*tuple(full_args), **new_kwargs)
        return g
    return decorator

結果

@force_kwargs(c=7)
def f(a, b='B', c='C', d='D', *args, **kw):
    return a, b, c, d, args, kw
#                               a    b  c    d  args      kwargs
f('r')                      # 'r', 'B', 7, 'D',   (),         {}
f(1,2,3)                    #   1,   2, 7,   3,   (),         {}
f(1, 2, 3, b=3, c=9, f='F') #   1,   3, 9,   2, (3,), {'f': 'F'}

バリアント

関数定義に記述されているデフォルト値を使用する場合f.func_defaultsは、デフォルト値を一覧表示するを使用して引数のデフォルト値にアクセスできます。これらのデフォルト値を変数名と一致させるzipには、の末尾をf.__code__.varnames指定する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.