これは、筆者の「2025振り返り用ひとりアドカレ」記事の一つです。
はじめに
本記事は普段 React, Next.jsなどを使う筆者が2025年に業務効率化を絶対目標としてPython学習したことに際し、「フロントエンド側の人間が 1からPythonを学んでみて」というテーマで書いていくシリーズ記事の一つです。
対象読者
- 業務効率化を願うビジネスパーソン
- バックエンド側に興味があるフロントエンド側の方々
- バックエンド側とフロントエンド側の言語における違いや共通性が知りたい方々
- Pythonを学び始めた方々
- Pythonを学ぼうとしているフロントエンドエンジニアの方々
わざわざ学ばなくても、AI(LLM)に任せれば良くね?
筆者がPython学習のモチベーションとしたのは「業務効率化を絶対目標」とすることです。
正直これはAIで簡単に行える世界になりつつありますが、筆者はAIの成果物を正しく活用するにはチェックできるだけの基礎力が大事だと思っています。
そのため遠回りに見えつつもPythonを体系的にじっくり学んでいくルートを選びました。
本記事では、Python特有の概念や構文、機能などを紹介していきます。
ただし、オフサイドルール(※コードのインデントが挙動に影響を及ぼすPythonでのコーディング制約)など基礎的な約束事のような部分は第一回で書きました。
ですから、本記事では少し踏み込んだ内包表記やラムダ関数といった周辺知識に関するところを書いていきます。
内包表記(comprehension)について
データ構造(リストなどのイテラブル)を作成するときに役立つ構文で、簡潔な記述(プログラム)でリスト(list)や集合(set)、辞書(dict)に格納する値を生成できます。
データ構造に応じて書き方が少しだけ変わっていくので注意してください。
-
タプルの内包表記はない
タプルチックな書き方をしたものは全く別物の「ジェネレーター」として扱われます。
# リスト
[式 for 変数 in イテラブル]
# 集合
{式 for 変数 in イテラブル}
# 辞書
{keyの式: valueの式 for 変数 in イテラブル}
内包表記は条件付き処理も行える
条件が合致した要素を返すfilter関数と似た働きを実現できます。
# 条件なし
[式 for 変数 in イテラブル]
# 条件付き(filterに相当)
[式 for 変数 in イテラブル if 条件]
# 条件付きの具体例
numbers = [1, 2, 3, 4, 5]
# 2で割り切れる要素を取得
[n for n in numbers if n % 2 == 0] # [2, 4]
内包表記と三項演算子の組み合わせ
[三項演算子 for 変数 in イテラブル]
# 三項演算子
# Trueの値 if 条件 else Falseの値
- 具体例(内包表記と三項演算子の組み合わせ)
def FizzBuzz():
FizzBuzz_result = [
"FizzBuzz"
if i % 5 == 0 and i % 3 == 0
else "Buzz"
if i % 5 == 0
else "Fizz"
if i % 3 == 0
else i
for i in range(1, 16)
]
print(FizzBuzz_result)
FizzBuzz()
上記のように可読性が悪くなる場合もあるので「内包表記 × 三項演算子」というパターンはケースバイケースの利用をお勧めします。
以下のcheck_APPLEみたいな単純なものだと開発効率が良いかも?
# 単純なものなら [三項演算子 for 変数 in イテラブル] は開発効率が良いかも
fruits = ["banana", "apple", "melon", "water-melon"]
check_APPLE = [fruit.upper() if fruit == "apple" else "" for fruit in fruits]
print(check_APPLE)
# ['', 'APPLE', '', '']
リストの内包表記
[式 for 変数 in イテラブル]
# 内包表記におけるアンパッキング(※アンパッキング:JSでいうスプレッド演算子みたいな展開処理))
[式 for 変数, ... in イテラブル]
- 具体例
# 以下の記述(0〜9までの2乗)
numbers = []
for i in range(10):
numbers.append(i**2)
print(numbers)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 先の処理を内包表記で実装
numbers = [i**2 for i in range(10)]
print(numbers)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
集合の内包表記
-
集合の性質に注意
取り出し順は保証されず、重複した値は排除される。
{式 for 変数 in イテラブル}
# 内包表記におけるアンパッキング(※アンパッキング:JSでいうスプレッド演算子みたいな展開処理)
{式 for 変数, ... in イテラブル}
- 具体例
# 集合
the_set = set()
for i in range(10):
the_set.add((i + 1) ** 2)
print(f"集合:{the_set}")
# 集合:{64, 1, 4, 36, 100, 9, 16, 49, 81, 25}
# 上記処理の内包表記ver
the_set_comprehension = {(i + 1) ** 2 for i in range(10)}
print(f"集合:{the_set_comprehension}")
# 集合:{64, 1, 4, 36, 100, 9, 16, 49, 81, 25}
辞書の内包表記
{keyの式: valueの式 for 変数 in イテラブル}
# 内包表記におけるアンパッキング(※アンパッキング:JSでいうスプレッド演算子みたいな展開処理)
{keyの式: valueの式 for 変数, ... in イテラブル}
- 具体例
# 辞書
the_dict = {}
for i in range(10):
# `dict[キー] = 値` で辞書に要素を追加
# 指定したキーが当該辞書に含まれていない場合は「キーと値の組を新たに追加」
# 含まれている場合は「そのキーに対応する値を書き換える」
the_dict[f"No.{i + 1}"] = (i + 1) ** 2
print(f"辞書:{the_dict}")
# 辞書:{'No.1': 1, 'No.2': 4, 'No.3': 9, 'No.4': 16, 'No.5': 25, 'No.6': 36, 'No.7': 49, 'No.8': 64, 'No.9': 81, 'No.10': 100}
the_dict_comprehension = {f"No.{i + 1}": (i + 1) ** 2 for i in range(10)}
print(f"辞書:{the_dict_comprehension}")
# 辞書:{'No.1': 1, 'No.2': 4, 'No.3': 9, 'No.4': 16, 'No.5': 25, 'No.6': 36, 'No.7': 49, 'No.8': 64, 'No.9': 81, 'No.10': 100}
多重ループの内包表記
これから以下の多重ループ(「九九(掛け算表)」)をベースに進めます。
- 文字列と改行ver
# 九九(掛け算表)
for y in range(9):
for x in range(9):
# ()は必須
column = (y + 1) * (x + 1)
# x軸数値が9の倍数 (x軸の数値が9で割り切れる場合) の処理
if (x + 1) % 9 == 0:
print(f"{column:2d}")
else:
print(f"{column:2d}", end="|")
# 1| 2| 3| 4| 5| 6| 7| 8| 9
# 2| 4| 6| 8|10|12|14|16|18
# 3| 6| 9|12|15|18|21|24|27
# 4| 8|12|16|20|24|28|32|36
# 5|10|15|20|25|30|35|40|45
# 6|12|18|24|30|36|42|48|54
# 7|14|21|28|35|42|49|56|63
# 8|16|24|32|40|48|56|64|72
# 9|18|27|36|45|54|63|72|81
- リスト形式ver
# 九九(掛け算表)配列形式
multiple_lists = []
for x in range(1, 10):
for y in range(1, 10):
multiple_lists.append(x * y)
for i in range(9):
print(multiple_lists[i::9])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
# [2, 4, 6, 8, 10, 12, 14, 16, 18]
# [3, 6, 9, 12, 15, 18, 21, 24, 27]
# [4, 8, 12, 16, 20, 24, 28, 32, 36]
# [5, 10, 15, 20, 25, 30, 35, 40, 45]
# [6, 12, 18, 24, 30, 36, 42, 48, 54]
# [7, 14, 21, 28, 35, 42, 49, 56, 63]
# [8, 16, 24, 32, 40, 48, 56, 64, 72]
# [9, 18, 27, 36, 45, 54, 63, 72, 81]
多重ループの内包表記では下記ルールが適用されるので注意してください。
多重ループの内包表記では、左に書いたものは外側のループ、右に書いたものは内側のループとなる。
更に右側に足していくとネストしたループ(三重ループ、四重ループ...)を実現できる。
集合や辞書でも([]が異なるだけで)以下と同じ記法です。
[式 for 変数A in イテラブルA for 変数B in イテラブルB]
- 具体例(多重ループの内包表記)
print([x * y for x in range(1, 10) for y in range(1, 10)])
# [1, 2, 3, 4, 5, 6, 7, 8, 9,
# 2, 4, 6, 8, 10, 12, 14, 16, 18,
# 3, 6, 9, 12, 15, 18, 21, 24, 27,
# 4, 8, 12, 16, 20, 24, 28, 32, 36,
# 5, 10, 15, 20, 25, 30, 35, 40, 45,
# 6, 12, 18, 24, 30, 36, 42, 48, 54,
# 7, 14, 21, 28, 35, 42, 49, 56, 63,
# 8, 16, 24, 32, 40, 48, 56, 64, 72,
# 9, 18, 27, 36, 45, 54, 63, 72, 81]
# リスト内リストを生成
# 以下のように階層的なループ処理も簡潔に実現できる
print([[x * y for x in range(1, 10)] for y in range(1, 10)])
# [
# [1, 2, 3, 4, 5, 6, 7, 8, 9],
# [2, 4, 6, 8, 10, 12, 14, 16, 18],
# [3, 6, 9, 12, 15, 18, 21, 24, 27],
# [4, 8, 12, 16, 20, 24, 28, 32, 36],
# [5, 10, 15, 20, 25, 30, 35, 40, 45],
# [6, 12, 18, 24, 30, 36, 42, 48, 54],
# [7, 14, 21, 28, 35, 42, 49, 56, 63],
# [8, 16, 24, 32, 40, 48, 56, 64, 72],
# [9, 18, 27, 36, 45, 54, 63, 72, 81]
# ]
ジェネレーター式 / ジェネレーター関数
ジェネレーターはリスト同様にイテラブルなオブジェクト(generator object)です。
ただし、リストのように既存要素を格納しているのではなく、処理要求される度に一つずつ生成する特徴を持ちます。
処理要求される度に一つずつ生成するため、メモリの消費を抑えたり、無制限の値を生成できたりする利点があるのです。
ジェネレーター式
※内包表記とかなり似た書き方なので注意してください。ジェネレータ式は()で表現されます。
(式 for 変数 in イテラブル)
# 変数が複数ある場合
(式 for 変数, ... in イテラブル)
- 具体例
# (式 for 変数 in イテラブル)
genObj = (i * i for i in range(10))
print(genObj)
# <generator object <genexpr> at 0x000001C6E41AB920>
内包表記と同じく、多重ループや条件分岐の実装も可能です。
ジェネレーター関数
ジェネレーター式で表現するには複雑になってしまいそうなものを扱う際に使用します。
通常の関数定義に似た記法ですが、returnの代わりにyieldを用いるのが特徴です。
-
yield
returnと違って即時終了しない性質を持つ。
def g():
for x in range(10):
yield x * x
for y in g():
print(y, end=" ")
# 0 1 4 9 16 25 36 49 64 81
yield from
既存のジェネレーター(ジェネレーター関数やジェネレーター式)を利用して別のジェネレーター(ジェネレーター関数)を定義します。
def my_range(start, finish):
x = start
while x < finish:
yield x # `return`と違って即時終了しない
x += 1
def my_range2(start, finish, count):
for _ in range(count):
yield from my_range(start, finish)
for y in my_range2(1, 10, 3):
print(y, end=" ")
# 1〜10までの数値ジェネレーター(my_range)が 3回出力される(my_range2)
# 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9
ラムダ(lambda)関数
関数型言語の機能の一つにラムダ式というものがあります。
これは関数型言語の基盤となっているラムダ計算という理論に由来しているそうです。
Python においてはラムダ式を用いることで簡単な関数定義において通常の関数定義よりも短く記述できるようになります。
lambda 引数: 式
# 上記は以下の関数定義と同義
def 関数名(引数):
return 式
- 引数が複数ある場合
lambda 引数, ...: 式
ラムダ関数のユースケースとしては「コールバック関数(別の関数の引数として渡される関数)」として利用する場合に通常の関数定義よりもプログラムが簡潔になる場合があります。
- 具体例:通常の関数定義
def loop(f):
for i in range(10):
print(f(i), end=" ")
def square(x):
return x * x
loop(square)
# 0 1 4 9 16 25 36 49 64 81
- 具体例:ラムダ関数
def loop(f):
for i in range(10):
print(f(i), end=" ")
# lambda 引数: 式
loop(lambda x: x * x)
# 0 1 4 9 16 25 36 49 64 81
sorted()関数を用いた具体例
# 食べ物, 値段, カロリー
target_sort_tuple_list = [("burger", 110, 234), ("potato", 150, 226), ("shake", 120, 218)]
# カロリーでソート
# 大きい順にソートさせるために reverse=True を指定
print(sorted(
target_sort_tuple_list,
reverse=True,
# item[2]とは、各要素のカロリー数を指す( 例:[0]"burger", [1]110, [2]234 )
key=lambda item: item[2])
)
# [('burger', 110, 234), ('potato', 150, 226), ('shake', 120, 218)]
# 上記を関数定義で実行
def highcalory_sort(item):
return item[2]
# sort() の key の関数には引数を明示的に指定しないので注意
print(sorted(target_sort_tuple_list, reverse=True, key=highcalory_sort))
# [('burger', 110, 234), ('potato', 150, 226), ('shake', 120, 218)]
-
key引数について
key引数に渡す関数は引数を明示的に指定する必要はありません。
注意事項として常に一つの要素だけを関数に渡すので直接的に複数引数を持つ関数には使えません。
セイウチ演算子(代入式)
式を評価した値を変数に代入するとともに、その値を代入式の結果として返します。
※:=が(縦向きにすると)セイウチに見える?ことが由来だそうです。
変数 := 式
代入式は端的にプログラミングできる(コードが短く簡潔になる)一方、可読性が低下するリスクもあるのでケースバイケースで使用するのが一般的です。
- 具体例
def walrus_example():
# 入力内容が q でない間は入力内容を出力し続ける
while (x := input()) != "q":
print(x)
walrus_example()
__pycache__について
__pycache__は、Python が自動的に作成するフォルダで、コンパイル済みの Pythonバイトコードを保存する場所です。
ハードコーディング(ファイル内にベタ打ち)した「秘匿キー」などセンシティブ情報もキャッシュに含んでしまうので注意してください
※そもそも秘匿キーをファイルにハードコーディングするな
GitHub(での管理時)には不要なフォルダで、プログラム実行における影響に関しては__pycache__が無ければプログラムの初回実行時にコンパイル処理が発生して少し処理が遅くなるというだけのものです。
デコレーター(@...)について
デコレーターは関数やクラスに特別な機能を付加する仕組みです。
# Bottle というPythonフレームワークを使ったデコレーターの利用例
@bottle.route('/bye')
def bye():
return html.format('Bye!')
上記デコレーターには「Webブラウザが特定のURLへアクセスしたときに結果を返す関数を指定する働き」があります。
さいごに
本記事では内包表記 / ラムダ関数など Python 独自の機能に触れてきました。
後半は駆け足でしたが、これらを理解することでPython力の底上げに繋がるはずです。
次の記事では、Pythonにおける関数定義・引数指定について書いていきたいと思います。
ここまで読んでいただき、ありがとうございました。