9:23 2023/09/23 (土) 追記改訂

Python クラスの基本的な構造

PCのOSがwindows10あるいは11なら、Microsoft Storeから検索でパイソンかpythonを入力すれば、Python10かPython11バージョンが表示されるだろう。インストールすると、あとは何もユーザー側で特別な操作をする必要はなく、開いて使用可能である。恐らくはidleの対話型シェル">>>"が表示されたものがでるだろう。idleのコマンドメニューでfileを開き、New Fileを開くとidle エディターという実際にプログラムコードを入力できるパッドが表示される。自分が書いてみたコードを実行するには、そのパッドでコマンドメニューのRUNをクリックして直下のRun Moduleをクリックすれば自動で対話型シェルに表示される。
(追記)"僕の方でもインストしてみた。どうやら、Pythonシェルとidleの両方が入るようだ。システム内部にインストされるので、僕が使用しているAnaconda3よりも利便性が落ちる。もし、Anacondaに興味があるなら、Youtubeでキノコードさんが配信しているPython環境構築の閲覧を推奨したい。僕は彼の動画を参考にし現在に至っている。しかし、Anaconda3をアンインストールするには若干、戸惑いを感じるかもしれない。利点だけ言って後々のことを言っておかないのは無責任と思われるのも困る。2023.8.3時点の参考にした情報で僕が行ったアンインストを記載しておく。Win11だがWin10での情報でも実行できた。アンインストには2つの方法がある。
①コントロールパネルの"プログラムと機能"で行う削除、②Anaconda Promptで'conda install anaconda-clean'をして削除ファイル確認後yes実行である。僕のanaconda3は紐づけされたファイルに不適合性(inconsistency)があり、②が使えなかった。①の方法で実行:Anaconda3はインストで"C:Users\所有者名\のパス"に入るであろうから、自分のファルダーの中でanaconda3フォルダーを見つけ、フォルダー内のanconda3\envsとanaconda3\pkgsフォルダーを削除、次にコントロールパネルのプログラムと機能でAnaconda3.x.x(xはバージョン)を削除。次にAnaconda3に関連する残ったと思われるフォルダーを全削除した。Anaconda3を再インストする前に、行う必要があるか分からないが、システム環境変数で追加した以前のAnaconda3用のPathを削除した。そして現在、Anaconda3を無事に再インスト後Python3.8からのPython3.11を使っている。ただ、pathでユーザー名が日本語になっていると、インストが成功しないかもしれない。C\Users\***の部分がアルファベットであるかを確認して欲しい。日本語であるなら、追加のMSアカウントでアルファベット名にし、そのアカウントでログインして、インストするほうが無難かもしれない。
これらのことを聞くと「ええっ....。」となりそうだが、PCリテラシーをある程度保持している人は臆することなく、ぜひともインストしてもらいたい。ネットには良い情報があるのでそれを探しだし自分で解決することの達成感ほど気持ち良いものはない。"
さて、以下のコードでdefとあるが、これは関数を定義する場合に使用する。ゆえに、クラスでは関数利用が前提である。
[関数定義]
Pythonでの関数定義はdef 関数名(引数, 引数, ・・・)コロンでインデント「※字下げのこと」を行う。例えば、引数をa, bとして関数名sum_funcで
a + bの演算を行うものと定義しよう。記号_はアンダースコアという。コロンは忘れずに‼さて、対話型シェルで示すと


>>> def sum_func(a, b):
        add_number = a + b 
        print(add_number) 
>>> sum_func(10, -6) 
4 
>>> 

引数にあてた、ここでは10や-6のことを実引数と呼び、aやbは仮引数と呼ばれる。また、aやbは位置引数ということにもなる。数の加算では問題ではないが、
例えばa割るbを考えていた場合、aを2、bを4と想定する場合とそれらを逆にした場合の数は当然異なる。文字列の操作で考えるとaをfirst_name、
bをlast_nameで定義し、太郎 山田という出力の想定で('山田', '太郎')と実引数を指定すると山田 太郎という異なる出力が出てしまうからである。
また、先のコードで値の出力を出す関数定義にしているが、出力を明示させない、returnを用いた戻り値(もどりち)あるいは返り値(かえりち)という方法がある。
これは、結局はy = f(x)でxが仮引数でyが戻り値と言っているだけで、言わば、表舞台にはまだ出ないでね、ということである。これを用いて表舞台に出すには、
実引数を入れた関数の値を別の変数に代入させ、print(その変数)を書く。上のコードを利用すると、


>>> def sum_func(a, b):
        add_number = a + b
        return add_number
>>> output_adding = sum_func(10, -6)
>>> print(output_adding)
4
>>> 

