Python オブジェクト指向定義クラス (クラス)

        オブジェクト指向には、カプセル化、継承、ポリモーフィズムという 3 つの基本的な特性があります。ここでのカプセル化により、現実に客観的に存在する特定のタイプのものを、抽象ロジックを備えたクラスにカプセル化することができます。抽象的であるということは、具体性がないということです。私たちがカプセル化するクラスには特定のデータや特定の動作はなく、単なるロジックのコレクションです。しかし、クラスをインスタンス化すると、そのクラスのインスタンスは特定のオブジェクトになり、特定のデータを持つだけでなく、特定の動作も実行できます。老子が『道経経』で言ったように、「名もなき天地の始まりは万物の母の名である」。クラスには、インスタンス化前には無限の可能性がありますが、インスタンス化後は、抽象化から具体化への 1 つの可能性のみがあります。例: 抽象的な論理ではなく、実際の例、具体的なもの。具現化:抽象化から具体化へのプロセス。

クラスをカプセル化する

基本構造

class 类名(要继承的类,可以继承多个类):
    定义属性及方法的逻辑语句

        以下では、人間を例として、抽象クラスをカプセル化する方法を説明します。人には名前、性別、年齢、身長、体重などの属性があり、これらはクラスの属性に属し、人には食べる、飲む、寝る、歩く、ジャンプするなどのメソッドに属する行動があります。クラス。次に、この情報に基づいてクラスをカプセル化します。

class Person:  # 使用class定义一个名为Person的类,默认继承object
    """人类"""
    name = None  # 定义姓名属性
    sex = None  # 定义性别属性
    age = None  # 定义年龄属性
    height = None  # 定义身高属性
    weight = None  # 定义体重属性
    
    def eat(self):  # 定义吃饭行为
        """吃饭"""
        
    def drink(self):  # 定义喝水行为
        """喝水"""
        
    def sleep(self):  # 定义睡觉行为
        """睡觉"""
        
    def walk(self):  # 定义行走行为
        """行走"""
        
    def jump(self):  # 定义跳跃行为
        """跳跃"""

これで person クラスができましたが、この person は客観的なものから抽象化された概念です。person には特定のデータがありません。つまり、person は特定の人を具体的に表すことができないため、person は誰でも代表することができます。人の属性名は、Zhang San、Li Si、Wang Wu、Xiao Ming、Funny Frogなどにすることができます。人の属性の性別は男性または女性にすることができ、年齢、身長、体重の値は正常な範囲内で選択できます人間の。person は特定の人物ではありませんが、誰でも構いません。クラスの抽象的な概念を理解できたので、今度は Person を使用して人 (クラスのインスタンス化) を具体的に表現してみましょう。

クラスのインスタンス化

        クラスのインスタンス化は関数の実行と似ています。変数名の後に括弧が追加されます。関数は実行を表し、クラスはインスタンス化を表します。関数の実行後に値が返され、クラスがインスタンス化された後にクラスのインスタンスが返されます。返されたインスタンスを受け取るには変数を使用する必要があります。このインスタンスはクラスによって表される特定のものであるため、インスタンスにはクラス内のすべてのプロパティとメソッドが含まれます。このインスタンスのプロパティに値を割り当て、インスタンス内のメソッドを使用できます。

person = Person()  # 实例化Person
person.name = '小明'  # 给name属性赋值
person.sex = '男'  # 给sex属性赋值
person.age = 20  # 给age属性赋值
person.height = 170  # 给height属性赋值
person.weight = 60  # 给weight属性赋值
print(f'姓名: {person.name}\n性别: {person.sex}\n年龄: {person.age}\n身高: {person.height}\n体重: {person.weight}')

実行結果は以下の通りです。

Person をインスタンス化した後、このインスタンスの属性に値を割り当てます。このインスタンスは Xiao Ming (本物、名前のある人) になります。

インスタンス化メソッド __init__

        Person クラスをインスタンス化しましたが、このインスタンス化方法は、プロパティに 1 つずつ値を割り当てる必要があり、非常に面倒であることがわかりました。では、属性に値をすばやく割り当てる方法はあるのでしょうか? 答えは「はい」です。初期化メソッド __init__ でこれを行うことができます。__init__ は、Python でオブジェクトを記述する基礎となるメソッドの 1 つです。Python でオブジェクトを記述する基礎となるメソッドは、マジック メソッドとも呼ばれます。Python でオブジェクトをインスタンス化するには、次のプロセスを実行します。

Python は最初にクラス内の __new__ メソッドを呼び出して、クラスのインスタンス オブジェクトを作成します。インスタンス オブジェクトが作成された後、次に __init__ メソッドを呼び出してインスタンス オブジェクトを初期化します。初期化後、オブジェクトを取得します。しかし、__new__ メソッドと __init__ メソッドが person で定義されていないのに、なぜ person がインスタンス化できるのかと言う人もいるかもしれません。これには、オブジェクト指向継承の 2 番目の基本機能が関係します。Python 3 では、継承クラスが定義されていない場合、class を使用してカスタマイズされたすべてのクラスがデフォルトでオブジェクト クラスを継承することが規定されています。したがって、 Person は Python が提供する基本クラスであるオブジェクト クラスを継承します。__new__、__init__、およびその他の基礎となる記述メソッドは object で定義されており、オブジェクト クラスはbuiltins.py ファイルにあります。ただし、そこにはメソッド名が表示されているだけで、メソッドを実装するロジックはすべて Python インタプリタに統合され、C 言語で実装されています。builtins.py ファイル内のすべてのメソッドには実際のロジックはなく、エディターでコードを作成するときにエラーを報告しないようにするためにのみ提供されていると言えます。

        上記の Person クラスは object の __new__ メソッドと __init__ メソッドを継承しているため、 Person を初期化することができます。現在は、object で __init__ メソッドを使用しなくなり、Personal で __init__ メソッドをカスタマイズすることを選択しました。__init__ メソッドが PERSON で定義されている場合、PERSON はインスタンス化するときに最初に独自の __init__ メソッドを使用します。これが継承の特徴で、自分のものを使う場合もあれば、ない場合は継承したものを使うこともあります。

