究極のPythonガイド:ビギナーからプロフェッショナルを目指す

Pythonのインストールから始まり、for/whileループといった基礎、クラスやメソッドの基本、そして継承、ポリモーフィズム、カプセル化、最後はJSONの扱いまで、まさに究極的に充実した内容です。元記事は以下より:

冒頭
もしあなたが、データサイエンスやWeb開発、ロボティクス、IoTといったものに興味があるのなら、Pythonを学ぶべきだ。Pythonは、様々な分野で重用されており、いまや最も急成長している言語である。

初心者もしくはこれまで技術系の経験が無い人にとって、Pythonを学ぶことは賢明な選択である。なぜかといえば、その構文が、普通に英語を話したり書いたりするのとよく似ているからである[訳注:日本人からすると特段ハードルが低く感じられないですね。笑]。Pythonと英語がどのくらい近いのかを示すために、構文の一例を挙げてみよう:

print("Hello folks")

このチュートリアルでは、広く使われているPython3を用いる。多くのフレームワークとライブラリはこのバージョンで使用できる。

注意1:バージョン3.5.2以上であれば、このガイドで使用するライブラリとフレームワークのほとんどに対応している。

注意2:このガイドの序盤は初心者向けに書かれている。もしPythonの経験がある程度あるようであれば、以下の目次から関心のある項目のリンクをクリックして序盤を飛ばしても差し支えない。

はじめに

Githubのoctoverseによれば、Pythonは2019年に2番目に多くデベロッパに使用された言語である。

画像1

どの言語を学ぶにしても、その言語がどのようにして生まれたのかを知っておくのは良いことだ。さてPythonはというと、オランダのプログラマであるGuido van Rossumによって開発され、1991年にリリースされた。

Pythonはインタプリタ型言語の一つである。Pythonコードをバイナリコードに変換するためにCPythonというインタープリタを使用する。初心者であれば、CPythonについて深く知っている必要は無いが、Pythonが内部的にどうやって動作しているのかぐらいは把握しておくと良いだろう。

Pythonの背後にある哲学は、コードは可読でなくてはならない、ということである。Pythonでは、インデントを使って可読性の高さを実現している。また、関数型およびオブジェクト指向といった、複数のプログラミングパラダイムをサポートしている。これらの内容については、記事の中で後述する。

ある言語でどのようなことができるのか?というのは、多くの初心者が抱えている基本的な疑問であろう。以下に、Pythonを使ってできることの一例を挙げる:

- サーバー側の開発 (Django, Flask)
- データサイエンス (Pytorch, Tensor-flow)
- データ分析・可視化 (Matplotlib)
- スクリプティング (Beautiful Soup)
- 組み込み開発

注意:上述のライブラリ名について、とくにこれを推奨する意図はない。各分野においてよく使われているものを挙げただけである。

Pythonのインストール

どの言語を学ぶにしても、まず最初のステップはそのインストール方法を知ることである。Pythonは、今日のほとんどのOSにおいてはインストール済である[訳注:Windowsの場合は多分入ってないと思います]。ターミナル(コマンドプロンプト)を開いて、以下のコマンドを入力すればPythonが利用可能な状態であるか確認できる:

python3 --version

以下のような出力が得られるはずだ:

Python 3.7.0

もしあなたの使っているシステムに複数バージョンのPythonがあれば、様々なバージョンの情報が表示されるだろう。Pythonがインストール済であり、バージョンが3.5.2以上であれば、以下の手順は省略して次のセクションへ進んで構わない。

Pythonがインストールされていなかったら、以下の作業を行おう:

Windowsの場合:
- Pythonの公式Webサイトにアクセス
- ダウンロードボタン(Download Python 3.8.2)をクリック [注意:表記バージョンはアクセス時点で変わっている場合もある]
- ダウンロードされたパッケージ(インストーラ)をダブルクリック
- "Add Python 3.x to PATH"にチェックをつけて、"Install Now"をクリック
- "Setup was successful"と表示されたら、インストールは完了。
- コマンドプロンプトに以下のコマンドを入力して、正しくインストールされたか確認しよう:

python3 --version

[訳注:Windowsへのインストールに関しては、Python Japanの手順を参照した方がいいように思います。また、確認コマンド(Pythonの実行も)は以下のようにpy.exeの使用が推奨されているようです:]

py --version

Macの場合:
- まずはApp Storeからxcodeをインストール
- Xcodeをターミナルからインストールしたい場合は以下のコマンドを使用する:

xcode-select --install

- Xcodeのインストールが済んだら、Brewパッケージマネージャを使ってPythonをインストールしていく。Brewのインストールと設定のため、以下のコマンドを入力する:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

- Brewがセットアップできたら、以下のコマンドで期限切れパッケージの更新を行う:

brew update

- 以下のコマンドでPythonをインストールする:

brew install python3

- Pythonが正しくインストールされたことを確認するため、以下のコマンドを実行する:

python3 --version

Linuxの場合:
- aptを使ってPythonをインストールするには、以下のコマンドを使用する:

sudo apt install python3

- yumを使ってPythonをインストールするには、以下のコマンドを使用する:

sudo yum install python3

- Pythonが正しくインストールされたことを確認するため、以下のコマンドを実行する:

python3 --version

Pythonシェル

シェルとは、今後知っていく様々なものの中で最も有用なツールの一つである。Pythonシェルを使用することで、考えついたコンセプトをアプリケーションに実際に組み込む前に、手早くテストすることができる。

ターミナルあるいはコマンドプロンプトを起動し、python3コマンド[訳注:Windowsならpyコマンド]を入力すると以下のような出力が得られる:

Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 26 2018, 23:26:24)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

このチュートリアルでは、このようなPython3シェルを使いながらコンセプトを学習する場面もある。以降、「Pythonシェルを使用して」のように書かれていたら、上記のようにpython3コマンドを使用するということを意味する。

シェルを使わずに学習する場面もあり、その場合は拡張子が.pyであるファイルを作成することになる。このファイルを実行する場合は、以下のようにコマンドを使用する:

python3 testing.py

では、Pythonシェルを起動しよう。>>>と表示された箇所に続けて、10 + 12と入力しよう。出力として22が得られる:

>>> 10 + 12
22

コメントの活用

コメントは、コードのある部分がどうしてそのように書かれたのかを私達(や他人)が理解しやすくなるように、コードに記載するものである。また、コードの可読性を向上させるという素晴らしい側面もある。

# Stay Safe

上記の構文をコードに記述した場合、Pythonインタープリタはこれがコメントであると理解する。コメントとして記載された内容は、何も実行されない。

どうしてコメントを使用するべきなのか、疑問に思うかもしれない。例えば、あなたはデベロッパで、ある巨大プロジェクトにアサインされたとしよう。そのプロジェクトが、数千行にも及ぶコードから成っているとしたら、そのコードがどのように動作しているのかを理解するために、1行ずつをきちんと読まなくてはならないだろう。