このreturnを用いたコードはreturnを用いないコードより複雑に感じるかもしれない。しかし、この値に何某かの数を持って演算操作をしたいと考えたとき、
一々、値を出力させて、新たにそういった演算を実行することに抵抗を感じないだろうか?例えば、5を乗じる、6で割る、平方根を求める、等々の連続操作で便利である。上のコードのoutput_adding = sum_func(10, -6)というコードの続きから、


>>> print(output_adding * 5)
20
>>> print(output_adding / 6)
0.6666666666666666 
>>> import math 
>>> print(math.sqrt(output_adding))
2.0
>>> 

こういったように、関数の値だけを舞台袖において置き必要な時に呼び出す。Pythonで扱うデータの要素は通常膨大であろうから、returnの扱いに慣れておくことは必要だろう。import mathはPythonのmathライブラリから数学関数を拝借して平方根を出している。mathのsqrtを使うという構造文はmathドット関数名(引数)である。関数によって、引数は複数の場合がある。また、from math import sqrtと指定すれば、直接print(sqrt())とできる。Pythonライブラリからのimportの方法は2種類あって、import ライブラリ名 か from ライブラリ名 import 関数名である。使用構文は前者が"ライブラリ名ドット関数名()"、後者が"関数名()"である。また、output_addingを略して使うこともできる。例えば、o_ad = output_addingとすれば良い。しかし、第三者と協力でコードを設定するなら、変数名は分り易いものが良いだろう。
ドット関数名()はメソッドと呼ばれ、以後、説明するクラスでは、このメソッドを用いる。クラスで扱うのでクラスメソッドと呼ばれる。


>>> output_adding = sum_func(10, -6)  
>>> o_ad = output_adding 
>>> print(o_ad * 5)
20
>>> print(o_ad / 6)
0.6666666666666666
>>> from math import sqrt
>>> print(sqrt(o_ad))
2.0
>>> 

[クラス]
以下のコードは3人の名前を特定し、名前をそれぞれ出力し、各自に挨拶したものである。詳細解説をコードの終わりにしている。


class User:                                       
    def __init__(self, first_name, last_name):    
           """属性を初期化する"""
        self.given_name = first_name
        self.family_name = last_name

    def describe_user(self):
        """ユーザーの情報を出力する"""
        print(f"ユーザー名:{self.given_name.title()} {self.family_name.title()}")

    def greet_user(self):
        """ユーザーに挨拶をする"""
        print(f"{self.given_name.title()}さん、こんにちは!\n")

user_1 = User('tom', 'longer')
user_2 = User('jesica', 'benetto')
user_3 = User('才蔵', '霧隠')

user_1.describe_user()
user_1.greet_user()

user_2.describe_user()
user_2.greet_user()

user_3.describe_user()
user_3.greet_user()

#実行された出力結果

ユーザー名:Tom Longer
Tomさん、こんにちは!

ユーザー名:Jesica Benetto
Jesicaさん、こんにちは!

ユーザー名:才蔵 霧隠
才蔵さん、こんにちは!

