Python 封裝


【封裝】

         隱藏對象的屬性和實現細節,僅對外提供公共訪問方式。

【好處】 

1. 將變化隔離; 

2. 便於使用;

3. 提高復用性; 

4. 提高安全性;

【封裝原則】

      1. 將不需要對外提供的內容都隱藏起來;

      2. 把屬性都隱藏,提供公共方法對其訪問。

私有變量和私有方法

在python中用雙下划線開頭的方式將屬性隱藏起來(設置成私有的)

一、私有變量

#其實這僅僅這是一種變形操作
#類中所有雙下划線開頭的名稱如__x都會自動變形成:_類名__x的形式:

class A:
    __N=0 #類的數據屬性就應該是共享的,但是語法上是可以把類的數據屬性設置成私有的如__N,會變形為_A__N
    def __init__(self):
        self.__X=10 #變形為self._A__X
    def __foo(self): #變形為_A__foo
        print('from A')
    def bar(self):
        self.__foo() #只有在類內部才可以通過__foo的形式訪問到.

#A._A__N是可以訪問到的,即這種操作並不是嚴格意義上的限制外部訪問,僅僅只是一種語法意義上的變形

這種自動變形的特點:

1.類中定義的__x只能在內部使用,如self.__x,引用的就是變形的結果

2.這種變形其實正是針對外部的變形,在外部是無法通過__x這個名字訪問到的。

3.在子類定義的__x不會覆蓋在父類定義的__x,因為子類中變形成了:_子類名__x,而父類中變形成了:_父類名__x,即雙下滑線開頭的屬性在繼承給子類時,子類是無法覆蓋的。

 

這種變形需要注意的問題是:

1.這種機制也並沒有真正意義上限制我們從外部直接訪問屬性,知道了類名和屬性名就可以拼出名字:_類名__屬性,然后就可以訪問了,如a._A__N

2.變形的過程只在類的內部生效,在定義后的賦值操作,不會變形

二、私有方法

3.在繼承中,父類如果不想讓子類覆蓋自己的方法,可以將方法定義為私有的

#正常情況
>>> class A:
...     def fa(self):
...         print('from A')
...     def test(self):
...         self.fa()
... 
>>> class B(A):
...     def fa(self):
...         print('from B')
... 
>>> b=B()
>>> b.test()
from B
 

#把fa定義成私有的,即__fa
>>> class A:
...     def __fa(self): #在定義時就變形為_A__fa
...         print('from A')
...     def test(self):
...         self.__fa() #只會與自己所在的類為准,即調用_A__fa
... 
>>> class B(A):
...     def __fa(self):
...         print('from B')
... 
>>> b=B()
>>> b.test()
from A

三、封裝與擴展性

封裝在於明確區分內外,使得類實現者可以修改封裝內的東西而不影響外部調用者的代碼;而外部使用用者只知道一個接口(函數),只要接口(函數)名、參數不變,使用者的代碼永遠無需改變。這就提供一個良好的合作基礎——或者說,只要接口這個基礎約定不變,則代碼改變不足為慮。

 

#類的設計者
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): #對外提供的接口,隱藏了內部的實現細節,此時我們想求的是面積
        return self.__width * self.__length


#使用者
>>> r1=Room('卧室','egon',20,20,20)
>>> r1.tell_area() #使用者調用接口tell_area


#類的設計者,輕松的擴展了功能,而類的使用者完全不需要改變自己的代碼
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): #對外提供的接口,隱藏內部實現,此時我們想求的是體積,內部邏輯變了,只需求修該下列一行就可以很簡答的實現,而且外部調用感知不到,仍然使用該方法,但是功能已經變了
        return self.__width * self.__length * self.__high


#對於仍然在使用tell_area接口的人來說,根本無需改動自己的代碼,就可以用上新功能
>>> r1.tell_area()

property屬性

什么是特性property

property是一種特殊的屬性,訪問它時會執行一段功能(函數)然后返回值

例一:BMI指數(bmi是計算而來的,但很明顯它聽起來像是一個屬性而非方法,如果我們將其做成一個屬性,更便於理解)