class Person:
    """人类"""
    name = None
    sex = None
    age = None
    height = None
    weight = None

    def __init__(self, name, sex, age, height, weight):
        """初始化"""
        self.name = name
        self.sex = sex
        self.age = age
        self.height = height
        self.weight = weight


person = Person('小明', '男', 20, 170, 60)  # 实例化Person
print(f'姓名: {person.name}\n性别: {person.sex}\n年龄: {person.age}\n身高: {person.height}\n体重: {person.weight}')

実行結果は以下の通りです。

このインスタンス化の方法は非常に簡単ですか? __init__ の最初のパラメータが self であるのはなぜなのか、self とは何なのかなど、いくつかの疑問が生じたり、self の存在について混乱したりするかもしれません。実際、関数はグローバル関数とローカル関数に分かれているため、この self はインスタンスのパラメーターを受け取るために使用されます。グローバル関数はどこからでも直接呼び出すことができ、グローバル関数の実行時に他のオブジェクトをバインドする必要はありません。ローカル関数はローカル スコープでのみ使用できるため、ローカル関数を実行するにはオブジェクトにバインドする必要があります。Python がローカル関数を実行するとき、関数がバインドされているオブジェクトにこのメソッドが定義されているかどうかが最初にチェックされます。このメソッドはオブジェクト内に定義されている場合のみ実行できます。定義されていない場合は属性エラーが発生します。投げられる。そのため、self は Person のインスタンス オブジェクトを受け取るために使用されます。__new__ によって Person のインスタンスが作成されると、渡したインスタンスと値が __init__ に渡されます。インスタンスは、self に渡されて割り当てられる最初のパラメータです。__init__ then他の値をインスタンス (自身) の属性に代入します。

        この self は __init__ に現れるだけでなく、すべてのインスタンス メソッドにも現れ、最初のパラメーターを占めます。すべてのインスタンス メソッドを実行するには、インスタンス オブジェクトにバインドする必要があるためです。次の例を参照してください。

class Person:
    name = None
    sex = None
    age = None
    height = None
    weight = None

    def __init__(self, name, sex, age, height, weight):
        self.name = name
        self.sex = sex
        self.age = age
        self.height = height
        self.weight = weight

    def eat(self):
        """吃饭"""
        print(f'{self.name}正在吃饭')


person = Person('小明', '男', 23, 170, 60)

person.eat()
Person.eat(person)

実行結果は以下の通りです。

person.eat() と person.eat(person) の両方が「Xiao Ming が食べています」を出力できることがわかります。 person.eat() は、インスタンス メソッドを直接呼び出すインスタンス メソッドです。このメソッドは、インスタンスを直接渡すことができます。パラメータ self; Person.eat(person) はクラスで使用されるインスタンス メソッドであり、インスタンス person を Eat のパラメータ self に渡す必要があります。これが self の役割です。self はインスタンスを受け取るためのローカル変数です。クラスでは、self をクラスのインスタンスとして使用することがよくあります。

        したがって、self に関連するものはすべてインスタンスに関連します。たとえば、最初のパラメータが self である関数はインスタンス関数と呼ばれ、self.name、self.age、self.sex はインスタンス属性と呼ばれます。self.name と name の違いは何ですか? Self.name はインスタンス属性であり、name はクラス属性です。インスタンス属性はインスタンス メソッドでのみ定義でき、クラス属性はクラスまたはクラス メソッドでのみ定義でき、インスタンス属性とクラス属性は同じ名前を持つことができます。インスタンス属性はインスタンス メソッドでのみ使用でき、クラス メソッドや静的メソッドでは使用できません。クラス属性はインスタンス メソッドとクラス メソッドで使用できますが、静的メソッドでは使用できません。インスタンス メソッドがクラス属性を使用する場合、それは self.xxx を通じても使用されるため、インスタンス属性とクラス属性が同じ名前 (self.name と name のように) を持つ場合、インスタンス メソッドはインスタンス属性を使用します。

クラス属性

        クラスまたはクラス メソッド内でのみ定義でき、他のメソッドでは定義できません。クラス内でのみ定義することをお勧めします。クラスメソッド、インスタンスメソッドで使用できますが、静的メソッドでは使用できません。

class Person:
    name = None  # 类属性
    sex = None  # 类属性
    age = None  # 类属性
    height = None  # 类属性
    weight = None  # 类属性
    complexion = None  # 类属性

クラスの外に新しいクラス属性を追加することもできます (例: Person.blood_type = 'AB')。しかし、これにはあまり意味がないので、通常はこのように使用しません。

インスタンスのプロパティ

        これはインスタンス メソッド内でのみ定義でき、他の場所では定義できません。できれば __init__ 内でのみ定義できます。インスタンス メソッドでのみ使用でき、クラス メソッドや静的メソッドでは使用できません。

class Person:

    def __init__(self, name, sex, age, height, weight):
        self.name = name  # 实例属性
        self.sex = sex  # 实例属性
        self.age = age  # 实例属性
        self.height = height  # 实例属性
        self.weight = weight  # 实例属性

新しい属性 person.blood_type = 'AB' をインスタンスに追加することもできます。これにはあまり意味がないので、パブリック変数またはローカル変数を直接使用することをお勧めします。クラスで __setattr__ をカスタマイズしていて、何か特別なことをしたい場合を除きます。

インスタンスメソッド

        インスタンス メソッドは、インスタンス (self) を最初のパラメータとして受け取るクラス内のメソッドであり、インスタンス属性とクラス属性を使用できます。インスタンスはインスタンス メソッドを直接呼び出すことができます。クラスがインスタンス メソッドを呼び出すときは、最初のパラメータをインスタンスに渡す必要があります。

