Python における可変長関数の引数

PythonPythonBeginner
今すぐ練習

This tutorial is from open-source community. Access the source code

💡 このチュートリアルは英語版からAIによって翻訳されています。原文を確認するには、 ここをクリックしてください

はじめに

このセクションでは、可変長関数の引数について説明します。これは、時には *args**kwargs として説明されます。

位置引数の可変長引数 (*args)

任意の数の引数を受け付ける関数は、可変長引数を使用すると言われます。たとえば:

def f(x, *args):
   ...

関数呼び出し。

f(1,2,3,4,5)

追加の引数はタプルとして渡されます。

def f(x, *args):
    ## x -> 1
    ## args -> (2,3,4,5)

キーワード引数の可変長引数 (**kwargs)

関数は、任意の数のキーワード引数を受け付けることもできます。たとえば:

def f(x, y, **kwargs):
  ...

関数呼び出し。

f(2, 3, flag=True, mode='fast', header='debug')

追加のキーワードは辞書で渡されます。

def f(x, y, **kwargs):
    ## x -> 2
    ## y -> 3
    ## kwargs -> { 'flag': True,'mode': 'fast', 'header': 'debug' }

両方を組み合わせる

関数は、任意の数の可変長キーワード引数と非キーワード引数を受け付けることもできます。

def f(*args, **kwargs):
 ...

関数呼び出し。

f(2, 3, flag=True, mode='fast', header='debug')

引数は位置引数とキーワード引数に分けられます。

def f(*args, **kwargs):
    ## args = (2, 3)
    ## kwargs -> { 'flag': True,'mode': 'fast', 'header': 'debug' }
 ...

この関数は、位置引数またはキーワード引数の任意の組み合わせを受け取ります。これは、ラッパーを書くときや、別の関数に引数を渡したいときに使用されることがあります。

タプルと辞書の渡し方

タプルは可変長引数に展開できます。

numbers = (2,3,4)
f(1, *numbers)      ## f(1,2,3,4) と同じ

辞書もキーワード引数に展開できます。

options = {
    'color' :'red',
    'delimiter' : ',',
    'width' : 400
}
f(data, **options)
## f(data, color='red', delimiter=',', width=400) と同じ

演習7.1:可変長引数の簡単な例

次の関数を定義してみましょう:

>>> def avg(x,*more):
        return float(x+sum(more))/(1+len(more))

>>> avg(10,11)
10.5
>>> avg(3,4,5)
4.0
>>> avg(1,2,3,4,5,6)
3.5
>>>

パラメータ *more が追加の引数をすべて収集する方法に注目してください。

演習7.2:引数としてタプルと辞書を渡す

あるファイルからデータを読み取り、次のようなタプルを取得したとしましょう。

>>> data = ('GOOG', 100, 490.1)
>>>

今、このデータから Stock オブジェクトを作成したいとします。data を直接渡そうとすると、うまくいきません。

>>> from stock import Stock
>>> s = Stock(data)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Stock.__init__() missing 2 required positional arguments:'shares' and 'price'
>>>

代わりに *data を使うことで簡単に修正できます。試してみてください。

>>> s = Stock(*data)
>>> s
Stock('GOOG', 100, 490.1)
>>>

辞書がある場合は、代わりに ** を使うことができます。たとえば:

>>> data = { 'name': 'GOOG','shares': 100, 'price': 490.1 }
>>> s = Stock(**data)
Stock('GOOG', 100, 490.1)
>>>

演習7.3:インスタンスのリストを作成する

あなたの report.py プログラムでは、次のようなコードを使ってインスタンスのリストを作成しました。

def read_portfolio(filename):
    '''
    株式ポートフォリオファイルを、name、shares、price のキーを持つ辞書のリストに読み込む。
    '''
    with open(filename) as lines:
        portdicts = fileparse.parse_csv(lines,
                               select=['name','shares','price'],
                               types=[str,int,float])

    portfolio = [ Stock(d['name'], d['shares'], d['price'])
                  for d in portdicts ]
    return Portfolio(portfolio)

代わりに Stock(**d) を使うことで、そのコードを簡略化できます。その変更を行ってください。

✨ 解答を確認して練習

演習7.4:引数の透過

fileparse.parse_csv() 関数には、ファイルの区切り文字を変更したり、エラー報告に関するオプションがいくつかあります。おそらくあなたは、上の read_portfolio() 関数にこれらのオプションを公開したいでしょう。この変更を行ってください。

def read_portfolio(filename, **opts):
    '''
    株式ポートフォリオファイルを、name、shares、price のキーを持つ辞書のリストに読み込む。
    '''
    with open(filename) as lines:
        portdicts = fileparse.parse_csv(lines,
                                        select=['name','shares','price'],
                                        types=[str,int,float],
                                        **opts)

    portfolio = [ Stock(**d) for d in portdicts ]
    return Portfolio(portfolio)

変更を行ったら、いくつかのエラーが含まれるファイルを読み取ってみてください。

>>> import report
>>> port = report.read_portfolio('missing.csv')
Row 4: Couldn't convert ['MSFT', '', '51.23']
Row 4: Reason invalid literal for int() with base 10: ''
Row 7: Couldn't convert ['IBM', '', '70.44']
Row 7: Reason invalid literal for int() with base 10: ''
>>>

次に、エラーを無視するようにしてみてください。

>>> import report
>>> port = report.read_portfolio('missing.csv', silence_errors=True)
>>>
✨ 解答を確認して練習

まとめ

おめでとうございます! あなたは可変長引数の実験を完了しました。あなたのスキルを向上させるために、LabExでさらに多くの実験を行って練習することができます。