類對象、實例對象、類屬性、實例屬性
下面的一個小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)