前の記事では self.shape = shape について、整理をしてみました。
こういう紛らわしいやつ、クラス以外でもよく出てくるような気がします。
「同じ文字並べるな!!やめてくれ!!区別つかないけど!?」ってやつです。
今回はその代表格を2つ取り上げます。
calendar.calendar(2026) # calendarが2回!?
json.dump(fp=fp, obj=data) # fpが2回!?
最初に見たとき、どちらも「え?タイプミス??」と思いました。
(ちなみにこれを「タイポ」というらしいですね。「Typographical error」の略で、印刷の世界の言葉がIT界隈にも広まったとか……)
しかしミスでもなんでもなく、ちゃんと意味のある正しい書き方なのだそうで。
初見でわかるわけがないぞ。
詳しく解説しているテキストなどにも出会ったことがないです。
皆さんそんなところで躓かないのかもしれません。
私はご覧の通り、大けがしています。
自分で深掘りするしかないので、やっていくとします。
① calendar.calendar(2026) 問題
import calendar
calendar.calendar(2026)
正確には上記のように使っていきます。
カレンダーをインポートする、は文字列から解釈すると、カレンダーという機能を取り込んだのかな?みたいなイメージが沸き、実際のイメージと相違ないかと思います。問題は、次です。
「えっ……calendar が2回出てきたけど何が違うの?」
カレンダーのカレンダー、って意味分からないですよね?
これ、ドット(.)の左右で役割が違うんですよね。
calendar . calendar(2026)
↑ ↑
モジュール名 関数名
- 左の
calendar→import calendarで読み込んだモジュール(Pythonファイル)の名前 - 右の
calendar→ そのモジュールの中にある関数の名前
つまり!
たまたま両方「calendar」という名前なだけで、Python的にはまったく別物として扱われているんです。
いや、ややこしいがな!!なんで同じ名前なんだよ!!とつっこみたい。
モジュールって何?という話をすると長くなるので簡単に言うと、「道具箱」 みたいなものです。import calendar は「calendarという道具箱を持ってくる」という意味で、calendar.calendar(2026) は「その道具箱の中にある、calendarという道具を使う」という意味です。
よくある解説と同じことしか言えないですが、非常にわかりやすい例えだなと思います。
今回は同じ名前が並んでいるので分かりづらいですが、別の関数と並べると構造が見えやすいので、以下に記載しておきます。
calendar.prmonth(2026, 5) # calendar道具箱の中の、prmonth道具を使う
calendar.calendar(2026) # calendar道具箱の中の、calendar道具を使う ← たまたま同名
ドットの左が道具箱(モジュール)、右が道具(関数)。これだけでした。
分かった上で見ても、やはり紛らわしい。
② json.dump(fp=fp) 問題
これは……混乱しましたよ。
だって、fp=fpですよ。同じ文字列を=で挟んでどうしたいんだ?ってなりませんか。
まずは参考コードを以下に記載します。
import json
fp = open("output.json", "w")
data = {"name": "田中", "age": 28}
json.dump(obj=data, fp=fp)
一応最初に何をしているコードかだけ解説しておきます。
-
fp = open("output.json", "w")→output.jsonというファイルを「書き込みモード」で開いて、fpと名付けた -
data = {"name": "田中", "age": 28}→ 保存したいデータを用意した -
json.dump(obj=data, fp=fp)→ そのデータをファイルに書き込んだ
つまり「田中さんのデータをJSONファイルに保存する」という処理です。
さて、改めて本題に入ります。
「fp=fp って……左と右の fp は何が違うの??」
大文字小文字の区別もないし……わからない。やめてほしい。
これは 引数名 = 変数名 という書き方で書かれていてキーワード引数と呼ばれるそうです。「どの席(引数)に何を渡すか」を名前で明示するための書き方です。ちょっと難しい。
json.dump( fp = fp )
↑ ↑
関数が決めた 自分が作った
引数の名前 変数
左の fp(=の左)は、json.dump という関数があらかじめ決めた引数の名前です。「ここにファイルを渡してください」という席の名札みたいなもの。自分では変えられません。
Python側がもともとこういう関数を用意しているからです。
こちら、少し補足します。
このあと、defという新キャラが登場するので、それについても少し補足を。
json.dump は先述したとおり、自分で書いた関数ではなく、Pythonが標準で用意している関数(標準ライブラリ)です。
-
defは「define(定義する)」の略で、関数を作るときに使う書き方です。 -
dumpは「捨てる・流し込む」という英語で、プログラミングの世界では「データをどこかに書き出す」という意味で使われます。ゴミをダンプカーに積み込むイメージです。なのでjson.dumpは「JSONの形式でデータをファイルに流し込む」という関数名になっています。
ここまでをぼんやりと頭の隅っこにおいて、以下のコードを見てみます。
# Python側があらかじめこう定義している(イメージ)
def dump(obj, fp):
# obj(データ)を fp(ファイル)に書き込む処理
fp という席は最初から存在していて、自分が json.dump(fp=fp) と書くとき、左の fp はその席の名前を指定しているだけ、ということです。
一方、右の fp(=の右)は、自分が open() で作った変数です。こっちは自由に名前をつけられます。
試しに右側だけ別の名前にしても動きます。
myfile = open("output.json", "w")
json.dump(obj=data, fp=myfile) # 右側をmyfileにしてもOK
つまり、左右の fp は「たまたま同じ名前にした」というだけで、別物です。
何故同じ名前にする必要が……??と思いますよね。
fpの由来に触れると納得できるかもしれませんので、少し触れておきます。
fpは「file object(ファイルオブジェクト)」、つまり「ファイルそのものを操作するためのオブジェクト」を指す変数につける定番の名前です。ちなみに昔は「file pointer」の略として使われていたこともあったようですが、最近のPythonの公式ドキュメントでは file object として扱われています。
慣習に従った結果、fp=fp という見た目になっているだけなんですね。
違う名前にしすぎると「これって、結局どの変数に入るんだっけ?」と迷子になってしまうので、統一されているというわけです。
「fp(ファイル)というデータは、どこまで行っても fp と呼ぶのが一番シンプル!」という、ある種の割り切りがこの書き方を生んでいます。
どう考えても意地悪じゃねーか、と思うんですけど、現場では一般的なんですよね。
因みにここまで書いた上での感想は「私には理解できない」です。相変わらず。だって、紛らわしくないのが一番じゃないのか??
そうまでしてわざわざ同じ名前にする必要があるのかなぁ、という感じです。
とはいえ「私は私の道を行くぜ!!」と慣習を破れるような人間ではないので、もれなく従うんですけどね。
エンジニアさん達は、全く気にしていないのかなぁ。
これはこういうもの、で割り切って、そんな細かいことは気にしていないのかも。
| 位置 | 正体 | 変えられる? |
|---|---|---|
= の左(fp) |
関数が決めた引数名 | 変えられない |
= の右(fp) |
自分が作った変数 | 自由につけてOK |
2つに共通していること
見た目は紛らわしいですが、結局のところ同じ名前でも役割が全然違うということです。
calendar.calendar も fp=fp も、最初は「え、タイプミス?」ってなりますよね。でも役割さえわかれば怖くないです(私はまだ怖いです)
前の記事で self.shape = shape の左右が違う、という話をしました。あれと全く同じことですね。……ややこしいって。
まとめ
| 疑問 | 答え |
|---|---|
calendar.calendar の左右は何が違うの? |
ドットの左がモジュール名、右が関数名 |
fp=fp の左右は何が違うの? |
左は関数が決めた引数名(キーワード引数)、右は自分が作った変数 |
少しずつパターンを理解して覚えていくしかないですね。
今回心の声が多くなった気がします。申し訳ございません。
リアルをお伝えしています。
次の記事では、これまでのまとめと復習を兼ねてself.name = name の謎に迫ります。