Python 類對象和實例對象訪問屬性的區別、property屬性

類對象、實例對象、類屬性、實例屬性

下面的一個小demo,定義了一個省份類  Province ,即類對象 Province ,開闢了一塊內存空間,空間中存放着 類屬性 country 和 __init__()方法 。

繼續創建了一個實例對象 obj ,又開闢了一塊新的內存空間(創建實例化對象時,會首先調用 __new__()方法,開闢空間,然後調用__init__()方法,對剛分配的內存空間初始化,此時的self 指向了實例對象的內存空間),參數 ‘山東省’ 傳遞到 __init__方法的 name 參數上,name又賦予 self.name ,self.name 爲新創建的實例屬性,在創建的實例對象中,存在默認的__class__屬性會指向類對象(知道自己是誰生的), 實例對象 obj2 也是同樣的操作,不再細說。

可以看出來,實例對象的實例屬性自己獨有,類對象的類屬性可以被每一個實例對象所調用,即

  • 類屬性在內存中只保存一份
  • 實例屬性在每個對象中都要保存一份

我們通過類創建實例對象時,如果每個對象需要具有相同名字的屬性,那麼就使用類屬性,用一份既可

因此,我們將共享的屬性放在類屬性中,而獨有的屬性放在實例屬性中

#!/usr/bin/env python
# _*_ coding:utf-8 _*_

class Province(object):  # 類也是一個對象  類對象
    # 類屬性 類空間內函數外定義的屬性
    country = '中國'

    def __init__(self, name):
        # 實例屬性
        self.name = name


# 創建了一個實例對象
obj = Province('山東省')
obj2 = Province('山西省')
# 直接訪問實例屬性
print(obj.name)
print(obj2.name)
# 直接訪問類屬性
Province.country

 

類方法、實例方法、靜態方法

三種方法在內存中都歸屬於類,區別在於調用方式不同。

  • 實例方法:由對象調用;至少一個self參數;執行實例方法時,自動將調用該方法的對象賦值給self;
  • 類方法:由類調用; 至少一個cls參數;執行類方法時,自動將調用該方法的類賦值給cls;
  • 靜態方法:由類調用;無默認參數;
#!/usr/bin/env python
# _*_ coding:utf-8 _*_

class Foo(object):
    def __init__(self, name):
        self.name = name

    def ord_func(self):
        """ 定義實例方法,至少有一個self參數 """
        # print(self.name)
        print('實例方法')

    @classmethod
    def class_func(cls):
        """ 定義類方法,至少有一個cls參數 """
        print('類方法')

    @staticmethod
    def static_func():
        """ 定義靜態方法 ,無默認參數"""
        print('靜態方法')


f = Foo("中國")
# 調用實例方法
f.ord_func()

# 調用類方法
Foo.class_func()

# 調用靜態方法
Foo.static_func()

對比

  • 相同點:對於所有的方法而言,均屬於類,所以 在內存中也只保存一份
  • 不同點:方法調用者不同、調用方法時自動傳入的參數不同。

 

 

property屬性

一種用起來像是使用的實例屬性一樣的特殊屬性,可以對應於某個方法,更便於閱讀代碼

property屬性的定義和調用要注意一下幾點:

  • 定義時,在實例方法的基礎上添加 @property 裝飾器;並且僅有一個self參數
  • 調用時,無需括號

如下: 

#!/usr/bin/env python
# _*_ coding:utf-8 _*_

class Foo:
    def func(self):
        pass

    # 定義property屬性
    @property
    def prop(self):  # 不能傳遞參數,加了參數,沒法寫
        return 100


foo_obj = Foo()
foo_obj.func()  # 調用實例方法
ret = foo_obj.prop  # 自動執行 @property 修飾的 price 方法,並獲取方法的返回值
print(ret)   # 100
# foo_obj.prop() # 報錯  foo_obj.prop == 100 100()報錯

 

小案例:

對於京東商城中顯示電腦主機的列表頁面,每次請求不可能把數據庫中的所有內容都顯示到頁面上,而是通過分頁的功能局部顯示,所以在向數據庫中請求數據時就要顯示的指定獲取從第m條到第n條的所有數據 這個分頁的功能包括:

  • 根據用戶請求的當前頁和總數據條數計算出 m 和 n

  • 根據m 和 n 去數據庫中請求數據

#!/usr/bin/env python
# _*_ coding:utf-8 _*_

class Pager:
    def __init__(self, current_page):
        # 用戶當前請求的頁碼(第一頁、第二頁...)
        self.current_page = current_page
        # 每頁默認顯示10條數據
        self.per_items = 10

    @property
    def start(self):
        val = (self.current_page - 1) * self.per_items
        return val

    @property
    def end(self):
        val = self.current_page * self.per_items
        return val


p = Pager(1)
start = p.start  # 就是起始值,即:m
end = p.end  # 就是結束值,即:n
print(start)
print(end)

由此可以看出:

Python的property屬性的功能是:property屬性內部進行一系列的邏輯計算,最終將計算結果返回(面向對象思想,封裝)。

 

property屬性的有兩種方式

  • 裝飾器 即:在方法上應用裝飾器(上述案例)
  • 類屬性 即:在類中定義值爲property對象的類屬性

