一般來說,在Python中,類實(shí)例屬性的訪問規(guī)則算是比較直觀的。
創(chuàng)新互聯(lián)科技有限公司專業(yè)互聯(lián)網(wǎng)基礎(chǔ)服務(wù)商,為您提供西云機(jī)房,高防物理服務(wù)器租用,成都IDC機(jī)房托管,成都主機(jī)托管等互聯(lián)網(wǎng)服務(wù)。
但是,仍然存在一些不是很直觀的地方,特別是對(duì)C++和Java程序員來說,更是如此。
在這里,我們需要明白以下幾個(gè)地方:
1.Python是一門動(dòng)態(tài)語言,任何實(shí)體都可以動(dòng)態(tài)地添加或刪除屬性。
2.一個(gè)類定義了一個(gè)作用域。
3.類實(shí)例也引入了一個(gè)作用域,這與相應(yīng)類定義的作用域不同。
4.在類實(shí)例中查找屬性的時(shí)候,首先在實(shí)例自己的作用域中查找,如果沒有找到,則再在類定義的作用域中查找。
5.在對(duì)類實(shí)例屬性進(jìn)行賦值的時(shí)候,實(shí)際上會(huì)在類實(shí)例定義的作用域中添加一個(gè)屬性(如果還不存在的話),并不會(huì)影響到相應(yīng)類中定義的同名屬性。
下面看一個(gè)例子,加深對(duì)上述幾點(diǎn)的理解:
復(fù)制代碼
代碼如下:
class A:
cls_i = 0
cls_j
= {}
def __init__(self):
self.instance_i =
self.instance_j =
{}
在這里,我們先定義類A的一個(gè)實(shí)例a,然后再看看類A的作用域和實(shí)例a的作用域中分別有什么:
復(fù)制代碼
代碼如下:
a = A()
a.__dict__
{'instance_j': {}, 'instance_i': 0}
A.__dict__
{'__init__': , '__module__': '__main__', 'cls_i': 0, 'cls_j': {},
'__doc__': None}
我們看到,a的作用域中有instance_i和instance_j,A的作用域中有cls_i和cls_j。
我們再來看看名字查找是如何發(fā)生的:
復(fù)制代碼
代碼如下:
a.cls_i
a.instance_i
在查找cls_i的時(shí)候,實(shí)例a的作用域中是沒有它的,卻在A的作用域中找到了它;在查找instance_i的時(shí)候,直接可在a的作用域中找到它。
如果我們企圖通過實(shí)例a來修改cls_i的值,那會(huì)怎樣呢:
復(fù)制代碼
代碼如下:
a.cls_i = 1
a.__dict__
{'instance_j': {}, 'cls_i': 1, 'instance_i': 0}
A.__dict__
{'__init__': , '__module__': '__main__', 'cls_i': 0, 'cls_j': {},
'__doc__': None}
我們可以看到,a的作用域中多了一個(gè)cls_i屬性,其值為1;同時(shí),我們也注意到A作用域中的cls_i屬性的值仍然為0;在這里,我們其實(shí)是增加了一個(gè)實(shí)例屬性,并沒有修改到類屬性。
如果我們通過實(shí)例a操縱cls_j中的數(shù)據(jù)(注意不是cls_j本身),又會(huì)怎么樣呢:
復(fù)制代碼
代碼如下:
a.cls_j['a'] =
'a'
a.__dict__
{'instance_j': {}, 'cls_i': 1, 'instance_i':
0}
A.__dict__
{'__init__': , '__module__': '__main__',
'cls_i': 0, 'cls_j': {'a': 'a'}, '__doc__': None}
我們可以看到a的作用域沒有發(fā)生什么變化,但是A的作用域發(fā)生了一些變化,cls_j中的數(shù)據(jù)發(fā)生了變化。
實(shí)例的作用域發(fā)生變化,并不會(huì)影響到該類的其它實(shí)例,但是類的作用域發(fā)生變化,則會(huì)影響到該類的所有實(shí)例,包括在這之前創(chuàng)建的實(shí)例:
復(fù)制代碼
代碼如下:
A.cls_k = 0
為了把類中的變量傳遞給類中的函數(shù),我們需要用到3個(gè)特定格式
① 第一個(gè)格式 @classmethod 的中文意思就是“類方法”,@classmethod聲明了函數(shù)1是類方法,這樣才能允許函數(shù)1使用類屬性中的數(shù)據(jù)。
② 第二個(gè)格式 cls 的意思是class的縮寫。如果類方法函數(shù)1想使用類屬性(也就是類中的變量),就要寫上cls為函數(shù)1的第一個(gè)參數(shù),也就是把這個(gè)類作為參數(shù)傳給自己,這樣就能被允許使用類中的數(shù)據(jù)。
③ 第三個(gè)格式是 cls.變量 。類方法想使用類屬性的時(shí)候,需要在這些變量名稱前加上cls. 這就好比類方法和類之間的約法三章,所以但凡有任何格式錯(cuò)誤都會(huì)報(bào)錯(cuò)。
如果缺①,即缺了“@classmethod”,類方法就不能直接利用類中的屬性,于是報(bào)錯(cuò)
上一篇文章介紹了什么是python 中的self,詳情請參考:
Python面試高頻問題:self到底是什么
簡單的說self是類(Class)實(shí)例化的對(duì)象。
面試中還有一個(gè)問題經(jīng)常被提及,那就是——什么是cls呢?
cls 是類(或子類)本身,取決于調(diào)用的是哪個(gè)類。
看下面的實(shí)例
輸出:
我們也可以通過實(shí)例的方法調(diào)用class method,例如:
同樣輸出
輸出:
使用cls 作為方法參數(shù)時(shí),通常該方法需要由@classmethod 修飾,@classmethod修飾的方法表示的是類方法。這里需要注意cls就是一個(gè)標(biāo)識(shí),你可以把他寫成abc,bcd都是可以的,只是出于習(xí)慣寫成了cls。
既然cls是一個(gè)類,那么我們就可以使用他來進(jìn)行實(shí)例化,具體代碼如下:
輸出:
可以看到通過obj1=cls()和obj2=cls(),成功創(chuàng)建了兩個(gè)實(shí)例,分別是0x000000690A1E0C08和0x000000690A1E0C48,而這兩個(gè)實(shí)例的類型都是MyClass3'
另外cls 還可以在python類中的方法 __new__里實(shí)現(xiàn),通常定義為:
最后我們可以簡單地總結(jié)一下self 和 cls:簡單的說self是類(Class)實(shí)例化的對(duì)象。
cls 是類(或子類)本身。我們也可以方便地理解self表示實(shí)例的,cls則表示類的!
cls是class的縮寫。
class A:
member = "this is a test."
def __init__(self):
pass
@classmethod
def Print1(cls):
#榪欎釜鏄被鏂規(guī)硶
print "print 1: ", cls.member
def Print2(self):
print "print 2: ", self.member
@classmethod
def Print3(paraTest):
print "print 3: ", paraTest.member
a = A()
A.Print1() #相當(dāng)于Print1(A)
a.Print2() #相當(dāng)于Print2(a), 請注意@classmethod
A.Print3()
可以看出來,python在通過“.”調(diào)用成員函數(shù)的時(shí)候,會(huì)將“.”前面的東西當(dāng)作函數(shù)的第一個(gè)參數(shù)調(diào)用。
而且pyhon并不關(guān)心我們把類的成員函數(shù)的第一個(gè)參數(shù)的名稱是什么,我們可以用任意的名稱,可以看Print3的定義就知道了。
這個(gè)方法是python線程中使用的。
對(duì)于可重入鎖,必須用acquire()獲得鎖