Python実践入門を読んでPythonを体系的に学び直した①

今までpythonをちゃんと体系的に学んだことがなかったので、Python実践入門を読んで再入門した。 Pythonを使いながらも恥ずかしながら今まで知らなかったこと、今まで理解が曖昧だった部分、Python3.8から導入された新機能など新たな学びがたくさんあったので、いくつかピックアップしてみる。

for文の変数スコープについて

Pythonのfor文は変数のスコープをブロック内に限定しない。つまり各要素に代入する時に使用した変数はfor文を抜けた後、最後の値が代入された状態になっている。

# 変数hogeが未定義であることを確認
>>> hoge
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'hoge' is not defined

# for文の変数にhogeを使用
>>> for hoge in ['a', 'b', 'c']:
...     pass
...

# for文を抜けたあとは、最後の値が代入されている
>>> hoge
'c'

# 変数hogeが定義済の場合は上書きされる
>>> for hoge in [1, 2, 3]:
...     pass
...
>>> hoge
3

式の中で代入が行える:=演算子

Pythonの3.8から追加された機能で、if文やループの条件式中などで変数への値の代入が行える演算子

>>> import random
>>> def lottery(fruits):
        # 式の中で代入を行っている
...     if fruit := random.choice(fruits):
...             return fruit
...     else:
...             return 'Miss..'
...
>>> fruits = ['apple', 'orange', 'banana', None, None]

# 実行ごとに結果が変わる
>>> lottery(fruits)
'Miss..'
>>> lottery(fruits)
'orange'

ifの条件式中で代入された変数のスコープはif文のブロック内に閉じないので注意が必要。

括弧()でくくられた複数の連続する文字列は一つの文字列とみなされる

長い文字列(URLなど)の長い文字列を定義する際に便利。

>>> URL = ('https://rnakamine.net'
...        '/blog/wdpress/archive'
...        '/2020/11/29/python')
>>>
>>> URL
'https://rnakamine.net/blog/wdpress/archive/2020/11/29/python'

f-stringの{}の中に=を加えると、評価結果と同時にその変数や式を文字列で表示できる

Pythonの3.8で追加された機能でデバッグなどで変数の値を確認したい時に便利(かもしれない)。

>>> x = 30
>>> y = 50
>>>
>>> f'{x} : {y}'
'30 : 50'
# =を追加
>>> f'{x=} : {y=}'
'x=30 : y=50'

f-stringはこちらの説明がわかりやすかった

note.nkmk.me

辞書(dict)でキーが存在しない時に例外(KeyError)を発生させたくない場合は、dict.get()を使う

これを知らなかったので、if文でわざわざ分岐して頑張ってた...

>>> fruits = {'apple': 200, 'orange': 250, 'banana': 150}
# 普通にキーを指定して取り出す
>>> fruits['apple']
200
# 存在しないキーを指定
>>> fruits['lemon']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'lemon'

# get()を使うとキーが存在しなくてもエラーにならない
>>> fruits.get('apple')
200
# キーがない場合のデフォルト値はNone
>>> fruits.get('lemon')
# デフォルト値を指定することもできる
>>> fruits.get('lemon', 'ありません')
'ありません'
>>>

デフォルト引数値がある関数は定義時に引数の式が評価される

関数を呼び出した時間を表示させたい関数があった時に

>>> from datetime import datetime
>>> def print_page(content, timestamp=datetime.now()):
...     print(content)
...     print(timestamp)
...
>>> print_page('my content 1')
my content 1
2020-11-29 16:57:59.306168
>>> print_page('my content 2')
my content 2
2020-11-29 16:57:59.306168

上記のようにデフォルト値でdatetime.now()とかやってしまうと、 表示される結果は同じ時刻になってしまう。

デフォルト引数値は関数定義時に評価されるため、デフォルト引数の式は関数が定義される時に一度だけ評価される。 なので、呼び出す時には計算済の値が毎回出力されてしまう。

期待通りの動きをさせたい場合は以下のようにする。

>>> def print_page(content, timestamp=None):
...     if timestamp is None:
...             timestamp = datetime.now()
...     print(content)
...     print(timestamp)
...
>>> print_page('my content 1')
my content 1
2020-11-29 17:03:24.791031
>>> print_page('my content 2')
my content 2
2020-11-29 17:04:00.716482

参考文献