23.

Python のシングルクォートとダブルクォートの違い|使い分けと docstring

編集
この記事の要点
  • Python では 'abc'"abc"完全に同じ。変数展開の有無といった違いは無い
  • 使い分けの目的はただ 1 つ、中に出てくる引用符をエスケープしないため
  • docstring と複数行文字列は必ず """(PEP 257)
  • フォーマッタ blackダブルクォートに統一する。チームで揃えるならこれに従う
  • JSON はダブルクォートしか認めない。str(dict) の結果を JSON として渡すと壊れる

どちらでも同じ

a = 'こんにちは'
b = "こんにちは"

print(a == b)        # True
print(a is b)        # True   同じ文字列オブジェクトとして扱われる
print(type(a))       # <class 'str'>

name = "田中"
print(f'{name} さん')    # 田中 さん   f-string はどちらでも同じ
print(f"{name} さん")    # 田中 さん

シェルスクリプトや PHP では「シングルは変数展開しない」という違いがありますが、Python にその区別はありません。変数を埋め込みたい場合は、どちらのクォートでも f を前に付けます。

使い分けの唯一の基準

# 中にシングルが出るならダブルで囲む
print("It's fine")

# 中にダブルが出るならシングルで囲む
print('彼は "はい" と答えた')

# 両方出るなら三重クォート、またはエスケープ
print("""It's "both" here""")

エスケープが必要になったら、囲むクォートを変えれば消せないかを先に考えます。バックスラッシュが減るほど読みやすくなります。

三重クォート

text = """1 行目
2 行目
3 行目"""
print(text)

# 先頭の改行を入れたくないときは行末に \ を置く
sql = """
SELECT id, name
  FROM users
 WHERE active = 1
"""
print(sql.strip())

def area(width, height):
    """長方形の面積を返す。"""      # docstring はダブルの三重クォート
    return width * height

PEP 257 は docstring に """ を使うことを定めています。1 行だけの docstring でも ' ではなく """ にします。

三重クォートをコメントアウト代わりに使わないでください。関数やクラスの先頭以外に置いた三重クォートは docstring にならず、実行時に評価される無駄な文字列リテラルとして残ります。

プロジェクトで統一する

pip install black
black .            # すべてダブルクォートに統一される(エスケープが増える場合を除く)

black は「ダブルに変えるとエスケープが増える」場合だけシングルを残します。クォートの流儀を人間が議論しても価値が出ないので、ツールに任せて設定は pyproject.toml に 1 か所だけ書きます。

[tool.ruff.format]
quote-style = "double"      # "single" にすればシングル統一もできる

JSON との関係

import json

data = {"name": "田中", "age": 30}

print(str(data))            # {'name': '田中', 'age': 30}   ← シングル。JSON ではない
print(json.dumps(data))     # {"name": "田中", "age": 30}
print(json.dumps(data, ensure_ascii=False))   # {"name": "田中", "age": 30}

json.loads("{'a': 1}")      # JSONDecodeError: Expecting property name enclosed in double quotes
json.loads('{"a": 1}')      # {'a': 1}   OK

JSON の仕様はダブルクォートのみです。print(dict) の見た目をそのままコピーして API に投げると必ず失敗します。文字列化には json.dumps() を使ってください(辞書型を文字列に変換する方法)。

その他のクォートの前置き

書き方意味使う場面
f"..."f-string。{} の中が式として評価される変数の埋め込み
r"..."raw 文字列。バックスラッシュをそのまま扱う正規表現、Windows のパス
b"..."バイト列バイナリ、通信のボディ
rf"..."組み合わせパターンに変数を埋める
import re

# raw 文字列なら正規表現がそのまま書ける
print(re.findall(r"\d+", "a12b345"))     # ['12', '345']

# f-string の中でクォートを使う(Python 3.12 以降は同じ種類でも書ける)
d = {"name": "田中"}
print(f"名前は {d['name']} です")          # 外がダブルなら中はシングル