もっと効率の良い方法はないだろうか?そうだ!コメントの出番である。コメントは、コードのある部分がどうしてそのように書かれたのか、またそれが何を返すのか、あるいは何をするのかを理解するための手助けになるものである。コメントを、コードの各部分に関するドキュメントのように捉えていただきたい。

Printの使い方

どうか信じてほしい、エディタのデバッグツール以外で、デベロッパが抱えるほとんどの問題の解決に役立つのはprint文である。print文は、プログラミング構文において最も過小評価されているものの一つである。どんな問題のデバッグに際しても、printが一番便利であることはおいおい分かってくることだろう。

printが問題の解決にどのように役立つのだろうか?では、あなたがあるモジュールを持っていて、その処理の流れを理解してバグを解決するために、処理内容を確認したいとしよう。この場合、2つの方法がある。デバッガを使うか、print文を追加するか、である。

デバッガに関しては、必ず使えるとは限らない。例えば、もしPythonシェルを使っているようであれば、デバッガは利用できない。このような場合には、printが役に立つ。あるいは、作成したアプリケーションが動作する場合に、そのログを表示するためにprintを追加することもあるだろう。これにより、実行中の状況を常に確認できる。

Pythonでは、組み込み済みのprintメソッドを、以下の構文のように利用できる:

print("Stay safe...")

[訳注:デバッグ目的であればprintよりloggingの方が推奨される、という記事は以前に書きましたので以下ご参考まで。とはいえ、printの方がやっぱり気軽に使えてしまいますね]

インデント

この言語に関して興味深い点の一つが、インデントである。なぜか?その答えは明快である:インデントによって、コードの可読性が増し、整った形式になるからである。Pythonでは、正しいインデントのルールを守らなくては正しいコードにならない。もしインデントが正しくない場合には、次のようなエラーが表示される:

IndentationError: unexpected indent

見ての通り、Pythonにおいてはエラー表示ですらとても読みやすく理解しやすいものとなっている。はじめの頃は、インデントのルールに悩まされることであろう。しかし経験を積んでいけば、インデントはデベロッパの良き友人であることを理解できるはずである。

変数(Variables)

その名前が示すとおり、変数とは変化を生じるものである。変数は、コンピュータプログラムで使用されるメモリ番地を参照するための方法の一つである。

さて、多くのプログラミング言語においては、変数にはその種類を明確に与える必要がある。しかしPythonでは、その必要はない。 例えば、C言語で整数を宣言する場合、構文としてint num = 5;と書く必要があるが、Pythonではnum = 5と書くだけで済む。

Pythonシェルを使って、以下の手順をひとつずつ実行してみよう:

整数(Integer):その名前が示すように、正負もしくは0で、小数点を伴わない値である。

>>> num = 5
>>> print(num)
5
>>> type(num)
<class 'int'>

見ての通り、上記手順ではnumという変数を宣言して、その値に5を代入した。Pythonの組み込みメソッドであるtypeを用いて、変数の種類を確認することができる。変数numの種類を確認したところ、得られた出力は<class 'int'>であった。現時点では、この出力内容のうちintの部分にのみ注目しよう。intとは、整数(integer)を意味するものである。

浮動小数点数(Float):整数と同じく数値であるが、小数点以下を伴う点が異なる。

>>> num = 5.0
>>> print(num)
5.0
>>> type(num)
<class 'float'>

ここでは、変数numに、小数点第一位までを持った数値を代入した。numの種類を確認すると、floatとなっていることがわかる。

文字列(String):文字もしくは整数の並びに他ならない。ダブルクォーテーションもしくはシングルクォーテーションを使って文字列を記述することができる。

>>> greet = "Hello user"
>>> print(greet)
Hello user
>>> type(greet)
<class 'str'>

ここでは、変数greetに文字列を代入した。greetの種類は、出力の通り文字列(str)であることがわかる。

ブール値(Boolean):真(True)もしくは偽(False)の値を扱うバイナリオペレータである。

>>> is_available = True
>>> print(is_available)
True
>>> type(is_available)
<class 'bool'>

ここでは、変数is_variableに真(True)を代入した。この変数の種類はブール値(bool)となっている。代入可能なのは真(True)か偽(False)のいずれかのみである。ひとつ注意としては、TおよびFは大文字でなくてはならず、もしこれが守られないと以下のようにエラーとなる:

>>> is_available = true
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
NameError: name 'true' is not defined

None型:これは、その変数が値を持たないときに用いられるものである。

>>> num = None
>>> print(num)
None
>>> type(num)
<class 'NoneType'>

演算子(Operators)

Pythonで利用できる算術演算子とその例を下図に示す。

画像2

各演算子をひとつずつ理解していこう。

算術演算子:この種類の演算子としては、足し算、引き算、掛け算、割り算、累乗演算、剰余演算、切り捨て除算がある[訳注:原文の列挙内容に誤記と不足があったので修正しています]。また、短縮記法も利用できる。

まずは、2つの変数aとbを宣言しよう。

>>> a = 6 # 代入(Assignment)
>>> b = 2

基本的な算術演算子を使ってみよう:

>>> a + b # 足し算(Addition)
8
>>> a - b # 引き算(Subtraction)
4
>>> a * b # 掛け算(Multiplication)
12
>>> a / b # 割り算(Division)
3.0
>>> a ** b # 累乗(Exponentiation)
36

他の算術演算子を試すため、変数abの値を変更しよう。

>>> a = 7
>>> b = 3
>>> a % b # 剰余(Modulus)
1
>>> a // b # 切り捨て除算(Floor division)
2

算術演算子の短縮記法(Shorthand)も利用可能である。記法については先ほどの図を参照していただき、上記と同じ方法で試すことができる。短縮記法の演算結果の表示には、print文を使用する。

[訳注:実際にやってみると以下のようになります]

>>> a = 6
>>> b = 2
>>> a += b # a = a + b と同じ
>>> print(a)
8
>>> a -= b # a = a - b と同じ
>>> print(a)
6
>>> a *= b # a = a * b と同じ
>>> print(a)
12
>>> a /= b # a = a / b と同じ
>>> print(a)
6.0

比較演算子:この種類の演算子としては、等値、大なり、小なりがある。

>>> a = 5 # 代入
>>> b = 2 # 代入
>>> a > b # 大なり
True
>>> a < b # 小なり
False
>>> a == b # 等値
False
>>> a >= 5 # 大なりもしくは等値(日本語でいう「以上」)
True
>>> b <= 1 # 小なりもしくは等値(日本語でいう「以下」)
False

論理演算子:この種類の演算子には、not、and、orがある。

>>> a = 10
>>> b = 2
>>> a == 2 and b == 10 # and
False
>>> a == 10 or b == 10 # or
True
>>> not(a == 10) # not
False
>>> not(a == 2)
True

条件分岐文(Conditional Statements)

その名前が示すように、条件分岐文とはある条件が真か偽であるかを評価するために使用されるものである。何らかのアプリケーション開発においては、条件を確認してその内容に応じて何らかの処理を行ったり、ある部分の処理を飛ばしたり、あるいは終了させるといったことがしょっちゅう行われる。このような場合、条件分岐文が役に立つ。Pythonで使用される条件分岐文は、if、elif、elseがある。

