Python3エンジニア基礎試験_公式問題集の復習②

# はじめに

「徹底攻略Python 3 エンジニア認定［基礎試験］問題集」の復習として記載しています。

https://www.amazon.co.jp/%E5%BE%B9%E5%BA%95%E6%94%BB%E7%95%A5Python-3-%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E8%AA%8D%E5%AE%9A%EF%BC%BB%E5%9F%BA%E7%A4%8E%E8%A9%A6%E9%A8%93%EF%BC%BD%E5%95%8F%E9%A1%8C%E9%9B%86-%E6%A0%AA%E5%BC%8F%E4%BC%9A%E7%A4%BE%E3%83%93%E3%83%BC%E3%83%97%E3%83%A9%E3%82%A6%E3%83%89/dp/4295016055

# 関数の問題

下記の実行結果を求めよ。

```func.py
default_message_1 = "Hello"

def message(message_1=default_message_1, message_2=""):
    print(f"{message_1} {message_2}")

default_message_1 = "こんにちは"
message_2 = "world"

message(message_2=message_2)
# Hello world
```

● 解説

引数のデフォルト値に関する問題です。
引数には、<font color="Red">**デフォルト値**</font>を設定できます。関数の実行時に値を与えられなくても、デフォルト値が関数の中で使われます。
<font color="Red">**引数のデフォルト値は、関数が定義された時点で評価され、関数の実行時に再評価されません。**</font>

● コードの動作
1. default_message_1 に "Hello" が代入されます。
1. message() 関数が定義されます。この関数は message_1 と message_2 の2つの引数を持ち、それぞれデフォルト値として default_message_1 と空の文字列が指定されています。message()関数を定義してから、変数default_message_1 に値を代入しても、message()関数の引数 message_1 のデフォルト値には影響を与えません。
1. message_2 に "world" が代入されます。
1. message 関数が呼び出されます。message_2 をキーワード引数として指定しているため、デフォルトの message_1 の値ではなく、キーワード引数の値が使用されます。したがって、"Hello world" となります。

# 関数の問題

下記の実行結果を求めよ。

```func.py
def function(number, default_arg_list=[]):
    default_arg_list.append(number)
    return default_arg_list

print(function(1))
print(function(2, [3, 4]))
print(function(3))
print(function(4, [5, 6]))
print(function(5))
# [1]
# [3, 4, 2]
# [1, 3]
# [5, 6, 4]
# [1, 3, 5]
```

● 解説

引数のデフォルト値に関する問題です。
<font color="Red">**引数のデフォルト値は、関数が定義された時点で評価され、関数の実行時に再評価されません。**</font>
また、引数のデフォルト値をリストにすると、その引数に値が加えられるたびにデフォルト値も変更されます。

まず、def function(number, default_arg_list=[]):では、第2引数のデフォルト値をリストにしています。この場合、引数の指定の仕方によって、実行する処理の内容が変わります。

・ 第2引数が指定されている場合：第2引数のリストに第1引数(number)の値を加える
・ 第2引数が指定されていない場合：default_arg_listのデフォルト値に第1引数(number)の値を加える

なお、実務で使うときは、引数のデフォルト値をリストにするのは避けるべきです。
次のようにすることで、引数の省略時にdefault_arg_listを空のリストにできます。

```func.py
def function(number, default_arg_list=None):
    if default_arg_list is None:
        default_arg_list = []
    default_arg_list.append(number)
    return default_arg_list

print(function(1))
print(function(2, [3, 4]))
print(function(3))
print(function(4, [5, 6]))
print(function(5))
# [1]
# [3, 4, 2]
# [3]
# [5, 6, 4]
# [5]
```

# 関数の問題

下記の実行結果を求めよ。

```func.py
def function(name, *args, **kwargs):
    print(name)
    print(args)
    print(kwargs)

function("spam", "ham", kwarg1="eggs", kwarg2="spamhameggs")
# spam
# ('ham',)
# {'kwarg1': 'eggs', 'kwarg2': 'spamhameggs'}
```

● 解説

引数をタプルやデイクショナリとして参照する方法についての問題です。
`*`が先頭に付いた仮引数には、指定済みの実引数を除く位置引数のタプルが入ります。
`**`が先頭に付いた仮引数には、指定済みの実引数を除くキーワード引数のデイクショナリが入ります。

# 関数の問題

下記の実行結果を求めよ。

```func.py
def concat(*args, sep="/"):
    return sep.join(args) # join() メソッドは、文字列や要素を指定した区切り文字で結合する

words = ["spam", "ham", "eggs"] # リスト
options = {"sep": "&"} # ディクショナリ
print(concat(*words, **options)) # *はリスト、**はディクショナリ
# spam&ham&eggs
```

● 解説

1. `concat`という名前の関数が定義されています。この関数は、可変長引数 `args` とキーワード引数 `sep` を受け取ります。デフォルトの `sep` の値は "/" です。
1. `concat` 関数の中で、引数 `args` を `sep` を区切り文字として結合しています。具体的には、`sep.join(args)` の形式で、`args` の要素を `sep` で連結しています。
1. `words` というリストが定義されており、要素として "spam"、"ham"、"eggs" が含まれています。
1. `options` という辞書が定義されており、キー "sep" に対して値 "&" を持っています。
1. `print` 関数を使って、`concat` 関数を呼び出しています。`*words` はリスト `words` の要素を個別の引数として渡すことを意味し、`**options` は辞書 `options` のキーワード引数を展開して渡すことを意味します。
1. 実行結果として、"spam&ham&eggs" が出力されます。