class Person:
    name = None  # 类属性
    sex = None  # 类属性
    age = None  # 类属性
    height = None  # 类属性
    weight = None  # 类属性
    food = '大米饭'  # 类属性

    def __init__(self, name, sex, age, height, weight):
        self.name = name  # 实例属性
        self.sex = sex  # 实例属性
        self.age = age  # 实例属性
        self.height = height  # 实例属性
        self.weight = weight  # 实例属性

    def eat(self, location):  # 实例方法
        """
        吃东西行为

        :param location: 位置
        :return:
        """
        print(f'{self.name}正在{location}吃{self.food}')  # 同时使用了类属性和实例属性


person = Person('小明', '男', 23, 170, 60)  # 实例化Person类,并把实例赋值给person
person.eat('家里')  # 实例调用实例方法
Person.eat(person, '家里')  # 类调用实例方法,第一个参数必须传入实例

実行結果は以下の通りです。

インスタンス メソッド Eat() は、インスタンス属性 self.name とクラス属性 food の両方を使用できます。インスタンスはインスタンス メソッドを直接呼び出すことができます。クラスがインスタンス メソッドを呼び出すときは、最初のパラメータをインスタンスに渡す必要があります。

クラスメソッド (@classmethod)

        クラス メソッドは、@classmethod デコレーターで装飾されたメソッドであり、最初のパラメーターとしてクラス (cls) を使用します。クラス メソッドはクラス属性のみを使用でき、インスタンス属性は使用できません。インスタンスとクラスの両方がクラス メソッドを直接呼び出すことができます。

class Person:
    name = None
    sex = None
    age = None
    height = None
    weight = None
    food = '大米饭'

    def __init__(self, name, sex, age, height, weight):
        self.name = name
        self.sex = sex
        self.age = age
        self.height = height
        self.weight = weight

    @classmethod
    def eat(cls, location):  # 使用@classmethod修饰后,就成了类方法,第一个参数cls接收类
        """
        吃东西

        :param location: 位置
        :return:
        """
        print(f'{cls.name}正在{location}吃{cls.food}')  # 只能使用类属性


person = Person('小明', '男', 23, 170, 60)
person.eat('家里')  # 实例调用类方法
Person.eat('家里')  # 类调用类方法

実行結果は以下の通りです。

インスタンス属性 self.name = 'Xiao Ming'、クラス属性名 = なし。クラスメソッドeatはクラス属性名しか使用できないため、実行結果はNoneとなり家でご飯を食べています。インスタンスとクラスの両方がクラス メソッドを直接呼び出すことができます。

静的メソッド (@staticmethod)

        静的メソッドとは、 @staticmethod デコレーターで装飾されたメソッドで、クラスの外に存在できるメソッドです。メンテナンスや関数の呼び出しを容易にするために、クラスの属性とは関係のないメソッドをクラス内に記述することがよくありますが、このメソッドは静的メソッドです。静的メソッドではクラス属性もインスタンス属性も使用できませんが、インスタンスとクラスの両方で静的メソッドを直接呼び出すことができます。

class Person:
    name = None
    sex = None
    age = None
    height = None
    weight = None
    food = '大米饭'

    def __init__(self, name, sex, age, height, weight):
        self.name = name
        self.sex = sex
        self.age = age
        self.height = height
        self.weight = weight

    @staticmethod
    def eat(location):  # 使用@staticmethod修饰后,就变成了静态方法
        """
        吃东西

        :param location: 位置
        :return:
        """
        print(f'正在{location}吃')


person = Person('小明', '男', 23, 170, 60)
person.eat('家里')  # 实例调用静态方法
Person.eat('家里')  # 类调用静态方法

実行結果は以下の通りです。

クラスのパブリックとプライベート

        クラスのパブリック プロパティとメソッドは、他の人に使用したいプロパティとメソッドであり、外部インターフェイスとも呼ばれます。クラスのプライベート プロパティとメソッドは、他の人に使用したくないプロパティとメソッドです。クラスの外から直接呼び出すことはできません。クラスを定義するときは、クラス内のロジックの正確性を確保するために、通常、クラス内の特定のメソッドや属性が悪意を持って呼び出されたり改ざんされたりすることを防ぎます。このとき、クラス内の一部のプロパティやメソッドをプライベートプロパティやメソッドとして設定しますが、プライベートプロパティやメソッドはクラス内でのみ使用でき、クラス外から呼び出したり変更したりすることはできません。これにより、クラス内のメソッドがデータを正しく返すことが効果的に保証されますが、残念ながら、Python には実際のプライベート プロパティやメソッドは存在しません。Python のプライベート性は、非表示と名前変更によって擬似的にプライベートになります。

        エディターでコードを記述するとき、便宜上、パブリック プロパティとメソッドが表示されます。以下に示すように:

属性値の悪意のある変更

        以下に、プライベート属性がないために属性値が悪意を持って変更され、関数の出力結果に影響を与える例を示します。

class Person:
    name = None
    sex = None
    age = None
    height = None
    weight = None
    food = '大米饭'

    def __init__(self, name, sex, age, height, weight):
        self.name = name
        self.sex = sex
        self.age = age
        self.height = height
        self.weight = weight

    def eat(self, location):
        """
        吃东西

        :param location: 位置
        :return:
        """
        print(f'{self.name}正在{location}吃{self.food}')


person = Person('小明', '男', 23, 170, 60)
person.name = '爆笑蛙'  # 在类外修改属性的值
person.eat('家里')

実行結果は以下の通りです。

インスタンス化するときに明らかに Xiao Ming を使用しましたが、クラスの外で self.name の値を直接変更すると、eat() メソッドを使用した出力結果が期待どおりになりません。複雑なプログラムでは、このようなエラーが発生すると、計算結果の精度に重大な影響を及ぼします。したがって、自由に変更できないいくつかのプロパティをプライベート プロパティとして設定する必要があります。

私有地

        プライベート プロパティには 2 つのタイプがあり、1 つはプロパティ名を非表示にすることによるプライベート、もう 1 つはプロパティ名を変更することによるプライベートです。

属性名を非表示にする

        属性名の前にアンダースコアを追加して、属性名を外部から隠すことができます。クラス属性とインスタンス属性は、アンダースコアを追加することで自身を非表示にします。隠し属性はクラス内で気軽に使用できるほか、クラス外で強制的に使用することもできます。

