Python編程思想(26):成員變量

李寧老師已經在「極客起源」 微信公衆號推出《Python編程思想》電子書,囊括了Python的核心技術,以及Python的主要函數庫的使用方法。讀者可以在「極客起源」 公衆號中輸入 160442 開始學習。

《Python編程思想》總目錄

《Python編程思想》專欄

 

目錄

1. 類變量和實例變量

2. 使用 property函數定義屬性


在類體內定義的變量,默認屬於類本身。如果把類當成類命名空間,那麼該類變量其實就是定義在類命名空間內的變量。

1. 類變量和實例變量

在類命名空間內定義的變量就屬於類變量,Python可以使用類來讀取、修改類變量。例如,下面代碼定義了一個 Teacher類,併爲該類定義了多個類變量。

示例代碼:class_var.py

class Teacher :
    name = '李寧'
    salary = 66666.66
    post_code = '12345678'
    def print_info (self):
        # 嘗試直接訪問類變量
#        print(name) # 報錯
        # 通過類來訪問類變量
        print(Teacher.name) # 輸出 廣州
        print(Teacher.post_code) # 輸出 510660
# 通過類來訪問Address類的類變量
print(Teacher.name)
teacher = Teacher()
teacher.print_info()
# 修改Teacher類的類變量
Teacher.name = '王軍'
Teacher.post_code = '87654321'
teacher.print_info()

在這段代碼中爲Teacher類定義了兩個類變量。

對於類變量而言,它們就是屬於在類命名空間內定義的變量,因此程序不能直接訪問這些變量,程序必須使用類名來調用類變量。不管是在全局範圍內還是函數內訪問這些類變量,都必須使用類名進行訪問。

當程序第1次調用 Teacher對象的print_info()方法輸出兩個類變量時,將會輸出這兩個類變量的初始值。接下來程序通過 Teacher類修改了兩個類變量的值,因此當程序第2次通過print_info方法輸出兩個類變量時,將會輸出這兩個類變量修改之後的值。

運行上面代碼,將會看到如下輸出結果:

李寧
李寧
12345678
王軍
87654321

實際上,Python完全允許使用對象來訪問該對象所屬類的類變量。看下面的程序:

示例代碼:class_instance_access_classvar.py

class Country:
    # 定義兩個類變量
    value1 = '中國'
    value2 = 960
    def print_info (self):
        print('info方法中: ', self.value1)
        print('info方法中: ', self.value2)

country = Country()
print(country.value1) # 中國
print(country.value2) # 960
country.print_info()
    
# 修改Country類的兩個類變量
Country.value1 = '美國'
Country.value2 = 1234
# 調用print_info()方法
country.print_info()

這段代碼的Country類中定義了兩個類變量,接下來程序完全可以使用 Country對象來訪問這兩個類變量。

在這段代碼的Country類的print_info方法中,程序使用self訪問 Country類的類變量,此時self代表print_info方法的調用者,也就是 Country對象,因此這是合法的。

在主程序代碼區,程序創建了 Country對象,並通過對象調用Country對象的value1和value2類變量,這也是合法的。

實際上,程序通過對象訪問類變量,其本質還是通過類名在訪問類變量。運行上面程序,將看到如下輸出結果:

中國
960
info方法中:  中國
info方法中:  960
info方法中:  美國
info方法中:  1234

由於通過對象訪問類變量的本質還是通過類名在訪問,因此如果類變量發生了改變,當程序訪問這些類變量時也會讀到修改之後的值。例如爲程序增加如下代碼(接前面的代碼),修改Country類的兩個類變量。

Country.value1='韓國'
Country.value2 = 250
# 調用info()方法
country.print_info()

上面程序修改了Country類的兩個類變量,然後通過對象調用print)info實例方法。運行上面代碼,將看到如下輸出結果。

info方法中:  韓國
info方法中:  250

從上面的輸出結果可以看到,通過實例訪問類變量的本質依然是通過類名在訪問。需要說明的是,Python允許通過對象訪問類變量,但如果程序通過對象嘗試對類變量賦值,此時性質就變了。Python是動態語言,賦值語句往往意味着定義新變量。因此,如果程序通過對象對類變量賦值,其實不是對“類變量賦值”,而是定義新的實例變量。

看下面的代碼:

實例代碼:new_class_var.py

class Product:
    # 定義兩個類變量
    name = 'iMac'
    price = 11000
    # 定義實例方法
    def buy(self, name, price):
        # 下面賦值語句不是對類變量賦值,而是定義新的實例變量
        self.name = name
        self.price = price

# 創建Product對象
product = Product()
product.buy('iPhone', 8000)
# 訪問product的name和price實例變量
print(product.name) # iPhone
print(product.price) # 8000
# 訪問Product的name和price類變量
print(Product.name) # iMac
print(Product.price) # 11000

Product.name = '類變量name'
Product.price = '類變量price'
# 訪問product的name和price實例變量
print(product.name)
print(product.price)

product.name = '實例變量name'
product.price = '實例變量price'
print(Product.name)
print(Product.price)

在這段代碼中通過實例對name和price變量賦值,看上去很像是對類變量賦值,但實際上不是,而是重新定義了兩個實例變量(如果第1次調用該方法)。

