day 28類のパッケージ

5589 ワード

導入
オブジェクト向けプログラミングには、パッケージ、継承、マルチステートの3つの特性があります.その中で最も重要な特性はパッケージです.パッケージとは、データと機能を統合することです.よく知られているように聞こえますが、間違いありません.私たちが前に言った「統合」という言葉は、実はパッケージの一般的な言い方です.それ以外に、オブジェクトやクラスにカプセル化された属性に対して、アクセスを厳格に制御し、2つのステップに分けて実現することができます.隠しとオープンインタフェース
二隠し属性
PythonのClassメカニズムは、二重下線の先頭を使用して属性を非表示にします(プライベートに設定されています)が、これは単なる変形操作であり、クラス内のすべての二重スライド線の先頭の属性は、クラス定義フェーズ、文法検出時に自動的に「_クラス名__属性名」の形式になります.
class Foo:
    __N=0 #    _Foo__N

    def __init__(self): #      ,       ,  __         
        self.__x=10 #    self._Foo__x

    def __f1(self): #    _Foo__f1
        print('__f1 run')

    def f2(self):  #      ,       ,  __         
        self.__f1() #   self._Foo__f1()

print(Foo.__N) #   AttributeError: Foo    __N

obj = Foo()
print(obbj.__x) #   AttributeError:  obj    __x

この変形に注意しなければならない問題は、
1、クラスの外部では二重スライド線の先頭の属性に直接アクセスできないが、クラス名と属性名を知っていれば名前をつづることができる.クラス名_プロパティを選択すると、Foo._などのアクセスが可能になります.A__N,従って,この操作は厳密な意味で外部アクセスを制限するものではなく,文法的な意味での変形にすぎない.
>>> Foo.__dict__
mappingproxy({..., '_Foo__N': 0, ...})

>>> obj.__dict__
{'_Foo__x': 10}

>>> Foo._Foo__N
0
>>> obj._Foo__x
10
>>> obj._Foo__N
0

2、クラス内部では二重スライド線の先頭の属性に直接アクセスできる.例えばself._f 1()は,クラス定義段階でクラス内部の二重スライド線の先頭の属性が統一的に変形したためである.
>>> obj.f2()
__f1 run

3、変形操作はクラス定義段階に一度だけ発生し、クラス定義後の付与操作は変形しない.
>>> Foo.__M=100
>>> Foo.__dict__
mappingproxy({..., '__M': 100,...})
>>> Foo.__M
100

>>> obj.__y=20
>>> obj.__dict__
{'__y': 20, '_Foo__x': 10}
>>> obj.__y
20

トリプルオープンインタフェース
属性を定義するのは使用するためなので、非表示は目的ではありません.
3.1データ属性の非表示
データを非表示にすると、クラス外部のデータに対する直接的な操作が制限され、クラス内には対応するインタフェースが提供され、クラス外部が間接的にデータを操作できるようにしなければならない.インタフェースには、データの操作を厳格に制御するために追加の論理を付加することができる.
>>> class Teacher:
...     def __init__(self,name,age): #           
...         self.__name=name
...         self.__age=age
...     def tell_info(self): #             
...         print('  :%s,  :%s' %(self.__name,self.__age))
...     def set_info(self,name,age): #             ,          
...         if not isinstance(name,str):
...             raise TypeError('          ')
...         if not isinstance(age,int):
...             raise TypeError('       ')
...         self.__name=name
...         self.__age=age
... 
>>>
>>> t=Teacher('lili',18)
>>> t.set_info(‘LiLi','19') #       ,    
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 11, in set_info
TypeError:        
>>> t.set_info('LiLi',19) #         ,     ,      
>>> t.tell_info() #        
  :LiLi,  :19

3.2関数属性の非表示
目的は複雑さを隔離するためで、例えばATMプログラムの引き出し機能、この機能は多くの他の機能構成があって、例えばカードを挿し込んで、身分認証、金額を入力して、領収書を印刷して、お金を引き出すなど、使用者にとって、引き出しという機能インタフェースを開発するだけで、その残りの機能は私達はすべて隠すことができます
>>> class ATM:
...     def __card(self): #  
...         print('  ')
...     def __auth(self): #    
...         print('    ')
...     def __input(self): #    
...         print('      ')
...     def __print_bill(self): #    
...         print('    ')
...     def __take_money(self): #  
...         print('  ')
...     def withdraw(self): #    
...         self.__card()
...         self.__auth()
...         self.__input()
...         self.__print_bill()
...         self.__take_money()
...
>>> obj=ATM()
>>> obj.withdraw()

隠し属性とオープンインタフェースをまとめると、本質は内外を明確に区別するためであり、クラス内部は外部呼び出し者のコードに影響を与えずにパッケージ内のものを修正することができる.クラスの外部は1つのインタフェースを手に入れるだけで、インタフェース名、パラメータが変わらない限り、設計者がどのように内部実装コードを変更しても、使用者はコードを変更する必要はありません.これにより、インタフェースという基礎的な約束が変わらない限り、コードの修正は心配できない良好な協力基礎が提供される.
四property
BMI指数は人の体重と身長が健康に与える影響を測定するための指標であり、計算式は
    (BMI)=  (kg)÷  ^2(m)
EX:70kg÷(1.75×1.75)=22.86

身長や体重は絶えず変化するため、BMI値を表示するたびに計算する必要があるが、BMIは機能ではなく特徴のように聞こえることが明らかになった.そのため、Pythonは装飾器propertyを提供し、クラスの関数を対象のデータ属性に「偽装」することができ、オブジェクトはこの特殊な属性にアクセスすると機能の実行をトリガーする.次に、このアクセスの結果として戻り値を使用します.たとえば、
>>> class People:
...     def __init__(self,name,weight,height):
...         self.name=name
...         self.weight=weight
...         self.height=height
...     @property
...     def bmi(self):
...         return self.weight / (self.height**2)
...
>>> obj=People('lili',75,1.85)
>>> obj.bmi #    bmi   , obj    self,               
21.913805697589478

propertyを使用すると、属性アクセスの一貫性が効果的に保証されます.またpropertyでは、次のようにプロパティを設定および削除する機能も提供します.
>>> class Foo:
...     def __init__(self,val):
...         self.__NAME=val #       
...     @property
...     def name(self):
...         return self.__NAME
...     @name.setter
...     def name(self,value):
...         if not isinstance(value,str):  #            
...             raise TypeError('%s must be str' %value)
...         self.__NAME=value #       ,  value        self.__NAME
...     @name.deleter
...     def name(self):
...         raise PermissionError('Can not delete')
...
>>> f=Foo('lili')
>>> f.name
lili
>>> f.name='LiLi' #  name.setter        name(f,’Egon')
>>> f.name=123 #  name.setter      name(f,123),    TypeError
>>> del f.name #  name.deleter     name(f),    PermissionError