class Person:
    _name = None  # 加下划线对外隐藏类属性name
    sex = None
    age = None
    height = None
    weight = None
    food = '大米饭'

クラス属性を非表示にすると、(エディターでコードを作成するとき) クラスの外部には表示されなくなります。

name 属性は表示されなくなり、属性 _name も表示されなくなります。

        name 属性は非表示になっていますが、強制的に使用することができます。

Person._name = '小明'
print(Person._name)  # 小明

        インスタンスのプロパティも同様に外部からは隠蔽されます。

class Person:

    def __init__(self, name, sex, age, height, weight):
        self._name = name  # 加下划线对外隐藏实例属性name
        self.sex = sex
        self.age = age
        self.height = height
        self.weight = weight

インスタンス属性を非表示にすると、(エディターでコードを作成するとき) インスタンスに表示されなくなります。

name 属性は表示されなくなり、属性 _name も表示されなくなります。

        name 属性は非表示になっていますが、強制的に使用することができます。

person = Person('小明', '男', 23, 170, 60)
person._name = '爆笑蛙'
print(person._name)  # 爆笑蛙
属性名の変更

        属性名の前に 2 つのアンダースコアを追加することで、属性名を変更できます。クラス属性とインスタンス属性は、アンダースコアを追加することで変更できます。変更した属性はクラス内で自由に使用できるほか、クラス外で強制的に使用することもできます。

class Person:
    __name = None  # 加两个下划线把类属性name改为_Person__name
    sex = None
    age = None
    height = None
    weight = None
    food = '大米饭'

クラス属性を変更すると、その属性はクラスの外部からも見えなくなります。また、変更されたクラス属性は、_クラス名__ 属性名の形式に変更されているため、アンダースコア 2 つと属性名を追加して強制的に使用することはできません。

print(Person.__name)

__name を介して属性にアクセスするとエラーが報告され、実行結果は次のようになります。

このエラーは、Personal に属性 __name が存在しないことを示しています。

        クラス属性名は _person__name に変更されているため、_person__name を通じてクラス属性名にアクセスできます。

print(Person._Person__name)  # None

        インスタンス プロパティも同様にプロパティ名を変更します。

class Person:

    def __init__(self, name, sex, age, height, weight):
        self.__name = name  # 加两个下划线把实例属性name改为_Person__name
        self.sex = sex
        self.age = age
        self.height = height
        self.weight = weight

インスタンスのプロパティを変更すると、それらのプロパティもインスタンス内で表示されなくなります。また、属性名が _class name__ 属性名の形式に変更されているため、変更されたインスタンス属性は、属性名にアンダースコア 2 つを追加することによって強制的に使用することはできません。

person = Person('小明', '男', 23, 170, 60)
print(person.__name)

__name を介して属性にアクセスするとエラーが報告され、実行結果は次のようになります。

このエラーは、Personal に属性 __name が存在しないことを示しています。Python はまずインスタンスに __name 属性があるかどうかを確認します。クラスに __name 属性がない場合は、クラスに属性がないことが判明するため、エラーは Person に __name 属性がないことを示します。

        インスタンス属性名は _person__name に変更されているため、_person__name を通じてインスタンス属性名にアクセスできます。

person = Person('小明', '男', 23, 170, 60)
print(person._Person__name)  # 小明

プライベートメソッド

        プライベート メソッドには、プライベート プロパティと同様に、プロパティ名を非表示にするプライベート メソッドと、プロパティ名を変更するプライベート メソッドの 2 種類があります。

メソッド名を隠す

        メソッド名の前にアンダースコアを追加して、メソッド名を外部から隠すことができます。クラス メソッドとインスタンス メソッドは、アンダースコアを追加することで自身を隠します。Hidden メソッドはクラス内で気軽に使用できますが、クラス外で強制的に使用することもできます。

class Person:

    __name = None  # 加两个下划线把类属性name改为_Person__name
    sex = None
    age = None
    height = None
    weight = None
    food = '大米饭'

    @classmethod
    def _eat(cls, location):  # 使用下划线对外隐藏类方法
        """
        吃东西

        :param location: 位置
        :return:
        """
        print(f'{cls.__name}正在{location}吃{cls.food}')

クラス メソッドを非表示にすると、(エディターでコードを作成するとき) クラスの外部には表示されなくなります。

Eat メソッドは表示されなくなり、_eat メソッドも表示されなくなります。

        Eat メソッドは隠蔽されていますが、強制的に呼び出すことができます。

Person._eat('家里')

実行結果は以下の通りです。

        インスタンスメソッドも同様に外部からは見えないようになっています。

class Person:
    food = '大米饭'

    def __init__(self, name, sex, age, height, weight):
        self.__name = name  # 加两个下划线把实例属性name改为_Person__name
        self.sex = sex
        self.age = age
        self.height = height
        self.weight = weight

    def _eat(self, location):  # 使用下划线对外隐藏实例方法
        """
        吃东西

        :param location: 位置
        :return:
        """
        print(f'{self.__name}正在{location}吃{self.food}')

インスタンス メソッドが非表示になると、(エディターでコードを作成するとき) インスタンスに表示されなくなります。

Eat メソッドは表示されなくなり、_eat メソッドも表示されなくなります。

        Eat メソッドは隠蔽されていますが、強制的に呼び出すことができます。

person = Person('小明', '男', 23, 170, 60)
person._eat('家里')

実行結果は以下の通りです。

メソッド名の変更

        メソッド名の前に 2 つのアンダースコアを追加することで、メソッド名を変更できます。クラス メソッドとインスタンス メソッドはどちらも、アンダースコアを追加することで自身を変更します。変更したメソッドはクラス内で気軽に使用できるほか、クラス外で強制的に呼び出すこともできます。