在這段diamante中調用 Product對象的 buy()方法之後,訪問 Product對象的name和price變量。由於該對象本身已有這兩個實例變量,因此程序將會輸出該對象的實例變量的值。接下來程序通過Product訪問它的name和price類變量,此時纔是真的訪問類變量。

運行上面程序,將看到如下輸出結果:

iPhone
8000
iMac
11000
iPhone
8000
類變量name
類變量price

如果程序通過類修改了兩個類變量的值,程序中 Product的實例變量的值也不會受到任何影響。例如如下代碼。

Product.name = '類變量name'
Product.price = '類變量price'
# 訪問product的name和price實例變量
print(product.name)
print(product.price)

運行上面代碼,可以看到如下輸出結果

iPhone
8000

上面程序開始就修改了Product類中兩個類變量的值,但這種修改對 Inventory對象的實例變量沒有任何影響。同樣,如果程序對一個對象的實例變量進行了修改,這種修改也不會影響類變量和其他對象的實例變量。例如如下代碼。

product.name = '實例變量name'
product.price = '實例變量price'
print(Product.name)
print(Product.price)

運行上面代碼,將會看到如下輸出結果。

類變量name
類變量 price

從上面的輸出結果來看,程序輸出的依然是之前對類變量所賦的兩個值。

2. 使用 property函數定義屬性

如果爲 Python類定義了getter和setter等訪問器方法,可使用 property函數將它們定義成屬性(相當於實例變量)。

property函數的語法格式如下:

property(fget=None, fset=None, fdel-None, doc=None)

從上面的語法格式可以看出,在使用 property函數時,可傳入4個參數,分別代表 getter方法、 setter方法、del方法和doc,其中doc是一個文檔字符串,用於說明該屬性。當然,開發者調用 property也可傳入0個(既不能讀,也不能寫的屬性)、1個(只讀屬性)、2個(讀寫屬性)、3個(讀寫屬性,也可刪除)和4個(讀寫屬性,也可刪除,包含文檔說明)參數。

例如,如下程序定義了一個 Rectangle類,該類使用 property函數定義了一個size屬性。

示例代碼:rectangle.py

class Rectangle:
    # 定義構造方法
    def __init__(self, width, height):
        self.width = width
        self.height = height
    # 定義set_size()函數
    def set_size (self , size):
        self.width, self.height = size
    # 定義getsize()函數
    def get_size (self):
        return self.width, self.height
     # 定義getsize()函數
    def del_size (self):
        self.width, self.height = 0, 0  
    # 使用property定義屬性
    size = property(get_size, set_size, del_size, '用於描述矩形大小的屬性')
# 訪問size屬性的說明文檔
print(Rectangle.size.__doc__)
# 通過內置的help()函數查看Rectangle.size的說明文檔
help(Rectangle.size)
rect = Rectangle(5, 6)
# 訪問rect的size屬性
print(rect.size) # (5, 6)
# 對rect的size屬性賦值
rect.size = 10, 12
# 訪問rect的width、height實例變量
print(rect.width) # 10
print(rect.height) # 12
# 刪除rect的size屬性
del rect.size
# 訪問rect的width、height實例變量
print(rect.width) # 0
print(rect.height) # 0

這段代碼使用 property函數定義了一個size屬性,在定義該屬性時一共傳入了4個參數,這意味着該屬性可讀、可寫、可刪除,也有說明文檔。所以,該程序嘗試對Rectangle對象的size屬性進行讀、寫、刪除操作,其實這種讀、寫、刪除操作分別被委託給 get_size()、 set_size()和 del_size()方法來實現。

運行上面程序,將會看到如下輸出結果:

用於描述矩形大小的屬性
Help on property:

    用於描述矩形大小的屬性

(5, 6)
10
12
0
0

在使用 property函數定義屬性時,也可根據需要只傳入少量的參數。例如,如下代碼使用property函數定義了一個讀寫屬性,該屬性不能刪除。

class Person :
    def __init__ (self, first, last):
        self.first = first
        self.last = last
    def get_fullname(self):
        return self.first + ',' + self.last
    def set_fullname(self, fullname):
        first_last = fullname.rsplit(',');
        self.first = first_last[0]
        self.last = first_last[1]
    # 使用property()函數定義fullname屬性,只傳入2個參數
    # 該屬性是一個讀寫屬性,但不能刪除
    fullname = property(get_fullname, set_fullname)
p = Person('子龍', '趙')
# 訪問fullname屬性
print(p.fullname)
# 對fullname屬性賦值
p.fullname = '雲長,關'
print(p.first)
print(p.last)

在這段代碼中使用 property函數定義了 fullname屬性,該程序使用 property()函數時只傳入兩個參數,分別作爲 getter和 setter方法,因此該屬性是一個讀寫屬性,不能刪除。

運行這段代碼,將看到如下輸出結果:

子龍,趙
雲長
關

-----------------支持作者請轉發本文,也可以加李寧老師微信:unitymarvel,或掃描下面二維碼加微信--------

歡迎關注  極客起源  微信公衆號,更多精彩視頻和文章等着你哦!

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章