条件分岐を用いることで、ある変数が特定値に等しいかどうか、あるいはブール値であれば真なのか偽なのか、といった変数の比較をすることができる。Pythonシェルを使って、様々な条件分岐を一つずつ実行してみよう:

条件分岐1:整数が1つと、条件が3つある場合を考える。最初の条件はif条件であり、整数の値が10であるかどうかを確認する。2番めの条件はelif条件であり、整数の値が10より小さいかどうかを確認する。最後の条件はelse条件で、これは上述の2つの条件のいずれにも合致しなかった場合に実行されるものである。

>>> number = 5
>>> if number == 10:
...     print("Number is 10")
... elif number < 10:
...     print("Number is less than 10")
... else:
...     print("Number is more than 10")
...

出力:

Number is less than 10

注意:2つの条件が等しいかどうかをif条件において確認しなくてはならないという決まりはない。elif条件で確認することもできる。

条件分岐2:ブール値が1つと、条件が2つある場合を考える。どのようにして条件が真であるかを確認しているかを見てもらいたい。もしブール変数is_availableが真であればavailableであると、そうでなければnot availableと表示する。

>>> is_available = True
>>> if is_available:
...     print("Yes it is available")
... else:
...     print("Not available")
...

出力:

Yes it is available


条件分岐3:先ほどの条件分岐2の処理を、not演算子を使って反転させたものである。

>>> is_available = True
>>> if not is_available:
...     print("Not available")
... else:
...     print("Yes it is available")
...

出力:

Yes it is available

条件分岐4:変数dataをNoneとして宣言し、if条件でdataが利用可能であるか否かを判別する。

>>> data = None
>>> if data:
...     print("data is not none")
... else:
...     print("data is none")
...

出力:

data is none

条件分岐5:Pythonではif条件のインライン記法を使うこともできる。この場合、以下のような記述になる:

>>> num_a = 10
>>> num_b = 5
>>> if num_a > num_b: print("num_a is greater than num_b")
...

出力:

num_a is greater than num_b

条件分岐6:if-else条件もインライン記法で書くことができる。この場合、以下のような文法になる:

ifTrueのときの処理 if 条件式 else ifFalseのときの処理

例:

>>> num = 5
>>> print("Number is five") if num == 5 else print("Number is not five")

出力:

Number is five

条件分岐7:複数階層(ネスト)のif-else条件を使うことができる。構文は次の例を参照のこと:

>>> num = 25
>>> if num > 10:
...     print("Number is greater than 10")
...     if num > 20:
...             print("Number is greater than 20")
...     if num > 30:
...             print("Number is greater than 30")
... else:
...     print("Number is smaller than 10")
...

出力:

Number is greater than 10
Number is greater than 20

条件分岐8:条件式にand演算子を使うこともできる。2つの条件がいずれもTrueであれば実行する、といった表現が可能になる。

>>> num = 10
>>> if num > 5 and num < 15:
...     print(num)
... else:
...     print("Number may be small than 5 or larger than 15")
...

出力:

10

与えたnumの値が5より大きく15より小さいため、出力として10が得られている。

条件分岐9:条件式にor演算子を使うこともできる。2つの条件のどちらかがTrueであれば実行する、といった表現が可能になる。

>>> num = 10
>>> if num > 5 or num < 7:
...     print(num)
...

出力:

10

numの値が10で、2番めの条件が「numは7より小さい」となっていることに混乱しただろうか?どうして出力として10が得られたのか?それは、or条件となっているからである。条件式のいずれか一方が一致すれば、処理が実行されるようになっているのである[訳注:num > 5がTrueとなった時点で、num < 7はTrueでもFalseでもprint(num)が実行されます]。

Forループ

どのプログラミング言語においても有用であるのが、反復処理である。何らかの処理を複数回実行しなければならないとしたら、どのように実装するだろうか?

print("Hello")
print("Hello")
print("Hello")

これもまぁ、やり方の一つではある。でも、もし数百回や数千回も同じ処理が必要だとしたら、どうだろうか?print文を膨大な数だけ書かなくてはならない。いや待ってほしい、良い解決方法がある。反復とかループとか呼ばれるものだ。forループもしくはwhileループを使うことができる。

ここではrangeメソッドを使って、ループを繰り返してほしい範囲を指定する。デフォルトでは、rangeの開始は0である。

>>> for i in range(3):
...     print("Hello")
...

出力:

Hello
Hello
Hello

rangeの範囲指定をrange(1,3)のようにすることも可能である。

>>> for i in range(1,3):
...     print("Hello")
...

出力:

Hello
Hello

rangeの指定を変えたことで、Helloが2回しか出力されなくなったことがわかる。rangeで指定した右の数 - 左の数の計算値と同じ回数だけ繰り返されると思っておくと良いだろう。

さて、forループにelse文を追加することも可能である。

>>> for i in range(3):
...     print("Hello")
... else:
...     print("Finished")

出力:

Hello
Hello
Hello
Finished

ループが3回(3-0)繰り返され、ループ終了時にelse文の内容が実行されていることがわかる。

ループ内に更にループを入れ子状にする(ネスト)も可能である。

>>> for i in range(3):
...     for j in range(2):
...             print("Inner loop")
...     print("Outer loop")
...

出力:

Inner loop
Inner loop
Outer loop
Inner loop
Inner loop
Outer loop
Inner loop
Inner loop
Outer loop

見ての通り、内側ループのprint文がまず2回実行される。それから、外側ループのprint文が実行される。再び、内側ループが2回実行されている。何が起きているか理解できただろうか?もし混乱しているようであれば、次のように考えてみよう:

- インタープリタがやってきて、コードを読み始めます。「おや、forループがあるぞ!」そしてその中に、もう一つのforループがあることを発見します。
- では、まずは内側のforループの内容を2回実行して、一旦終わりにしましょう。すると、外側のforループが「あと2回繰り返しておくれ」と頼んできます。
- 最初の位置に戻り、内側のforループをまた発見して、同じことを繰り返します。

さて、forループ中で特定の状況にあるときに「パス(pass)」をすることもできる。ここでいうパスとはどのような意味か?forループの反復中に、インタープリタがpass文を発見すると、何も実行せずに次の行へ進むのである。

>>> for i in range(3):
...     pass
...

この場合、シェルには何の出力も得られない。
[訳注:話の本筋から少し逸れそうですが、pass文の意味・役割が少し分かりづらいですね。文字通り「何もしない」ための文で、どんなときに使う必要があるのかは下記の情報などが参考になりそうです:]

Whileループ

Pythonで利用できるもう一つのループ(反復)がwhileループである。forループを使って実現できた処理は、whileループでも実現可能な場合がある。

>>> i = 0
>>> while i < 5:
...     print("Number", i)
...     i += 1
...

出力:

Number 0
Number 1
Number 2
Number 3
Number 4

