24. 企業級開發基礎5:物件導向特徵(封裝)

大牧莫邪發表於2017-05-23

物件導向程式設計最主要的有三個特徵:封裝、繼承、多型

本節內容主要講解物件導向的第一個特徵:封裝

1 封裝的意義

在我們程式開發過程中,定義好型別之後就可以通過型別來建立物件 如:我們定義一箇中華人民共和國公民的型別 ```

# 建立一個人的型別
class Person(object):
    def __init__(name, age):
        self.name = name
        self.age = age

此時如果我們建立好物件之後,對物件的資料進行如下修改,大家是否認為合適呢?

# 建立一個人的物件
xiaoMing = Person("小明", 18)
# 修改屬性
xiaoMing.age = 1000

``` 我們會發現,上面的程式碼在執行時是正確的,也就是可以修改age屬性為1000 此時我們需要明確一個概念:程式碼執行正確,但是不代表符合業務邏輯,這樣的程式碼我們一般會說程式碼處理不合法!

2. 實現封裝的過程

對於上面這樣的問題,我們應該怎麼處理呢 常規的方案就是: 1. 定義一種這樣的屬性,屬性只有在當前類的內部可以訪問 2. 類的外部不能訪問這個屬性,只能通過類提供的方法來進行屬性的取值和賦值 3. 在取值或者賦值的方法中,就可以新增一定的限制處理的程式碼了

python中,提供了這樣的一種特殊的變數,變數名稱使用兩個下劃線開頭,這樣的變數智慧在類的內部訪問,類的外部是訪問不了的,我們稱之為私有屬性 ```

# 定義型別
class Person(object):
    def __init__(self, name, age):
        self.__name = name;
        self.age = age
# 建立物件
xiaoMing = Person("小明明", 18)
# 訪問屬性
print(xiaoMing.age)
~ 18,可以訪問,age只是一個普通的成員屬性
print(xiaoMing.__name)
~ 出現錯誤,AttributeError: 'Person' object has no attribute '__name'

這樣我們就限制了變數的訪問範圍。 但是變數定義出來就是為了被訪問和操作的,如果上述程式碼中的__name一旦限制了不讓訪問,就木有存在的價值了。所以,我們通過自定義的方法給__name屬性新增訪問控制

# 建立一個人的型別
class Person(object):  
    # 物件的初始化方法
    def __init__(self, name):
       # 初始化私有成員變數__name
        self.__name = name
    # 定義獲取__name的函式
    def get_name(self):
        return self.__name
    # 定義設定__name的函式
    def set_name(self, name):
        self.__name = name
# 根據型別建立物件
xiaoMing = Person("小明");
# 訪問資料
xiaoMing.set_name("小明明");
print(xiaoMing.get_name()) 
# 執行結果:小明明

OK,通過以上對屬性進行私有化(屬性名稱雙下劃線開頭),給屬性提供set/get的訪問方法完成封裝過程,此時就可以對本文開頭的年齡設定問題進行一定的邏輯限制了

# 定義一個人的型別
class Person(object):
    # 初始化變數
    def __init__(self, name, age):
        self.__name = name
        if(age >= 0 and age <= 100):
            self.__age = age
        else:
            age = 18
    # 定義訪問屬性的get方法
    def get_name(self):
        return self.__name
    def get_age(self):
        return self.__age
    # 定義訪問屬性的get方法
    def set_name(self, name):
        self.__name = name;
    def set_age(self, age):
        if(age >= 0 and age <= 100):
            self.__age = age
        else:
            print("您設定的年齡不合法,還原預設值")
# 建立物件
p = Person("張小凡", 19)
p.set_age(1000)
print(p.get_age())
# 執行結果
~ 您設定的年齡不合法,還原預設值
~ 19

``` 以上執行的結果,才是我們想要的結果

什麼是封裝 封裝,就是將物件敏感的資料封裝在類的內部,不讓外界直接訪問,但是提供了讓外界可以間接訪問的set/get方法,我們可以在set/get方法中新增資料的訪問限制邏輯,完善我們的程式碼,提高程式的健壯性

3. 封裝的高階使用方式

我們從上面的程式碼中已經看到了,可以通過函式操作的形式來進行屬性的處理 但是某些情況下,函式操作的形式並不是特別美妙,我們突發奇想~想再提供了set/get訪問方法的情況下,對屬性的操作還能像以前那樣直接賦值或者取值進行操作 ```

# 封裝以後通過函式操作的方式
p.set_name("tom")
print(p.get_name())
# 封裝以前通過屬性直接操作的方式
p.name = "tom"
print(p.name)

``` 將類中的set/get方法操作的形式,轉換成屬性直接操作的形式,python中是可以的

首先:給get方法上新增@property註解,(關於註解的東東,之前的函式裝飾器章節中已經有使用,可以參考一下操作原理),就可以讓get方法當成屬性進行直接取值操作了 其次,@property同時它會自動生成一個@get方法名稱.setter註解,將@get方法名稱.setter註解寫在set方法上,就可以讓set方法進行直接賦值操作了,程式碼如下: ```

class Person(object):
    def __init__(self, name):
        self.__name = name;
    @property
    def get_name(self):
        return self.__name
    @get_name.setter
    def set_name(self, name):
        self.__name = name
# 建立物件
p = Person("tom")
print(p.get_name)
p.set_name = "jerry"
print(p.get_name)
# 執行結果
~ tom
~ jerry

上述程式碼我們可以看出來,set/get方法已經可以當成普通的屬性取值賦值的方式進行快捷的操作了,我們繼續改造一下上述程式碼,讓set/get更加符合屬性取值賦值的方式

class Person(object):
    def __init__(self, name):
        self.__name = name;
    @property
    def name(self):
        return self.__name
    @name.setter
    def name(self, name):
        self.__name = name
# 建立物件
p = Person("tom")
print(p.name)
p.name= "jerry"
print(p.name)
# 執行結果
~ tom
~ jerry

``` 此時,你還能在不看原來型別定義中的get/set,區分出來name是否是Person型別的屬性還是方法呢?

封裝的註解方式,在一定程度上,能隱藏我們方法在底層的實現,讓呼叫者的操作變得簡單。但是同時也降低了程式碼的可讀性,後續的操作中,我們還是遵循原來封裝的操作方案將類的屬性私有化,提供set/get方法進行屬性的操作。

相關文章