現役文大生がつまづいたPython疑問【発展編】
はじめに
はじめまして!文系慶應SFCの1年生のナベと申します!
これは、情報基礎1・2などでPythonを触り始めてつまずいた疑問まとめの【発展編】です。【入門編】では型・リストと辞書・条件分岐などを扱いましたが、こちらは関数・クラス・importなど、少し抽象度が上がる疑問を集めました。
分からないたびにAIへ質問していたログをもとに、当時の素朴な疑問をそのまま見出しにしています。入門編よりは難しめですが、引っかかった順番に読めばつながるように並べました。
1. 「クラス」と「オブジェクト」の違いがわからん
ここはかなり長く止まりました。
結論から書くと、こうです。
- クラス = 設計図
- オブジェクト = 設計図から作られた実物
たい焼きの例が一番しっくりきました。型がクラスで、焼き上がったたい焼きがオブジェクトです。あんこでもカスタードでも、同じ型から作れます。
a = [10, 20, 30]
b = [40, 50, 60]
print(type(a)) # <class 'list'>
この場合、list がクラスです。a と b は、同じ list という設計図から作られた別々のオブジェクトです。
さらに混乱したのが、「じゃあリストの中の 10 とかは何なの?」という点でした。
答えは、10 もオブジェクトです。
print(type(a[0])) # <class 'int'>
Pythonでは、数値も、文字列も、リストも、全部オブジェクトです。リストというオブジェクトの中に、int のオブジェクトが入っている。そういう世界観です。
最初は大げさに聞こえたんですが、この考え方を押さえると、次の「メソッド」の話がかなり分かりやすくなります。
2. 「関数」と「メソッド」って何が違うの?
関数もメソッドも、処理を実行する仕組みです。違いは呼び出し方です。
fruits = ["apple", "banana", "orange"]
print(len(fruits)) # 関数
fruits.append("grape") # メソッド
- 関数は
len(fruits)のように、そのまま呼ぶ - メソッドは
fruits.append("grape")のように、オブジェクトにくっつけて呼ぶ
メソッドは、「そのオブジェクトができること」です。fruits.append("grape") は、「fruits が grape を追加する」と読めます。英語の文みたいです。
ここで大事なのは、メソッドは種類によって使えるものが違うということです。
a = [10, 20]
a.append(30) # OK
b = 10
b.append(30) # エラー。intにappendはない
リストには append() があるけれど、整数にはありません。当然といえば当然ですが、「クラスが使える操作を決めている」という話とつながると、かなり納得感がありました。
辞書の items() もメソッドです。つまり、「クラスとオブジェクト」と「関数とメソッド」はセットで理解したほうがいいです。
3. 「pop()」で取り出したものが変数に入るのはなんで?残りじゃないの?
fruits = ["apple", "banana", "orange", "grape"]
last_item = fruits.pop()
print(last_item) # grape
print(fruits) # ['apple', 'banana', 'orange']
ここも引っかかりました。
fruits.pop() で最後の要素を取り出したなら、last_item には「残ったリスト」が入りそうな気がしたんです。でも実際に入るのは、取り出した "grape" です。
ポイントは、メソッドには2つの顔があることです。
- 元のデータをどう変えるか
- 結果として何を返すか
pop() は、
- リストの末尾を削除する
- 削除した要素を戻り値として返す
という動きをします。
だから last_item = fruits.pop() は、「右側を実行して、その戻り値を左側に入れる」という意味になります。= は「変更後のリストを保存する」という意味ではありません。
| メソッド | データへの変更 | 戻り値 |
|---|---|---|
append(x) |
末尾に追加する | None |
pop() |
末尾を削除する | 削除した要素 |
sort() |
並べ替える | None |
append() が何も返さないのも、ここでようやく腑に落ちました。「変更したこと」と「返したもの」は別々に決まっている。これが分かると、メソッドの見え方が変わります。
4. 「f-string の f」は数字を文字に変えるものじゃないの?あと「return の string」って何?
def greet(name):
string = f'{name}さん、こんにちは'
return string
s = greet('佐藤')
print(s) # 佐藤さん、こんにちは
ここでは2つの勘違いが重なっていました。
まず f です。f は「数字を文字列に変える記号」ではありません。f-string の f は、文字列の中に変数の値を埋め込むためのものです。
name に '佐藤' が入っていれば、f'{name}さん、こんにちは' は '佐藤さん、こんにちは' になります。name がもともと文字列でも問題ありません。
次に string です。これは特別な命令ではありません。ただの変数名です。自分でつけた名前です。だから x でも message でも動きます。
return string は、「string に入っている値を関数の外へ返す」という意味です。
そして return と print の違いも、ここで整理できました。
| return | ||
|---|---|---|
| 役割 | 関数の結果を外に返す | 画面に表示する |
| 結果を変数に保存 | できる | できない |
| 関数を終了する | する | しない |
この違い、分かっている人には当たり前なんでしょうけど、初心者にはかなり重要でした。print は「見せるだけ」、return は「渡すもの」です。
5. 関数の中と外で「同じ変数名」を使ってもいいの?「グローバル変数」は関数の中で使える?
使っても大丈夫です。関数の中の変数と、外の変数は別物として扱われます。
x = 5
def test():
x = 100
print(x)
test() # 100
print(x) # 5
関数の中で x = 100 と書いても、それは関数の中だけの x です。外の x は 5 のままです。
この「変数が使える範囲」をスコープと呼びます。名前は難しそうですが、要するに「内側と外側は別の作業スペース」というイメージです。
グローバル変数は、関数の中から読むだけなら使えます。
x = 10
def show():
print(x)
show() # 10
関数の中に x がなければ、Pythonは外側を探しに行きます。
ただし、関数の中で同じ名前の変数に代入すると、それはローカル変数として扱われます。外側の変数を書き換えたい場合は global x と書く方法もありますが、基本的には「引数で渡して、return で返す」ほうが分かりやすいです。
引数もローカル変数の一種です。だから、関数の外にある r と、関数の引数の r が同じ名前でも問題なかったわけです。
6. 「def my_sum(a, b=10):」の b=10 は、先に定義しちゃってるってこと?
def my_sum(a, b=10):
return a + b
print(my_sum(5, 8)) # 13
print(my_sum(5)) # 15
最初は、「b には最初から10が入っているの?」と思いました。
正確には違います。b=10 は、「何も指定されなかったら10を使う」という意味です。これをデフォルト引数と呼びます。
my_sum(5, 8) と呼べば、b は 8 になります。my_sum(5) と呼んだときだけ、b に 10 が自動で使われます。
C++にも同じ仕組みがあります。Pythonだけの謎ルールではありませんでした。こういうのを知ると、少し安心します。
7. import の「as」と「from」が分からない
最後は import です。ここは序盤でかなり混乱しました。
import numpy as np
from sklearn.linear_model import LinearRegression
まず as です。これは「別名をつける」という意味です。
import numpy as np
print(np.pi)
numpy を読み込んで、それを np という短い名前で呼べるようにしています。機能は変わりません。名前が短くなるだけです。
別名は自由につけられますが、普通は慣習名を使います。
| ライブラリ | 慣習名 |
|---|---|
| NumPy | np |
| pandas | pd |
| matplotlib.pyplot | plt |
次に from です。これは英語の from そのままで、「〜の中から取り出す」という意味です。
import math
print(math.pi)
この書き方だと、math.pi のように毎回 math. を書きます。
from math import pi
print(pi)
こちらだと、pi を直接使えます。
import random と from random import choice の違いも同じです。
-
import randomは、random 全体を使えるようにする -
from random import choiceは、random の中の choice だけを直接使えるようにする
ここで自分がつまずいたのが、from p import pi の話です。
import math as p と書いたら、math は p という名前で呼べるようになります。じゃあ from p import pi でいいのでは、と思いました。
でも違いました。p はあくまでプログラムの中でつけた呼び名です。モジュールそのものの名前が p に変わったわけではありません。from の後ろには、本当のモジュール名を書く必要があります。
import math as p
from math import pi
この形なら、math 全体は p で呼べて、pi は直接呼べます。
覚え方はこれです。
from A import B as C = AからBを取り込んで、Cという名前で使う
import 文はこの形に分解すると、だいぶ読めるようになりました。
まとめ
発展編のつまずきは、言葉の意味が曖昧なまま進んでいたことが原因でした。
- クラスは設計図、オブジェクトは実物。メソッドはオブジェクトにくっついた関数
- メソッドには「変更」と「戻り値」という2つの側面がある
- 変数にはスコープがあり、import にも分解できる形がある
「関数とクラスの違いがわからない」と思っていた頃よりは、だいぶ地図が描けるようになってきました。
次は、勉強していてつまずいた「機械学習って結局ただの計算じゃないの?」という疑問についてまとめます。線形回帰、訓練用データとテスト用データ、誤差の話です。