Pythonで「区切り文字も欠かさず取り出したい」という場面では、partition
がとても有効です。
本記事では、split
では失われがちな区切り文字を残したまま、文字列を確実に3分割する方法と、その使い所、落とし穴までを丁寧に解説します。
初心者の方でも段階的に理解できるよう、実行例と出力を示しながら進めます。
Pythonのpartition()とは
区切り文字を残して3分割する仕組み
str.partition(sep)
は、文字列の左側から最初に見つかった区切り文字(文字列)で分割し、3つの要素からなるタプルを返します。
返り値には区切り文字そのものも含まれるため、解析や再構築が簡単になります。
sep
は空文字にはできません(空なら例外が発生します)。
戻り値の順序と役割
返り値は次の3要素です。
- 先頭部分(区切りより左)
- 区切り文字そのもの
- 末尾部分(区切りより右)
例として、"a=b=c".partition("=")
なら、最初の=
で分割するため「左」「区切り」「右」は次のようになります。
要素 | 意味 | 具体例 |
---|---|---|
先頭部分 | 最初の区切り文字の手前 | “a” |
区切り文字 | 見つかった区切り文字 | “=” |
末尾部分 | 区切り文字の直後から末尾 | “b=c” |
基本構文と最小の使い方
最小の使い方は、元の文字列に対してpartition
を呼び、タプルを3変数にアンパックするだけです。
区切り文字を使わない場合は「捨てる変数」として慣習的に_
を使います。
# 基本の使い方
s = "key=value=more"
left, sep, right = s.partition("=")
print(left, sep, right) # 3つの値として展開
# 区切り文字を使わないなら _ に捨てる
key, _, value_and_more = s.partition("=")
print(key, value_and_more)
key = value=more
key value=more
なお、空文字を区切りに指定するとValueError
になります。
# 空文字は不可(例外が出ます)
try:
"abc".partition("")
except ValueError as e:
print(type(e).__name__, e)
ValueError empty separator
partition()の使い方と実例
key=valueをpartitionで3分割
設定ファイルや環境変数のようにkey=value
形式の行を解析するには、partition("=")
が直感的で頑健です。
最初の=
だけで分けるため、値側に=
が含まれても安全です。
# .env風の行を解析する例
lines = [
"DB_HOST=localhost",
"PATH=/usr/bin:/bin:/usr/local/bin",
"SECRET=abc=def=ghi", # 値に = を含むケース
"NO_VALUE_LINE" # 区切りが無いケース
]
for line in lines:
key, sep, value = line.partition("=")
if sep: # 見つかった場合は sep が "=" になる
print(f"key({key}) sep({sep}) value({value})")
else:
print(f"区切り無し: {line}")
key(DB_HOST) sep(=) value(localhost)
key(PATH) sep(=) value(/usr/bin:/bin:/usr/local/bin)
key(SECRET) sep(=) value(abc=def=ghi)
区切り無し: NO_VALUE_LINE
このように、値に=
が複数含まれても、最初の1つでだけ分割されるため、左側のキーが取り違えられることはありません。
最初の区切りだけで分割する
partition
は常に「最初の一致」で3分割します。
split
でもmaxsplit=1
を指定すれば似たことができますが、split
は区切り文字を返り値に含めません。
s = "a=b=c"
# partition は区切り文字を保持する
left, sep, right = s.partition("=")
print(left, sep, right)
# split は区切り文字を捨てるが、maxsplit=1 で2要素リスト
parts = s.split("=", 1)
print(parts) # ["a", "b=c"]
a = b=c
['a', 'b=c']
区切り文字を保持できるかどうかが、partition
の大きな利点です。
区切りが見つからないときの挙動
partition
は区切りが見つからない場合でも例外を出さず、(元の文字列, “”, “”)を返します。
sep
が空文字になることを利用して存在判定が簡単にできます。
s = "hello"
left, sep, right = s.partition(",")
print(f"left={left!r}, sep={sep!r}, right={right!r}")
print("区切りが見つかったか:", bool(sep)) # sep が空文字なら False
left='hello', sep='', right=''
区切りが見つかったか: False
splitやrpartitionとの違い
splitとの違いと使い分け
split
との主な違いを押さえておくと選択を誤りません。
観点 | partition | split |
---|---|---|
返すもの | タプル(3要素固定) | リスト(要素数可変) |
区切り文字 | 返り値に含まれる | 返り値に含まれない |
分割回数 | 最初の1回のみ | 指定しなければ全て、maxsplit で制御可能 |
区切り未検出 | (s, “”, “”) | [s] |
既定の区切り | 必ず指定が必要 | 既定は空白類(特殊動作) |
- 区切り文字を保持したい、最初の1回だけ分けたい、常に3つにアンパックしたい時は
partition
が最適です。 - 区切り文字は不要で、可変個に分割したい時は
split
が向いています。
rpartitionで末尾から3分割
末尾側(右側)から最初に見つかった区切りで3分割するのがrpartition
です。
ファイル名と拡張子、URLの最後のスラッシュなど、右側から切りたいケースで便利です。
filename = "archive.tar.gz"
left, sep, right = filename.rpartition(".")
print(left, sep, right)
no_dot = "README"
l2, s2, r2 = no_dot.rpartition(".")
print(l2, s2, r2) # 見つからない場合は ("", "", s) を返す
archive.tar . gz
README
1行目では最後のピリオドで分割され、拡張子gz
を簡単に取り出せています。
2行目は区切りが見つからず、左と区切りが空文字、右に元文字列が入る点がpartition
と対称的です。
どちらを選ぶかの基準
- 左から最初の区切りで分けたい →
partition
- 右から最初の区切りで分けたい →
rpartition
- 区切り文字は不要で、複数回に分けて配列として扱いたい →
split
(必要ならmaxsplit
を指定)
実務では、ヘッダー名と値、スキームと残り、パスと拡張子のように「一度だけ・方向固定」で分けたい場面が多く、partition
とrpartition
が短く明快なコードにつながります。
よくある落とし穴とベストプラクティス
先頭や末尾が区切りのときの結果
区切りが先頭や末尾にあると「空文字」が返ります。
これは正常な挙動なので、必要に応じて空文字チェックを入れます。
print("=value".partition("=")) # 先頭が区切り
print("value=".partition("=")) # 末尾が区切り
print("=only=".partition("=")) # 両側に区切り
('', '=', 'value')
('value', '=', '')
('', '=', 'only=')
空文字を許容するかどうかは要件次第です。
例えば「キーは空不可」なら、if key:
で簡単に検証できます。
連続する区切り文字の扱い
partition
は「最初に一致した1回」だけを利用します。
連続する記号でも圧縮したりはしません。
区切りは1文字でも複数文字でも構いません。
s1 = "a==b"
print(s1.partition("=")) # "=" で分けると最初の1つだけ
print(s1.partition("==")) # "==" で分ければ2文字の区切りとして動作
s2 = "a|||b"
print(s2.partition("||")) # 最初に一致する "||" が使われる
('a', '=', '=b')
('a', '==', 'b')
('a', '||', '|b')
どの文字列が区切りかを明確に決め、入力に合わせて単一文字か複数文字かを選ぶことが重要です。
複数回の分割を行う手順
「一度で3分割」以上に段階的な解析をしたい場合、partition
やrpartition
を組み合わせると読みやすくなります。
例えばURLからスキーム、ホスト、ポート、パスを抜き出す簡単な例です。
url = "https://example.com:443/path/to/page?query=1"
# 1) スキームと残り
scheme, sep, rest = url.partition("://")
if not sep:
scheme, rest = None, url # スキーム無しURLの簡易対応
print("scheme:", scheme)
# 2) ホスト(とポート)とパス以降
host_port, sep, path_and_query = rest.partition("/")
print("host_port:", host_port)
print("path_and_query:", "/" + path_and_query if sep else "")
# 3) ホストとポートを右から分割(ポートが無いURLにも対応)
host, sep, port = host_port.rpartition(":")
if not sep:
host, port = host_port, None
print("host:", host)
print("port:", port)
scheme: https
host_port: example.com:443
path_and_query: /path/to/page?query=1
host: example.com
port: 443
このように、小さな確実な分割を段階的に積み上げると、例外に強く読みやすいコードになります。
多数の区切りで一括に分けたい場合はsplit
が向きますが、「この地点で1回だけ確実に切りたい」という用途にはpartition
やrpartition
が適しています。
まとめ
partition
は「区切り文字を保持したまま、最初の一致で3分割する」というシンプルかつ強力なメソッドです。
返り値は常に(左, 区切り, 右)の3要素で、区切りが無い時も例外を出さず(元文字列, “”, “”)となるため、条件分岐が容易です。
split
と違って区切り文字が失われないため、ログや設定の解析、文字列の再構築に向いています。
末尾側から切りたい時は対になるrpartition
を使い分けましょう。
空文字を区切りに指定できない点や、先頭・末尾が区切りのときに空文字が返る点、連続する記号の扱いなどの挙動を押さえておけば、堅牢で読みやすい文字列処理が実現できます。