class Person:

    __name = None  # 加两个下划线把类属性name改为_Person__name
    sex = None
    age = None
    height = None
    weight = None
    food = '大米饭'

    @classmethod
    def __eat(cls, location):  # 使用两个下划线把类方法eat改为_Person__eat
        """
        吃东西

        :param location: 位置
        :return:
        """
        print(f'{cls.__name}正在{location}吃{cls.food}')

クラス メソッドが変更されると、クラスの外部からも見えなくなります。また、メソッド名が「_クラス名__メソッド名」の形式に変更されているため、メソッド名にアンダースコア2つを追加することで変更後のクラスメソッドを強制的に使用することはできません。

Person.__eat('家里')

クラス メソッド Eat を __eat 経由で呼び出すとエラーが報告され、実行結果は次のようになります。

このエラーは、Personal に __eat 属性が存在しないことを示しています。

        クラス メソッド Eat は _Person__eat に変更されているため、_Person__eat を通じてクラス メソッド Eat にアクセスできます。

Person._Person__eat('家里')

実行結果は以下の通りです。

        インスタンス メソッドも同じメソッドを使用してメソッド名を変更します。

class Person:
    food = '大米饭'

    def __init__(self, name, sex, age, height, weight):
        self.__name = name  # 加两个下划线把实例属性name改为_Person__name
        self.sex = sex
        self.age = age
        self.height = height
        self.weight = weight

    def __eat(self, location):  # 使用两个下划线把实例方法eat改为_Person__eat
        """
        吃东西

        :param location: 位置
        :return:
        """
        print(f'{self.__name}正在{location}吃{self.food}')

インスタンス メソッドが変更されると、そのメソッドもインスタンス内で表示されなくなります。また、メソッド名が「クラス名__メソッド名」の形式に変更されているため、メソッド名にアンダースコア2つを追加することで変更後のインスタンスメソッドを強制的に使用することはできません。

person = Person('小明', '男', 23, 170, 60)
person.__eat('家里')

インスタンス メソッド Eat を __eat 経由で呼び出すとエラーが報告され、実行結果は次のようになります。

このエラーは、Personal に __eat 属性が存在しないことを示しています。Python は、まずインスタンス内の __eat メソッドを検索します。__eat メソッドがない場合は、次にクラス内の __eat メソッドを検索します。その結果、クラス内に __eat メソッドがないため、エラーは、__eat メソッドが存在しないことを示します。属性 __eat in person。

        インスタンス メソッド Eat は _Person__eat に変更されているため、_Person__eat を通じてインスタンス メソッド Eat にアクセスできます。

person = Person('小明', '男', 23, 170, 60)
person._Person__eat('家里')

実行結果は以下の通りです。

        Python のプライベート プロパティは強制的に呼び出すことができるため、Python のプライベート プロパティは単なる擬似プライベートです。したがって、他の人が書いたクラスにプライベート属性が見つかった場合は、そのプライベート属性を変更せず、プライベート メソッドを呼び出して、他の人が書いたクラスを使用する際のバグを回避してください。

クラスの継承

クラスを継承する

        クラスは他のクラスから継承でき、1 つのクラスのみを継承することも、複数のクラスを継承することもできます。python3 では、class で定義されたすべてのクラスはオブジェクト クラスを継承します。クラス B が別のクラス A を継承すると、B は A のすべてのプロパティとメソッドを取得します。

class Person:
    """人类"""
    food = '大米饭'

    def __init__(self, name, sex, age, height, weight):
        self._name = name
        self.sex = sex
        self.age = age
        self.height = height
        self.weight = weight

    def eat(self, location):
        """
        吃东西

        :param location: 地点
        :return:
        """
        print(f'{self._name}正在{location}吃{self.food}')


class Student(Person):
    """学生类"""


student = Student('小明', '男', 16, 170, 60)
print(student._name)
student.eat('家里')

上記のコードでは、Student は Person から継承するため、Student は Person のすべてのプロパティとメソッドを取得します。Student でメソッドや属性を定義する必要はありません。また、Personal の __init__ メソッドを通じてインスタンスを作成し、その属性とメソッドを Person で使用することもできます。実行結果は以下の通りです。

複数のクラスを継承する

        クラスが複数のクラスから継承する場合、継承されたすべてのクラスからプロパティとメソッドを取得します。

class Singer:
    """歌者"""
    song = '罗刹海市'

    def __init__(self, name):
        self.name = name

    def singing(self):
        print(f'{self.name}正在唱{self.song}')


class Dancer:
    """舞者"""
    dance = '街舞'

    def __init__(self, name):
        self.name = name

    def dancing(self):
        print(f'{self.name}正在跳{self.dance}')


class Perform(Singer, Dancer):
    """表演者"""


perform = Perform('小明')
perform.singing()
perform.dancing()

実行結果は以下の通りです。

Perfom は Singer と Dancer を継承するため、Perform は Singer と Dancer のプロパティとメソッドを持ちます。しかし、Singer と Dancer の両方に __init__ メソッドがあるので、Perfom は誰の __init__ メソッドを使用するのでしょうか? 答えはシンガーです。

        複数の継承クラスに同じメソッドが存在する場合は、継承位置が最も高いクラスのメソッドが使用されます。

class Singer:
    """歌者"""
    song = '罗刹海市'

    def __init__(self, name):
        print('使用Singer实例化')
        self.name = name

    def practice(self):
        print(f'{self.name}正在练习唱{self.song}')


class Dancer:
    """舞者"""
    dance = '街舞'

    def __init__(self, name):
        print('使用Dancer实例化')
        self.name = name

    def practice(self):
        print(f'{self.name}正在练习跳{self.dance}')


class Perform(Singer, Dancer):
    """表演者"""


perform = Perform('小明')
perform.practice()

実行結果は以下の通りです。

インスタンス化するときに Singer のメソッドが使用され、practice() メソッドを呼び出すときにも Singer のメソッドが使用されることがわかります。継承順位ではシンガーが1位でダンサーが2位だからです。したがって、Singer と Dancer に同じメソッドが存在する場合は、Singer のメソッドが使用されます。