# 波かっこそのものを出したいときは 2 個重ねる
print(f"{{ これは波かっこ }}")             # { これは波かっこ }

文字列の連結

# 隣り合ったリテラルは自動で連結される(クォートの種類が違っても可)
message = ("長い文章を "
           'かっこの中で '
           "分けて書ける")
print(message)      # 長い文章を かっこの中で 分けて書ける

# リストの中でカンマを忘れると意図せず連結される
items = [
    "りんご"
    "みかん",       # ← カンマ忘れ。1 要素 'りんごみかん' になる
    "ぶどう",
]
print(items)        # ['りんごみかん', 'ぶどう']

自動連結は長い文を分けて書くのに便利ですが、リストや引数の中でカンマを忘れたときに気づきにくいという副作用があります。要素数が合わないときはここを疑ってください。

エスケープ文字の一覧

書き方意味
バックスラッシュ + n改行
バックスラッシュ + tタブ
バックスラッシュ + "ダブルクォートそのもの
バックスラッシュ + 'シングルクォートそのもの
バックスラッシュ 2 個バックスラッシュそのもの
\u3042Unicode のコードポイント指定(「あ」)
print("1 行目\n2 行目")        # 改行が入る
print(r"1 行目\n2 行目")       # raw なのでそのまま表示される
print("\u3042")               # あ

# Windows のパスは raw 文字列にする
path = r"C:\Users\taro\Desktop"     # \U や \t が誤解釈されない
print(path)

"C:\Users\..." と普通の文字列で書くと、\U が Unicode 指定として解釈され SyntaxError になります。Windows のパスと正規表現は r"..." と覚えてください。

クォートを含む文字列を作る

name = "田中"

# HTML の属性値を組み立てる
print(f'<a href="/user/{name}">{name}</a>')

# SQL を文字列連結で作らない(SQL インジェクションになる)
# cur.execute(f"SELECT * FROM users WHERE name = '{name}'")   ← 危険

# プレースホルダを使う
cur.execute("SELECT * FROM users WHERE name = %s", (name,))

クォートを自分で組み立てて SQL やコマンドを作らないでください。値に ' が含まれるだけで構文が壊れ、意図的に仕込まれれば任意の SQL を実行されます。パラメータは必ずライブラリに渡します。

文字列を囲むもの以外の記法

name, price = "りんご", 128

print(f"{name} は {price} 円")            # f-string(推奨)
print("{} は {} 円".format(name, price))   # format メソッド
print("%s は %d 円" % (name, price))       # 古い書き方

# 桁揃えや書式も f-string でできる
print(f"{price:,}")          # 128        3 桁区切り
print(f"{price:>8}")        #      128   右寄せ 8 桁
print(f"{3.14159:.2f}")      # 3.14       小数点以下 2 桁

# ログには f-string を使わない(出力しないときも文字列を作ってしまう)
import logging
logging.info("%s は %d 円", name, price)

通常のコードでは f-string が最も読みやすく、速度も有利です。ログ出力だけは例外で、遅延評価される %s 形式が推奨されています。

関連

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 基本的なルール
  2. 変数
  3. 演算子
  4. 標準ライブラリ
  5. 外部ライブラリ
  6. 制御構文
  7. リスト(配列)
  8. タプル
  9. セット
  10. 辞書(dict)
  11. クラスとメソッド
  12. 継承の概念と必要性
  13. 継承の構文
  14. コンストラクタ
  15. cookieの値の設定と取得
  16. 例外処理
  17. 例外を文字列で出力する方法
  18. httpリクエスト(curl)をする方法
  19. Responseオブジェクトの中身の確認
  20. 変数が空かどうか判定する方法
  21. タイムゾーンの設定と現在日時の取得と文字列化
  22. シングルクォーテーションとダブルクォーテーションの違い

最近更新/作成されたページ