whileループを使う場合には、忘れてはならない重要な点が一つある。それは、whileループがある時点で終了するように、インクリメント文などを追加する必要があるということだ。これを忘れると、whileループは永遠に実行され続けることになる。

もう一つの方法として、whileループ文にbreak文を追加することがある。これにより、ループを中止させることができる。

>>> i = 0
>>> while i < 5:
...     if i == 4:
...             break
...     print("Number", i)
...     i += 1
...

出力:

Number 0
Number 1
Number 2
Number 3

この例では、iが4になったらwhileループが中止されている。

また、whileループにelse文を追加することもできる。else文に記述した内容は、whileループが完了したら実行される[訳注:breakで中止した場合はelseは実行されません]。

>>> i = 0
>>> while i < 5:
...     print("Number", i)
...     i += 1
... else:
...     print("Number is greater than 4")
...

出力:

Number 0
Number 1
Number 2
Number 3
Number 4
Number is greater than 4

continue文を使うと、現在実行中のループ処理を飛ばして、次の処理に進めることができる[訳注:continue以降に記述されている処理を省略して、whileループの先頭に戻ります]。

>>> i = 0
>>> while i < 6:
...     i += 1
...     if i == 2:
...             continue
...     print("number", i)
...

出力:

number 1
number 3
number 4
number 5
number 6

ユーザー入力(User Inputs)

コマンドラインアプリケーションを構築中であると想定しよう。いま、ユーザーからの入力を受け取り、その内容に応じた処理を行わなくてはならなくなったとする。このような場合、Python組み込みのinputメソッドが使える。

inputメソッドの文法は以下にようになる:

variable = input(".....")

例:

>>> name = input("Enter your name: ")
Enter your name: Sharvin

シェルでinputメソッドを使用してEnterキーを押すと、inputメソッドに与えたテキストが表示される。代入した内容が正しく反映されているか確認してみよう。

>>> print(name)
Sharvin

ちゃんと入ってた!完璧である。ここで、Sharvinは文字列型となっている。

>>> type(name)
<class 'str'>

文字列ではなく整数を代入した場合に型がどうなるか、確認するためにもう一つの例をやってみよう。

>>> date = input("Today's date: ")
Today's date: 12
>>> type(date)
<class 'str'>

混乱してる?整数の12を入力したのに、その型は文字列になっているのである。これはバグではなく、inputの意図的な動作である。この文字列を整数に変換するには、型変換を使うことになる。

型変換(Typecasting)

先に確認したとおり、inputメソッドは整数を入力しても文字列を返すようになっている。ここで、この出力を他の整数と比較したいとすると、文字列を整数に変換する方法が必要になる。このようなとき、型変換が役に立つ。

>>> date_to_int = int(date)
>>> type(date_to_int)
<class 'int'>

先ほどのユーザー入力セクションで宣言した変数dateを、Python組み込みのintメソッドによって整数に変換した。このような操作を型変換と呼ぶ。

以下のような変換が、型変換により可能である:

- 整数を文字列へ: str()
- 文字列を整数へ: int()
- 整数を浮動小数点数へ: float()
注意:浮動小数点数から整数への変換も可能である。

>>> type(date)
<class 'str'>

# 文字列から浮動小数点数への型変換
>>> date_to_float = float(date)
>>> type(date_to_float)
<class 'float'>

# 浮動小数点数空文字列への型変換
>>> date_to_string = str(date_to_float)
>>> type(date_to_string)
<class 'str'>

# 浮動小数点数から整数への型変換
>>> date_to_int = int(date_to_float)
>>> type(date_to_int)
<class 'int'>

ディクショナリ(Dictionaries)

何らかのユーザー詳細情報を保持しておきたいとしよう。どうやったらこういった詳細情報を保持できるだろうか?そう、変数を使えば以下のように詳細を保持することが可能である:

>>> fname = "Sharvin"
>>> lname = "Shah"
>>> profession = "Developer"

この値にアクセスしたい場合には、次のようにすれば良い:

>>> print(fname)
Sharvin

でも、これは洗練された最適なやり方だろうか?答えはNOである。もっと使いやすくするために、キーと値によるディクショナリという形式でデータを保持するやり方が、私達を救ってくれるだろう。

ディクショナリとは何か?ディクショナリは、順不同でミュータブル(つまり、内容を更新可能である)な集合である。

ディクショナリの書式は次のようになっている:

data = {
	"key" : "value"
}

具体例を使って、ディクショナリの扱いをより理解しよう:

>>> user_details = {
...     "fname": "Sharvin",
...     "lname": "Shah",
...     "profession": "Developer"
... }

ディクショナリの値にアクセスする:ディクショナリ内の値にアクセスするには、2つの方法がある。まずは両方のやり方を知り、それからどちらの方法がより良いのか検討していくことにしよう。

方法その1:ディクショナリuser_detailsfnameキーの値にアクセスするには、以下の構文を使用できる:

>>> user_details["fname"]
'Sharvin'

方法その2:ディクショナリuser_detailsfnameキーの値にアクセスするのに、getを使うこともできる。

>>> user_details.get("fname")
'Sharvin'

方法その1の方がわかりやすい、と思ったことだろう。この方法では、ディクショナリに存在しないデータにアクセスしようとすると問題が発生する。

>>> user_details["age"]
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
KeyError: 'age'

指定したキーは存在しないということを示す、KeyErrorが発生する。同じことを、方法その2でもやってみよう。

>>> user_details.get("age")

コンソールには何も出力されない。どうしてこうなるのかを理解するために、さらなるデバッグをしていこう。getを用いた操作の出力を変数ageに代入して、これをprint文でコンソールに表示してみよう。

>>> age = user_details.get("age")
>>> print(age)
None

なるほど!!つまりgetは指定されたキーを見つけられなかった場合、値としてNoneを設定するようになっており、だからエラーを生じなかったわけだ。さて、どちらの方法が適切か、まだ迷っているところだろうか?殆どの場合は方法その2を使うのが理にかなっているが、厳密な確認を行う状況では方法その1を使う必要が出てくる。

キーが存在するか確認する:Pythonで、ディクショナリが特定のキーを持っているかどうかをどうやって確認したらいいか、疑問に思っているかもしれない。Pythonには、これを解決するための組み込みメソッドkeys()がある。

>>> if "age" in user_details.keys():
...     print("Yes it is present")
... else:
...     print("Not present")
...

これにより以下の出力が得られる:

Not present

ディクショナリが空かどうかを確認したいとしたら?実際にやってみるため、空のディクショナリを次のように宣言しよう:

>>> user_details = {}

ディクショナリをif-elseの条件に直接使うと、データが存在すればTrueを、空であればFalseを返すようになっている。

>>> if user_details:
...     print("Not empty")
... else:
...     print("Empty")
...

出力:

Empty

Pythonの組み込みメソッドboolを使用して、ディクショナリが空であるか否かを確認することもできる。この場合も、boolはディクショナリが空であればFalseを、中身があればTrueを返すことに留意しよう。