多重継承

        上記の継承関係は、あるクラスが別のクラスを継承するというものであり、継承関係は1つだけである。クラスは多重継承することもでき、サブクラスは親クラスから継承し、親クラスは祖父クラスから継承し、祖父クラスは祖父クラスから継承します。このように上まで遡ることもできますし、逆に下まで継承することもできます。

class Animal:
    """动物"""

    def __init__(self, name):
        print('使用Animal实例化')
        self.name = name

    def move(self):
        """移动"""
        print(f'{self.name}可以动')


class Mammals(Animal):
    """哺乳类"""

    def __init__(self, name):
        print('使用Mammals实例化')
        self.name = name

    def nurse(self):
        """哺乳"""
        print(f'{self.name}小时候会喝奶')


class Person(Mammals):
    """人类"""

    def study(self):
        """学习"""
        print(f'{self.name}会向他人学习')


person = Person('小明')
person.move()
person.nurse()
person.study()

実行結果は以下の通りです。

人は哺乳類から継承し、哺乳類は動物から継承し、人は哺乳類と動物のプロパティとメソッドを持ちます。実行結果から、クラスが多重継承に属している場合、取得したプロパティやメソッドまで遡ることができ、対応するプロパティやメソッドを遡った時点で追跡が停止していることがわかります。たとえば、__init__ メソッドは哺乳類と動物の両方で定義されていますが、人は哺乳類で __init__ メソッドを使用します。

クラスのポリモーフィズム

        先ほど、オーバーロード装飾関数を使用して実装されるポリモーフィック関数について説明しました。ここでは、装飾された関数をオーバーロードせずに、クラス継承でポリモーフィック メソッドを実装します。クラス B がクラス A から継承する場合、B は A のメソッドをオーバーライドできます。メソッド名は同じである必要がありますが、受け取ったパラメーター、メソッド内の操作ロジック、および返されるコンテンツは異なっていてもかまいません。上で説明した __init__ メソッドと同様に、親クラスに __init__ メソッドがありますが、サブクラスで独自の __init__ メソッドを定義することもできます。属性は多態性を持つこともでき、親クラスで特定の属性が定義されている場合、同じ名前の属性をサブクラスで定義したり、属性の値が異なったりすることがあります。

メソッド多態性

class Person:
    """人类"""
    food = '大米饭'

    def __init__(self, name, sex, age, height, weight):
        self._name = name
        self.sex = sex
        self.age = age
        self.height = height
        self.weight = weight

    def eat(self, location):
        """
        吃东西

        :param location: 地点
        :return:
        """
        print(f'{self._name}正在{location}吃{self.food}')


class Student(Person):
    """学生类"""
    book = '理论力学'

    def eat(self):
        """吃饭"""
        print(f'{self._name}边看{self.book}边吃饭')


student = Student('小明', '男', 16, 170, 60)
student.eat()

実行結果は以下の通りです。

Personのeat()メソッドとStudentのeat()メソッドはメソッド名は同じですが、受け取るパラメータの数が異なり、出力される内容も異なります。メソッドであれ属性であれ、所有していれば独自のものを持ち、自分が持っていないものは継承します。実行の結果、Xiao Ming は理論力学を見ながら食事をすることになります。これがメソッドの多態性です。

        PERSON クラスをインスタンス化するときも、PERSON で Eat() メソッドを使用します。

person = Person('小明', '男', 16, 170, 60)
person.eat('家里')

実行結果は以下の通りです。

属性の多態性

class Person:
    """人类"""
    food = '大米饭'

    def __init__(self, name, sex, age, height, weight):
        self._name = name
        self.sex = sex
        self.age = age
        self.height = height
        self.weight = weight

    def eat(self, location):
        """
        吃东西

        :param location: 地点
        :return:
        """
        print(f'{self._name}正在{location}吃{self.food}')


class Student(Person):
    """学生类"""
    food = '面包'


student = Student('小明', '男', 16, 170, 60)
student.eat('家里')

実行結果は以下の通りです。

人の食べ物の属性値は「ご飯」ですが、学生の食べ物の属性値は「パン」です。メソッドであれ属性であれ、所有していれば独自のものを持ち、自分が持っていないものは継承します。実行結果は、シャオミンが家でパンを食べているという属性の多態です。

        PERSON クラスをインスタンス化するときも、PERSON の food 属性を使用します。

person = Person('小明', '男', 16, 170, 60)
person.eat('家里')

実行結果は以下の通りです。

スーパー(スーパークラス)

        名前が示すように、スーパーはスーパークラスと呼ばれるクラスです。継承順序 (MRO 順序) に従って、特定のクラスの親クラスを検索し、その親クラスを返すことができます。super は、返された親クラスをインスタンス オブジェクトにバインドすることもできます。インスタンス オブジェクトをバインドした後、インスタンス オブジェクトを通じて親クラスのメソッドとプロパティを使用できるようになります。

MROシーケンス

        Python では、クラスは __mro__ メソッドを通じて MRO 順序 (継承順序) を取得できます。

class A:
    """A"""


class B(A):
    """B"""


class C(A):
    """C"""


class D(B, C):
    """D"""


print(D.__mro__)

実行結果は以下の通りです。

D の継承順序は D -> B -> C -> A -> オブジェクトであり、D の最初の継承は D 自体であることがわかります。ここで、継承された機能がある場合はそれが使用され、継承された機能がない場合は使用される理由が明確にわかるはずです。

スーパーの使い方

        super は 2 つのパラメータを受け取ることができます。最初のパラメータはクラス (class)、2 番目のパラメータはクラス (class) またはインスタンス (self) です。super は、2 番目のパラメーターの MRO 順序で最初のパラメーターの次のクラスを検索し、その次のクラスを返します。2 番目のパラメーターがクラスの場合、スーパーはクラス属性とクラス メソッドを直接使用することのみが可能です。インスタンス メソッドを使用する場合は、インスタンスを渡す必要があります。2 番目のパラメーターがインスタンスの場合、スーパーはクラス属性、クラス メソッド、およびインスタンスを使用できます。メソッド。super にパラメータを渡さない場合、super のデフォルトでは、最初のパラメータが現在のクラスになり、2 番目のパラメータが現在のクラスまたは現在のクラスのインスタンスになります (インスタンス メソッドで使用される場合は、現在のクラスのインスタンスになります)。クラス (現在のクラスのクラス メソッドで使用される場合)。

