8日目、オブジェクトに向かってステップアップ

17242 ワード

8日目、オブジェクトに向かってステップアップ
@(python)[メモ]
目次
 、isinstance() issubclass()
    1. isinstance()
    2. issubclass()

 、  
    1. hasattr()
    2. getattr()
    3. setattr()
    4. delattr()
    5.     

 、__setattr__、__delattr__、__getattr__
 、        (  )
      
 、__next__ __iter__       
 、__doc__
 、__module__ __class__
 、__str__
 、__del__    
 、__setitem__,__getitem__,__delitem__
  、__enter__ __exit__
  、__call__
  、  metaclass
        exec
      

一、isinstance()とissubclass()
1. isinstance()
構文:isinstance(obj,cls)機能:オブジェクトobjがクラスclsのインスタンスであるかどうかを確認します.
class foo:
    pass
obj = foo()
print(isinstance(obj,foo))  #True

2. issubclass()
構文:issubclass(sub, super)機能:subクラスがsuperクラスの派生クラス(サブクラス)であるかどうかを確認します.
class foo:
    pass
obj = foo()
print(isinstance(obj,foo))  #True

class bar(foo):
    pass
print(issubclass(bar,foo))  #True

二、反射
反射は主にプログラムがそれ自体の状態や行為にアクセス、検出、修正できる能力を指し、自省とも呼ばれる.
pythonオブジェクト向けの反射とは、オブジェクト関連のプロパティを文字列で操作することです.pythonのすべてのものはオブジェクトであり、反射を使用することができます.
pythonでは、自省できる4つの関数を実現します.
次の方法はクラスとオブジェクトに適用されます(すべてがオブジェクトであり、クラス自体もオブジェクトです).
1. hasattr()
構文:hasattr(object,"name")機能:objectにname文字列に対応するメソッドまたは属性があるかどうかを判断します.使用方法:
class foo:
    name = "egon"
    pass

obj = foo()
print(hasattr(obj,"name"))  #True
print(hasattr(obj,"func"))  #True
print(hasattr(foo,"name"))  #True
print(hasattr(foo,"func"))  #True

2. getattr()
構文:getattr(object, "name"[, default=None])機能:オブジェクトobjectからname文字列対応のメソッドを取得し、name文字列対応のメソッドがなく、defaultが設定されていない場合はエラーを報告します.name文字列に対応するメソッドがなくdefaultが設定されている場合、defaultの値が返されます.getattr(object,"name")object.nameに等しい
class foo:
    name = "egon"
    def func(self):
        print("Hello world")

obj = foo()
print(getattr(obj,"func"))
    #>
    #    ,           

f1 = getattr(obj,"func")
f2 = getattr(obj,"bar","   ")
f1()   #Hello world
print(f2)  #   

3. setattr()
構文:setattr(x, y, v)機能:属性を設定し、setattr(x,'y',v)は「x.y = v」の使用法に等価です.
class foo:
    name = "egon"
    def func(self):
        print("Hello world")

obj = foo()
setattr(obj,"age",20)
setattr(obj,"show_name",lambda self:self.name+"_NB")
print(obj.__dict__)   #      
print(obj.show_name(obj))

'''
  :
{'age': 20, 'show_name':  at 0x000000000115E2F0>}
egon_NB
'''