1 裝飾器方式

經典類(類沒有繼承object),只具有一種@property裝飾器

# ############### 定義 ###############    
class Goods:
    @property
    def price(self):
        return "laowang"
# ############### 調用 ###############
obj = Goods()
result = obj.price  # 自動執行 @property 修飾的 price 方法,並獲取方法的返回值
print(result)

 

新式類(類繼承object),具有三種@property裝飾器

  • 經典類中的屬性只有一種訪問方式,其對應被 @property 修飾的方法
  • 新式類中的屬性有三種訪問方式,並分別對應了三個被@property、@方法名.setter、@方法名.deleter修飾的方法
#coding=utf-8
# ############### 定義 ###############
class Goods:
    """python3中默認繼承object類
        以python2、3執行此程序的結果不同,因爲只有在python3中才有@xxx.setter  @xxx.deleter
    """
    @property            
    def price(self):          # 必須在第一個 剩下兩個順序無所謂
        print('@property')

    @price.setter
    def price(self, value):
        print('@price.setter')
 
    @price.deleter    
    def price(self):        # 不常用
        print('@price.deleter')

# ############### 調用 ###############
obj = Goods()
obj.price          # 自動執行 @property 修飾的 price 方法,並獲取方法的返回值
obj.price = 123    # 自動執行 @price.setter 修飾的 price 方法,並將  123 賦值給方法的參數value
del obj.price      # 自動執行 @price.deleter 修飾的 price 方法

由於新式類中具有三種訪問方式,我們可以根據它們幾個屬性的訪問特點,分別將三個方法定義爲對同一個屬性:獲取、修改、刪除 案例如下:

class Goods(object):

    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   # 修改商品原價
del obj.price     # 刪除商品原價

 

2 類屬性方式,創建值爲property對象的類屬性

當使用類屬性的方式創建property屬性時,經典類新式類無區別

class Foo:
    def get_bar(self):
        return 'laowang'

    BAR = property(get_bar)

obj = Foo()
reuslt = obj.BAR  # 自動調用get_bar方法,並獲取方法的返回值
print(reuslt)

property方法中有個四個參數

  • 第一個參數是方法名,調用 對象.屬性 時自動觸發執行方法
  • 第二個參數是方法名,調用 對象.屬性 = XXX 時自動觸發執行方法
  • 第三個參數是方法名,調用 del 對象.屬性 時自動觸發執行方法(不常用)
  • 第四個參數是字符串,調用 對象.屬性.__doc__ ,此參數是該屬性的描述信息(不常用)

小demo

#coding=utf-8
class Foo(object):
    def get_bar(self):
        print("getter...")
        return 'laowang'

    def set_bar(self, value): 
        """必須兩個參數"""
        print("setter...")
        return 'set value' + value

    def del_bar(self):
        print("deleter...")
        return 'laowang'

    BAR = property(get_bar, set_bar, del_bar, "description...")

obj = Foo()

obj.BAR  # 自動調用第一個參數中定義的方法:get_bar
obj.BAR = "alex"  # 自動調用第二個參數中定義的方法:set_bar方法,並將“alex”當作參數傳入
desc = Foo.BAR.__doc__  # 自動獲取第四個參數中設置的值:description...
print(desc)
del obj.BAR  # 自動調用第三個參數中定義的方法:del_bar方法

實例:

class Goods(object):

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

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

    def set_price(self, value):
        self.original_price = value

    def del_price(self):
        del self.original_price

    PRICE = property(get_price, set_price, del_price, '價格屬性描述...')

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

 

綜上所述:

  • 定義property屬性共有兩種方式,分別是【裝飾器】和【類屬性】,【裝飾器】方式針對經典類和新式類又有所不同。
  • 通過使用property屬性,能夠簡化調用者在獲取數據的流程。

 

property屬性-應用

1 私有屬性添加getter和setter方法(其他語言私有屬性實現方法)

class Money(object):
    def __init__(self):
        self.__money = 0      #  私有屬性,不能直接獲取
 
    def getMoney(self):
        return self.__money

    def setMoney(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print("error:不是整型數字")

 

2 使用property升級getter和setter方法

class Money(object):
    def __init__(self):
        self.__money = 0

    def getMoney(self):
        return self.__money

    def setMoney(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print("error:不是整型數字")

    # 定義一個屬性,當對這個money設置值時調用setMoney,當獲取值時調用getMoney
    money = property(getMoney, setMoney)  

a = Money()
a.money = 100  # 調用setMoney方法
print(a.money)  # 調用getMoney方法
#100

 

3 使用property取代getter和setter方法

重新實現一個屬性的設置和讀取方法,可做邊界判定

class Money(object):
    def __init__(self):
        self.__money = 0

    # 使用裝飾器對money進行裝飾,那麼會自動添加一個叫money的屬性,當調用獲取money的值時,調用裝飾的方法
    @property
    def money(self):
        return self.__money

    # 使用裝飾器對money進行裝飾,當對money設置值時,調用裝飾的方法
    @money.setter
    def money(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print("error:不是整型數字")

a = Money()
a.money = 100
print(a.money)

 

 

 

 

 

 

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