Pythonオブジェクト向け2

10060 ワード

オブジェクトに向かってステップアップ
パッヶージ
≪カプセル化|Package|oem_src≫:オブジェクトの作業の詳細を外部世界に非表示にします.
アクセスの表示
前にStudentクラスを書きましたが、このクラスではオブジェクトにname、school、ageの3つのプロパティをバインドしました.定義時に二重下線の先頭を使用し、変数名に続く方法で名前を付けます.どうしてそうするの?pythonではメソッドとオブジェクトのアクセス権は他のプログラミング言語C+、Javaと同様に、プライベートで公開されている2つのアクセス権があります.属性がプライベートで外部オブジェクトへのアクセスが許可されていない場合は、属性に名前を付けるときに2つの下線を先頭に使用できます.
Pythonはクラスを定義する際に属性をプライベートに設定できるが、Pythonは文法的にプライベート属性やメソッドのプライバシーを厳格に保証していない.プライベート属性やメソッドに名前を変えてアクセスを「妨げる」だけで、ネーミングルールを知っていればアクセスできる.したがって、ほとんどのpythonプログラマーは、属性名を単一の下線で始まるように属性が保護されていることを隠喩するネーミング慣例に従います.
説明:Pythonプロパティのアクセスの可視性については、次のように参照してください.http://blog.csdn.net/jackfrued/article/details/79513824
包装器/装飾器(decorator)
装飾モードには、ログの挿入、パフォーマンステスト、トランザクションなど、古典的な使用シーンがたくさんあります.装飾器があれば、関数内の自分の機能に関係のない類似コードを大量に抽出し、コードの再利用の目的を達成することができます.最大のメリットは、関数の内部で何が起こっているのかを知ることです.前に高次関数を学びましたが、ここでは高次関数の知識で簡単な装飾器を書くことができます.
def record(fn):
    def wrapper(*args, **kwargs):
        print('    %s  ' % fn.__name__)
        #         ,                     
        #                        
        val = fn(*args, **kwargs)
        print('%s      ' % fn.__name__)
        print('   val', val)
        #             
        return val

    return wrapper

#        f  , f                  
#  @record  f(n)      ,        :f = record(f)        f   
#    wrapper,  f      wrapper  (     f    )        wrapper   
@record
def f(n):
    if n == 0 or n == 1:
        return 1
    else:
        return n * f(n - 1)


def main():
    f(5)


if __name__ == '__main__':
    main()
    f  
    f  
    f  
    f  
    f  
f      
   val 1
f      
   val 2
f      
   val 6
f      
   val 24
f      
   val 120

