{ }codelper

学ぶ › Python

言語ごとの特徴 · 40

Python らしいコード

内包表記、ステップ付きのスライス、アンパック、enumerate・zip、in、f 文字列、そして3つの落とし穴。

この単元は Python 向けです。
ほかの言語は同じことをループかライブラリのメソッドで書きます(JavaScript map・filter、Java のストリーム、C# の LINQ、C・C++・Craykon はループと push_back・追加)。

Python には、何行ものループを1行に縮める短い書き方がたくさんあります。
下の例は内包表記・スライス・アンパック・enumerate・zip・f 文字列を順に使い、最後にエラーなしで間違った答えを出す落とし穴を2つ見せます。

nums = list(map(int, input().split()))words = input().split() odd_squares = [x * x for x in nums if x % 2 == 1]print(*odd_squares)print({x % 3 for x in nums})print({w: len(w) for w in words}) print(*nums[::-1])print(*nums[1:6:2])print(words[0][::-1]) first, *rest = numsa, b = first, rest[-1]a, b = b, aprint(a, b, len(rest)) for i, w in enumerate(words, start=1):    print(f"{i}. {w:<6}|")print(*(f"{w}={n}" for w, n in zip(words, nums)))print(9 in nums, "lo" in words[0]) gen = (x for x in nums)print(sum(gen), sum(gen)) grid = [[0] * 3] * 2grid[0][0] = 1print(grid)grid = [[0] * 3 for _ in range(2)]grid[0][0] = 1print(grid)
stdin
3 1 4 1 5 9 2 6
hello world codes
stdout
9 1 1 25 81
{0, 1, 2}
{'hello': 5, 'world': 5, 'codes': 5}
6 2 9 5 1 4 1 3
1 1 9
olleh
6 3 7
1. hello |
2. world |
3. codes |
hello=3 world=1 codes=4
True True
31 0
[[1, 0, 0], [1, 0, 0]]
[[1, 0, 0], [0, 0, 0]]

ポイント

  • 内包表記(comprehension)は、ループ1行で新しいリストを作ります。
    [x * x for x in nums if x % 2 == 1] は「奇数だけを選んで2乗したリスト」で、空のリストに append する4行のループがこの1行になります。
  • 括弧を変えるだけで、同じ形で集合 {x % 3 for x in nums} や辞書 {w: len(w) for w in words} も作れます。
  • スライス a[開始:終わり:ステップ] は終わりを含まず、省略した部分は先頭・末尾・1 になります。
    nums[1:6:2] は 1・3・5 番、nums[::-1] は逆順の新しいリスト、"hello"[::-1] は "olleh" です。
  • 範囲を超えるスライスはエラーにならず、ある分だけを返します(nums[5:100])。
    負の番号は後ろから数えるので、a[-1] が最後です。
  • アンパック:a, b = b, a は一時変数なしで入れ替え、first, *rest = nums は最初の値と残りのリストに分けます。
    番号は enumerate(words, start=1)、2つのリストを並べて回すなら zip(words, nums)(短い方で止まります)を使います。
  • in は含まれているかを調べます。
    リストは前から1つずつ探すので遅く、集合・辞書はほぼすぐに見つけます。
    文字列では "lo" in "hello" のように部分文字列を探します。

言語ごとの書き方

Pythonf 文字列 f"{i}. {w:<6}|" は波括弧の中に式、: のあとに書式(<6 は6桁左寄せ、.2f は小数第2位まで)。
f"{x=}" は x=3 のように名前まで出すのでデバッグに便利です。
range(len(...)) で番号を回すことはあまりせず、ステップ付きのスライスや [::-1] は Python だけの短い書き方です

よくある間違い

デフォルト値に [] を置いた

def add(x, bucket=[]):
    bucket.append(x)
    return bucket
add(1); print(add(2))

→ [1, 2] — [] は関数を作るときに一度だけ作られるので、呼ぶたびに同じリストを使います

bucket=None にして、関数の中で if bucket is None: bucket = [] と新しく作ります。

ジェネレータを2回使った

gen = (x for x in nums)
print(sum(gen), sum(gen))

→ 31 0 — 丸括弧 ( ) はリストではなく、一度使うと空になるジェネレータです

2回使うなら角括弧 [ ] でリストを作ります。

[[0] * 3] * 2 で2次元リストを作った

grid = [[0] * 3] * 2
grid[0][0] = 1
print(grid)

→ [[1, 0, 0], [1, 0, 0]] — 外側の * 2 は内側のリストをコピーせず、同じリストを2回入れます

[[0] * 3 for _ in range(2)] のように内包表記で行ごとに新しく作ります([0] * 3 のように数を掛けるのは大丈夫です)。

言語ごとの違い
掛け算(* m)で作った2次元リストと =[] のデフォルト値は、エラーなしで同じリストを共有するので、思わぬところが変わります。
リストを複数作るなら内包表記、デフォルト値は None にしてください。

練習問題