>>> bool(user_details)
False
>>> user_details = {
...     "fname" : "Sharvin"
... }
>>> bool(user_details)
True

既存キーの値を更新する:ここまでで、特定のキーの値を取得したり、そのキーが存在するかどうかを確認する方法がわかった。では、ディクショナリのキーを更新したい場合はどうしたら良いのだろう?

次のようにディクショナリを宣言しよう:

>>> user_details = {
...     "fname":"Sharvin",
...     "lname": "Shah",
...     "profession": "Developer"
... }

値を更新するには、以下の構文を使用する:

>>> user_details["profession"] = "Software Developer"
>>> print(user_details)
{'fname': 'Sharvin', 'lname': 'Shah', 'profession': 'Software Developer'}

ディクショナリにおけるあるキーの値を更新する手順は、変数に値を代入する手順と同じということだ。

キーと値のペアを追加する:次なる疑問は、ディクショナリに新しい値を追加する方法ではないだろうか?キーageと値100のペアを追加してみよう。

>>> user_details["age"] = "100"
>>> print(user_details)
{'fname': 'Sharvin', 'lname': 'Shah', 'profession': 'Software Developer', 'age': '100'}

見ての通り、新しいキーと値をディクショナリに追加することができた。

キーと値のペアを削除する:ディクショナリからキーと値を削除するために、Pythonにはpopと呼ばれる組み込みメソッドがある。

>>> user_details.pop("age")
'100'
>>> print(user_details)
{'fname': 'Sharvin', 'lname': 'Shah', 'profession': 'Software Developer'}

これによりキーageとその値がディクショナリuser_detailsから削除される。また、delオペレータを使って値を削除することも可能だ。

>>> del user_details["age"]
>>> print(user_details)
{'fname': 'Sharvin', 'lname': 'Shah', 'profession': 'Software Developer'}

delメソッドは、完全にディクショナリを削除するのに使うこともできる。この場合はdel user_detailsのような構文を使う。

ディクショナリをコピーする:ディクショナリは、昔ながらの方法でコピーすることはできない。つまり、dictAの値をdictBとしてコピーするのに以下のような方法ではできないのである[訳注:説明の内容と等式の左右が逆と思われたので修正しています]:

dictB = dictA

[訳注:上記構文でもエラーにはなりませんが、dictBはdictAと同一のディクショナリを指すものとなるため、dictAの内容を更新するとdictBも同じように更新されることになります。完全に別物として複製するには以下のようにcopyが必要です]
ディクショナリの値をコピーするには、copyメソッドを使う必要がある。

>>> dictB = user_details.copy()
>>> print(dictB)
{'fname': 'Sharvin', 'lname': 'Shah', 'profession': 'Software Developer'}

リスト(Lists)

ラベル付けされていない(定義するためのキーを持たない)大量のデータがあるとしよう。さてこれをどうやって保持したら良いだろうか?ここでリストの登場である。リストは以下のようにして定義される:

data = [ 1, 5, "xyz", True ]

リストは、内容が自由で、順序が保持される、ミュータブルな(つまり更新可能な)集合である。

リスト要素へのアクセス:ではリストの最初の要素にアクセスしてみよう:

>>> data[1]
5

ちょっと待って何が起きた?リストの最初の要素にアクセスしたのに、2番めの要素が得られているではないか。なぜか?リストのインデックスはゼロから始まるのである。それはつまりどういうことかというと、要素の位置を示すインデックスがゼロから始まるということである。ある要素にアクセスするための構文は以下のようになる:

list[position_in_list]

最初の要素にアクセスするには、以下のようにする必要がある:

>>> data[0]
1

アクセスする要素を、その位置による範囲で指定することも可能である。

>>> data[2:4]
['xyz', True]

この場合、ほしい範囲の開始位置(インデックス)を最初の値として指定し、このインデックスに到達する手前までがほしいという終了位置を最後の値として指定する[訳注:上記例だと、インデックス2から始めて、インデックス4に到達しない位置まで(つまりインデックス3まで)の範囲がほしい、ということになります]。

リストにアイテムを追加する:リストにアイテムを追加するには、Pythonのappendメソッドを使う。

>>> data.append("Hello")
>>> data
[1, 5, 'abc', True, 'Hello']

アイテムの値を変更する:あるアイテムの値を変更するには、以下の構文を使用する:

>>> data[2] = "abc"
>>> data
[1, 5, 'abc', True]

リストからアイテムを削除する:リストからあるアイテムを削除するには、Python組み込みのremoveメソッドを使用できる。

>>> data.remove("Hello")
>>> data
[1, 5, 'abc', True]

リストを使用したループ:リストを使って、リスト内の特定の要素を見つけたりそれを使った操作をするようなループをさせることも可能である。

>>> for i in data:
...     print(i)
...

出力:

1
5
abc
True

アイテムが存在するかどうか確認する:リストにある特定のアイテムが存在するかどうか確認するには、ifの反復処理を使って以下のようにできる:

>>> if 'abc' in data:
...     print("yess..")
...
yess..

コピー:あるリストのデータを別のリストとしてコピーするにはcopyメソッドを使う必要がある。

>>> List2 = data.copy()
>>> List2
[1, 5, 'abc', True]

長さ:リストの長さを確認するには、Python組み込みのlenメソッドを使う。

>>> len(data)
4

リストの結合:2つのリストを結合するのに、+演算子を使うことができる。

>>> list1 = [1, 4, 6, "hello"]
>>> list2 = [2, 8, "bye"]
>>> list1 + list2
[1, 4, 6, 'hello', 2, 8, 'bye']

リストに存在しない位置の要素にアクセスしようとしたら何が起きるだろうか?この場合、list index out of rangeエラー(リストインデックス範囲外エラー)となる。

>>> list1[6]
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
IndexError: list index out of range

タプル(Tuples)

タプルとは、順序が保持され、イミュータブルな(つまりデータを変更できない)データ形式の一つである。

タプルを実際に作ってみよう:

>>> data = ( 1, 3 , 5, "bye")
>>> data
(1, 3, 5, 'bye')

タプルの要素にアクセスする:タプル内の要素には、リスト要素と同じようにしてアクセスできる:

>>> data[3]
'bye'

インデックスの範囲指定によってもアクセスできる:

>>> data[2:4]
(5, 'bye')

タプルの値を変更する:どうやったらタプルの値を変更できるだろう、と考えているのであれば、あなたはまさしく私の友人だ。タプルはイミュータブルなので、その値を変更することはできない。タプルの値を変更しようとすると、次のようにエラーが発生する:

>>> data[1] = 8
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

どうしてもタプルの値を変更したい場合には、以下のような回避策がある:

>>> data = ( 1, 3 , 5, "bye")
>>> data_two = list(data) # リストに変換する
>>> data_two[1] = 8 # リストはミュータブルなので、値を更新できる
>>> data = tuple(data_two) # タプルに変換する
>>> data
(1, 8, 5, 'bye')

リストのセクションで確認したその他のメソッドは、タプルに対しても適用可能である。