成人的BMI數值:
過輕:低於18.5
正常:18.5-23.9
過重:24-27
肥胖:28-32
非常肥胖, 高於32
  體質指數(BMI)=體重(kg)÷身高^2(m)
  EX:70kg÷(1.75×1.75)=22.86



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)

p1=People('egon',75,1.85)
print(p1.bmi)
BMI指數
import math
class Circle:
    def __init__(self,radius): #圓的半徑radius
        self.radius=radius

    @property
    def area(self):
        return math.pi * self.radius**2 #計算面積

    @property
    def perimeter(self):
        return 2*math.pi*self.radius #計算周長

c=Circle(10)
print(c.radius)
print(c.area) #可以向訪問數據屬性一樣去訪問area,會觸發一個函數的執行,動態計算出一個值
print(c.perimeter) #同上
'''
輸出結果:
314.1592653589793
62.83185307179586
'''
圓的周長和面積
#注意:此時的特性area和perimeter不能被賦值
c.area=3 #為特性area賦值
'''
拋出異常:
AttributeError: can't set attribute
'''

為什么要用property

將一個類的函數定義成特性以后,對象再去使用的時候obj.name,根本無法察覺自己的name是執行了一個函數然后計算出來的,這種特性的使用方式遵循了統一訪問的原則

除此之外,看下

ps:面向對象的封裝有三種方式:
【public】
這種其實就是不封裝,是對外公開的
【protected】
這種封裝方式對外不公開,但對朋友(friend)或者子類(形象的說法是“兒子”,但我不知道為什么大家 不說“女兒”,就像“parent”本來是“父母”的意思,但中文都是叫“父類”)公開
【private】
這種封裝對誰都不公開

python並沒有在語法上把它們三個內建到自己的class機制中,在C++里一般會將所有的所有的數據都設置為私有的,然后提供set和get方法(接口)去設置和獲取,在python中通過property方法可以實現

class Foo:
    def __init__(self,val):
        self.__NAME=val #將所有的數據屬性都隱藏起來

    @property
    def name(self):
        return self.__NAME #obj.name訪問的是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 TypeError('Can not delete')

f=Foo('egon')
print(f.name)
# f.name=10 #拋出異常'TypeError: 10 must be str'
del f.name #拋出異常'TypeError: Can not delete'

一個靜態屬性property本質就是實現了get,set,delete三種方法

class Foo:
    @property
    def AAA(self):
        print('get的時候運行我啊')

    @AAA.setter
    def AAA(self,value):
        print('set的時候運行我啊')

    @AAA.deleter
    def AAA(self):
        print('delete的時候運行我啊')

#只有在屬性AAA定義property后才能定義AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
setter和deleter
class Foo:
    def get_AAA(self):
        print('get的時候運行我啊')

    def set_AAA(self,value):
        print('set的時候運行我啊')

    def delete_AAA(self):
        print('delete的時候運行我啊')
    AAA=property(get_AAA,set_AAA,delete_AAA) #內置property三個參數與get,set,delete一一對應

f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
class Goods:

    def __init__(self):
        # 原價
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 實際價格 = 原價 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price


obj = Goods()
obj.price         # 獲取商品價格
obj.price = 200   # 修改商品原價
print(obj.price)
del obj.price     # 刪除商品原價
商品價格打折

classmethod

class Classmethod_Demo():
    role = 'dog'

    @classmethod
    def func(cls):
        print(cls.role)

Classmethod_Demo.func()

staticmethod

class Foo:
    def func(self):
        print('in father')


class Son(Foo):
    def func(self):
        print('in son')

s = Son()
s.func()
# 請說出上面一段代碼的輸出並解釋原因?
class A:
    __role = 'CHINA'
    @classmethod
    def show_role(cls):
        print(cls.__role)

    @staticmethod
    def get_role():
        return A.__role

    @property
    def role(self):
        return self.__role

a = A()
print(a.role)
print(a.get_role())
a.show_role()
# __role在類中有哪些身份?
# 以上代碼分別輸出哪些內容?
# 這三個裝飾器分別起了什么作用?有哪些區別?

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM