第三章:深いクラスとオブジェクト

11167 ワード

1.アヒルのタイプと多態
アヒルのタイプは鳥が道を歩くとアヒルのようになり、泳ぐとアヒルのようになり、鳴くとアヒルのようになり、この鳥をアヒルと呼ぶことができます.すなわち、多くのクラスが同じ方法を実現すると、これらのクラスは同じ種類と見なすことができる.Pythonの変数自体にはタイプがないので、それ自体が多態を表し、任意のタイプとして表すことができる.
# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/14 14:52'


class Cat(object):
    def say(self):
        print('     ')


class Dog(object):
    def say(self):
        print('     ')


class Duck(object):
    def say(self):
        print('      ')


animal_list = [Cat(),Dog(),Duck()]

for animal in animal_list:
    animal.say()
:list extend lst.extend(self ,iteralbe) iterableはアヒルのタイプであり、反復可能なタイプを表しています.
a = [1,2]
b = [2,1]
c = (3,4)
d = dict(x=1,y=2) #          ,           
e = set()
e.add(5)
e.add(6)

a.extend(b)
a.extend(c)
a.extend(d)
a.extend(e)
print(a)

output: 1, 2, 2, 1, 3, 4, 'x', 'y', 5, 6]
2.抽象ベースクラス(abc abstract classの略)
抽象ベースクラスとは何か.あるクラスが抽象ベースクラスを継承する場合、抽象ベースクラスのすべての方法を実現しなければならない.抽象ベースクラスはインスタンス化できません.オブジェクトを作成できません.
Pythonはアヒルのタイプに基づいている以上、すべてのクラスに魔法の方法を追加すれば特殊な特性を実現することができ、静的言語のようにクラスを継承しなければ特性を得ることができない.では、なぜ抽象的なベースクラスが必要なのでしょうか.抽象的なベースクラスによって、あるタイプに属しているかどうかを判断できることがあるからです.
Pythonのcollections.abcモジュールには抽象的なベースクラスがあり、isinstance(object,abcClass)に基づいてクラスが何らかのタイプかどうかを判定することができます.
抽象ベースクラスをどのようにシミュレートしますか?
#                   ,           .
#                 
class CacheBase():
    def get(self,key):
        raise NotImplementedError
    def set(self,key,value):
        raise NotImplementedError

class RedisCache(CacheBase):
    pass

redis_cache = RedisCache()
redis_cache.set('key','value')

このような欠点は、メソッドを呼び出すときに異常を投げ出すことであり、初期化時に問題なく、初期化時に異常を投げ出す方法はありませんか.抽象的なベースクラスを実現しなければならない方法を教えてくれませんか.abcモジュールの@abcを使用する.abstractmethod装飾器の方式は実現できる.
import abc
class CacheBase(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def get(self,key):
        pass

    @abc.abstractmethod
    def set(self,key,value):
        pass

class RedisCache(CacheBase):
    def get(self,key):
        pass
    def set(self,key,value):
        pass

cache = RedisCache()

3.isinstanceとtypeの違いは?
typeはこのオブジェクトまたはクラスの直属オブジェクトのみを判断し、isinstanceはその親クラスに遡ることができる.すべての場合typeはisinstanceが正確ではありません.
# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/14 16:53'


class A:
    pass


class B(A):
    pass
b = B()
print(isinstance(b,B))
print(isinstance(b,A))
print(type(b) is B)
print(type(b) is A)

4.クラス変数とインスタンス変数
1.クラス変数はクラスに属する、インスタンスにも属する.すべてのインスタンスオブジェクトとクラスにはクラス変数が共有する.クラス変数2を呼び出すことができる.インスタンス変数はインスタンスに属する、クラスに属しない.インスタンス変数は、具体的なインスタンスが単独で持つ.また、インスタンス変数は、特定のインスタンスによってのみ呼び出すことができる.クラス変数で呼び出すことはできません.3.インスタンス変数とクラス変数が重複する場合、インスタンス変数は独自のインスタンス変数を呼び出し、インスタンスオブジェクトを介してクラス変数に値を割り当てると、クラス変数は呼び出されず、同名のインスタンス属性が再び追加される.この点はピットで、グローバル変数と局所変数の味に似ている.したがって、我々が使用する場合、クラスを使用してクラス変数を呼び出し、インスタンスオブジェクトを使用してクラス変数を呼び出す場合を減らすことが最善の習慣である.実はここでクラスで呼び出すと、global宣言が付加され、この変数がクラス変数であることを示す.インスタンス・オブジェクトで呼び出すと、アクセスすると上に検索され、割り当てなどの変更では上に検索されません.このプロパティがなければ、直接作成されます.
# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/14 16:59'

class A:
    aa = 1 #    
    def __init__(self,x,y):
        self.x = x  #     
        self.y = y  #     

a = A(2,3) # a     
A.aa = 11  #                 ,    .
a.aa = 100 #                ,              aa
           #                   ,               
print(a.x,a.y,a.aa) #         ,      ,      
print(A.aa) #             
# print(A.x,A.y) #             

印刷結果:2 3 100 11
5.クラス属性とインスタンス属性および検索順序
属性:変数とメソッドを総称属性Python 3が採用する属性検索順序は、C 3アルゴリズム、すなわち、まず広さを優先し、次にサブクラスがサブクラスを呼び出す方式である.1つのクラスのmro属性によって1つのクラスの属性検索順序を表示することができる.
# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/14 19:03'
#    
class D:
    pass
class E:
    pass
class C(E):
    pass
class B(D):
    pass
class A(B,C):
    pass
print(A.__mro__)

('__main__.A'>, '__main__.B'>, '__main__.D'>, < class '__main__.C' >, < class '__main__.E' >, )
6.クラスメソッド、スタティックメソッド、インスタンスメソッド
クラスメソッドはクラスに属し、すべてのオブジェクトとクラスにこのクラスメソッドがあり、@classmethod装飾器で装飾する.インスタンスオブジェクトとクラスオブジェクトはクラスメソッドを呼び出すことができますが、クラスメソッドはインスタンスプロパティを使用できません.clsパラメータ、すなわちクラスパラメータが必要です.
静的方法は、普通の関数の定義と何の違いもなく、パラメータにも要求されず、普通の関数をクラスに配置し、@staticmethod装飾器を加える.静的方法では、一般にインスタンス変数は使用できない.クラスオブジェクトとインスタンスオブジェクトは、静的メソッドを呼び出すことができます.
インスタンスメソッドでは、最初のパラメータは具体的なインスタンスオブジェクトでなければならないが、一般的に定義する際にselfを使用して代替し、追加の装飾器を追加する必要はない.インスタンスメソッドは、通常、インスタンスオブジェクトのみが呼び出すことができます.クラスオブジェクトは呼び出すことができません.
# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/14 19:08'


class Date:
    #        
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
    @staticmethod
    def parse_from_string(date_str):
        year,month,day = tuple(date_str.split('-'))
        return Date(int(year),int(month),int(day))

    @classmethod
    def from_string(cls,date_str):
        year,month,day = tuple(date_str.split('-'))
        return cls(int(year),int(month),int(day))

    def tomorrow(self):
        self.day += 1
        return self.day

    def __str__(self):
        return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)

if __name__ == '__main__':
    new_day = Date(2019,1,2)
    print(new_day)

    print(new_day.tomorrow())

    date_str = '2018-12-31'
    #  staticmethod      
    new_day = Date.parse_from_string(date_str)
    print(new_day)

    #  classmethod      
    new_day = Date.from_string(date_str)
    print(new_day)

7.データパッケージとプライベート属性. :は二重下線で私有属性を宣言し、直接アクセスすることはできないが、名前を変更しただけで直接アクセスすることはできない.でも通過できるclassname__var方式でアクセスする.
この方法は、実は私たちがプログラムを書くときにもっと規範化するために、プログラマーという変数をどのように使うかを示す役割を果たすことが多い.この方法のもう一つの利点は、継承後の変数名の重複の問題を効果的に区別することです.変数の前に自分の_を付けるからです.classname
# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/14 19:45'


class User:
    def __init__(self, birthyear):
        self.__birthyear = birthyear

    def get_age(self):
        #     
        return 2019 - self.__birthyear


if __name__ == '__main__':
    user = User(1990)
    # print(user.__birthyear) #       ,            
    #               ,      _User__birthyear   .
    print(user.get_age())


8.Pythonの自省
自省自省とはPythonのオブジェクトが実行するときに何らかのメカニズムで自分のタイプを知ることである.dir()は、1つのオブジェクトの複数の属性リストを取得することができ、属性のみに値がない.__dict__変数は、1つのオブジェクトの独自の属性を取得することができ、一般的にはオブジェクト自身が所有するものであり、継承されているものではなく、内蔵されているものでもない.辞書のキーは属性名であり、辞書の値は属性変数に対応する値である.この辞書で属性の追加と修正を行うことができる.
# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/14 19:57'


class Person:
    name = 'user'


class Student(Person):
    def __init__(self, school_name):
        self.school_name = school_name


if __name__ == '__main__':
    user = Student('   ')
    person = Person()
    #   __dict__           
    print(user.__dict__) #{'school_name': '   '}
    print(person.__dict__) # {}
    print(Person.__dict__)
    # {'__module__': '__main__', 'name': 'user', '__dict__': ,
    # '__weakref__': , '__doc__': None}

    #     __dict__            
    user.__dict__['school_addr'] = '   '
    print(user.school_addr) #    

    print(dir(user))
    # dir()               ,       ,         

Pythonでのsuper()関数の使用
二つの問題?1.たとえば、あるメソッドを書き換えたのに、なぜsuper()でその上位レベルのこのメソッドを呼び出すのか.なぜなら、上位レベルで完了したいくつかの機能を再利用する必要がある場合があるからだ.例えばThreadクラスを書き換えると__init__ を再利用できます
# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/14 20:12'
#            ,        super() ?
#                            .

#   super()             ,             ?
# super()        __mro__       ,         .
#             .

from threading import Thread


class MyThread(Thread):
    def __init__(self, name, target, time):
        self.time = time
        super().__init__(target=target, name=name)

    def run(self):
        pass

class A:
    def __init__(self):
        print('A')

class B(A):
    def __init__(self):
        print('B')
        super().__init__()

class C(A):
    def __init__(self):
        print('C')
        super().__init__()

class D(B,C):
    def __init__(self):
        print('D')
        super().__init__()

if __name__ == '__main__':
    d = D()
    print(D.__mro__)

[output:] D B C A (main.D'>, main.B'>, main.C'>, main.A'>, )
親super()メソッドを単純に呼び出すだけであれば、上記の印刷結果がd=D()の場合は、D,B,A,C,Aであるべきであるが、実際の呼び出し状況はD B C AとD.__mro__の順序が一致する
Pythonのwith文
まずtryを見て...except .. else ... finallyの用法try:先に実行するモジュールで、ここで異常をキャプチャする.except:異常をキャプチャするとexcept文を実行するコードelse:異常をキャプチャしなければelse文を実行するコードfinally:exceptとelseが実行するかどうかにかかわらずfinallyは必ず実行する.
def exe_try():
    try:
        print('code started')
        raise KeyError
        return 1
    except KeyError as e:
        print('key error')
        return 2
    else:  #            
        print('other error')
        return 3
    finally:  #            ,  finally   return  . 
        #        finally   return  
        print('finally')
        return 4
: finallyは一般的に資源を解放し、最後の処理を行うために使用される.
Pythonのwith文with文はコンテキストマネージャとも呼ばれ、1つのオブジェクトがwith文を使用できるのは、このオブジェクトがコンテキストマネージャプロトコルを実現しているからである.このプロトコルの実現は、魔法法__enter__()__exit__によって実現される.with文を使用すると、後続のオブジェクトは、初期化時にこのオブジェクトの__enter__()メソッドを呼び出し、終了時に__exit__()メソッドを自動的に呼び出す.
class Sample():
    def __enter__(self):
        print('__enter__')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')

    def do_something(self):
        print('doing something')

# with                  __enter__  
#         __exit__    __enter__  __exit__    
#              ,               with  .
with Sample() as simple:
    simple.do_something()
, contexlib contextmanager .

:ここでの関数はyieldを使用する必要があり、yield以前のコードは成都が__enter__()で実現された論理コードであることを理解することができる.一方、yield以降のコードは、成都が__exit__()で実現するコードロジックであることを理解することができる.
@contextlib.contextmanager
def file_open(file_name):
    print('file open')
    yield {}
    print('file end')

with file_open('bob.txt') as f_opened:
    print('  ')

output:file open中間file end
このような書く利点は、コードが比較的理解しやすく、より直感的であることである.