[注意:タプルは一旦作成されると、新しい値を追加することはできない。]

セット(Sets)

セットは、これもPythonにおけるデータタイプのひとつで、順序無関係でインデックス付けされないものである。セットは以下のように宣言される:

>>> data = { "hello", "bye", 10, 15 }
>>> data
{10, 15, 'hello', 'bye'}

値にアクセスする:セットはインデックス付けされていないので、値に直接アクセスすることはできない。このため、セットの値にアクセスするにはforループを使う必要がある。

>>> for i in data:
...     print(i)
...
10
15
hello
bye

値の変更:セットは一度作成されると、その値を変更することはできない。

アイテムの追加:セットにアイテムを追加するために、Pythonにはaddと呼ばれる組み込みメソッドが用意されている。

>>> data.add("test")
>>> data
{10, 'bye', 'hello', 15, 'test'}

長さの確認:セットの長さを確認するには、lenメソッドを使う。

>>> len(data)
5

アイテムの削除:アイテムを削除するにはremoveメソッドを使う:

>>> data.remove("test")
>>> data
{10, 'bye', 'hello', 15}

関数と引数(Functions and Arguments)

関数とは、実行させたい作業を宣言するのに便利な方法の一つである。関数を活用することで、実行させたい作業に関するロジックを分離できるのである。

何度も使うロジックを再利用しやすいような、ひとかたまりのコードとして存在するものである。関数は組み込みのものもあれば、ユーザー定義のものも存在しうる。

関数を宣言するには、キーワードdefを使用する。関数の文法は以下のようになる:

>>> def hello_world():
...     print("Hello world")
...

ここでは、呼び出されるとHello worldと表示する関数を宣言している。関数を呼び出すときは、以下の文法を使用する。

>>> hello_world()

これにより以下のように出力される:

Hello world

関数を呼び出す際、丸かっこ()をつけることによりその関数を実行するという意味になることを覚えておこう。試しに、丸かっこをつけずに関数を呼び出してみよう。

>>> hello_world

次のような出力が得られるはずだ:

<function hello_world at 0x1083eb510>

丸かっこをつけずに関数を呼び出すと、その関数の参照先を示す。上記の出力例の場合、hello_world関数はメモリー番地0x1083eb510を参照先とすることがわかる。

足し算の操作を実行しなくてはならないとしよう。変数aとbを宣言して、足し算の式を実行することで実現できる。

>>> a = 5
>>> b = 10
>>> a + b
15

これも一つのやり方ではあるが、もしabの値が変更されて、また同じ操作をやらなくてはならない場合を考えてみてほしい。

>>> a = 5
>>> b = 10
>>> a + b
15
>>> a = 2
>>> b = 11
>>> a + b
13

これでもまだやれないことはない。では、2つの数値の組み合わせを足し合わせる操作を100回やらなくてはならないと考えてみよう。組み合わせの数値はすべて異なるものとする。これをさっきと同じように書くのは大変だ。でも大丈夫、これを解決するために関数というものがあるのだ。

>>> def add(a,b):
...     print(a+b)
...

ここでは、変数abを必須の引数としてadd関数に追加している。この関数を呼び出すには、以下の文法を使う:

>>> add(10,5)

出力:

15

関数を定義して使うのはさほど難しくないとお分かりいただけただろう。では、引数なしでこの関数を呼び出すとどうなるだろうか?

>>> add()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: add() missing 2 required positional arguments: 'a' and 'b'

TypeErrorが出力され、引数が2つ必要であることを教えてくれる。

引数を3つにした場合はどうなるだろうか?

>>> add(10,5,1)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: add() takes 2 positional arguments but 3 were given

このように、2つの引数しか受け付けられないのに3つの引数が与えられた、と教えてくれる。

さて、ある関数がいくつの引数を必要とするかわからない場合、どうしたら良いだろうか?これを解決するために、argsとkwargsを使おう[訳注:慣例的にargsおよびkwargsと呼ばれているようですが、以降のセクションで示すように複数の引数をまとめて渡す仕組みのことを指すようです]。

可変長タプル引数(Args)

関数にいくつの引数が渡されるかわからない、という状況のときはargsとkwargsが使われる。

任意のn個の引数をある関数にわたす場合、argsを使う。引数の前に*を追加しよう。

*を前に追加すると、引数のタプルを受け取ることになると覚えておこう。

>>> def add(*num):
...     print(num)
...

ここでは*numがargsである。この関数addを、任意のn個の引数を渡して呼び出してもTypeErrorとならないことを確認しよう。

>>> add(1,2,3)
(1, 2, 3)
>>> add(1,2,3,4)
(1, 2, 3, 4)

それでは、足し算の作業をするために、Pythonの組み込み関数sumを使うことにしよう。

>>> def add(*num):
...     print(sum(num))
...


これで、関数addを呼び出すと以下のように出力が得られる:

>>> add(1,2,3) # 関数呼び出し
6
>>> add(1,2,3,4) # 関数呼び出し
10

キーワード引数(Keyword Arguments)

関数を呼び出すときに、どのような順番で引数が渡されるかわからない場合もあるだろう。そのような場合、任意の順番で引数を渡すことができ、また関数がその値を正しく認識できるように、キーワード引数を用いる。例を用いて、この考え方を理解しよう。

>>> def user_details(username, age):
...     print("Username is", username)
...     print("Age is", age)
...

この関数を以下のように呼び出してみよう:

>>> user_details("Sharvin", 100)

次のような出力が得られる:

Username is Sharvin
Age is 100

この出力は正しそうだ。しかし次のようにこの関数を呼び出すとどうだろうか:

>>> user_details(100, "Sharvin")

次のような出力が得られる:

Username is 100
Age is Sharvin

これは正しくなさそうだ。何が起きたかといえば、usernameに100が渡され、ageにSharvinが渡されてしまったのである。このように引数の順番がどうなるかわからない場合には、関数を呼び出すときにキーワード引数を使うことができる:

>>> user_details(age=100, username="Sharvin")

出力:

Username is Sharvin
Age is 100

出力を正しく得るための魔法をご覧いただいた。

デフォルト引数(Default Argument)

関数が呼び出されるときに、特定の引数についてはその値が得られるかどうかわからない、という状況を想定しよう。このような場合、次のようにデフォルト引数を使うことができる:

>>> def user_details(username, age = None):
...     print("Username is", username)
...     print("Age is", age)
...

ここでは引数ageにNoneを代入している。この関数を呼び出すときに2番めの引数を与えなければ、デフォルト値としてNoneが渡される。

関数を呼び出してみよう:

>>> user_details("Sharvin")

出力:

Username is Sharvin
Age is None

ここで、2番めの引数を渡した場合には、与えられた引数によりNoneを上書きして使用される。

>>> user_details("Sharvin", 200)
Username is Sharvin
Age is 200

もし、この関数の1番めの引数をデフォルト引数にして、2番めの引数を必須引数にした場合はどうなるだろうか?Pythonシェルで実際にやってみよう。

