いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Pythonメモ-01 (アスタリクスのみの引数, 後続のキーワード引数のキーワード指定を強制する)

個人的なPythonメモです。

環境

$ python
Python 3.6.0 |Anaconda custom (x86_64)| (default, Dec 23 2016, 13:19:00) 
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 

アスタリクスのみの引数

書籍「Effective Python」読んでたら、以下のような関数がありました。

def func(x, y, *, z=100):
    print(x, y, z)

なんか、引数の真ん中に変なのいる

このように引数に アスタリクス (*) のみを記載すると、これ以降では、キーワード引数を指定する際に

キーワードの指定を強制できるとのこと。

通常

def with_keyword_params(x, y, z=100, zz=200):
    print(x, y, z, zz)

ってクソ関数があった場合に

with_keyword_params(10, 20, 30, 40)

って指定も出来るし

with_keyword_params(10, 20, 30, zz=333)

って指定もできます。

でも、アスタリクスだけの引数を追加した関数

def with_keyword_params2(x, y, *, z=100, zz=200):
    print(x, y, z, zz)

だと

with_keyword_params2(10, 20, 30, 40)

ってやるとエラーになります。 キーワード引数部分を指定する際は必ず

with_keyword_params(10, 20, z=11, zz=22)

ってやらないと駄目に出来る。

サンプル

以下サンプルです。

# coding: utf-8

"""
関数についてのサンプルです。
"""
from trypython.common.commoncls import SampleBase
from trypython.common.commonfunc import hr, pr


class Sample(SampleBase):
    def exec(self):
        # -----------------------------------------
        # 最も基本的な関数の形
        # -----------------------------------------
        func = Sample.normal_func
        hr(func.__name__)
        pr(func.__name__, func(10, 20))

        # -----------------------------------------
        # 可変引数あり
        # 可変引数は、慣習的に *args と記述する
        # -----------------------------------------
        func = Sample.with_args
        hr(func.__name__)
        pr(func.__name__, func(10, 20, 30, 40, 50))

        # -----------------------------------------
        # デフォルト値あり
        # -----------------------------------------
        func = Sample.with_default_value
        hr(func.__name__)
        pr(func.__name__, func(10, 20))

        # -----------------------------------------
        # キーワード引数あり
        # キーワード引数は、慣習的に **kwargs と記述する
        # -----------------------------------------
        func = Sample.with_kwargs
        hr(func.__name__)
        pr(func.__name__, func(10, 20, z=100, zz=200))

        # -----------------------------------------
        # 特殊な アスタリクスのみ引数 を持つ関数
        # -----------------------------------
        # 引数リストの中に「*」のみの引数を定義すると
        # それ以降の引数について強制的にキーワード指定
        # をして呼び出すように出来る。
        #
        # 通常 func(x, y, z=100) のように定義して
        # いると、 func(10, 20, z=111) のように
        # 呼ぶことも出来るし、 func(10, 20, 111) と
        # 呼ぶことも出来る。
        #
        # だが、func(x, y, *, z=100) と定義した場合
        # func(10, 20, 111)と呼ぶとエラーとなり
        # func(10, 20) か func(10, 20, z=111) と
        # いうようにキーワード引数を明示的に指定しないと
        # 受け付けないように出来る。
        # -----------------------------------------
        func = Sample.with_asterisk_sentinel
        hr(func.__name__ + '(sentinel)')
        pr(func.__name__, func(10, 20))
        pr(func.__name__, func(10, 20, z=500))

        try:
            pr(func.__name__, func(10, 20, 300))
        except TypeError as err:
            pr('with_asterisk_sentinel', err)

    @staticmethod
    def normal_func(x, y):
        return x + y

    @staticmethod
    def with_args(x, y, *args):
        return sum((x, y, *args))

    @staticmethod
    def with_default_value(x, y, z=100):
        return x + y + z

    @staticmethod
    def with_kwargs(x, y, **kwargs):
        return sum((x, y, *(kwargs.values())))

    @staticmethod
    def with_asterisk_sentinel(x, y, *, z=100, zz=200):
        return x + y + z + zz


def go():
    obj = Sample()
    obj.exec()


if __name__ == '__main__':
    go()

結果

以下のようになります。

----------------normal_func----------------
normal_func=30
----------------with_args----------------
with_args=150
----------------with_default_value----------------
with_default_value=130
----------------with_kwargs----------------
with_kwargs=330
----------------with_asterisk_sentinel(sentinel)----------------
with_asterisk_sentinel=330
with_asterisk_sentinel=730
with_asterisk_sentinel=TypeError('with_asterisk_sentinel() takes 2 positional arguments but 3 were given',)

ソース

以下の場所でも見れます。

github.com

疑問

このアスタリクス(*)だけの引数、Python界隈では正式にはなんて用語で呼ぶのでしょうか???

8. 複合文 (compound statement) — Python 3.6.1 ドキュメント

ここみても、特に明記してあるようには見えなかったのです。

2017-05-04 ちゃんとPEPとして発行されているのを発見。

PEP 3102 – Keyword-Only Arguments www.python.org


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。