Python初心者が強い友人と一緒にbotを作った話

キューマン・エノビクトと申します。
初投稿なので至らない点も多数あるとは思いますがよろしくお願いします。
この記事では私が初心者としての視点からコードを書いた感想などを述べていきます。

## 結論
- 初心者だと結構躓くところがある
- 強い人と一緒にやることはとても良い

## 開発に至ったきっかけ
[SOLD OUT 2](https://so2.mutoys.com/)(以下SO2)という、ファンタジー世界でお店を経営するシミュレーションゲームがあります。
ゲーム内ではプレイヤーとNPCによる経済が成り立っていて、さらにありがたいことに市場全体の情報を取得できる[API](https://so2-docs.mutoys.com/common/api.html)が用意されています。
これを使ったサービスに[SOLD OUT 2 市場情報](https://market.kagamikarasu.net/)というものがあって、市場の情報の他、価格の推移や街の人口の推移もグラフで見ることができます。
私はDiscordのSO2の身内サーバーに入っているのですが、Discord上でアイテムの価格が見られたら楽だなあと思うようになって、そこそこPythonを得意としている友達を誘って開発を開始しました。

## 何に躓いたか
以下に2点、私が躓いたポイントを挙げます。
リポジトリは[こちら](https://github.com/Qman11010101/SO2MarketInfoBotForDiscord)です。
どんな感じで開発してたかをだいたい忘れたため、Pull requestやIssue、Commitの履歴を見ながら書いてます。

### if文
サーバーでは、「ハーブティー」を「はぶち」、「スピードポーション」を「スピポ」と略していたので、この略称が使えれば楽だと思ってエイリアスの関数を書きました。この関数に略称を渡すと正式名称が返ってくるように登録していました。以下がその関数(というかそれを記述したpyファイル)です。

```python:Alias.py
def alias(itemName):
    if itemName == "紙":
        return "紙束"
    elif itemName == "彩薬":
        return "駆け出し勇者の彩薬"
    elif itemName == "極彩薬":
        return "駆け出し勇者の極彩薬"
    elif itemName == "はぶち" or "ハブチ" or "ハーブティ":
        return "ハーブティー"
    elif itemName == "スピポ":
        return "スピードポーション"
    else:
        # エイリアス名がない場合はそのまま返す
        return itemName
```

問題となったのはハーブティーの部分でした。この関数にアイテム名を投げるとすべてハーブティーとして扱われてしまうのです。
原因はPythonの真偽値に関する仕様で、**文字列の場合、空文字列以外はTrueとして扱われる**というものでした。andやorなどの論理演算子は条件式に対して使うものなので、この書き方だと条件式の部分は```if "ハブチ":```や```if "ハーブティ":```と書いたも同然なので当然Trueになり、文字列```"ハーブティー"```がreturnされてしまう、というわけです。

```python:
elif itemName == "はぶち" or itemName == "ハブチ" or itemName == "ハーブティ":
    return "ハーブティー"
```

結局上記のように書くことで解決しましたが、あまりスマートではないように思えるので好きではありません。結局この```Alias.py```ファイルはエイリアスをまとめたjsonファイルに取って代わられますが、同じ変数に対していくつかの条件式を適用する場面は他にもあるので似たような書き方の部分がなくなったわけではありませんでした。もう少しスマートな書き方はないでしょうか？未だにわかりません。

(追記)コメント欄で書き方を教えていただきました！辞書型を使うといいそうです。

```python:
elif itemName in ("はぶち", "ハブチ", "ハーブティ"):
    return "ハーブティー"
```

### 三連引用符を使ったときのインデント

```python:Client.py
if len(msgParse) == 0:
                helpMsg = f"""
                    {commandMarket}で商品を指定したときに、登録されたエイリアスを正式名称に変換します。
・add
　エイリアスを追加します。
　使用方法: {commandAlias} add <エイリアス名> <正式名称>
・help
　このヘルプを表示します。
・（コマンド指定なし or 上記以外のコマンド）
　エイリアス一覧を表示します。
"""
```

これはヘルプメッセージですが、三連引用符で囲ったところだとインデントの空白も表示されてしまうので、変な表示にならないためにはこのように左寄せにする必要がありました。しかしこれだとさすがにコードの見栄えが悪いのでなんとかしようと思いました。
調べた結果、```textwrap.dedent()```というものがありました。(参考: [公式ドキュメント](https://docs.python.org/ja/3/library/textwrap.html#textwrap.dedent))
>*この関数は通常、三重引用符で囲われた文字列をスクリーン/その他の左端にそろえ、なおかつソースコード中ではインデントされた形式を損なわないようにするために使われます。*

まさにこのために用意された関数でした。しっかり調べてみてよかったです。

## 実際に少し大きめのプログラムを書いてみて
### エラーとか
**想像以上に躓くことが多い**。何かを実装しては躓いてテストを繰り返して原因を見つけて…というループでした。
これに関しては本当に根気が必要でした。それこそ何度か心が折れそうになったくらいです。
ただ、エラーが出さえすればなにがおかしいのかわかるパターンは多くて助かりました。某刺身にタンポポの記事(現在は削除されてしまっているのが少し残念。知らない人は「[刺身にタンポポ　プログラミング](https://www.google.com/search?client=firefox-b-d&q=%E5%88%BA%E8%BA%AB%E3%81%AB%E3%82%BF%E3%83%B3%E3%83%9D%E3%83%9D%E3%80%80%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0)」とかでググってみよう)をちゃんと読んでいたおかげですね。エラーメッセージは大切。

### コードとか
今回は自分よりPythonが上手い(?)友人と一緒に書いていましたが、勉強になったと思った点がいくつもありました。
例えば、文字列や変数の結合で、私はいつも連結演算子```+```を使っていましたが、友人はformatやf-stringsをどんどん使って書いていました。私もそれを実際に真似して書いてみると、スマートに書ける上わざわざ分割する必要がなくなり、非常に書きやすかったです。それからは私も基本的にformatやf-stringsを使っています。

## まとめ
プログラミングに慣れないうちは大変ではありますが、エラーを出しては調べるという行為を連続的にすることが重要です。わからなければすぐググる、くらいでいいと思います(ググらないと本当に前に進めないので)。

グダグダとした記事になってしまったかもしれませんが、ここまで読んでいただき、ありがとうございました。