上記の例では,階乗関数の再帰と遡及の過程を直感的に理解できる.Pythonには3つの内蔵装飾器があり、いずれもclassに関連しています:staticmethod、classmethod、property.
  • staticmethodはクラス静的メソッドであり、メンバーメソッドとの違いはselfパラメータがないことであり、クラスがインスタンス化されずに
  • を呼び出すことができる.
  • classmethodとメンバーメソッドの違いは、受信された最初のパラメータがself(クラスインスタンスのポインタ)ではなくcls(現在のクラスの特定のタイプ)
  • であることである.
  • propertyは属性の意味で、クラスインスタンスを通じて直接アクセスできる情報を表します!
  • class Triangle(object):
        def __init__(self, a, b, c):
            self._a = a
            self._b = b
            self._c = c
    
        #    ,          ,         
        @classmethod
        def is_valid2(cls, a, b, c):
            return a + b > c and b + c > a and a + c > b
    
        #     ,        ,          ,      
        @staticmethod
        def is_valid(a, b, c):
            return a + b > c and b + c > a and a + c > b
    
        @property
        def primeter(self):
            return self._a + self._b + self._c
    
        @property
        def area(self):
            half = self.primeter * 0.5
            return (half * (half - self._a) * (half - self._b) * (half - self._c)) ** 0.5
    
    
    def main():
        a = 2
        b = 3
        c = 1
        if Triangle.is_valid(a, b, c):
            t1 = Triangle(a, b, c)
            print(t1.primeter)
            print(t1.area)
        else:
            print('       ')
    
        if Triangle.is_valid2(a, b, c):
            t2 = Triangle(a, b, c)
            print(t2.primeter)
            print(t2.area)
        else:
            print('       ')
    
        a = b = c = 5
        t3 = Triangle(a, b, c)
        print(t3.primeter)
        print(t3.area)
        
    
    
    
    if __name__ == '__main__':
        main()
    
    
           
           
    15
    10.825317547305483
    

    説明:クラスメソッドと静的メソッドは、オブジェクトを作成せずに使用できますが、累積メソッドはclsキーでクラスのプロパティにアクセスできます.静的メソッドはクラスのプロパティpropertyプロパティメソッドにアクセスできません.通常、クラスのプロパティ情報を返すために使用しますが、クラスのプロパティを変更する方法はsetterです.setterを使用する前にpropertyが必要です.通常、propertyをビューア、setterをモディファイヤと呼びます.
    継承
    私たちはクラスを定義するときに必ず2つの似たようなクラスの定義に出会ったことがあると信じています.例えば、猫クラスと犬クラスを定義します.この中には大きさ、色、走り、食べ物などの特性があります.この中には多くの繰り返しコードがあります.Martin Fowlerは「コードには悪い味がたくさんあります.重複は最悪の一種である'だから、このような類似クラスで重複コードが発生する問題を解決する方法はありますか?
    この問題を解決するにはクラスの継承が必要です.クラスを定義するときにclassキーワードを使用してクラス名とカッコを付けます.カッコの中には現在のクラスが継承している親の名前があります.もちろん、前にobjectと書いています.objectはすべてのクラスの親であることができるからです.上記の問題では、継承があれば1つの親を定義するだけで、親で子クラスの類似の属性と方法を定義し、子クラスが親クラスを継承する方法でこれらの重複コードを除去することができます.例は次のとおりです.
    class Animals(object):
        def __init__(self, name, color):
            self._name = name
            self._color = color
    
        def run(self):
            print('%s is running...' % self._name)
    
        def eat(self):
            print('%s is eating...' % self._name)
    
    class Dog(Animals):
        def __init__(self, name, color, where):
            super().__init__(name, color) #  name color          ,          ,         ,                  
            self._where = where
            
    class Cat(Animals):
        def __init__(self, name, color, age):
            super().__init__(name, color)
            self._age = age
            
    dog = Dog('jerry', 'black')
    cat = Cat('Tom', 'white')
    dog.run()
    cat.eat()
    

    上記のコードの出力結果は次のとおりです.
    jerry is running...
    Tom is eating...
    

    Pythonではクラスの継承も同様にマルチ継承がサポートされており、マルチ継承の場合、1つのメソッドがサブクラスで複数の親クラスで定義されていない場合、メソッドを呼び出す場合は、そのメソッドを持つ最初の親クラスで定義されたメソッドを左から右へ順に呼び出す.もちろんここではマルチ継承はお勧めしません.
    メソッド書き換え
    子クラスは親メソッドを継承した後にメソッドを再実装し,メソッド書き換えと呼ぶ.やはり上記の例では、犬を2回走らせるにはDogクラスでrunメソッドを書き直すだけです.
    class Dog(Animals):
        def __init__(self, name, color, where):
            super().__init__(name, color)
            self._where = where
            
        def run(self):
            print('%s is running...twice' % self._name)
    

    上記のコードの出力結果は次のとおりです.
    jerry is running...twice
    Tom is eating...
    

    子クラスが親クラスのメソッドを書き換えた後、子クラスオブジェクトにこのメソッドを実行するメッセージを送信すると、子クラスの書き換え後のメソッドが実行され、親クラスのメソッドではありません.
    注:サブクラスオブジェクトは、親または子の独自のメソッドを呼び出すことができ、親オブジェクトは子のメソッドを呼び出すことができません.
    演算子の再ロード
    Pythonは同様に演算子のリロードをサポートしており、クラスの独自の方法をリロードすることができます.例は次のとおりです.
    from math import gcd
    
    
    class Fraction(object):
    
        def __init__(self, num, den):
            if den == 0:
                raise ValueError('     0') # raise         
            self._num = num
            self._den = den
    
        def __str__(self):
            if self._num == 0:
                return '0'
            elif self._den == 1:
                return self._num
            else:
                return '%d/%d' % (self._num, self._den)
    
        @property
        def den(self):
            return self._den
    
        @property
        def num(self):
            return self._num
    
        def add(self, other):
            return Fraction(self._num * other.den + \
                            other.num * self._den, self._den * other.den)
    
        #      :
        #                           
        def __add__(self, other):
            return self.add(other)
    
        def sub(self, other):
            return Fraction(self._num * other.den - \
                            other.num * self._den, self._den * other.den)
    
        def __sub__(self, other):
            return self.sub(other)
    
    f1 = Fraction(-3,5)
    f2 = Fraction(1,2)
    print(f1.add(f2))
    print(f1.sub(f2))
    print(f1 + f2)
    print(f1 - f2)
    

    プログラムの実行結果は次のとおりです.
    -1/10
    -11/10
    -1/10
    -11/10
    

    マルチステート
    抽象クラス
    抽象クラスは抽象メソッドを含むクラスであり、抽象メソッドには実現可能なコードは含まれず、そのサブクラスで抽象関数を実現できるコードしかありません.Pythonは抽象クラスの概念を言語的にサポートしておらず,abcモジュールによって抽象クラスの効果を製造することができる.したがって、Pythonで抽象クラスを実装する前にabcモジュールのメタクラス(ABCMeta)とアクセサリ(abstractmethod)をインポートする必要があります.
  • 元類(ABCMeta)
  • 抽象クラスを作成するときにmetaclass=ABCMetaを使用すると、クラスが抽象クラスとして作成されることを示し、metaclass=ABCMetaはクラスのメタクラスがABCmetaであることを指定します.メタクラスとはクラスを作成するクラスです.
  • 装飾器(abstractmethod)
  • 方法を定義する際にこの装飾器を用いて包装し,作成した方法を抽象的な方法と定義する.
    抽象メソッドには実装可能なコードが含まれていないため、その関数体はpassを使用することが多い.抽象クラスの実装とマルチステートについて説明します.マルチステートとは,抽象クラスでメソッドを定義し,そのサブクラスで再実装することができ,異なるサブクラスで実装する方法も異なる.
    """
                       、       
                               
                15000 
                       150 
           1200         5%   
    """
    from abc import ABCMeta, abstractmethod
    
    class Employee(object, metaclass=ABCMeta):
    
        def __init__(self, name):
            self._name = name
    
        @property
        def name(self):
            return self._name
    
        @abstractmethod  #            ,      get_salary  
        def get_salary(self):
            pass
    
    
    class Manager(Employee):
    
        def get_salary(self):
            return 15000
    
    
    class Programmer(Employee):
    
        def __init__(self, name):
            super().__init__(name)
            self._working_hour = 0
    
        @property
        def working_hour(self):
            return self._working_hour
    
        @working_hour.setter
        def working_hour(self, hour):
            self._working_hour = hour if hour > 0 else 0
    
        def get_salary(self):
            return 150.0 * self._working_hour
    
    class Salesman(Employee):
    
        def __init__(self, name, sales=0):
            super().__init__(name)
            self._sales = sales
    
        @property
        def sale(self):
            return self._sales
    
        @sale.setter
        def sale(self, sale):
            self._sales = sale if sale > 0 else 0
    
        def get_salary(self):
            return self._sales * 0.05 + 1200.0
    
    
    def main():
        emps = [Manager('   '), Programmer('    '),
                Manager('   '), Salesman('   '),
                Salesman('   '), Programmer('    '),
                ]
    
        for emp in emps:
            if isinstance(emp, Programmer):  #        ,                
                emp.working_hour = int(input('   %s      :' % emp.name))
            if isinstance(emp, Salesman): 
                emp.sale = int(input('   %s      :' % emp.name))
            #      get_salary()  ,              
            #           get_salary()  ,         
            print('%s     :¥%.2f ' % (emp.name, emp.get_salary()))
    
    
    if __name__ == '__main__':
        main()
    
    
            :¥15000.00 
                 :25
             :¥3750.00 
            :¥15000.00 
                :15000
            :¥1950.00 
                :5
            :¥1200.25 
                 :56
             :¥8400.00 
    

    注意:
    1.抽象クラスはオブジェクトを作成できません(つまりインスタンス化できません)、抽象クラスの存在の意味は他のクラスに継承されます.
    2.クラスは抽象メソッドがない場合は抽象クラスではなく、抽象メソッドがある場合は抽象クラスではインスタンス化できません
    3.子クラスが親クラスの抽象メソッドを上書きした場合、子クラスは抽象クラスではなく、インスタンス化できます.子クラスに親クラスを上書きする抽象メソッドがなく、親クラスから継承する場合、子クラスも抽象クラスであり、インスタンス化できません.
    4.抽象メソッドはサブクラスで書き直さなければなりません.そうしないと、エラーが発生します.