TK氏がブログで投稿した記事「Learning Python: From Zero to Hero (Posted Oct 1, 2017)」を翻訳してご紹介しています。
なお、この記事は原著者の許諾を得て翻訳・掲載しています。
まず初めに、Pythonとは何でしょう?その開発者であるグイド・ヴァンロッサム(Guido van Rossum)は、Pythonについて以下のように説明しています。
「Pythonとは、高水準プログラミング言語です。その中核となる設計哲学は、コードの読みやすさと、プログラマーが数行のコードで概念を表現できる構文論です。」
僕がPythonを学習しようと思った最初の理由は、実際にPythonが美しいプログラミング言語だったからです。僕にとってPythonでコーディングして自分の考えを表現することは本当に自然なことでした。
もう一つの理由は、Pythonを使ったコーディングを様々な方法で使用することができるからです。例えば、Pythonを使えばデータサイエンス、ウェブ開発、機械学習のすべてを行うことができます。Quora、Pinterest、Spotifyはすべて、バックエンドのウェブ開発に向けてPythonを使用しています。今回はPythonについて少し学習してみましょう。
基本
1. 変数
変数とは、値を格納する単語であると考えることができます。そのくらいシンプルに考えて大丈夫です。
Pythonでは、変数を定義して値を設定するのは非常に簡単です。「one(1)」という変数に1という数字を格納したいとします。やってみましょう。
one = 1
なんてシンプルなんでしょう。今まさに変数「one(1)」に値1を割り当てたところです。
two = 2
some_number = 10000
また、他の好きな変数に任意の値を割り当てることもできます。上の表のように、変数「two(2)」には整数の2が格納され、「some_number」には10,000が格納されています。
整数のほかに、ブーリアン型(True/False)、文字列型、浮動小数点数型など、他の多くのデータ型を使用することもできます。
# booleans
true_boolean = True
false_boolean = False
# string
my_name = "Leandro Tk"
# float
book_price = 15.80
2. 制御フロー:条件文
「if」は、文がTrue(真)かFalse(偽)かを評価するために式を使用します。Trueの場合は、「if」文の内部にあるコードが実行されます。例えば:
if True:
print("Hello Python If")
if 2 > 1:
print("2 is greater than 1")
2は1より大きいため、「print」のコードが実行されます。
「if」文の条件式がfalseの場合、「else」文が実行されます。
if 1 > 2:
print("1 is greater than 2")
else:
print("1 is not greater than 2")
1は2より大きくないので、「else」文内のコードが実行されます。
「elif」文を使うこともできます。
if 1 > 2:
print("1 is greater than 2")
elif 2 > 1:
print("1 is not greater than 2")
else:
print("1 is equal to 2")
3. ループ/イテレータ
Pythonでは、様々な形で反復処理ができます。その中でも、whileとforについて説明したいと思います。
whileループ:文がTrueである間、ブロック内のコードが実行されます。したがって、以下のコードは1から10までの数値を出力します。
num = 1
while num <= 10:
print(num)
num += 1
whileループは「ループ条件」を必要とします。そしてループ条件がTrueのままであれば反復を続けます。この例では、numが11のとき、ループ条件はFalseになります。
理解を深めるために、もう一つ基本的なコードを書いてみます。
loop_condition = True
while loop_condition:
print("Loop Condition keeps: %s" %(loop_condition))
loop_condition = False
ループ条件はTrueであるため、ループ条件をFalseに設定するまで反復を続けます。
forループ:ブロックに変数「num」を適用すると、「for」文がそれを繰り返し処理します。以下のコードでは、whileコードの時と同じように1から10までの数値が表示されます。
for i in range(1, 11):
print(i)
ほら、とてもシンプルですよね。範囲が1から始まり、11番目の要素まで続きます(ちなみに10は10番目の要素です)。
リスト:コレクション|配列|データ構造
変数1に整数1を格納するとします。しかし、その後2、3、4、5…といった数を格納したくなったとします。
この時、何百万という変数を書くことなく、すべての整数を格納する方法はあるのでしょうか。あなたは考えます。これらを格納する方法は必ずあるはずだと。
Listは、(これらの整数などの)値のリストを格納するために使用できるコレクションです。ではそれを使用してみましょう。
my_integers = [1, 2, 3, 4, 5]
とてもシンプルですね。僕たちは配列を作成し、それをmy_integerに格納しました。
しかし、おそらくあなたは「この配列からどのように値を取得できるのだろうか?」ということを考えていると思います。
素晴らしい質問ですね。Listにはインデックスという概念があります。最初の要素は0(ゼロ)のインデックスを取得し、2番目の要素は1のインデックスを取得し、それが続いていきます。もう理解できましたね。
もう少し説明すると、配列と各要素をそのインデックスで表すことができます。絵に書いてみますね。
Pythonの構文を使用すると、さらに理解しやすくなります。
my_integers = [5, 7, 1, 3, 4]
print(my_integers[0]) # 5
print(my_integers[1]) # 7
print(my_integers[4]) # 4
では、整数を格納したくない場合はどうでしょう。親戚の名前のリストのような文字列を格納したい場合です。僕の場合は、以下のようなコードになります。
relatives_names = [
"Toshiaki",
"Juliana",
"Yuji",
"Bruno",
"Kaio"
]
print(relatives_names[4]) # Kaio
整数と同じように動作しました。やりましたね。
Listsのインデックスがどのように機能するかを学習してきました。さらに、Listのデータ構造(リストに対する項目)に要素を追加する方法もお伝えしておきます。
Listに新しい値を追加する最も一般的なメソッドはappendです。どのように動作するか見てみましょう。
bookshelf = []
bookshelf.append("The Effective Engineer")
bookshelf.append("The 4 Hour Work Week")
print(bookshelf[0]) # The Effective Engineer
print(bookshelf[1]) # The 4 Hour Work Week
appendは非常にシンプルです。要素を(例:「The Effective Engineer」)appendパラメータとして適用するだけで済みます。
さて、Listsについてはもう大丈夫ですね。別のデータ構造について話しましょう。
ディクショナリ(辞書):キー・バリューデータ構造
Listsは整数でインデックスされていることがわかりましたね。しかし、インデックスとして整数値を使用したくない場合はどうすればよいでしょうか?その場合に使用できるいくつかのデータ構造には、数値、文字列、または他の種類のインデックスがあります。
Dictionaryのデータ構造について学習しましょう。Dictionaryは、key(インデックス番号の代わりに使われる名前)とvalue(値)のペアの集合です。以下のコードを見てみましょう。
dictionary_example = {
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
キーは値を指すインデックスです。Dictionaryの値にはどのようにアクセスすればよいでしょうか?もうわかりましたね。キーを使えばよいのです。試してみましょう。
dictionary_tk = {
"name": "Leandro",
"nickname": "Tk",
"nationality": "Brazilian"
}
print("My name is %s" %(dictionary_tk["name"])) # My name is Leandro
print("But you can call me %s" %(dictionary_tk["nickname"])) # But you can call me Tk
print("And by the way I'm %s" %(dictionary_tk["nationality"])) # And by the way I'm Brazilian
僕に関するDictionaryを作ってみました。僕の名前、ニックネーム、国籍です。これらの属性がDictionaryのキーです。
インデックスを使用してListにアクセスする方法を学習したので、インデックスを使用してDictionaryに格納された値(Dictionaryコンテキストのキー)にアクセスしてみましょう。
この例では、Dictionaryに格納されているすべての値を使用して僕のフレーズを出力しました。かなりシンプルですよね。
Dictionaryに関するもう一つの素晴らしい点は、何でも値として使用できることです。僕が作成したDictionaryに、キーとして「age(年齢)」とそれに対応する自分の整数年齢を追加しようと思います。
dictionary_tk = {
"name": "Leandro",
"nickname": "Tk",
"nationality": "Brazilian",
"age": 24
}
print("My name is %s" %(dictionary_tk["name"])) # My name is Leandro
print("But you can call me %s" %(dictionary_tk["nickname"])) # But you can call me Tk
print("And by the way I'm %i and %s" %(dictionary_tk["age"], dictionary_tk["nationality"])) # And by the way I'm Brazilian
このコードでは、「age(年齢)」という文字列をキーとして、「24」という整数を値としてペアで使用しています。
Listsと同様に、Dictionaryに要素を追加する方法を学習しましょう。値を指すキーは、Dictionaryを構成する大きな部分です。これは、Dictionaryに要素を追加するときにも当てはまります。
dictionary_tk = {
"name": "Leandro",
"nickname": "Tk",
"nationality": "Brazilian"
}
dictionary_tk['age'] = 24
print(dictionary_tk) # {'nationality': 'Brazilian', 'age': 24, 'nickname': 'Tk', 'name': 'Leandro'}
Dictionaryのキーに値を割り当てるだけです。何も難しくないですよね。
イテレーション(反復処理):データ構造をループさせる
Pythonの基本で学習したとおり、Listの反復は非常にシンプルです。僕のようなPythonの開発者は一般的にForループを使用しています。やってみましょう。
bookshelf = [
"The Effective Engineer",
"The 4 hours work week",
"Zero to One",
"Lean Startup",
"Hooked"
]
for book in bookshelf:
print(book)
bookshelf(本棚)の各book(本)についてprint(出力)しました(これで何でもできます)。かなりシンプルかつ直感的ですね。それこそがPythonなのです。
ハッシュデータ構造の場合、forループを使用することもできますが、今回はkeyを適用します。
dictionary = { "some_key": "some_value" }
for key in dictionary:
print("%s --> %s" %(key, dictionary[key]))
# some_key --> some_value
このコードは、その使用方法の一例です。dictionaryの各keyについて、keyとそれに対応するvalueをprintしています。
これを行うもう1つの方法は、iteritemsメソッドを使用することです。
dictionary = { "some_key": "some_value" }
for key, value in dictionary.items():
print("%s --> %s" %(key, value))
# some_key --> some_value
2つのパラメータをkeyとvalueとして命名しましたが、これは必ずしも必要なことではありません。何でも命名することができます。以下を見てみましょう。
dictionary_tk = {
"name": "Leandro",
"nickname": "Tk",
"nationality": "Brazilian",
"age": 24
}
for attribute, value in dictionary_tk.items():
print("My %s is %s" %(attribute, value))
# My name is Leandro
# My nickname is Tk
# My nationality is Brazilian
# My age is 24
Dictionaryのkeyとしてattribute(属性)を使用していることがわかりますね。正しく動作しています。やりましたね!
クラスおよびオブジェクト
理論について少しお話しします。
オブジェクトは、車、犬、または自転車といった現実世界にある物の表現です。オブジェクトは、データと振る舞いという2つの主要な特徴を共有します。
車には、車輪の数、ドアの数、座席定員といったデータがあります。また車には、加速し、停止し、燃料残量を表示するといった振る舞いがあります。
オブジェクト指向プログラミングでは、データを属性、振る舞いをメソッドとして識別します。それはこのように表すことができます。
データ→属性および振る舞い→メソッド
そしてクラスは、個々のオブジェクト作成の青写真です。現実世界では、同じ種類のオブジェクト(物)が多数見つかることがよくあります。例えば車がそうです。すべて同じような型式とモデルです(すべてエンジン、車輪、ドアなどがあります)。それぞれの車は、同じ青写真のセットから作られ、同じコンポーネントを持っています。
Pythonのオブジェクト指向プログラミングモード:オン
Pythonは、オブジェクト指向プログラミング言語として、クラスとオブジェクトといった概念を持っています。
クラスとは、そのオブジェクトのモデルである青写真です。
つまり、クラスは単にモデルであり、(理論のセクションで説明したように)属性や振る舞いを定義する方法です。例えば、車両クラスは、どのオブジェクトが車両であるかを定義する独自の属性を持っています。車輪の数、タンクの種類、座席定員、最大速度はすべて車両の属性です。
これを念頭に置いて、クラスのPython構文を見てみましょう。
class Vehicle:
pass
class文を使ってクラスを定義します。たったそれだけです。なんて簡単なんでしょう。
オブジェクトはクラスのインスタンスです。クラスを命名してインスタンスを作成します。
car = Vehicle()
print(car) # <__main__.Vehicle instance at 0x7fb1de6c2638>
ここで、carはVehicleクラスのオブジェクト(またはインスタンス)です。
僕たちの車両クラスには、4つの属性がありましたね。車輪の数、タンクの種類、座席定員、最高速度です。これらの属性はすべて、車両オブジェクトの作成時に設定します。ここでは、クラスを定義して、クラスを開始するときにデータを受け取れるようにします。
class Vehicle:
def __init__(self, number_of_wheels, type_of_tank, seating_capacity, maximum_velocity):
self.number_of_wheels = number_of_wheels
self.type_of_tank = type_of_tank
self.seating_capacity = seating_capacity
self.maximum_velocity = maximum_velocity
ここではinitメソッドを使用しました。これをコンストラクタメソッドと呼びます。つまり、車両オブジェクトを作成するときに、これらの属性を定義することができるのです。テスラ・モデルSが大好きで、この種のオブジェクトを作成したいと考えているとします。それは4つの車輪があり、電気エネルギーで走行し、5席分スペースがあり、最高速度は250km/時(155mph)です。このオブジェクトを作成してみましょう。
tesla_model_s = Vehicle(4, 'electric', 5, 250)
車輪4つ+電気の「タンク型」種類+5席+最高速度250km/時です。
すべての属性を設定しました。しかし、これらの属性値にどのようにアクセスすることができるのでしょうか?その答えは、オブジェクトに、オブジェクト自身について尋ねるメッセージを送ることです。それをメソッドと呼びます。これはオブジェクトの振る舞いです。実装してみましょう。
class Vehicle:
def __init__(self, number_of_wheels, type_of_tank, seating_capacity, maximum_velocity):
self.number_of_wheels = number_of_wheels
self.type_of_tank = type_of_tank
self.seating_capacity = seating_capacity
self.maximum_velocity = maximum_velocity
def number_of_wheels(self):
return self.number_of_wheels
def set_number_of_wheels(self, number):
self.number_of_wheels = number
上のコードは、number_of_wheelsとset_number_of_wheelsという2つのメソッドの実装例です。それをgetterとsetterと呼びます。getterは属性値を取得(get)するためのもので、setterは属性の新しい値を設定(set)するためのものです。
Pythonでは、getterとsetterを定義するために、@property(decorators)を使用することができます。コードで見てみましょう。
class Vehicle:
def __init__(self, number_of_wheels, type_of_tank, seating_capacity, maximum_velocity):
self.number_of_wheels = number_of_wheels
self.type_of_tank = type_of_tank
self.seating_capacity = seating_capacity
self.maximum_velocity = maximum_velocity
@property
def number_of_wheels(self):
return self.number_of_wheels
@number_of_wheels.setter
def number_of_wheels(self, number):
self.number_of_wheels = number
そして、これらのメソッドを属性として使うことができます。
tesla_model_s = Vehicle(4, 'electric', 5, 250)
print(tesla_model_s.number_of_wheels) # 4
tesla_model_s.number_of_wheels = 2 # setting number of wheels to 2
print(tesla_model_s.number_of_wheels) # 2
これは、メソッドの定義とは少し異なり、メソッドはあくまで属性として機能します。例えば、新しい車輪の数を設定する場合、「2」をパラメータとして適用するのではなく、値2をnumber_of_wheelsに設定するのです。これは、pythonic getterとsetterコードを記述する1つの方法です。
しかし、「make_noise(音を出す)」メソッドのように、他のことのためにメソッドを使うこともできます。以下で見てみましょう。
class Vehicle:
def __init__(self, number_of_wheels, type_of_tank, seating_capacity, maximum_velocity):
self.number_of_wheels = number_of_wheels
self.type_of_tank = type_of_tank
self.seating_capacity = seating_capacity
self.maximum_velocity = maximum_velocity
def make_noise(self):
print('VRUUUUUUUM')
このメソッドを呼び出すと、文字列「VRRRRUUUUM」が返されます。
tesla_model_s = Vehicle(4, 'electric', 5, 250)
tesla_model_s.make_noise() # VRUUUUUUUM
カプセル化:情報隠蔽
カプセル化は、オブジェクトのデータやメソッドに対する直接的なアクセスを制限するメカニズムです。しかし同時に、カプセル化により、そのデータ(オブジェクトのメソッド)の操作が容易になります。
「カプセル化は、データメンバーとメンバーの機能を隠蔽するために使用できます。この定義下において、カプセル化とは、オブジェクトの内部表現が、通常、オブジェクトの定義の外側から隠蔽されることを意味します。」 - Wikipedia
オブジェクトの内部表現は、すべて外部から隠蔽されています。オブジェクトのみが内部データとやり取りできます。
第一に、publicインスタンス変数およびnon-publicインスタンス変数とメソッドがどのように機能するかを理解する必要があります。
パブリックインスタンス変数
Pythonクラスの場合、コンストラクタメソッド内でpublic instance variableを初期化することができます。以下で見てみましょう。
コンストラクタメソッド内で、以下のコードを書きます。
class Person:
def __init__(self, first_name):
self.first_name = first_name
ここでは、first_nameの値を引数としてpublic instance variableに適用しています。
tk = Person('TK')
print(tk.first_name) # => TK
クラス内で、以下のコードを書きます。
class Person:
first_name = 'TK'
ここでは、引数としてfirst_nameを適用する必要はなく、すべてのインスタンスオブジェクトはTKで初期化されたclass attributeを持ちます。
tk = Person()
print(tk.first_name) # => TK
できましたね。ここでは、public instance variablesとclass attributesを使用できることを学習しました。public instance variablesに関するもう一つの興味深い点は、変数の値を管理できることです。それは何を意味するのでしょう?それは、オブジェクトで、その変数の値(GetとSetの変数の値)を管理することができるということです。
Personクラスを念頭に置いて、first_name変数に別の値を設定したいと思います。
tk = Person('TK')
tk.first_name = 'Kaio'
print(tk.first_name) # => Kaio
できました。first_nameインスタンス変数に別の値(kaio)を設定するだけで、値がアップデートされました。シンプルですね。これを行うことができたのは、それがpublic変数だからです。
ノンパブリックインスタンス変数
Pythonには(一般的に余計な作業なしで)真の意味でプライベートな属性が存在しないので、ここでは「プライベート」という用語は使用しません。- PEP 8(Pythonのコーディング規約)
public instance variableと同じように、non-public instance variableをコンストラクタメソッド内またはクラス内の両方で定義することができます。public instance variableの構文と違うところは、non-public instance variablesの場合は、変数名の前にアンダースコア(_)を使用するところです。
「オブジェクトの中からしかアクセス出来ない「プライベート」インスタンス変数は、 Python には存在しません。しかし、ほとんどのPythonコードが従っている慣習があります。それは、アンダースコアで始まる名前(例:_spam)は、(関数であれ、メソッドであれ、データメンバーであれ)APIのノンパブリックな部分として扱うべきだということです。」 - Pythonソフトウェア財団
以下が、その例です。
class Person:
def __init__(self, first_name, email):
self.first_name = first_name
self._email = email
email変数があるのがわかりますか?これが、non-public variableを定義する方法です。
tk = Person('TK', 'tk@mail.com')
print(tk._email) # tk@mail.com
それにアクセスしてアップデートすることができます。Non-public variablesは慣習に過ぎず、APIのノンパブリックな部分として扱う必要があります。
そのため、クラスの定義の中でそれを行うことができるメソッドを使用します。このことを理解するために、2つのメソッド(emailとupdate_email)を実装しましょう。
class Person:
def __init__(self, first_name, email):
self.first_name = first_name
self._email = email
def update_email(self, new_email):
self._email = new_email
def email(self):
return self._email
これらのメソッドを使用して、non-public variablesにアクセスしてアップデートできるようになりましたね。見てみましょう。
tk = Person('TK', 'tk@mail.com')
print(tk.email()) # => tk@mail.com
tk._email = 'new_tk@mail.com'
print(tk.email()) # => tk@mail.com
tk.update_email('new_tk@mail.com')
print(tk.email()) # => new_tk@mail.com
1. first_name「TK」とemail「tk@mail.com」で新しいオブジェクトを初期化しました
2. メソッドを使用してnon-public variableにアクセスし、電子メールを出力しました
3. クラス外から新しいemailを設定しようとしました
4. non-public variableをAPIのノンパブリックな部分として扱う必要があります
5. インスタンスメソッドでnon-public variableをアップデートしました
6. 成功しましたね!ヘルパーメソッドを使ってクラス内でそれをアップデートすることができます
パブリックメソッド
public methodsを使用すると、クラス外からそれを使用することもできます。
class Person:
def __init__(self, first_name, age):
self.first_name = first_name
self._age = age
def show_age(self):
return self._age
試してみましょう。
tk = Person('TK', 25)
print(tk.show_age()) # => 25
やりましたね、問題なく使用することができます。
ノンパブリックメソッド
しかし、non-public methodsでは、それを行うことはできません。同じPersonクラスを実装してみましょう。でも今回は、アンダースコア(_)を使用するshow_ageのnon-public methodを使用します。
class Person:
def __init__(self, first_name, age):
self.first_name = first_name
self._age = age
def _show_age(self):
return self._age
オブジェクトを使ってこのnon-public methodを呼び出してみましょう。
tk = Person('TK', 25)
print(tk._show_age()) # => 25
それにアクセスしてアップデートすることができます。Non-public methodsは慣習に過ぎず、APIのノンパブリックな部分として扱う必要があります。
どのように使用できるかの例を見てみましょう。
class Person:
def __init__(self, first_name, age):
self.first_name = first_name
self._age = age
def show_age(self):
return self._get_age()
def _get_age(self):
return self._age
tk = Person('TK', 25)
print(tk.show_age()) # => 25
ここでは、_get_ageのnon-public methodとshow_ageのpublic methodを使用しています。show_ageはオブジェクト(クラス外)で使用でき、_get_ageはクラス定義内(show_ageメソッド内)でのみ使用できます。しかし、繰り返しますが、これは慣習の問題です。
カプセル化のまとめ
カプセル化を使えば、オブジェクトの内部表現を確実に外部から隠蔽させることができます。
継承:振る舞いと特徴
特定のオブジェクトには、共通するものがあります。それは振る舞いと特徴です。
例えば、僕は父親からいくつかの特徴や振る舞いを継承しました。僕は、父の目と髪を特徴として継承し、父の短気で内向的な性格を振る舞いとして継承しました。
オブジェクト指向プログラミングでは、クラスは別のクラスから共通の特徴(データ)と振る舞い(メソッド)を継承することができます。
他の例を参照して、Pythonで実装してみましょう。
車を想像してみてください。車輪の数、座席定員、最高速度はすべて車の属性です。ElectricCarクラスは、通常のCarクラスと同じ属性を継承していると言えるでしょう。
class Car:
def __init__(self, number_of_wheels, seating_capacity, maximum_velocity):
self.number_of_wheels = number_of_wheels
self.seating_capacity = seating_capacity
self.maximum_velocity = maximum_velocity
Carクラスを実装します。
my_car = Car(4, 5, 250)
print(my_car.number_of_wheels)
print(my_car.seating_capacity)
print(my_car.maximum_velocity)
初期化すると、作成したすべてのinstance variablesを使用できます。やりましたね。
Pythonでは、child classにparent classをパラメータとして適用します。ElectricCarクラスはCarクラスから継承することができます。
class ElectricCar(Car):
def __init__(self, number_of_wheels, seating_capacity, maximum_velocity):
Car.__init__(self, number_of_wheels, seating_capacity, maximum_velocity)
シンプルですね。このクラスはすでにCarクラスから継承されているため、他のメソッドを実装する必要はありません。それを証明してみましょう。
my_electric_car = ElectricCar(4, 5, 250)
print(my_electric_car.number_of_wheels) # => 4
print(my_electric_car.seating_capacity) # => 5
print(my_electric_car.maximum_velocity) # => 250
美しいですね。
最後に
この記事では、Pythonの基本について多くのことを学習しました。
- Pythonの変数の仕組み
- Pythonの条件文の仕組み
- Pythonのループ(whileとfor)の仕組み
- リストの使い方:コレクション|配列
- ディクショナリのキー・バリューコレクション
- データ構造の反復方法
- オブジェクトとクラス
- オブジェクトのデータとしての属性
- オブジェクトの振る舞いとしてのメソッド
- Pythonのゲッターおよびセッターとプロパティ・デコレータの使用
- カプセル化:情報隠蔽
- 継承:振る舞いと特徴
おめでとうございます!Pythonに関するこの密度の濃い学習内容を完了しましたね。
僕のプログラミングの学習・マスターに関する記事を参照したい場合は、僕のブログである「The Renaissance Developer」をフォローしてください。
楽しく学習とコーディングを続けてくださいね。
僕のInstagram、Twitter、Github、LinkedInのリンクです。
CREDIT:原著者の許諾のもと翻訳・掲載しています。
[原文]Learning Python: From Zero to Hero (Posted Oct 1, 2017) by TK