05オブジェクト指向プログラミング / Object Oriented Programming

【目次/TOC】

  1. 関数
    Function
  2. オブジェクトとクラス
    Objects and Classes

1. 関数 /Function

5.1.1 組み込み関数/Built-in Functions

Pythonインタプリタには数多くの関数と型が標準的に組み込まれています。なぜ「関数や型」とこの2つを同様に扱えるかというと,オブジェクト指向プログラミングの見地からすると,関数も型もオブジェクトだからです。
まずは簡単な組み込み関数(built-in functions)の例としてデータ型を確認するtype関数を使ってみましょう。前回作成したlikeというオブジェクトは,Python インタプリタの終了とともに消えてしまっているので,第4回のコード18を再度実行してから,下記のコード1を実行しましょう。同様に,必要だと考えるオブジェクトはあらかじめ作成しておきましょう。

The Python interpreter comes with a number of standard functions and types. The reason why functions and types can be treated in the same way is that from the perspective of object-oriented programming, functions and types are both objects.
The object named "like"created in the previous session has disappeared with the termination of the Python interpreter, so please run code 18 in Session 04, and then run code-1 below. Likewise, please create object when you think necessary.

type(like)

インデックスの項目のときに作った n の型は何だったでしょうか。下記は,コード2です。

What was the type of n we created for the part of index? The following code is code-2.

n = ["1番","2番","3番"]
type(n)
n = ["No.1","No.2","No.3"]
type(n)

Pythonの標準ライブラリ(standard library)には,type()のような組み込み関数から組み込み型,そしてmath のような標準モジュールまでが含まれています。例えばmathというモジュールにはsqrt という関数が含まれていて,平方根を計算したい場合にはmath モジュールのsqrtを実行することになります。ライブラリを使用するにはimport による読込が必要です。下記は,コード3です。

Python's standard library includes built-in functions such as type(), built-in types, and even standard modules such as math. For example, the math module contains a function called sqrt, and if you want to calculate the square root, you would execute sqrt from the math module. To use the library, you need to load it via import. The following code is code-3.

import math
math.sqrt(36)

モジュールには Python に組み込まれている標準ライブラリのモジュールの他に,外部ライブラリ(third-party library)のモジュールがあります。外部ライブラリを使用する場合にはライブラリの読込の前にライブラリのインストールが必要です。

In addition to the standard library modules built into Python, there are also modules from third-party libraries. If you want to use an external library, you need to install the library before importing it.

5.1.2 ライブラリの読込/Importing Libraries

Pythonでは,インタープリタのセッションを再開するたびに自分の使いたいライブラリ,パッケージやモジュールを読み込みます。ライブラリはパッケージやモジュールから構成されていて,パッケージはモジュールの集合体です。 外部ライブラリは,読み込む前にpipコマンドでインストールしなければならないのですが,よく使われるライブラリは標準的にColabにプリインストールされています。 たとえばpandasというColabに組み込まれているライブラリの場合,もっとも簡素な読込の形が下記のコード4です。しかし,一般的には別名をつけておきます。たとえば,pandasにはpdという別名をつけて読み込みむのがその下のコード5です。
本章あたりの段階ででPythonコードの実行が上手くいかない場合,原因は大抵、以下のどれかです:

  1. スペルミス(特に全角になってしまっている記号に気をつける)
  2. 必要なオブジェクトを作り直す必要がある
  3. 必要なライブラリが読み込まれていない

With Python interpreter, each time you resume a session, you import libraries, packages or modules you would like to use. A library contains related packages and modules, and a package contains modules. Third-party libraries should be installed with the pip command before importing them, but most commonly used libraries are pre-installed in Colab. In the case of the pandas library, the simplest form of importing it is code-4. Usually, we put an alias, and code-5 below will import pandas with pd as its alias.
If you have trouble running Python code at this stage, the cause is usually one of the following:

  1. Spelling errors
  2. Need to re-create objects
  3. Need to import libraries

import pandas
import pandas as pd

5.1.3 モジュール関数/Module Functions

モジュール内のクラスや関数を使うときのドットの使い方です。たとえばpandasモジュールで定義されているExcelの生データのようなデータの型であるDataFrameクラスをpandas.DataFrameで呼び出して用いることができます。また,モジュール内の関数read_csvをpandas.read_csvで呼び出せば,その関数を使ってcsvファイルを読み込みます。いちいちpandasと入力するのは面倒なので,あらかじめ付けておいた別名を使い,pd.DataFrameやpd.read_csvと短く表記するのがふつうです。。