>>> def user_details(username=None, age):
...     print("Username is", username)
...     print("Age is", age)
...

次のようなエラーが発生する:

 File "<stdin>", line 1
SyntaxError: non-default argument follows default argument

重要:すべての必須引数は最初に宣言し、それからデフォルト引数を宣言する必要がある。

可変長ディクショナリ引数(kwargs)

関数にいくつの引数が渡されるかわからない、という状況があることだろう。その場合、kwargsを使うことができる。

kwargsを使うには、引数の手前に**を追加する。

重要:手前に**を追加すると、引数のディクショナリを受け取ることになる。

例を使って仕組みを理解しよう。引数usernameを、その手前に**を追加して受け入れる関数を宣言しよう。

>>> def user(**username):
...     print(username)
...

このuser関数を呼び出すと、ディクショナリを受け取ることになる。

>>> user(username1="xyz",username2="abc")

出力:

{'username1': 'xyz', 'username2': 'abc'}

さて何が起きたのか?argsと同じように見えるだろうか?いや、違う。argsの場合は、タプルとして引数を受け取るので、特定の値に名前を使ってアクセスすることはできない。kwargsの場合は、見ての通りディクショナリ形式でデータを受け取っている。このため、その値に簡単にアクセスできるのだ。実際の例を考えてみよう:

>>> def user(**user_details):
...     print(user_details['username'])
...

この関数を呼び出してみよう:

>>> user(username="Sharvin",age="1000")

そうすると次の出力が得られることになる:

Sharvin

スコープ(Scope)

スコープとは、変数や関数が利用可能な場所を定義するものである。Pythonには、グローバルとローカルの2種類のスコープがある。

グローバルスコープ:Pythonコードのメインボディで作成された変数や関数は、グローバル変数あるいはグローバル関数と呼ばれ、グローバルスコープの一部である。この考え方を、例を使って理解しよう:

>>> greet = "Hello world"
>>> def testing():
...     print(greet)
...
>>> testing()
Hello world

ここでは変数greetはPythonのボディにおいて宣言されているため、グローバルに利用可能である。

ローカルスコープ:ある関数内で作成された変数や関数はローカル変数およびローカル関数と呼ばれ、ローカルスコープの一部である。こちらも、例を使って理解しよう:

>>> def testing():
...     greet = "Hello world"
...     print(greet)
...
>>> testing()
Hello world

ここで変数greetはtesting関数の中で作成されており、この関数内でしか利用できない。greetにメインボディからアクセスを試みるとどうなるか、やってみよう。

>>> print(greet)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
NameError: name 'greet' is not defined

重要:この例のテストをやる際には、Pythonコンソールを再起動するためにCtrl + dを押してpython3コマンドによりもう一度Pythonシェルを起動しよう[訳注:Ctrl + dはLinuxの場合ですかね?Windowsの場合はCtrl + zを押してからEnterキーでPythonシェルが終了します。MacならCtrl + zを押すのみ]。この理由は、メモリ内で変数greetが既にグローバルスコープで宣言されてしまっており利用可能になっているからである。

greetはグローバルには利用できないので、定義されていないというエラーが発生するのである。

Return文

ここまで作成してきた関数は、まったく役立たずである。これまでやってきたことといえば、データを受け取り、処理し、printで表示させるだけである。しかし実際の現場においては、関数には何らかの出力が求められる。そうすれば、その出力をまた別の処理で使うことができるからである。

これを実現するには、return文を使う。returnは単なる関数やメソッドの一部であると覚えておこう。return文の文法は極めて簡単である。

>>> def add(a, b):
...     return a + b
...
>>> add(1,3)
4

足し算の結果を表示する代わりに、出力を返している。このように返された出力値は、変数に格納することも可能だ。

>>> sum = add(5,10)
>>> print(sum)
15

ラムダ記述(Lambda Expression)

ある関数内で、それほど多くの処理を実行したくないという状況を考えてみよう。そのような場合、本格的な関数をわざわざ書くのは割に合わない。これを解決するため、ラムダ記述を使う。

ラムダ関数とは何か?ラムダ関数とは無名関数の一つであり、単文記述に制約されているものである。ラムダ関数は任意数nの引数を受け取ることができる。

ラムダ関数の文法は以下のようになっている:

variable = lambda arguments: operation

例を使って、理解を深めよう:

>>> sum = lambda a: a + 10

ここでは変数sumを宣言しており、その際にラムダ関数を呼び出している。aはこの関数に渡される引数を表現するものである。

この関数を呼び出してみよう[訳注:原文の関数名が思いっきり間違っているので修正しています]:

>>> sum(5)
15

[訳注:ちょっとわかりづらいですが、sumはラムダ関数が代入されている変数、ということになるかと思います。ラムダ関数については以前に別記事でも紹介しているのでご参考まで:]

リスト内包表記(List comprehension)

二乗値のリストがほしいとしよう。普通にやるなら、squaresリストを宣言して、forループで各値の二乗を計算していくだろう。

>>> squares = []
>>> for x in range(10):
...     squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

確かにこれは実現可能ではあるが、リスト内包表記を使うと1行で書くことができる。

これには2つの方法がある。両方やってみよう。

>>> squares = list(map(lambda x: x**2, range(10)))
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

ここではlistコンストラクタを使って、リストを構築する際にラムダ関数によって数値の二乗を計算している。同じことをもう一つの方法でやると以下のようになる:

>>> squares = list(x**2 for x in range(10))
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

簡潔で読みやすく、理解しやすいので、私はこちらの方法をより好んで使う。

同じ値の2つの数字の組み合わせがほしい、という場合はどうだろうか。2つのforループと1つのifを書く必要がありそうだ。

実際にどの様になるか見てみよう:

>>> num_list = []
>>> for i in range(10):
...     for j in range(10):
...             if i == j:
...                     num_list.append((i,j))
...
>>> num_list
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9)]

これでは、読むのに一苦労で理解が難しい。

同じことを、リスト内包表記を使ってやってみよう。

>>> num_list = list((i,j) for i in range(10) for j in range(10) if i == j)
>>> num_list
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9)]

同じ結果を得るのが、単文ならいかに簡単であるかは見てのとおりである。そう、これがリスト内包表記の力だ。実際にアプリケーションの開発を始めれば、リスト内包表記がいかに強力であるか実感できることだろう。

オブジェクト指向プログラミングの考え方

Pythonはマルチパラダイムのプログラミング言語である。これが何を意味するかというと、Pythonは何か問題を解く際に、複数の異なるアプローチを用いることができるということである。パラダイムの一つが、手続き型あるいは関数型のプログラミング手法である。これは、レシピを書くようにコードを構築していくものである。つまり、関数やコードブロックにより構成される一組の手順を記述するということだ。

問題を解くためのもう一つのアプローチが、クラスとオブジェクトを作成することである。このやり方はオブジェクト指向プログラミングとして知られる。オブジェクトとは、データ(変数)とそのデータに作用するメソッドの集まりである。それから、クラスとはオブジェクトの設計図にあたるものである。