素晴らしい()

class A:
    value = 'a'

    def say(self):
        print(f'我是a value = {self.value}')


class B(A):
    value = 'b'

    def say(self):
        print(f'我是b value = {self.value}')


class C(A):
    value = 'c'

    def say(self):
        print(f'我是c value = {self.value}')


class D(B, C):
    value = 'd'

    def say(self):
        print(super().value)  # 打印上一级的属性
        super().say()  # 调用上一级的方法


d = D()
d.say()

D で super() を使用することは、super(D, self) と同等です。最初のパラメーターは D 自体で、2 番目のパラメーターは D のインスタンスです。D の MRO 順序は次のとおりです: D -> B -> C -> A -> object.MRO 順序で D にある次のクラスは B であるため、super() は B を返し、B は D のインスタンスをバインドします。したがって、D のインスタンス メソッドで super() を使用する必要があります。そうしないと、パラメーター欠落エラーが報告されます。実行結果は以下の通りです。

super(type, obj)

class A:
    value = 'a'

    def say(self):
        print(f'我是a value = {self.value}')


class B(A):
    value = 'b'

    def say(self):
        print(f'我是b value = {self.value}')


class C(B):
    value = 'c'

    def say(self):
        print(f'我是c value = {self.value}')


c = C()  # 定义C的实例
a_super = super(B, c)  # 返回B的下一级A,并绑定C的实例c
print(a_super.value)  # 打印A的属性
a_super.say()  # 调用A的方法

C の MRO シーケンスは次のとおりです: C -> B -> A -> オブジェクト MRO シーケンス内で見つかった B の次のクラスは A です。したがって、a_super.value は A の value の値であり、a_super.say() は A のsay() メソッドを実行します。また、say() メソッドは C のインスタンス c にバインドされており、これは A.say(c) と同等であるため、A.say() で使用される self.value は c.value です。実行結果は以下の通りです。

スーパー(タイプ、タイプ2)

class A:
    value = 'a'

    def say(self):
        print(f'我是a value = {self.value}')


class B(A):
    value = 'b'

    def say(self):
        print(f'我是b value = {self.value}')


class C(B):
    value = 'c'

    def say(self):
        print(f'我是c value = {self.value}')


c = C()  # 定义C的实例
b_super = super(C, C)  # 返回C的下一级B
print(b_super.value)  # 打印B的属性
b_super.say(c)  # 要调用实例方法时必须传入实例

C の MRO シーケンスは次のとおりです: C -> B -> A -> オブジェクト MRO シーケンス内の C で見つかった次のクラスは B です。したがって、 b_super.value は B の value の値であり、 b_super.say() は B の Say() メソッドを実行します。これは B.say() と同等であるため、インスタンスをインスタンス メソッド Say() に渡す必要があり、B.say() で使用される self.value は c.value です。実行結果は以下の通りです。

抽象クラス

        クラスとは本来抽象的な概念ですが、抽象クラスとはどのようなクラスなのでしょうか?抽象クラスは、多数の同様のクラスから同様の属性とメソッドを抽出し、これらの属性とメソッドを新しいクラスに結合します。この新しいクラスには 1 つ以上の抽象メソッドが含まれます。簡単に言えば、クラス内に抽象メソッドがある場合、そのクラスは抽象クラスです。クラスがインスタンス化されると、すべてのプロパティとメソッドがインスタンスに追加されます。ただし、抽象メソッドは使用できないため、インスタンス化中に抽象メソッドをインスタンスに追加することはできません。そのため、抽象クラスのインスタンス化処理は、抽象メソッドの追加時にエラー報告により失敗するため、インスタンス化できないことが抽象クラスの最大の特徴です。

抽象メソッド

        Python の abc ライブラリには、メタクラスとして ABCMeta を使用するツール クラス ABC があり、クラスが ABC を継承する場合、またはメタクラスとして ABCMeta を使用する場合、abc ライブラリの abstractmethod 装飾関数を使用して抽象メソッドを装飾できます。

抽象インスタンスメソッド

        ABC を継承するクラス、またはメタクラスとして ABCMeta を使用するクラスでは、@abstractmethod で装飾されたメソッドを使用します。

from abc import ABC, abstractmethod


class Person(ABC):  # 直接继承ABC工具类
    """人类"""
    food = '面包'

    @abstractmethod  # 使用abstractmethod函数装饰eat方法,把eat方法变成抽象实例方法
    def eat(self, location):
        """
        吃东西

        :param location: 地点
        :return:
        """


person = Person()
person.eat('家里')

Person をインスタンス化するとエラーが報告され、実行結果は次のようになります。

エラー メッセージは、Person に抽象メソッド Eat() が存在するため、Person をインスタンス化できないことを示しています。

抽象クラスメソッド

        ABC を継承するクラス、またはメタクラスとして ABCMeta を使用するクラスでは、@abstractmethod と @classmethod を使用してメソッドを修飾します。

from abc import ABCMeta, abstractmethod


class Person(metaclass=ABCMeta):  # 使用metaclass指定元类为ABCMeta
    """人类"""

    @classmethod  # 使用classmethod装饰abstractmethod(eat)方法,把abstractmethod(eat)变成类方法
    @abstractmethod  # 使用abstractmethod装饰eat方法,把eat方法变成抽象方法
    def eat(cls, location):
        """
        吃东西

        :param location: 地点
        :return:
        """


person = Person()
person.eat('家里')

Person をインスタンス化するとエラーが報告され、実行結果は次のようになります。

エラー メッセージは、Person に抽象メソッド Eat() が存在するため、Person をインスタンス化できないことを示しています。

抽象静的メソッド

        ABC を継承するクラス、またはメタクラスとして ABCMeta を使用するクラスでは、@abstractmethod および @staticmethod を使用してメソッドを修飾します。