Dots are used when using classes and functions in a module. For example, the class DataFrame (two-dimensional tabular data that looks like raw data in Excel) in the pandas module can be used with pandas.DataFrame. Or with pandas.read_csv, you can use the function read_csv defined in the module and read a csv file. Instead of typing "pandas", an alias is normally used, such as pd.DataFrame or pd.read_csv.

5.1.4 関数の定義/Defining Functions

関数を自分で作成するときには,def文を用いて括弧 () 内に仮引数を入れて定義し, returnで戻り値を指定します。
日本の小学生の年次を引数に与えると年度はじめの年齢に換算するageという関数を自分で作るとしましょう。関数を定義するためのdef文,それに続くのが新しく定義する関数名,それに続く(丸括弧)内のものは仮引数といいます。仮のものですのでわかりやすければどんな名前でも構いません。下記のコード6では,ageとyearが自らが付けた任意の名前で,ageが関数,yearが仮引数です。インデントされた行の中で,仮引数を使って関数を定義します。

You can create a function by using a def statement with parameters in parentheses () and ending with return to get the return value.
Suppose you want to create a function called age that converts the grade of Japanese elementary school into the students' age at the beginning of the school year. The def statement is followed by the name of the function, and parameters in round brackets. You can choose any name for the new function, and easily understandable names are better. In the following code-6, age and year are arbitrary names, where age is the function name and year is the parameter. In the indented lines, the command of the function is defined using parameters.

def age(year):
    age = year + 5 
    return age

これで age という関数が定義できたので,コード7で引数を与えてみましょう。このように実際に関数を使用するときに与える引数のことを実引数といいます。

Now that we have defined a function called age, let's give it an argument like code-7. Argumentst are the values given in round brackets when the function is actually used.

age(4)

下記コード8のように,printで結果を表示させたい場合には,returnは必要ありません。

If you simply want to print the result as in code-8, you do not need to use return.

 def age(year):
    age = year + 5
    print(f"小学{year}年生は{age}歳です。")
 def age(year):
    age = year + 5
    print(f"The elementary school students in the grade {year} are {age} years old.")

今度はコード9で2という引数を関数に渡してみましょう。関数に引数を渡す場合には,仮引数=実引数という書き方もよく使われます。これをキーワード引数といいます。キーワード引数がよく使われるのは,それにより引数の順番を変えたり,複数ある引数の一部だけを指定したりすることができるからです。

Now let's pass 2 as the argument into the function with code-9. When passing arguments to a function, it is also common to use keyword arguments expressed as parameter = argument. Keyword arguments are useful because they allow you to change the order of arguments or to specify optional parameters when only some of them are in use.

 age(year = 2)

2. オブジェクトとクラス/Objects and Classes

5.2.1 オブジェクトとクラス/Objects and Classes

関数には,単独で使えるprintのような関数と,オブジェクトに属する関数があり,後者をメソッドといいます。オブジェクトにドットと関数を付けて使います。メソッドでよく使われるのが,たとえばappendです。リストの関数で,リストに要素を追加(append)するメソッドです。たとえば下記コード10の2行目のn.appendは,2つの要素を持つリストを格納したnという変数に.appendとつけてメソッドを呼び出し, nの要素に(引数)を追加します。

There are two kinds of functions: functions like print() that can be used alone, and functions that belong to objects (methods). The latter is called methods, and an object is followed by a dot and a function. One of the frequently used methods is append. This method is a function of the list type, and it appends an element to a list. See code-10 below. There is a variable named n that contains a list of two elements. Then n.append in the second line calls the method of appending the argument to n.

n = ["1番","2番"]
n.append("3番")
print(n)
n = ["No.1","No.2"]
n.append("No.3")
print(n)

5.2.2 クラスとインスタンス/Classes and Instances

組み込みデータ型のリストにはappendという関数が定義されていましたが,自分でクラスを定義してデータと動作を組み合わせることができます。下記コード11を実行するとPeopleというクラスが定義されます。クラス内で作るオブジェクトをクラスオブジェクトと呼び,標準の組み込み型を参照する属性参照とインスタンス化の操作が可能になっています。クラスオブジェクトとして関数を作成するコード11では,defを用いてlotteryなどの関数オブジェクトを作成しています。

The built-in data type of list has the append function in it, and likewise it is also possible to define a class by which you can bundle data and functionality. Running code-11 below defines a class named People. Objects created in a class are called class objects, and attribute references, that allow users to refer to the standard built-in types, and instantiation are made possible with them. Class objects can be functions, and Code-11 uses def to create function objects such as lottery.