オブジェクト指向プログラミングを理解するために重要な点は、オブジェクトは両パラダイムの中間にあるもので、データを表現するだけでなくプログラムの構造も表現するものであるということである。

これから解こうとしている問題に適したパラダイムを選択することもできるし、一つのプログラムに複数のパラダイムを混在させることもできるし、プログラムの構築が進むにつれて一方のパラダイムからもう一方へ切り替えることもできる。

オブジェクト指向プログラミングの優位点:

- 継承(Inheritance):オブジェクト指向プログラミングにおいて最も便利な概念の一つが継承である。継承により、子オブジェクトは親オブジェクトの特性や振る舞いをすべて持つことができる。つまり継承によって、他のクラスからすべてのメソッドを継承するようなクラスを定義することが可能になるのである。
- ポリモーフィズム(Polymorphism):ポリモーフィズムを理解するために、この単語を2つの部分に分けてみよう。"poly"は数が多いこと(many)を意味し、"morph"は成形すること(form)や形状(shape)を意味する。なのでこの2つから成るポリモーフィズムとは、あるタスクが複数の異なるやり方で実行できることを意味する。
例えば、あなたが「動物」というクラスを持っていて、すべての動物は鳴くものであるとしよう。ただし、鳴き方は動物によって異なる。ここで、「鳴く」という振る舞いはポリモーフィック(様々なやり方がある)であり、その動物が何であるかに依存する。つまり、「動物」という概念においては「鳴く」の実際の内容は含まずに、特定の動物(犬とか猫とか)が「鳴く」動作の具体的な実装を持つことになる。ポリモーフィズムとは、同じ関数名やメソッド名が、異なる内容で使われることを指すのである。
- カプセル化(Encapsulation):オブジェクト指向プログラミングにおいては、メソッドや変数へのアクセスに制限をかけることができる。つまり、メソッドや変数をプライベートなものにできるということだ。これによってデータが意図せず変更されてしまうのを防ぐことができ、このような考え方をカプセル化と呼ぶ。

まずはクラス、オブジェクト、コンストラクタについて理解し、それから上記の特性を再度確認していくことにする。クラスやオブジェクト、コンストラクタについて既に知っているようなら、自分が読むべきと思う箇所まで読み飛ばして差し支えない。

クラス(Classes)

Pythonには様々な基本データ構造がある。例えば数値や文字列、リストなどだ。これらは全て、なにかの名称や、場所、コストなどといった簡単に表現できるものに使用することができる。

しかし、もっと複雑なデータを扱いたい場合はどうだろうか?もしデータ特性の繰り返しの中にあるパターンがあるとしたら、どうしたら良いだろうか?

100種類の異なる動物を扱うとしよう。いずれの動物も名前、年齢、脚の数といった情報を持っている。もしすべての動物に対して新たな特性を追加したいとしたら?あるいはそのリストに新しい動物を追加するとしたら?このような複雑な状況を扱うため、クラスが必要なのである。

Python公式ドキュメントによれば:

クラスはデータと機能を組み合わせる方法を提供します。 新規にクラスを作成することで、新しいオブジェクトの 型 を作成し、その型を持つ新しい インスタンス が作れます。

各クラスのインスタンスは、その状態量を保持するために、関連付けられた属性を持つことができる。クラスインスタンスはまた、その状態量を修正するためのメソッド(そのクラスで定義される)を持つことができる。

クラスの文法:

class ClassName:
    <expression-1>
    .
    .
    .
    <expression-N>

クラスを定義するにはclassというキーワードを使う。Carというクラスを定義してみよう:

class Car:
    pass

メソッド(Methods)

メソッドは、見た目上は関数と同じである。唯一違うのは、メソッドはオブジェクトに依存するという点である。関数はその名称によって呼び出すことができるのに対して、メソッドはそのクラス参照を用いることで呼び出す必要がある。クラスの内側で定義される。

例として、2つのメソッドを作成していこう。一つはエンジンで、もう一つはホイールである。これら2つのメソッドは私達のCarクラスにおいて利用可能な部分を定義するものだ。

次のプログラムで、クラスの考え方が少しわかるようになるだろう:

>>> class Car:
...     def engine(self):
...             print("Engine")
...
>>> Car().engine()
Engine

ここではCar()参照を利用してengineメソッドを呼び出している。

まとめると、何が定義されるべきかという設計図を与えるものがクラスであるが、クラスからは実際の内容は与えられない。上記のCarクラスはエンジンを定義しているが、特定の自動車のエンジンを表現するものにはなっていない。そのような指定はオブジェクトによって成されるのである。

オブジェクト(Objects)

オブジェクトとは、クラスのインスタンスである。先ほどのCarの例を考えてみよう。ここで、Carがクラスで、toyotaがある自動車のオブジェクトであるとする。オブジェクトはいくつも複製することが可能である。いずれのオブジェクトも、クラスを使って定義されなくてはならない。

オブジェクトを作成する文法は以下のようになる:

toyota = Car()

先ほどのCarの例を引き続き使って、より理解を深めていこう:

class Car:

    def engine(self):
        print("Engine")

    def wheel(self):
        print("Wheel")

toyota = Car()

上記のtoyota = Car()クラスオブジェクトである。クラスオブジェクトは、属性の参照とインスタンス化という二種類の作業をサポートする。

クラスのインスタンス化には関数記法を用いる。このようなインスタンス化の手順(クラスオブジェクトを「呼び出す」)により、空のオブジェクトが生成される。

さて、先ほど生成したtoyotaオブジェクトを使って、Carクラスから様々なメソッドを呼び出すことができる。engineメソッドとwheelメソッドを呼び出してみよう。

エディタを起動して、mycar.pyという名前のファイルを作成しよう。そのファイルに、以下のコードをコピーしよう:

class Car:

   def engine(self):
       print("Engine")

   def wheel(self):
       print("Wheel")

if __name__ == "__main__":
   toyota = Car()
   toyota.engine()
   toyota.wheel()


このコードを保存しよう。では、このプログラムの内容を詳しく見ていきたい。

Carクラスを利用して、toyotaオブジェクトを作成している。toyota.engine()はメソッドオブジェクトである。メソッドオブジェクトが呼び出されたとき、実際には何が起きるだろうか?

toyota.engine()を呼び出す際、何も引数を与えていないが、メソッドの宣言部を見ると引数selfが記述していることにお気づきかと思う。

なぜエラーを生じないのか、不思議に思うかもしれない。実は、メソッドオブジェクトを使用するためにtoyota.engine()を呼び出すと、Car.engine(toyota)に変換されるのである。selfについては、この先のセクションでより深く学習する。

次のコマンドでプログラムを実行しよう。

python mycar.py

次のような出力が得られるはずだ:

Engine
Wheel

コンストラクタ(Constructor)

Pythonでは、__init__メソッドはコンストラクタメソッドと呼ばれる。コンストラクタメソッドは、データの初期化のために使用される。

ここから先は

15,233字

¥ 300

期間限定 PayPay支払いすると抽選でお得に!