最近學(xué)到面向?qū)ο罅耍杏X到Python這方面的語法也有點神奇,這里專門歸納一下Python面向?qū)ο笾?strong>我覺得比較重要的筆記。
10年積累的成都做網(wǎng)站、網(wǎng)站設(shè)計經(jīng)驗,可以快速應(yīng)對客戶對網(wǎng)站的新想法和需求。提供各種問題對應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識你,你也不認(rèn)識我。但先網(wǎng)站制作后付款的網(wǎng)站建設(shè)流程,更有德興免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
Python里面向?qū)ο缶幊痰?strong>類屬性和實例屬性與普通情況下全局變量和局部變量還是有相似之處的:
我們可以通過實例名訪問實例屬性和類屬性,就像上面例子中的new_instance.test_var
和new_instance.pub_var
。就像局部作用域能訪問局部變量和全局變量。
我們可以通過創(chuàng)建賦值讓實例對象有 與類屬性同名 的屬性,比如new_instance.pub_var = 'own property'
就會在new_instance本身創(chuàng)建一個屬性,從而屏蔽 通過實例名對于類屬性的訪問。而在沒有g(shù)lobal關(guān)鍵字的情況下,局部變量在局部作用域被創(chuàng)建賦值后也會屏蔽同名的全局變量。
對于第2點可以試試通過實例名來刪除類屬性:
class Test:
pub_var = 'Hello' # 類屬性
def __init__(self):
pass
new_instance = Test()
print(new_instance.pub_var) # Hello
del new_instance.pub_var # AttributeError: pub_var
很明顯通過實例名是無法刪除類屬性pub_var
的,但如果我們給實例創(chuàng)建賦值一個同名屬性呢?
# 緊接上面例子
new_instance = Test()
print(new_instance.pub_var) # 此時訪問了類屬性,輸出:Hello
new_instance.pub_var = 'Hello World'
print(new_instance.pub_var) # 此時訪問的是實例自身屬性,輸出:Hello World
del new_instance.pub_var # 刪除了實例自身屬性,一切正常
print(new_instance.pub_var) # 實例在自身找不到同名屬性了,就又指向了類屬性,輸出:Hello
del Test.pub_var # 可以通過類名刪除類屬性
print(new_instance.pub_var) # 在實例自身和類里都找不到pub_var屬性了,返回no attribute異常
可以看出通過實例名可以刪除實例自身的屬性,當(dāng)實例在自身上找不到屬性時,就會轉(zhuǎn)而尋找類屬性。類比局部變量和全局變量,局部變量也是先在局部作用域找,如果沒找到就去找同名的全局變量。
通過類名,可以在很多地方訪問到類屬性,并可以進(jìn)行修改(比如在實例的方法函數(shù)里就可以直接通過類名訪問。
class Test:
def __init__(self, val):
self.__secret_value = val
def my_value(self):
return self.__secret_value
new_instance = Test(233)
print(new_instance.my_value())
上面例子中我們將類實例化為對象 new_instance
(用類創(chuàng)建對象),該對象得到了my_value()
方法,同時Python自動調(diào)用了__init__
給 new_instance
綁定了屬性__value
并進(jìn)行賦值。
當(dāng)我們要獲得值的時候就要調(diào)用實例對象new_instance
的my_value()
方法:
print(new_instance.my_value())
如果 使用了@property修飾器 呢?
class Test:
def __init__(self, val):
self.__secret1value = val
@property
def my_value(self):
return self.__secret1value
new_instance = Test(233)
print(new_instance.my_value) # 末尾不用再加()了,因為這不是一個可調(diào)用的方法,而是一個屬性
@property的作用正如其名,將實例的方法轉(zhuǎn)換為了屬性,上面例子中原本的方法my_value()
被修飾后只用訪問對應(yīng)的屬性名my_value
我們就能獲得同樣的返回值。
這個修飾器本質(zhì)上其實仍然是對方法的調(diào)用,咱改一下上面的例子:
class Test:
def __init__(self, val):
self.__value = val
@property
def my_value(self):
print('Here I am.') # 調(diào)用方法的時候輸出'Here I am.'
return self.__value
new_instance = Test(233) # 實例化的時候沒有任何輸出
print(new_instance.my_value) # 訪問這個屬性時實際上內(nèi)部調(diào)用了my_value()的方法,因為輸出了 'Here I am.' 和 233
再進(jìn)一步想想,new_instance.my_value
這個屬性取的其實就是原本my_value()
方法的return
返回值。
接著再鉆一下,原本my_value()
這個方法 只是讀取了屬性__value
并返回 ,并沒有進(jìn)行修改。沒錯,這也意味著:
被@property修飾后產(chǎn)生的屬性是只讀的
可以試試修改這個屬性:
new_instance.my_value = 450
# AttributeError: can't set attribute
很明顯,my_value
現(xiàn)在對于new_instance
而言是只讀屬性。由此,在用戶不知道原方法my_value()
操作的私有屬性時能起一定的保護(hù)作用。
作為實例對象的一個屬性,其和方法有一定的區(qū)別,我們調(diào)用實例對象的方法時候是可以傳參的,但屬性不行,這意味著@property
修飾的方法只能有self
一個參數(shù)(否則訪問屬性的時候會報參數(shù)缺少的異常)。
另外一個實例對象是有其他屬性的,@property等修飾器修飾的方法也好,普通的實例方法也好,一定不要和已有的屬性重名。舉個例子:
class Test:
def __init__(self, val):
self.__secret1value = val
self.my_value = 'pre'
@property
def my_value(self):
print('Here I am.')
return self.__secret1value
new_instance = Test(233)
# self.my_value='pre' -> AttributeError: can't set attribute
# 其實從這里還能看出來,@property修飾先于實例初始化進(jìn)行,導(dǎo)致拋出的異常是無法修改屬性值
上面我們嘗試修改了@property修飾而成的屬性,但返回了can't set attribute
。其實是因為咱沒有定義這個屬性的寫入(setter)方法.
需要修改這個@property屬性的話,我們就需要請出附贈的修飾器@已被修飾的方法名.setter
了:
class Test:
def __init__(self, val):
self.__secret1value = val
@property
def my_value(self):
return self.__secret1value
@my_value.setter # [被@property修飾的方法名].setter
def my_value(self, val2set): # 這里的方法仍然是my_value
self.__secret1value = val2set
new_instance = Test(233)
print(new_instance.my_value) # 233
new_instance.my_value = 450 # 此時這個屬性有修改(setter)的方法了,我們可以修改它
print(new_instance.my_value) # 450
和@property
修飾的方法不同,@已被修飾的方法名.setter
修飾的方法除了self
外還可以接受第二個參數(shù),接收的是修改的值。在上面例子中我將這個形參命名為了val2set
。
有了讀和寫,還差什么呢——刪!
和setter類似,@property修飾器還贈有@已被修飾的方法名.deleter
修飾器,其修飾的方法和@property修飾的一樣都只接受一個參數(shù)self
:
class Test:
def __init__(self, val):
self.__secret1value = val
@property
def my_value(self):
return self.__secret1value
@my_value.deleter # [被@property修飾的方法名].deleter
def my_value(self): # 注意這里只接受一個self參數(shù)
del self.__secret1value
new_instance = Test(233)
print(new_instance.my_value) # 233
try:
new_instance.my_value = 450
except:
print('Set failed.') # Set failed.
del new_instance.my_value
print(new_instance.my_value)
# AttributeError: 'Test' object has no attribute '_Test__secret1value'
這個例子中咱沒有定義my_value
屬性的setter
方法,所以其無法被修改。但因為定義了deleter
方法,在用del
對屬性進(jìn)行移除的時候會通過deleter調(diào)用原方法,原方法中用del去刪掉實例對象自己的私有屬性,達(dá)成刪除的目的。
總結(jié)一下修飾器@property
相關(guān)的著重點:
@property
讓實例方法作為屬性被訪問。
這一類修飾器能在一定程度上保護(hù)實例的私有屬性不被隨意修改(之所以是說一定程度上,是因為一旦用戶知道了私有屬性名就可以用_類名__私有屬性名
進(jìn)行訪問,Python,很神奇吧 ( ̄ε(# ̄)☆╰╮o( ̄皿 ̄///)) 。
實例的方法名不要和自身其他方法或?qū)傩?/strong>重名。
@property
和@已被修飾的方法名.deleter
修飾的方法只能接受self
一個參數(shù);而@已被修飾的方法名.setter
修飾的方法除了self
外可以接受第二個參數(shù)作為被修改的值。
除了@property
這種修飾器寫法外,Python還提供了內(nèi)置方法 property(getter,setter,deleter,doc)
來達(dá)成相同的效果:
class Test:
pub_var = 'Hello'
def __init__(self, val):
self.__secret1value = val
self.test_val = 'World'
def __getter(self):
return self.__secret1value
def __deleter(self):
del self.__secret1value
my_value = property(__getter, None, __deleter)
new_instance = Test(233)
print(new_instance.test_var) # World (通過實例名訪問實例屬性)
print(Test.pub_var) # Hello (嘗試通過類名訪問類屬性)
print(new_instance.pub_var) # Hello (嘗試通過實例訪問類屬性)
print(Test.my_value) # (這個其實也是類屬性,通過類名能訪問到)
print(new_instance.my_value) # 233 (通過實例名訪問類屬性,間接調(diào)用了__getter,綁定上了self
property(getter,setter,deleter,doc)
接受的四個參數(shù)分別為讀方法
,寫方法
,刪方法
和描述信息
,這四個參數(shù)都是可以留空的,當(dāng)getter也留空時訪問這個屬性會提示unreadable attribute
。
通過上面的例子可以看出,property
方法返回的是類屬性
,而實例對象是可以訪問到類屬性的,所以當(dāng)我們訪問new_instance.my_value
的時候就是在綁定實例的基礎(chǔ)上訪問getter方法,但一旦對new_instance.my_value
屬性進(jìn)行寫或刪操作后就給new_instance
自身創(chuàng)建了一個屬性my_value
,再訪問就不是指向類屬性了。(詳細(xì)看實例屬性和類屬性的訪問 )
再回去看實例屬性和類屬性的訪問,加上這個內(nèi)置方法property()
,于是就有了奇妙的騷操作:
class Test:
def __init__(self, val):
Test.test_var = property(lambda self: val) # 閉包寫法
new_instance = Test(233)
print(new_instance.test_var) # 233
這個操作中首先利用了一個匿名函數(shù)充當(dāng)getter方法,傳入property
第一個參數(shù),然后property會返回一個類屬性。
因為在實例方法里我們也能訪問到類名,于是我們將這個property類屬性賦值給Test.test_var
,test_var
便是一個名副其實的類屬性了。
通過實例名new_instance
能訪問到類屬性test_var
。
從之前的這個例子可以看出,當(dāng)我們通過類名訪問property屬性時只會返回一個property object,但是通過已創(chuàng)建的實例對象來訪問就能間接調(diào)用getter方法。
在上面過程中,始終沒有new_instance
的自身屬性出現(xiàn),取而代之我們利用閉包機(jī)制保護(hù)了創(chuàng)建實例時傳入的值,我們完全無法通過實例名修改或者刪除test_var
這個屬性,真正將其保護(hù)起來了。
當(dāng)然,別讓用戶知道了類名,不然一句Test.test_var = xxx
直接破防(,,#?Д?)。
To be updated......