class People():
    def __init__(self, id, name, age): 
        self.id = id   
        self.name = name
        self.age = age
    def lottery(self):
        print(self.name + "さんに宝くじが当たりました! ")
    def bee(self):
        print(self.name + "さんは蜂に刺されました。")
class People():
    def __init__(self, id, name, age):
        self.id = id
        self.name = name
        self.age = age
    def lottery(self):
        print(self.name + " has won the lottery!")
    def bee(self):
        print(self.name + " was stung by a bee.")

上記コード11を実行後,type関数でクラスオブジェクトであるPeople.lotteryの型を確かめると,属性参照によりfunctionと表示されるはずです。
次にインスタンス化を下記コード12で見てみます。インスタンスを作るには,クラス名を関数のように丸カッコをつけて生成します。クラスで__init_(self)メソッドを定義しておけば,selfに続く引数が,インスタンス化の丸カッコ内の引数を受け取ってくれます。インスタンス化により生成されたオブジェクトを代入したperson1等がインスタンスです。

After running code-11 above, checking the type of the class object, People.lottery, with the type function should return "function" by attribute reference.
Next, let's see Instantiation with code-12 below. To generate an instance, use the class name like a function folllowed by round brackets. If the method __init__(self) is defined in the class, the arguments following self will received the arguments in the round brackets of the class instantiation operator. The generated objects are assigned to person 1, etc., which are instances.

person1 = People("001", "ささき", "30")
person2 = People("002", "たけいし", "45")
person1 = People("001", "Sasaki", "30")
person2 = People("002", "Takeishi", "45")

インスタンスオブジェクトが理解する属性参照にはデータとメソッドがあります。関数typeでperson1.nameの型を調べると,データが文字列であることがわかるはずです。
下記のコード13では,インスタンスがクラスの関数オブジェクトを参照しています。

Instance objects understand attribute references, which have data and methods. If you examine the type of person1.name with the function type, you should see that the data is string.
Instances in code-13 below refer to function objects of the class.

person1.lottery() 
person2.bee()

コード13の実行後,関数typeでperson1.lotteryの型を調べると,メソッドであることがわかるはずです。
person1.idはインスタンス変数で,各インスタンスの固有のデータのものですが,下記コード14のPeople.totalはクラス変数です。

If you check the type of person1.lottery with the function type() after running code-13, you should see that it is a method.
person1.id is an instance variable, which is for data unique to each instance,whereas People.total in code-14 below is a class variable.

class People ():
    total = 0
    def __init__(self, id, name, age): self.id = id
        self.name = name
        self.age = age
        People.total += 1
    def lottery(self):
        print(self.name + "さんに宝くじが当たりました! ")
    def bee(self):
        print(self.name + "さんは蜂に刺されました。")
    def number(self):
        print(f"人数は{People.total}人です 。")
class People():
    total = 0
    def __init__(self, id, name, age):
        self.id = id
        self.name = name
        self.age = age
        People.total += 1
    def lottery(self):
        print(self.name + " has won the lottery!")
    def bee(self):
        print(self.name + " was stung by a bee.")
    def number(self):
        print(f"The number of people is {People.total}.")

下記はコード15です。

The following code is code-15.

