はじめに
普段は物流トラックドライバーをしながらPython学習をしている tosane932 です。
僕がPython学習を始めた初期から現在進行形で開発している、
🍞 sales_data_app 🥖
の実際のコードを教材に、Python基礎を学び直しています。
これまで、
1時限目
変数・データ型・比較演算子・if文
2時限目
リスト・辞書・set・for文
3時限目
関数・引数・return・関数の呼び出し・型ヒント
4時限目
モジュール・import・例外・try・except
5時限目
クラス・インスタンス・__init__・self・属性
6時限目
f文字列・アンパック・リスト内包表記・join
と学び直してきました。
6時限目の最後には、
key=lambda item: item[1]
という、まだ詳しく学んでいない lambda も出てきました。
🐹「次はlambdaかな?」
とも思いましたが、その間にも sales_data_app の開発は進みました。
現在は、店舗で使うための店舗メモツールもかなり形になっています。
せっかく教材そのものが成長しているので、
無理に前回の続きを追いかけず、
今回は現在の店舗メモのコードを読んでみます。
🐹「教材の方に新しいページが増えてる。」
今回見るのは、
shop_memos.py
です。
その中には、
title = raw_title.strip()
や、
if len(title) > SHOP_MEMO_TITLE_MAX_LENGTH:
さらに、
for line in body.splitlines():
stripped_line = line.strip()
if stripped_line:
return stripped_line[:SHOP_MEMO_TITLE_MAX_LENGTH]
というコードがあります。
🐹「strip、len、splitlines、最後の [:...] は何だ?」
今回は店舗メモの文字列を扱う実コードから、
strip・len・splitlines・スライス
について学び直していきます。
🍞 Guest Demoを公開しています
sales_data_app のGuest Demoを公開しています。
現在は店舗メモツールも実際に操作できます。
https://bakery-salesdata.onrender.com/
Render上で公開しているため、初回アクセス時は画面が表示されるまで少し時間がかかる場合があります。
今回のコードはどこにある?
今回使うコードは、
sales_data_app
↓
shop_memos.py
にあります。
まず、店舗メモの入力内容を確認する、
_validate_memo()
という関数があります。
実コードの一部を見ると、
def _validate_memo(form):
raw_title = form.get("title", "")
title = raw_title.strip()
raw_body = form.get("body", "")
body = raw_body.strip()
if not title:
return None, None, raw_title, raw_body, "タイトルを入力してください。"
if len(title) > SHOP_MEMO_TITLE_MAX_LENGTH:
return None, None, raw_title, raw_body, "タイトルは100文字以内で入力してください。"
if not body:
return None, None, raw_title, raw_body, "メモを入力してください。"
if len(body) > SHOP_MEMO_BODY_MAX_LENGTH:
return None, None, raw_title, raw_body, "メモは2000文字以内で入力してください。"
となっています。
さらに、
def _derive_title_from_body(body):
for line in body.splitlines():
stripped_line = line.strip()
if stripped_line:
return stripped_line[:SHOP_MEMO_TITLE_MAX_LENGTH]
return body.strip()[:SHOP_MEMO_TITLE_MAX_LENGTH]
という関数もあります。
今回はこの2つの関数を中心に読んでいきます。
🍞 それでは7時限目
今回のテーマ
「strip・len・splitlines・スライスとは?」
今回学び直すのは4つです。
1. ✂️ strip()とは?
まずは、
title = raw_title.strip()
です。
strip() は、
文字列の前後にある空白などを取り除く
ために使えます。
例えば、
text = " メロンパン "
という文字列があるとします。
これを、
text.strip()
とすると、
メロンパン
になります。
前後にあった空白が取り除かれます。
前後だけを取り除く
ここで大事なのは、
文字列の途中にある空白まで全部消すわけではない
ということです。
例えば、
text = " 今日の おすすめ "
なら、
text.strip()
の結果は、
今日の おすすめ
です。
途中の、
今日の おすすめ
↑
この空白は残ります。
取り除くのは基本的に、
今日の おすすめ
↑ ↑
前 後
の部分です。
🍞 店舗メモでは?
実コードでは、
raw_title = form.get("title", "")
title = raw_title.strip()
となっています。
例えば利用者が、
明日のおすすめパン
と入力した場合、
最初に、
raw_title
には、
" 明日のおすすめパン "
が入ります。
そこから、
raw_title.strip()
すると、
"明日のおすすめパン"
になります。
そして、その結果を、
title
へ入れています。
流れにすると、
入力された文字列
↓
raw_title
↓
strip()
↓
前後の余分な空白を取り除く
↓
title
です。
🐹「入力された文字列を、そのまま使うんじゃなくて一度整えてるんだ。」
bodyでも同じことをしている
タイトルだけではありません。
店舗メモの本文でも、
raw_body = form.get("body", "")
body = raw_body.strip()
としています。
つまり、
raw_title
↓
title
raw_body
↓
body
の両方で、
strip() を使って前後を整えています。
2. 🔢 len()とは?
次は、
len(title)
です。
len() は、
文字列などの長さを調べる
ために使えます。
例えば、
bread = "メロンパン"
なら、
len(bread)
は、
5
になります。
メ
ロ
ン
パ
ン
で5文字だからです。
🍞 店舗メモでは文字数制限に使っている
shop_memos.py には、
SHOP_MEMO_TITLE_MAX_LENGTH = 100
SHOP_MEMO_BODY_MAX_LENGTH = 2000
という値があります。
店舗メモでは、
タイトル
↓
100文字まで
本文
↓
2000文字まで
という上限を設定しています。
その確認に使われているのが、
len()
です。
実コードでは、
if len(title) > SHOP_MEMO_TITLE_MAX_LENGTH:
となっています。
分けて読むと、
len(title)
↓
タイトルの文字数
>
SHOP_MEMO_TITLE_MAX_LENGTH
↓
100
なので、
タイトルの文字数が
100より大きいなら
という条件になります。
1時限目の比較演算子がまた出てきた
ここで、
>
が登場しています。
1時限目で学んだ比較演算子です。
if len(title) > SHOP_MEMO_TITLE_MAX_LENGTH:
は、
len()
↓
文字数を調べる
>
↓
大小を比較する
if
↓
条件によって処理を分ける
と、これまで学んだ基礎がまた組み合わさっています。
🐹「新しいlenの中にも、1時限目のifがいる。」
本文も同じ
本文では、
if len(body) > SHOP_MEMO_BODY_MAX_LENGTH:
となっています。
こちらは、
本文の文字数
↓
2000文字より大きい?
を確認しています。
長すぎる場合は、
"メモは2000文字以内で入力してください。"
という文字列を return しています。
ここにも3時限目で学んだ、
return
が出てきます。
3. 📄 splitlines()とは?
次は、
body.splitlines()
です。
これは店舗メモのタイトルを本文から作る処理に登場します。
実コードはこちらです。
def _derive_title_from_body(body):
for line in body.splitlines():
stripped_line = line.strip()
if stripped_line:
return stripped_line[:SHOP_MEMO_TITLE_MAX_LENGTH]
🐹「一気に知らないものが増えた。」
まずは、
splitlines()
だけ見てみます。
複数行の文字列を行ごとに分ける
例えば、
memo = """明日のおすすめ
メロンパン
クロワッサン"""
という文字列があるとします。
画面上では、
明日のおすすめ
メロンパン
クロワッサン
と3行あります。
これを、
memo.splitlines()
とすると、
イメージとしては、
[
"明日のおすすめ",
"メロンパン",
"クロワッサン",
]
のように、
1行ずつ分けた形
として扱えます。
2時限目のリストとforにつながる
実コードでは、
for line in body.splitlines():
となっています。
body.splitlines() で行ごとに分けて、
その内容を、
for
で1行ずつ見ています。
つまり、
メモ本文
↓
splitlines()
↓
行ごとに分ける
↓
for
↓
1行ずつlineへ入れる
という流れです。
🐹「splitlinesで分けて、forで1個ずつ見るのか。」
ここでも2時限目で学んだ for が再登場しています。
4. 🍞 店舗メモでは何のために行ごとに見る?
では、なぜ店舗メモの本文を、
splitlines()
で分けているのでしょうか。
実コードをもう一度見ます。
def _derive_title_from_body(body):
for line in body.splitlines():
stripped_line = line.strip()
if stripped_line:
return stripped_line[:SHOP_MEMO_TITLE_MAX_LENGTH]
ここでは、
本文を行ごとに分ける
↓
1行ずつ見る
↓
strip()する
↓
文字が入っている行を見つける
↓
その行をタイトルとして返す
という処理をしています。
例えば本文が、
明日のおすすめ
メロンパンを多めに焼く
だったとします。
最初の行が空なら、
次の行へ進みます。
そして、
明日のおすすめ
を見つけると、
その文字列をタイトルとして使います。
🐹「本文の最初のちゃんと文字がある行を、タイトルにしてるんだ。」
if stripped_line:も出てきた
実コードには、
if stripped_line:
があります。
今回は細かい仕組みまで深入りしませんが、
文字列が空なら、
""
となり、
文字が入っていれば、
"明日のおすすめ"
のようになります。
ここでは、
文字が入っている行を見つけたら
くらいに読んで進みます。
5. 🍰 スライスとは?
最後は、
stripped_line[:SHOP_MEMO_TITLE_MAX_LENGTH]
です。
🐹「角カッコの中にコロンがいる。」
これはスライスという書き方です。
スライスを使うと、
文字列やリストなどの一部分を取り出す
ことができます。
簡単な例
例えば、
bread = "メロンパン"
があります。
ここで、
bread[:3]
とすると、
メロン
を取り出せます。
イメージとしては、
メ ロ ン パ ン
↑─────↑
最初から3文字分
です。
[:3]を分けて見る
[:3]
は、
:
↓
範囲を指定する
左側が空
↓
最初から
3
↓
3の手前まで
という形です。
今回は初心者向けに、
最初から3文字分を取り出す
くらいのイメージで進めます。
🍞 店舗メモでは100文字まで取り出す
実コードでは、
stripped_line[:SHOP_MEMO_TITLE_MAX_LENGTH]
となっています。
そして、
SHOP_MEMO_TITLE_MAX_LENGTH = 100
なので、
実際には、
stripped_line[:100]
と同じような意味になります。
つまり、
文字列の先頭から最大100文字まで取り出す
ということです。
なぜスライスしている?
店舗メモのタイトルには、
100文字まで
という上限があります。
本文の最初の行がとても長かったとしても、
そのまま全部タイトルへ使うのではなく、
[:SHOP_MEMO_TITLE_MAX_LENGTH]
によって、
最大100文字までにしています。
流れにすると、
本文の1行を取り出す
↓
strip()で前後を整える
↓
文字が入っているか確認する
↓
[:100]で最大100文字にする
↓
タイトルとしてreturn
となります。
🔗 4つを全部つなげてみる
ここまで学んだ、
strip
len
splitlines
スライス
を、店舗メモの実コードでつなげてみます。
まず入力内容の確認では、
raw_title = form.get("title", "")
title = raw_title.strip()
として、
入力されたタイトル
↓
strip()
↓
前後を整える
そして、
if len(title) > SHOP_MEMO_TITLE_MAX_LENGTH:
によって、
len()
↓
文字数を調べる
↓
100文字以内か確認する
という処理をしています。
さらに、本文からタイトルを作る処理では、
def _derive_title_from_body(body):
for line in body.splitlines():
stripped_line = line.strip()
if stripped_line:
return stripped_line[:SHOP_MEMO_TITLE_MAX_LENGTH]
return body.strip()[:SHOP_MEMO_TITLE_MAX_LENGTH]
となっています。
こちらは、
body
↓
splitlines()
↓
行ごとに分ける
↓
forで1行ずつ見る
↓
strip()
↓
前後を整える
↓
文字がある行を見つける
↓
[:100]
↓
最大100文字まで取り出す
↓
return
という流れです。
🐹「1個ずつ見ると、そんなに怖くない。」
📝 本文からタイトルが作られるまで
例えば、店舗メモへ、
明日のおすすめパン
メロンパンを多めに焼く
クロワッサンも追加する
と入力したとします。
まず、
body.splitlines()
によって、
行ごとに分かれます。
イメージすると、
[
"",
"明日のおすすめパン",
"",
"メロンパンを多めに焼く",
"クロワッサンも追加する",
]
です。
そして、
for line in body.splitlines():
で1行ずつ見ます。
最初は、
""
なのでタイトルには使いません。
次に、
"明日のおすすめパン"
があります。
これを、
line.strip()
で整えます。
文字が入っているので、
if stripped_line:
の中へ進みます。
最後に、
stripped_line[:SHOP_MEMO_TITLE_MAX_LENGTH]
によって、
最大100文字まで取り出して return します。
結果として、
明日のおすすめパン
がタイトルとして使われます。
📝 7時限目まとめ
今回は、shop_memos.py の実コードから、
strip()
文字列の前後にある空白などを取り除く。
title = raw_title.strip()
len()
文字列などの長さを調べる。
len(title)
splitlines()
複数行の文字列を行ごとに分ける。
body.splitlines()
スライス
文字列などの一部分を取り出す。
stripped_line[:SHOP_MEMO_TITLE_MAX_LENGTH]
を学び直しました。
店舗メモでは、
入力された文字列
↓
stripで整える
↓
lenで文字数を調べる
↓
splitlinesで行ごとに分ける
↓
スライスで必要な部分を取り出す
という形で使われています。
🤔 自分で確認してみる
最後に、今回の内容を自分でも確認してみます。
Q1
次のコードは何をしているでしょう?
title = raw_title.strip()
A. 文字数を数える
B. 前後の空白などを取り除く
C. 文字列を行ごとに分ける
🅰️答えを見る
B. 前後の空白などを取り除く
です。
例えば、
" メロンパン "
なら、
"メロンパン"
になります。
Q2
次の結果はいくつでしょう?
len("メロンパン")
🅰️答えを見る
5
です。
メ
ロ
ン
パ
ン
の5文字です。
Q3
店舗メモで、
if len(title) > SHOP_MEMO_TITLE_MAX_LENGTH:
としているのは何のためでしょう?
🅰️答えを見る
タイトルが、
100文字
を超えていないか確認するためです。
Q4
次のコードは何をするでしょう?
body.splitlines()
🅰️答えを見る
複数行の文字列を、
行ごとに分けます。
例えば、
メロンパン
あんぱん
クロワッサン
なら、
イメージとして、
[
"メロンパン",
"あんぱん",
"クロワッサン",
]
のようになります。
Q5
次の結果は何でしょう?
bread = "メロンパン"
bread[:3]
🅰️答えを見る
メロン
です。
先頭から3文字分を取り出します。
Q6
店舗メモの、
return stripped_line[:SHOP_MEMO_TITLE_MAX_LENGTH]
では何をしているでしょう?
🅰️答えを見る
stripped_line の先頭から、
SHOP_MEMO_TITLE_MAX_LENGTH
で決められた最大文字数まで取り出して、
その値を return しています。
現在は、
SHOP_MEMO_TITLE_MAX_LENGTH = 100
なので、
最大100文字です。
Q7
次の処理を順番に並べるとどうなるでしょう?
strip()
splitlines()
[:100]
for
🅰️答えを見る
今回の _derive_title_from_body() では、
splitlines()
↓
本文を行ごとに分ける
for
↓
1行ずつ見る
strip()
↓
前後を整える
[:100]
↓
最大100文字まで取り出す
という流れです。
🐹 7時限目を終えて
今回は、
6時限目の最後に出てきた lambda をそのまま追いかけるのではなく、
開発が進んだ店舗メモツールのコードを教材にしました。
そこには、
title = raw_title.strip()
if len(title) > SHOP_MEMO_TITLE_MAX_LENGTH:
for line in body.splitlines():
return stripped_line[:SHOP_MEMO_TITLE_MAX_LENGTH]
というPythonの基礎が使われていました。
最初にコードだけ見ると、
🐹「文字列を扱ってるのは分かるけど、何をしてるのか細かくは分からない。」
となります。
でも分けてみると、
strip()
↓
前後を整える
len()
↓
長さを調べる
splitlines()
↓
行ごとに分ける
スライス
↓
必要な範囲を取り出す
という役割が見えてきました。
そして今回も、
if
for
return
など、
1時限目から学んできた基礎が何度も登場しました。
新しい機能を作る
↓
実コードが増える
↓
知らない書き方を見つける
↓
Python基礎として掘る
↓
またコードが少し読めるようになる
今回は特に、
アプリが成長すると、教材も一緒に増える
というのを感じました。
もちろん今回も、記事を1本書いただけで、
「Pythonの文字列操作を全部理解した!」
とは考えません。
これからコードを読んでいて、
.strip()
を見つけたら、
何の前後を整えているんだろう?
と考える。
len(...)
を見つけたら、
何の長さを調べているんだろう?
と考える。
.splitlines()
を見つけたら、
なぜ行ごとに分ける必要があるんだろう?
と考える。
[:100]
のような書き方を見つけたら、
どの範囲を取り出しているんだろう?
と考える。
そんな読み方が少しずつできれば、
今までなら読み飛ばしていた店舗メモのコードも、
少しずつ意味が見えるようになりそうです。
そして shop_memos.py をもう少し読むと、
isinstance(...)
や、
while
など、
まだ詳しく学んでいないPython基礎も残っています。
6時限目で残った、
lambda
も消えたわけではありません。
🐹「教材候補が渋滞してきた。」
全部を順番通りに追いかける必要はありません。
そのときの sales_data_app の実コードから、
気になった基礎を一つずつ掘っていきます。
まだまだPython基礎の学び直しは続きます。
次の時限も、🍞 sales_data_app の実コードを教材にして読んでいきます🐹
📚️ 基礎学習シリーズ
🏫1時限目
「変数・データ型・比較演算子・if文とは?」
🏫2時限目
「リスト・辞書・set・for文とは?」
🏫3時限目
「関数・引数・return・関数の呼び出し・型ヒントとは?」
🏫4時限目
「モジュール・import・例外・try・exceptとは?」
🏫5時限目
「クラス・インスタンス・init・self・属性とは?」
🏫6時限目
「f文字列・アンパック・リスト内包表記・joinとは?」
🏫7時限目
「strip・len・splitlines・スライスとは?」
🏫8時限目
「None・is・is not・isinstanceとは?」