from abc import ABC, abstractmethod


class Person(ABC):  # 直接继承ABC工具类
    """人类"""

    @staticmethod  # 使用staticmethod装饰abstractmethod(eat)方法,把abstractmethod(eat)变成静态方法
    @abstractmethod  # 使用abstractmethod装饰eat方法,把eat方法变成抽象方法
    def eat(location):
        """
        吃东西

        :param location: 地点
        :return:
        """


person = Person()
person.eat('家里')

Person をインスタンス化するとエラーが報告され、実行結果は次のようになります。

エラー メッセージは、Person に抽象メソッド Eat() が存在するため、Person をインスタンス化できないことを示しています。

抽象クラスの適用

        抽象クラスはインスタンス化することさえできないので、抽象クラスはどのような用途に使えるのでしょうか? オブジェクト指向パラダイム プログラミングでは、抽象クラスは便利であるだけでなく、非常に便利です。抽象クラスは依存関係逆転の機能を実現するのに役立ち、インターフェイス クラスを実装する基礎となります。抽象クラスはインスタンス化できませんが、抽象クラス内の抽象メソッドがサブクラスに実装され、抽象メソッドが抽象でなくなる場合は、抽象クラスのサブクラスをインスタンス化できます。

依存関係の逆転

        クラスの継承特性から、サブクラスは親クラスに依存しており、サブクラス自身が親クラスのメソッドを実装する必要はなく、親クラスを継承することで親クラスのメソッドを使用することができます。依存関係の逆転とは、親クラスが子クラスに依存することを意味します。親クラスにはメソッド名のみがあり、特定のロジックはありません。親クラスでメソッドを実行する場合、子クラスのメソッドのロジックのみが使用できます。依存関係の反転により、クラス間の結合が軽減され、システムの安定性が向上し、コードの保守性が向上します。

        まず、非依存性反転の例を示します: ペット トレーニング ゲームでは、猫、犬、ハムスター、その他のペットから選択でき、これらのペットに餌を与えたり、寝かせたり、その他の操作を選択できます。次に、次のコードを設計できます。

class Cat:

    def __init__(self, name):
        self.name = name

    def cat_eat(self):
        print(f'给{self.name}喂食猫粮')

    def cat_sleep(self):
        print(f'让{self.name}进猫窝睡觉')


class Dog:

    def __init__(self, name):
        self.name = name

    def dog_eat(self):
        print(f'给{self.name}喂食狗粮')

    def dog_sleep(self):
        print(f'让{self.name}进狗窝睡觉')


class GameSystem:

    def __init__(self, typ):
        self.typ = typ

    def eat(self):
        if type(self.typ) == Cat:
            self.typ.cat_eat()
        else:
            self.typ.dog_eat()

    def sleep(self):
        if type(self.typ) == Cat:
            self.typ.cat_sleep()
        else:
            self.typ.dog_sleep()


cat = Cat('小猫')
dog = Dog('小狗')
game = GameSystem(cat)
game.eat()
game.sleep()
game.typ = dog
game.eat()
game.sleep()

実行結果は以下の通りです。

GameSystem のコードが非常に肥大化しており、GameSystem の Eat() メソッドと sleep() メソッドで typ 属性の型を決定するための判定ステートメントが使用されていることがわかりました。さらにペットの種類を追加したい場合は、GameSystem に多くの判定ステートメントを記述する必要があります。属性の型を判断して対応するメソッドを実行する、高結合のコードです。しかし、それほど多くの判断ステートメントを作成する必要がないように、すべてのペット タイプで同じメソッド名を指定すればよいのではないかと思われるかもしれません。それはいいのですが、この例ではメソッドの数が比較的少ないので、同じメソッド名を強制的に定義することもできますが、システムが複雑になってしまうと、強制的に同じメソッド名を定義することが難しくなります。クラスの作成が完了したら、クラス内のすべてのメソッド名と対応するロジックを覚えて、別のクラスでも同じ方法で作成する必要があります。複数人で同時に開発するとメソッド名の同期が難しくなり、新しいメソッドを書くたびに他の人と同期する必要があります。

        したがって、すべての ペット クラスを支配する抽象クラスが必要ですが、抽象クラスでは、抽象メソッドの名前を定義するだけで、すぐに抽象クラスを定義できます。次に、他のペット クラスがこの抽象クラスを継承するため、メソッドの名前は同じになります。どれだけの人が同時に開発しても、自分が書いたペットクラスの抽象クラスの抽象メソッドを実装していない限り、自分が書いたクラスはデバッグ時にインスタンス化されず、はっきりとわかります。彼があまり書いていないメソッド。以下では、抽象クラスを使用して依存関係の反転を実装します。

from abc import ABC, abstractmethod


class Pet(ABC):

    def __init__(self, name):
        self.name = name

    @abstractmethod
    def eat(self):
        """喂食"""

    @abstractmethod
    def sleep(self):
        """睡觉"""


class Cat(Pet):

    def eat(self):
        print(f'给{self.name}喂食猫粮')

    def sleep(self):
        print(f'让{self.name}进猫窝睡觉')


class Dog(Pet):

    def eat(self):
        print(f'给{self.name}喂食狗粮')

    def sleep(self):
        print(f'让{self.name}进狗窝睡觉')


class GameSystem:

    def __init__(self, typ: Pet):
        self.typ = typ

    def eat(self):
        self.typ.eat()

    def sleep(self):
        self.typ.sleep()


cat = Cat('小猫')
dog = Dog('小狗')
game = GameSystem(cat)
game.eat()
game.sleep()
game.typ = dog
game.eat()
game.sleep()

実行結果は以下の通りです。

依存関係の反転により、すべてのペット クラスに必要なメソッドがすべて確実に含まれるようになり、新しいペット タイプを追加するときに間違いがなくなります。したがって、依存関係の反転により、クラス間の結合が軽減され、システムの安定性が向上し、コードの保守性が向上します。

おすすめ

転載: blog.csdn.net/qq_40148262/article/details/131872966