Python学習ノート(五)OOP


モジュール
モジュールimport を使用します.あるモジュールの1つのクラスまたは関数のみをインポートし、from import を使用して実装するものもあります.モジュール名の競合を回避するために、Pythonはパッケージ(Package)と呼ばれるディレクトリ別にモジュールを整理する方法を導入した.aディレクトリの下のb.pyモジュール名はa.bです.aディレクトリの下には、ファイルの内容が空であっても、ファイル__ini__.pyが必要です.さもないとaは普通のディレクトリになります.
標準モジュールファイルテンプレート:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

'a test module' #                      

__author__ = 'Your Name'

...
def f():
...

if __name__ == '__main__':  #         ,if    。               
    f()
  • モジュール内の非公開関数プレフィックス_、例えばdef _private_1():
  • で直接参照できる特殊な変数は、一般に__argname__という前後の2つの下線の形式で表され、自分が定義した公開関数または変数は一般的に一般的な形式def fで表され、このような下線の形式は使用されない.
  • Pythonはprivate関数または変数へのアクセスを完全に制限する方法がないことに注意してください.だからこれは自覚に頼らなければならない
  • サードパーティモジュールはpipを使用してpip install numpyをインストールすることができ、pythonの公式サイトから自動的にダウンロードおよびインストールされます.自分でダウンロードしてインストールすることもできます.
    オブジェクト向けプログラミング
    クラス構文
    class Student(object):
        pass
    objectはデフォルトの親の名前です.インスタンスの作成にはtom = Student()が使用されます.動的言語として、Pythonはtomバインドクラス外の新しい属性tom.name = 'Tom'を自由にサポートする.インスタンスの作成時にバインドする必要があるプロパティがある場合は、クラスの__ini__関数に一般的に書き込まれます.
    def __ini__(self, arg1, arg2):
        self.__arg1 = ...
        self.arg2 = ...
    __ini__の最初のパラメータは常にselfであり、自動的に入力されます.このような__ini__があれば、インスタンスを作成する際にarg1およびarg2パラメータを入力する必要がある.__arg1の2つの前下線は、外部からアクセスできないプライベート属性であることを示しています.
    アヒルのタイプ:アヒルのように見え、走るとアヒルのように見える限り、アヒルと見なすことができます
    Pythonのダイナミック性も継承に現れている.クラスを呼び出す関数があると仮定すると、受信したオブジェクトにもこのメソッドがある限り、そのオブジェクトがクラスまたはクラスのサブクラスであるかどうかのインスタンスに関係なく実行できます.
    オブジェクト情報の取得
  • は、オブジェクトの情報を取得するためにtype()を使用することができ、import typesであれば、type( ) == types.type を使用して、オブジェクトが特定の関数タイプであるか否かを判断することもできる.
  • isinstance( , )の判定方法は、そのオブジェクトがパラメータ内のクラスの親である場合にもTrueを返す.typeで判断できる基本タイプはisinstanceを使用し、2番目のパラメータはlistであってもよく、要素の1つであればTrueを返します.
  • dir()は、1つのオブジェクトのすべての属性およびメソッドを
  • に戻すことができる.
  • オブジェクトの詳細が分からない場合、hasattr(obj, 'arg')を使用してobjにarg属性または方法があるかどうかを得ることができる.getattr(obj, 'arg')は、属性またはメソッドを返す.setattr(obj, 'arg', value)属性またはメソッドの設定(ない場合は直接増加)
  • オブジェクト向けの高度なプログラミング__slots__の使用
    Pythonは動的言語であるため、クラス、インスタンスに属性またはメソッドを任意にバインドできます.バインディングメソッドは、from types import MethodTypes.f = MethodType(f, s)を使用して、メソッドfをクラスまたはインスタンスsにバインディングします.同時に、fの最初のパラメータがselfであればバインドできないことに注意してください.
    インスタンスのプロパティを制限する場合は、クラスを定義するときに、インスタンスにバインドされたプロパティ名がname 1またはname 2に限定される特殊な変数__slots__ = ('name1','name2')を定義する必要があります.
    分析:
    from types import MethodType
    
    class Student(object):
        __slots__ = ('name', 'sex', 'set_name', 'set_grade')
    #                  。           #
    
    #         
    def set_name(self, name):
        self.name = name
    
    #         #
    def set_grade(self, grade):
        self.grade = grade
    
    #         #
    def set_score(self, score):
        self.score = score
    
    #         #
    def set_sex(self, sex):
        self.sex = sex
    
    tom = Student()
    jane = Student()

    (1)インスタンスに属性をバインドするには:
    tom.name = 'Tom'    #  ,name    
    jane.grade = 98    #  ,grade    ,    
    print(jane.name)    #  ,      jane name
    
    #                     

    (2)クラスへのバインド属性:
    Student.name = 'name'    #       
    Student.grade = 0    #        ,  __slots__      
    
    tom.name = 'Tom'    #  ,                    
    print(jane.name)    #   name
    

    (3)インスタンスへのバインド方法:
    tom.set_name = MethodType(set_name, tom)    #   ,       
    tom.set_grade = MethodType(set_name, tom)    #   ,       ,        
    tom.set_score = MethodType(set_score, tom)    #   ,    
    tom.set_sex = MethodType(set_name, tom)    #   ,    
    

    (4)クラスへのバインド方法
    #     
    Student.set_name = MethodType(set_name, Student)
    
    tom.name = 'Tom'
    print(jane.name)    #  ,              ,“  .   =”             
    
    tom.set_name('Tom')
    jane.set_name('Jane')    #     ,          
    tom.name = 'Tom'    #  ,           (3)   ,  。         
    print(jane.name)
    print(tom.name)    #    Jane,                ,                         
    
    
    #     ,       
    Student.set_grade = MethodType(set_grade, Student)
    tom.set_grade(4)    #          
    tom.grade = 4    #     ,      
    print(tom.grade)    #        ,      ,  ,         

    残りの2つの状況は、__slots__がクラスを制限しないため、任意の方法および属性をバインドすることができるため、言うまでもない.ただし、呼び出しメソッドによってバインドされた属性は読み取り専用であり、呼び出しメソッドによってのみ変更でき、インスタンスで.賦値を直接使用して変更することはできません.__slots__は、インスタンスのメソッドまたは属性バインドのみを制限し、クラスは制限しません.クラスバインドのメソッドとプロパティは、インスタンスで呼び出すことができますが、クラスプロパティはインスタンスでは読み取り専用であり、その値は最後の呼び出しメソッドまたは . =で付与された結果であり、インスタンスでは . =で変更できません.
    @propertyの使用
    属性を変更する必要がある場合、パラメータをチェックできるように、一般的にメソッドを呼び出して属性値を設定します.しかし、これでは少し面倒になります.このとき@propertyアクセサリーが機能します.
    @property
    def name(self):
        return self._name
    
    @name.setter #         , name        
    def name(self,value):
        ...     
        self._name = value

    注意属性_nameは必ず下線を引いて属性と方法を区別しなければならない.そうしないと、名前が同じ方法になり、上のコードが再帰になり、再帰回数オーバーフローエラーが報告される.
    多重継承
    継承された複数の親が同じメソッドを持っている場合、最初の親に準じて、後の親のメソッドは機能しません.
    カスタムクラス__slots__のような両端に下線が2つある変数や関数は特殊な用途があるため、このようなオブジェクトを定義することでクラスをカスタマイズすることができます.__str__(self):print( )を実行すると、プログラムは属性とメモリアドレスを印刷し、特定のコンテンツを印刷したい場合は、__str__メソッドを定義して、特定のコンテンツを出力することができる.printではなくクラス名出力情報を直接呼び出す場合は、__repr__()メソッドを定義する必要があります.簡単な定義メソッドは__repr__ = __str__高速定義です.__iter__(self):クラスを反復するには、__iter__メソッドを定義してselfを返し、__next__(self)メソッドを定義して反復で行うことを定義する必要があります.__getitem__(self, n):クラスをlistのように下付き要素で操作できます.__getitem__で要素値を返します.スライスなどの機能を実現するには、伝達されたパラメータがsliceであるかどうかを判断するなど、この関数をさらに細分化する必要があります.同様に、クラスをdictのようにして__setitem__を使用する.__delitem__を使用して要素を削除します.以上の方法では,クラスをlistやdictの「アヒル」に変え,クラスをlistやdictのように操作できるようにした.__getattr__(self, attr):ダイナミックプロパティまたはメソッドを返します.この関数の下でattrを判断し,所定の属性や方法をバインドする.バインドプロパティはプロパティ値を返し、バインドメソッドはlambda関数を返します.制限がなければ、任意のプロパティまたはメソッドがバインドできることは明らかです.この場合、自身を返すと、チェーン呼び出しが実現されます.これを利用して,異なるAPIに対するSDKを構築する.以下のコードは、GitHubのAPIのURL出力を実現します.
    class Chain(object):
    
        def __init__(self, path =''):
            self._path = path
    
        def __getattr__(self, path):
            return Chain('%s/%s' %(self._path, path))
    
        def __call__(self,name):
            return Chain('%s/:%s' %(self._path, name))
    
        def __str__(self):
            return 'GET ' + self._path        
    
    print(Chain().users('hermanncain').repos)
    
    #   GET /users/:hermanncain/repos
    
    __call__(self):インスタンス自体が呼び出されます.すなわち、s()が呼び出されると__call__(self)が実行される.パラメータを渡すために__call__(self, *args, **kw)を使用してもよく、s(*args, *kw)が呼び出されるとその内容が実行される.これにより、クラスとインスタンスの境界が曖昧になります.
    列挙クラス
    from enum import Enum
    
    Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 
    
    'Oct', 'Nov', 'Dec'))
    
    for name, member in Month.__member__.items():
        print(name, '=>', member, ',', member.value)

    valueはメンバーのint値を列挙し、デフォルトは1から
    もう1つのEnumから派生したカスタム列挙クラスは、構文がC++の列挙タイプに似ています.
    from enum import Enum, unique
    
    @unique
    class Weekday(Enum):
        Sun = 0
        Mon = 1
        ...
    @uniqueデコレーションは、繰り返し値をチェックするために使用される.
    列挙クラスのメンバーには、次の参照方法があります.
    print(Weekday.Sun)
    print(Weekday['Sun'])
    print(Weekday(1))
    #       Weekday.Sun
    
    print(Weekday.Sun.value)
    #  0

    メタ*type()を使用したクラスの作成
    動的言語の関数とクラスの定義は、実行時に動的に作成されます.a.pyのモジュールを書いて、モジュールの中でclass Aを定義して、それからメインプログラムの中でfrom a import Aで、モジュールをロードする時モジュールのすべての文を実行して、結果は動的に1つのAのclassを作成します.type(A)戻り値がtypetype(A )戻り値がclass '__main__'.Aです.ただし、type()は、クラスを動的に作成するためにも使用できます.実際にPythonインタプリタがclass定義に遭遇した場合も、type()を使用してクラスを動的に作成します.
    def f(self, ...):
        ...
    
    A = type('A', (object,), dict(af = f)

    これにより、Aクラスが動的に作成され、親はobjectであり(多重継承親はtupleに配置されることに注意し、親が1つしかない場合、tupleには1つの要素しかなく、カンマは少なくない)、afという方法がある.metaclass元*
    クラスはメタクラスのインスタンスに相当します.metaclassはPythonがオブジェクトに向かって最も理解しにくく、最も使いにくいマジックコードで、一般的には使えません
    *この部分は省略しておき、使用するときは実例に基づいて整理する