詳細解説   ▲ クラス先頭へ戻る   ▲ 関数定義へ戻る
関数定義の基本を説明したので、クラスを説明してゆく。プリントアウトして参照するのがいいだろう。
上のコードはUserのクラスを作成し、クラスメソッドを利用して、出力したものである。classと宣言して、クラス名をキャメルケース(接頭辞大文字)コロンにする。クラス名でloginuserを想定するなら、LoginUser:とする必要がある。これをログインユーザー:という日本語にしてもいけるが、プログラミングの入力コード言語は英語主体であるから、英文字には慣れるしかない。ただし、英語文法や綴り字ミスは関係はない。しかし、綴り字ミスしたものを変数としたなら、それを以後コードで使用しなければ、エラーがでるだろう。
宣言したあと、まずは基本となるUserが持つ属性(名前、色、身長、年齢、住所等々であり、宣言されたクラス名に左右される)を決定し、その値を初期化する。def __init__(self, first_name, last_name): initの両サイドにはアンダースコアを二回繰り返し入力している。見づらいかもしれない。一本の下線ではない。初期化での値を引っ張ってくる変数(値を呼び出す変数)にはselfドット変数名となる。self.given_nameがそうである。なぜ、初期化というかは、仮引数first_name,last_nameには何の情報も入っておらず、無の状態であるから、それを呼び出すself.given_nameも無の状態である。
def が記載されるコードの全ての引数にselfがある。世界には何百万以上の名前があるだろう。'jim'と言った段階では不特定の名前である。selfをつけることでクラス Userの名前であることを特定したことになるのだろう。すなわち、Userである彼自身の、彼女自身の名前となる。メソッドでのselfはUserの属性を必ず参照する意味がある。したがって、メソッドでの最終的に参照する変数はクラス属性の持つ変数になる。メソッドの定義文をよく見よう。
User('tom', 'longer')は当初Userには()がなかったように、ここで初めて実引数をいれることで、Userを実体化したことになる。これをクラスのインスタンス化と呼ぶ。そして、まるでreturnの機能のように舞台袖にあったかのように、そのインスタンスを別変数で呼び出し、メッソド方式での関数利用で出力している。user_1 = User('tom', 'longer') user_1.describe_user()。user_1.describe_user()の()に引数がないのは、そのメッソドの定義の引数にはselfがあり、 user_1は既にUserの情報を持っているので、user_1.describe_user()の()に引数を入れる必要はない。
tom longerが出力でTom Longerとなっているが、print(f"ユーザー名:{self.given_name.title()} {self.family_name.title()}")でのドットtitle()というメソッドが先頭文字を大文字にしている。これをタイトルケースという。.title()の()に引数がないが、('tom')や('longer')は自明なので、user_1.describe_user()のほうでも入れる必要が無い。
print(f"ユーザー名:{self.given_name.title()} {self.family_name.title()}")の f は f-strings と呼ばれ、 f はフォーマットを表していて、波括弧で囲まれた変数を、その値に変換することで、文字列をフォーマットする。
説明が前後してしまうが、クラスの一部となっている __init__( ) もメッソドであり、特殊メソッドと呼ばれるものの一つである。2つのアンダースコアを用いる理由はコード作成者が使用するメソッド名との衝突を防ぐことにある。このメソッドを利用して、インスタンス作成でself引数での自身参照となり、属性や作成メソッドにアクセスすることになる。
僕自身はそういったものか、と現状は軽く考えて、構造の理解に努めている。いずれにせよ、自らの手・頭を動かしコードを作成し、実行してみて、もしエラーが発現すれば、どこがオカシイかを探り、ある程度想定する実行結果に辿りつくかが重要と考えている。Practice makes perfect.である。
順序が逸脱したが、もし、かようなコードを書いていて、出力情報を追記したいと思うことがあるかもしれない。User属性の追加が可能である。しかし、def __init__(self, first_name, last_name):の引数に書かなくてもよい。クラスメソッドを定義するときに、そのメソッドに入れた変数(引数)をself.変数名で呼び出すようにするのである。実際のコードが以下に代わる。
なお、\が表示されてたら、それは"バックスラッシュ \"である。文字コードの関係でそうなってしまう。予備知識で知っておいてほしいが、Windowsの文字コードはShift-JISである。世界で多く利用されているのはUTF-8である。僕のホームページの文章作成に使っているエディターはUTF-8対応だが、ブラウザで確認すると\が\になっている。Windowsの文字コードがUTF-8に今後、全対応するかわからない。自分のPCでシステムロケールの設定変更でβ版のUTF-8に対応させることは可能である。しかし、アプリによっては文字化けする可能性がある。僕のアプリでもそのような現象に見舞われたことがある。


class User:                                       
    def __init__(self, first_name, last_name):    
           """属性を初期化する"""
        self.given_name = first_name
        self.family_name = last_name
        self.age = 0        

    def describe_user(self):
        """ユーザーの情報を出力する"""
        print(f"ユーザー名:{self.given_name.title()} {self.family_name.title()}")

    def greet_user(self):
        """ユーザーに挨拶をする"""
        print(f"{self.given_name.title()}さん、こんにちは!")

    def user_age(self, age):
        """ユーザーの年齢を出力する"""
        self.age = age
        print(f"{self.age}歳\n")

user_1 = User('tom', 'longer')

user_1.user_age(45)

新たなメソッドに引数を指定したので、インスタンス出力の引数に45を入れている。メソッドで引数を指定したら、必ず初期化する属性を追記することを忘れない。もちろん、スペルミス(綴り字ミス)を犯さないことに注意する。属性の初期化でself.age = 0 としているが、self.age = ''でも良い。また、引数は('45')あるいは('45')のように文字列半角、文字列全角でも想定される出力結果になる。条件付きで全角文字列ではエラーになるコード設定もできるのであろう。
僕の使用参考書:最短距離でゼロからしっかり学ぶ Python入門 Eric Matthes 著 出版:技術評論社
YouTubeやネットサイトも参考になるだろう。また、対話型シェルでimport thisと入力エンターすれば、The Zen of Python, by Tim PetersというPythonを扱う上での心構えが見れる。英語だが、ネットで翻訳アップしているから、読んでみよう。
さあ、興味が湧いたら" It'll be better to do now rather than do tomorrow. "
▲ トップへ戻る


戻る
カテゴリー