person1 = People("001", "ささき"", "30") 
person1.number()
person1 = People("001", "Sasaki", "30") 
person1.number()

下記はコード16です。

The following code is code-16.

pperson2 = People("002", "たけいし","45") 
person2.number()
pperson2 = People("002", "Takeishi","45") 
person2.number()

下記はコード17です。

The following code is code-17.

person1.number()

5.2.3 名前空間とスコープ/Namespace and Scope

オブジェクトに続いてドット(.)の後に続くのは属性の名前です。属性参照によってその名前で参照する範囲(Scope)にある辞書のことを名前空間といいます。上記のクラス定義の後に,Local名前空間をlocals()で確認してみてください。名前空間が辞書であるという感覚がわかると思います。辞書というのは,コロンの前後で,名前(キー)とその名前に格納されているデータなどの属性の中身(値)を対応させている,キー:値という構造です。Peopleというクラス内でtotalという属性を作成したとしても,そのクラスの外部からはtotalの中身を参照できません。それはtotalがクラス内のLocalな名前空間に追加されているからです。このように,名前から属性を参照する範囲には,内側から,Local, Enclosing, Global, Built-inがあります。Pythonを立ち上げなおすとモジュール内を参照範囲とするGlobalがLocalであるレベルに戻り,それ以下の,自分で定義したPeopleのようなクラスは削除されています。

When an object is followed by a dot (.), what follows after the dot is a name of an attribute. The Scope referred to by the attribute reference by its name is called a namespace, which is a dictionary. You will get the sense that the namespace is usually a dictionary if you try checking the Local namespace with locals() after defining the class above. A dictionary appears, which takes the structure of key:value, with which a name (key) is associated with the contents of attributes (value) such as data stored in the name. If you create an attribute named total in the class People, you cannot refer to the contents of total from outside the class. This is because total is added to the local namespace in the class. In order from smallest to largest , there are Local, Enclosing, Global, and Built-in. When Python interpreter is re-launched, it returns to the level where the Local is Global, where the module is the scope referred, and scopes at the levels below that, including defined classes such as People, have been removed.

5.2.4 継承・カプセル化・ポリモーフィズム/Inheritance, Encapsulation, and Polymorphism

すでにHumanというクラスがあるとしましょう。クラスPeopleを定義するのに

class People (Human):
を下記の代わりに使うと,
class People ():
HumanクラスをPeopleクラスに受け継ぐすることができます。このPythonの特徴を継承といいます。

Let's say you already have a calss Human, and if you define the class People like this

class People (Human):
instead of
class People ():
then you can inherit the Human class. This feature of Python is called inheritance.

次は,カプセル化についてです。下記コード18では、名前の前にアンダースコアが1つ付いているものと2つ付いているものがあります。これらはname manglingと呼ばれ、そもそも名前を上書きしてしまう事故を防ぐために設計されたものですが、この機能は変数をプライベート変数にしたいことを表現するカプセル化にも使われます。ただしどちらも完全なアクセス防止にはならないことに注意してください。

Next, let's look at encapsulation. In code-18 below, there are two parts with unerscores: one underscore and two underscores. These are called name mangling and originally designed to avoid accidents of overriding names but this feature is used also for encapsulation to express that you would like variables to be private variables. Note that neither is completely access-preventive.

class People ():
      def __init__(self, id, name, age):
          self.id = id 
          self._name = name 
          self.__age = age

下記はコード19です。

The following code is code-19.

pcerson1 = People("001", "ささき", 30) 
print(person1.id) 
print(person1._name) 
print(person1.__age)
person1 = People("001", "Sasaki ", 30) 
print(person1.id) 
print(person1._name) 
print(person1.__age)

さて、ポリモーフィズムについてです。以下のコード20では、同じ関数year_ageを使いながら,.gradeメソッドを通じてインスタンス(pupil1など)ごとに異なる機能を持たせています。この性質をポリモーフィズムと呼びます。

Now, about polymorphism. With code-20 below, you can use the same function year_age, while through .grade method, you can apply different functionalities to each instance (pupil1, etc). This feature is called polymorphism.

class School:
    def __init__(self, name, year): 
        self.name = name
        self.year = year

    def grade(self):  
        return f"{self.name}は{self.year}年生"

class Elementary(School): 
    def grade(self):
        age = self.year + 5 
        return super().grade() + f"で{age}歳です。"

class Juniorhigh(School): 
    def grade(self):
        age = self.year + 11 
        return super().grade() + f"で{age}歳です。"

class High(School): 
    def grade(self):
        age = self.year + 14 
        return super().grade() + f"で{age}歳です。"

def year_age(x): 
    print(x.grade())

pupil1 = Elementary("はる", 3)
pupil2 = Juniorhigh("なつ", 3)
pupil3 = High("あ き", 3)

year_age(pupil1) 
year_age(pupil2) 
year_age(pupil3)
class School:
    def __init__(self, name, year): 
        self.name = name
        self.year = year

    def grade(self):  
        return f"{self.name} is in Grade {self.year}"

class Elementary(School): 
    def grade(self):
        age = self.year + 5 
        return super().grade() + f" and {age} years old."

class Juniorhigh(School): 
    def grade(self):
        age = self.year + 11 
        return super().grade() + f" and {age} years old."

class High(School): 
    def grade(self):
        age = self.year + 14 
        return super().grade() + f" and {age} years old."

def year_age(x): 
    print(x.grade())

pupil1 = Elementary("Haru", 3)
pupil2 = Juniorhigh("Natsu", 3)
pupil3 = High("Aki", 3)

year_age(pupil1) 
year_age(pupil2) 
year_age(pupil3)