4. delattr()
構文:delattr(x,y)機能:属性を削除し、delattr(x,'y')は``del x.y`'に相当する.
delattr(obj,"show_name")
# delattr(obj,"sex")   #   ,   
print(obj.__dict__) #{'age': 20}

5.拡張用法
現在のモジュールを反射
import sys
def s1():
    print("s1")

def s2():
    print("s2")

this_module = sys.modules[__name__]
print(hasattr(this_module,"s1"))   #True
print(getattr(this_module,"s2"))
    #,      

他のモジュールをインポートし、そのモジュールにメソッドプログラムディレクトリがあるかどうかを反射で検索します.
  • module_test.py
  • current.py
  • # module_test.py
    def test():
        print("from the module_test.test")
    
    #current.py
    
    import module_test as mt
    
    print(hasattr(mt,"test"))   #True
    f = getattr(mt,"test","   ")
    f()   #from the module_test.test
    

    三、__setattr____delattr____getattr____setattr__:属性の追加/変更は、その実行をトリガーします.__delattr__:属性を削除するとトリガーされます.__getattr__:ポイント呼び出し属性が使用され、属性が存在しない場合にのみトリガーされます.
    class Foo:
        x = 1
        def __init__(self,y):
            self.y = y
    
        def __getattr__(self, item):
            print("--->         ")
    
        def __setattr__(self, key, value):
            print("---> from __setattr__")
            # self.key = value  #         ,    __dict__      
            self.__dict__[key] = value   #         
    
        def __delattr__(self, item):
            print("---> from __delattr__")
    
    f1 = Foo(10)  #     ,  __setattr__  ,---> from __setattr__
    print(f1.__dict__)
        #{},      ,         __setattr__  ,
        #    __setattr__         
    
    f1.z          #  __getattr__  ,--->         
    
    del f1.x      #  __delattr__  ,---> from __delattr__
    

    四、二次加工標準タイプ(包装)
    パッケージ:pythonはユーザーに標準データ型と豊富な内蔵方法を提供しています.多くのシーンでは、標準データ型に基づいて自分のデータ型をカスタマイズし、新しい/書き換え方法をカスタマイズする必要があります.これは継承と派生の知識に使用され、他の標準タイプは以下の方法で二次加工することができます.
    例1:listを二次加工し、appendがintの整数データしか増加できないことを制限する.midメソッドを追加し、リストの中間値を得る.残りの方法はlistを継承します.
    class List(list):
        def append(self,p_object):
            #      append  ,     list  append  
            if not isinstance(p_object,int):
                raise TypeError("%s must be int"%p_object)
            super(List, self).append(p_object)
    
        @property
            #            ,    ,    property
        def mid(self):
            mid_num = len(self) // 2
            return self[mid_num]
    
    
    l = List([1,2,3,4])
    print(l)             #[1, 2, 3, 4]
    l.append(5)
    print(l)             #[1, 2, 3, 4, 5]
    print(l.mid)         #   3
    

    例2:listclearメソッドに権限を追加する
    class List(list):
        def __init__(self,item,perm=False):
            super(List, self).__init__(item)
            self.perm = perm
                #          
    
        def clear(self):
            if not self.perm:
                raise PermissionError("    ")
            super(List, self).clear()
    
    l = List([1,2,3])
    print(l)          #[1, 2, 3]
    # l.clear()       #  “    ”   
    l = List([1,2,3],True)  #        True
    l.clear()
    print(l)          #[],        
    

    権限
    ライセンスはパッケージの特性であり、パッケージのタイプは通常、既存のタイプのいくつかのカスタマイズであり、既存の製品の機能を新規、変更、または削除することができ、その他はそのままです.ライセンスのプロセスは、更新されたすべての機能が新しいクラスの一部で処理されますが、既存の機能はオブジェクトのデフォルトのプロパティにライセンスされます.
    ライセンスの実現の鍵は、__getattr__を上書きする方法です.
    例3:open()関数を使用してファイルプロセッサを再カスタマイズし、書き込みコンテンツの追加時間を増やす機能.
    import time
    
    class FileHandler:
        def __init__(self,filename,mode='r',encoding="utf-8"):
            self.file = open(filename,mode,encoding=encoding)
            #self.file         
    
        def write(self,line):
            t = time.strftime("%Y-%m-%d %X")
            self.file.write("%s %s"%(t,line))
    
        def __getattr__(self, item):
            return getattr(self.file,item)
            #     FileHandler        ,   open()   item        ;
            
    
    f1 = FileHandler("a.txt","r+")
    f1.write("   
    ") f1.seek(0) print(f1.tell()) #0

    例4:
    #     b    
    import time
    class FileHandle:
        def __init__(self,filename,mode='r',encoding='utf-8'):
            if 'b' in mode:
                self.file=open(filename,mode)
            else:
                self.file=open(filename,mode,encoding=encoding)
            self.filename=filename
            self.mode=mode
            self.encoding=encoding
    
        def write(self,line):
            if 'b' in self.mode:
                if not isinstance(line,bytes):
                    raise TypeError('must be bytes')
            self.file.write(line)
    
        def __getattr__(self, item):
            return getattr(self.file,item)
    
        def __str__(self):
            if 'b' in self.mode:
                res="<_io.bufferedreader name="%s">" %self.filename
            else:
                res="<_io.textiowrapper name="%s" mode="%s" encoding="%s">" %(self.filename,self.mode,self.encoding)
            return res
    f1=FileHandle('b.txt','wb')
    # f1.write('       ') #    write,     encode        ,  ,  
    f1.write('   '.encode('utf-8'))
    print(f1)
    f1.close()
    

    例5:ライセンス方式でlistappendメソッドを再カスタマイズし、リストにintの整数データしか追加できない.例と比較して、両者の違いを見てみましょう.
    
    #  ,  list
    
    class List:
        def __init__(self,seq):
            self.seq = list(seq)
    
        def append(self,p_object):
            if not isinstance(p_object,int):
                raise TypeError("'%s' must be int"%p_object)
            self.seq.append(p_object)
    
        def __getattr__(self, item):
            return getattr(self.seq,item)
    
        def __str__(self):
            return str(self.seq)
    
    l = List([1,2,3])
    l.append("4")   #TypeError: '4' must be int
    print(l)
    

    要約:この方法は、カスタムソースがクラスではない場合に使用することを許可します.例3では、カスタムopen()関数
    五、__next____iter__は反復器プロトコルを実現する
    反復器には__next____iter__の方法が必要です.今すぐ自分で反復器を実現しましょう.シンプルな実装:
    class Foo:
        def __init__(self,x):
            self.x=x
    
        def __iter__(self):
            return self
    
        def __next__(self):
            n=self.x
            self.x+=1
            return self.x
    
    f=Foo(3)
    for i in f:
        print(i)
    

    シミュレーション実装range()関数:
    class foo:
        def __init__(self,start,stop=None,step=1):
            self.start = start
            self.stop = stop
            self.step = step
            if isinstance(self.start,str) \
                    or isinstance(self.stop,str) \
                    or isinstance(self.step,str):
                raise InterruptedError("Must be Numeric")
    
        def __next__(self):
            if self.stop:
                res = self.compute()
            else:
                self.stop = self.start
                #          ,           
                self.start = 0
                #          ,    0    
                res = self.compute()
            return res
    
        def __iter__(self):
            return self
            #     __iter__  ,       
    
        def compute(self):
            if self.start >= self.stop:
                #            
                raise StopIteration
                #                       
            iter_val = self.start     #     
            self.start += self.step
            return iter_val
    
    for i in foo('a',20,3.5):
        print(i)
    
    for i in foo(5,15,3):
        print(i)
    

    六、__doc__オブジェクトの説明の表示
    def func():
        '''         '''
        pass
    print(func.__doc__)
    
    class Foo:
        '''        '''
        pass
    
    class bar(Foo):
        pass
    
    obj = Foo()
    print(obj.__doc__)
    print(Foo.__doc__)
    print(bar.__doc__)   #          
    
    '''
      :
             
            
            
    None
    '''
    

    七、__module____class____module__は、現在動作しているオブジェクトがそのモジュールにあることを示す__class__現在操作されているオブジェクトのクラスが何であるかを示す
    次の2つのファイルは、同じレベルのディレクトリの下にあります.
    #current.py
    
    class C:
        def __init__(self):
            self.name = "alex"
    
    #test.py
    
    import current
    
    def test():
        print("from the test.test")
    
    obj = current.C()
    print(obj.name)
    print(obj.__class__)  #              
    print(obj.__module__) #        
    '''
      :
    alex
    
    current
    '''
    

    八、__str__
    l = list([1,2,3])
    print(l)   #    [1, 2, 3]
    
    class mysql:
        def __init__(self,host,port):
            self.host = host
            self.port = port
    
    conn = mysql("127.0.0.1","3306")
    print(conn) #    <__main__.mysql object="" at="">
    

    以上の2つのセグメントのコードから、私たちが自分で定義したクラスは、生成したオブジェクトが直接印刷されるとき、印刷されるのはオブジェクトのメモリアドレスであり、これは私たちが望んでいるものではありません.私たちが実際に望んでいるのも、最初のセグメントのコードのように有用な情報を返すことです.このとき、__str__という内蔵方法を使用します.それはクラスの内部に定義されています.クラスがインスタンス化されると、__str__の実行が自動的にトリガーされ、インスタンス化されたオブジェクトに値が返されます.次の例を示します.
    class mysql:
        def __init__(self,host,port):
            self.host = host
            self.port = port
    
        def __str__(self):
            return "Host:%s,Port:%s"%(self.host,self.port)
    
    conn = mysql("127.0.0.1","3306")
    #  `__str__`           conn
    print(conn) 
    
    '''
      :
    Host:127.0.0.1,Port:3306
    '''
    

    九、__del__析構方法
    オブジェクトがメモリから解放されると、__del__が自動的にトリガーされます.
    注意:この方法は一般的に定義する必要はありません.Pythonは高度な言語であり、プログラマは使用時にメモリの割り当てと解放に関心を持つ必要はありません.この作業はPython解釈器に任せて実行されるため、構造関数の呼び出しは解釈器がゴミ回収を行うときに自動的にトリガーされます.
    簡単な例:
    class Foo:
        def __del__(self):
            print("  __del__")
    
    f1 = Foo()
    del f1   #  f1,   __del__  
    print("------->")
    
    '''
      :
      __del__
    ------->
    '''
    

    次のような状況を見てみましょう.
    class Foo:
        def __del__(self):
            print("  __del__")
    
    f1 = Foo()
    # del f1    #   
    print("------->")
    
    '''
      :
    ------->
      __del__
    '''
    #    __del__           ,     
    

    十、__setitem____getitem____delitem__クラスにはこの3つの方法があります.それは、クラスの属性が辞書のように操作できることを意味します.
    class Foo:
        def __init__(self,name):
            self.name = name
    
        def __getitem__(self, item):
            print("from __getitem__")
    
        def __setitem__(self, key, value):
            print("from __setitem__")
            self.__dict__[key] = value  #     ,      
    
        def __delitem__(self, key):
            print("from __delitem__")
            self.__dict__.pop(key)
    
    f1 = Foo("egon")
    # f1.age = 18     #    __setitem__   
    f1["age_2"] = 20  #   __setitem__   ,       ;
    print(f1.__dict__)
    del f1["age_2"]
    print(f1.__dict__)
    print(f1["name"])   #   __getitem__  ,  __getitem__       ,      None
    
    '''
        :
    from __setitem__
    {'name': 'egon', 'age_2': 20}
    from __delitem__
    {'name': 'egon'}
    from __getitem__
    None
    '''
    #       ,age_2 = 20         
    

    十一、__enter____exit__コンテキスト管理プロトコル、すなわち__enter__文は、__exit__およびwithの2つの方法によって実現され得る.オブジェクトがwith文と互換性を持つためには、__enter__メソッドと__exit__メソッドをこのオブジェクトのクラスに宣言する必要があります.
    class Open:
        def __init__(self,name):
            self.name = name
    
        def __enter__(self):
            #  with  ,   __enter__       ,        as     
            print("from __enter__")
            return 3   #    as       
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print("from __exit__, with          ")
    
    with Open("egon") as f:
        print("from with    ")
        print("f:",f)
    
    '''
      :
    from __enter__
    from with    
    f: 3
    from __exit__, with          
    '''
    
    __exit__(self, exc_type, exc_val, exc_tb)の3つのパラメータの意味:
  • exc_typeは異常タイプ
  • を表す.
  • exc_valは異常値
  • を表す.
  • exc_tb異常を表すトレーサビリティ情報
  • class Open:
        def __init__(self,name):
            self.name = name
    
        def __enter__(self):
            #  with  ,   __enter__       ,        as     
            # print("from __enter__")
            return self   #    as       
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print("from __exit__, with          ")
            print("exc_type:",exc_type)
            print("exc_val:",exc_val)
            print("exc_tb:",exc_tb)
            return True  #  True,        ,with            , with   raise        。
    
    with Open("egon") as f:
        print("from with    ")
        raise AttributeError("    ")
        # print("=====>      ")    #    
    print("=====>      ,with  ")   #__exit__  True    
    
    '''
      :
    from with    
    from __exit__, with          
    exc_type: 
    exc_val:     
    exc_tb: 
    =====>      
    '''
    

    例:open()をシミュレートし、コンテキスト管理を実現
    #  open()  
    
    class Open:
        def __init__(self,filename,mode='r',encoding="utf-8"):
            self.filename = filename
            self.mode = mode
            self.encoding = encoding
    
        def __enter__(self):
            self.file = open(self.filename,self.mode,encoding =self.encoding)
            return self.file
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.file.close()
            return True  #       ,with         
    
    with Open("a.txt","w+") as f:
        f.write("11111
    ") f.write("22222
    ") f.seek(0) print(f.tell()) f.abc # , __exit__

    十二、__call__オブジェクトの後ろにカッコを付けると、__call__メソッドの実行がトリガーされます.逆クラスには__call__メソッドがあり、このクラスで生成されたオブジェクトによってカッコを付けて実行できます.
    class foo:
        def __init__(self):
            print("from __init__")
    
        def __call__(self, *args, **kwargs):
            print("from __call__")
    
    obj = foo()    #  __init__
    obj()          #  __call__
    
    '''
      :
    from __init__
    from __call__
    '''
    

    十三、元類metaclassまずexecを見てみましょう