python中創(chuàng)建實(shí)例屬性
雖然可以通過Person類創(chuàng)建出xiaoming颊艳、xiaohong等實(shí)例丧靡,但是這些實(shí)例看上除了地址不同外,沒有什么其他不同籽暇。在現(xiàn)實(shí)世界中,區(qū)分xiaoming饭庞、xiaohong要依靠他們各自的名字戒悠、性別、生日等屬性舟山。
如何讓每個(gè)實(shí)例擁有各自不同的屬性绸狐?由于Python是動(dòng)態(tài)語(yǔ)言卤恳,對(duì)每一個(gè)實(shí)例,都可以直接給他們的屬性賦值寒矿,例如亮元,給xiaoming這個(gè)實(shí)例加上name募书、gender和birth屬性:
xiaoming = Person()
xiaoming.name = 'Xiao Ming'
xiaoming.gender = 'Male'
xiaoming.birth = '1990-1-1'
給xiaohong加上的屬性不一定要和xiaoming相同:
xiaohong = Person()
xiaohong.name = 'Xiao Hong'
xiaohong.school = 'No. 1 High School'
xiaohong.grade = 2
實(shí)例的屬性可以像普通變量一樣進(jìn)行操作:
xiaohong.grade = xiaohong.grade + 1
列子:
請(qǐng)創(chuàng)建包含兩個(gè) Person 類的實(shí)例的 list,并給兩個(gè)實(shí)例的 name 賦值,然后按照 name 進(jìn)行排序卖局。
class Person(object):
pass
p1 = Person()
p1.name = 'Bart'
p2 = Person()
p2.name = 'Adam'
p3 = Person()
p3.name = 'Lisa'
L1 = [p1, p2, p3]
L2 = sorted(L1,lambda p1,p2:cmp(p1.name,p2.name))
print L2[0].name
print L2[1].name
print L2[2].name
python中初始化實(shí)例屬性
雖然我們可以自由地給一個(gè)實(shí)例綁定各種屬性,但是辈灼,現(xiàn)實(shí)世界中甫匹,一種類型的實(shí)例應(yīng)該擁有相同名字的屬性。例如蓝牲,Person類應(yīng)該在創(chuàng)建的時(shí)候就擁有name
趟脂、gender
和birth
屬性,怎么辦例衍?
在定義 Person 類時(shí)昔期,可以為Person類添加一個(gè)特殊的init()
方法,當(dāng)創(chuàng)建實(shí)例時(shí)佛玄,init()
方法被自動(dòng)調(diào)用硼一,我們就能在此為每個(gè)實(shí)例都統(tǒng)一加上以下屬性:
class Person(object):
def __init__(self, name, gender, birth):
self.name = name
self.gender = gender
self.birth = birth
init()
方法的第一個(gè)參數(shù)必須是 self
(也可以用別的名字,但建議使用習(xí)慣用法)翎嫡,后續(xù)參數(shù)則可以自由指定欠动,和定義函數(shù)沒有任何區(qū)別。
相應(yīng)地惑申,創(chuàng)建實(shí)例時(shí)具伍,就必須要提供除 self
以外的參數(shù):
xiaoming = Person('Xiao Ming', 'Male', '1991-1-1')
xiaohong = Person('Xiao Hong', 'Female', '1992-2-2')
有了init()
方法,每個(gè)Person實(shí)例在創(chuàng)建時(shí)圈驼,都會(huì)有name
人芽、gender
和 birth
這3個(gè)屬性,并且绩脆,被賦予不同的屬性值萤厅,訪問屬性使用.操作符:
print xiaoming.name
# 輸出 'Xiao Ming'
print xiaohong.birth
# 輸出 '1992-2-2'
要特別注意的是,初學(xué)者定義init()
方法常常忘記了 self
參數(shù):
>>> class Person(object):
... def __init__(name, gender, birth):
... pass
...
>>> xiaoming = Person('Xiao Ming', 'Male', '1990-1-1')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() takes exactly 3 arguments (4 given)
這會(huì)導(dǎo)致創(chuàng)建失敗或運(yùn)行不正常靴迫,因?yàn)榈谝粋€(gè)參數(shù)name被Python解釋器傳入了實(shí)例的引用惕味,從而導(dǎo)致整個(gè)方法的調(diào)用參數(shù)位置全部沒有對(duì)上。
例子:
要定義關(guān)鍵字參數(shù)玉锌,使用**kw
名挥;
除了可以直接使用self.name = 'xxx'
設(shè)置一個(gè)屬性外,還可以通過 setattr(self, 'name', 'xxx')
設(shè)置屬性主守。
參考代碼:
class Person(object):
def __init__(self, name, gender, birth, **kw):
self.name = name
self.gender = gender
self.birth = birth
for k, v in kw.iteritems():
setattr(self, k, v)
xiaoming = Person('Xiao Ming', 'Male', '1990-1-1', job='Student')
print xiaoming.name
print xiaoming.job
python中訪問限制
Python對(duì)屬性權(quán)限的控制是通過屬性名來實(shí)現(xiàn)的禀倔,如果一個(gè)屬性由雙下劃線開頭(__)
榄融,該屬性就無法被外部訪問【群看例子:
class Person(object):
def __init__(self, name):
self.name = name
self._title = 'Mr'
self.__job = 'Student'
p = Person('Bob')
print p.name
# => Bob
print p._title
# => Mr
print p.__job
# => Error
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute '__job'
可見愧杯,只有以雙下劃線開頭的"__job"
不能直接被外部訪問。
但是鞋既,如果一個(gè)屬性以"__xxx__"
的形式定義力九,那它又可以被外部訪問了,以"__xxx__"
定義的屬性在Python的類中被稱為特殊屬性涛救,有很多預(yù)定義的特殊屬性可以使用畏邢,通常我們不要把普通屬性用"__xxx__"
定義。
以單下劃線開頭的屬性"_xxx"
雖然也可以被外部訪問检吆,但是舒萎,按照習(xí)慣,他們不應(yīng)該被外部訪問蹭沛。
例子:
class Person(object):
def __init__(self, name, score):
self.name = name
self.__score = score
p = Person('Bob', 59)
print p.name
print p.__score
運(yùn)行結(jié)果:
Traceback (most recent call last):
File "index.py", line 9, in
print p.__score
AttributeError: 'Person' object has no attribute '__score'
Bob
python中創(chuàng)建類屬性
類是模板臂寝,而實(shí)例則是根據(jù)類創(chuàng)建的對(duì)象。
綁定在一個(gè)實(shí)例上的屬性不會(huì)影響其他實(shí)例摊灭,但是咆贬,類本身也是一個(gè)對(duì)象,如果在類上綁定一個(gè)屬性帚呼,則所有實(shí)例都可以訪問類的屬性掏缎,并且,所有實(shí)例訪問的類屬性都是同一個(gè)煤杀!也就是說眷蜈,實(shí)例屬性每個(gè)實(shí)例各自擁有,互相獨(dú)立沈自,而類屬性有且只有一份.
定義類屬性可以直接在 class 中定義:
class Person(object):
address = 'Earth'
def __init__(self, name):
self.name = name
因?yàn)轭悓傩允侵苯咏壎ㄔ陬惿系淖萌澹裕L問類屬性不需要?jiǎng)?chuàng)建實(shí)例枯途,就可以直接訪問:
print Person.address
# => Earth
對(duì)一個(gè)實(shí)例調(diào)用類的屬性也是可以訪問的忌怎,所有實(shí)例都可以訪問到它所屬的類的屬性:
p1 = Person('Bob')
p2 = Person('Alice')
print p1.address
# => Earth
print p2.address
# => Earth
由于Python是動(dòng)態(tài)語(yǔ)言,類屬性也是可以動(dòng)態(tài)添加和修改的:
Person.address = 'China'
print p1.address
# => 'China'
print p2.address
# => 'China'
因?yàn)轭悓傩灾挥幸环堇乙模粤裥ィ?dāng)Person類的address改變時(shí),所有實(shí)例訪問到的類屬性都改變了晚岭。
例子:
請(qǐng)給Person
類添加一個(gè)類屬性 count
插掂,每創(chuàng)建一個(gè)實(shí)例,count
屬性就加 1,這樣就可以統(tǒng)計(jì)出一共創(chuàng)建了多少個(gè) Person
的實(shí)例辅甥。
由于創(chuàng)建實(shí)例必定會(huì)調(diào)用__init__()
方法,所以在這里修改類屬性 count
很合適燎竖。
參考代碼:
class Person(object):
count = 0
def __init__(self, name):
Person.count = Person.count + 1
self.name = name
p1 = Person('Bob')
print Person.count
# => 1
p2 = Person('Alice')
print Person.count
# => 2
p3 = Person('Tim')
print Person.count
# => 3
python中類屬性和實(shí)例屬性名字沖突怎么辦
修改類屬性會(huì)導(dǎo)致所有實(shí)例訪問到的類屬性全部都受影響璃弄,但是,如果在實(shí)例變量上修改類屬性會(huì)發(fā)生什么問題呢构回?
class Person(object):
address = 'Earth'
def __init__(self, name):
self.name = name
p1 = Person('Bob')
p2 = Person('Alice')
print 'Person.address = ' + Person.address
p1.address = 'China'
print 'p1.address = ' + p1.address
print 'Person.address = ' + Person.address
print 'p2.address = ' + p2.address
結(jié)果如下:
Person.address = Earth
p1.address = China
Person.address = Earth
p2.address = Earth
我們發(fā)現(xiàn)夏块,在設(shè)置了p1.address = 'China'
后,p1訪問 address 確實(shí)變成了 'China'纤掸,但是脐供,Person.address和p2.address仍然是'Earch',怎么回事借跪?
原因是 p1.address = 'China'并沒有改變 Person 的 address政己,而是給p1這個(gè)實(shí)例綁定了實(shí)例屬性address,對(duì)p1來說掏愁,它有一個(gè)實(shí)例屬性address(值是'China')歇由,而它所屬的類Person也有一個(gè)類屬性address,所以:
訪問p1.address時(shí)果港,優(yōu)先查找實(shí)例屬性沦泌,返回'China'。
訪問 p2.address 時(shí)辛掠,p2沒有實(shí)例屬性address谢谦,但是有類屬性address,因此返回'Earth'萝衩。
可見回挽,當(dāng)實(shí)例屬性和類屬性重名時(shí),實(shí)例屬性優(yōu)先級(jí)高欠气,它將屏蔽掉對(duì)類屬性的訪問。
當(dāng)我們把 p1 的 address 實(shí)例屬性刪除后预柒,訪問 p1.address 就又返回類屬性的值 'Earth'了:
del p1.address
print p1.address
# => Earth
可見,千萬不要在實(shí)例上修改類屬性宜鸯,它實(shí)際上并沒有修改類屬性,而是給實(shí)例綁定了一個(gè)實(shí)例屬性淋袖。
列子:
請(qǐng)把上節(jié)的 Person 類屬性 count 改為 __count鸿市,再試試能否從實(shí)例和類訪問該屬性。
class Person(object):
__count = 0
def __init__(self, name):
Person.__count = Person.__count + 1
self.name = name
print Person.__count
p1 = Person('Bob')
p2 = Person('Alice')
print Person.__count
python中定義實(shí)例方法
一個(gè)實(shí)例的私有屬性就是以__
開頭的屬性焰情,無法被外部訪問,那這些屬性定義有什么用内舟?
雖然私有屬性無法從外部訪問合敦,但是,從類的內(nèi)部是可以訪問的验游。除了可以定義實(shí)例的屬性外充岛,還可以定義實(shí)例的方法耕蝉。
實(shí)例的方法就是在類中定義的函數(shù),它的第一個(gè)參數(shù)永遠(yuǎn)是 self
蒜魄,指向調(diào)用該方法的實(shí)例本身爪膊,其他參數(shù)和一個(gè)普通函數(shù)是完全一樣的:
class Person(object):
def __init__(self, name):
self.__name = name
def get_name(self):
return self.__name
get_name(self)
就是一個(gè)實(shí)例方法,它的第一個(gè)參數(shù)是self
推盛。__init__(self, name)
其實(shí)也可看做是一個(gè)特殊的實(shí)例方法。
調(diào)用實(shí)例方法必須在實(shí)例上調(diào)用
p1 = Person('Bob')
print p1.get_name() # self不需要顯式傳入
# => Bob
在實(shí)例方法內(nèi)部榔昔,可以訪問所有實(shí)例屬性瘪菌,這樣,如果外部需要訪問私有屬性诵肛,可以通過方法調(diào)用獲得默穴,這種數(shù)據(jù)封裝的形式除了能保護(hù)內(nèi)部數(shù)據(jù)一致性外,還可以簡(jiǎn)化外部調(diào)用的難度蓄诽。
列子:
請(qǐng)給 Person 類增加一個(gè)私有屬性 __score仑氛,表示分?jǐn)?shù)闸英,再增加一個(gè)實(shí)例方法 get_grade(),能根據(jù) __score 的值分別返回 A-優(yōu)秀, B-及格, C-不及格三檔甫何。
class Person(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def get_grade(self):
if self.__score >= 80:
return 'A'
if self.__score >= 60:
return 'B'
return 'C'
p1 = Person('Bob', 90)
p2 = Person('Alice', 65)
p3 = Person('Tim', 48)
print p1.get_grade()
print p2.get_grade()
print p3.get_grade()
python中方法也是屬性
我們?cè)?class 中定義的實(shí)例方法其實(shí)也是屬性米酬,它實(shí)際上是一個(gè)函數(shù)對(duì)象:
class Person(object):
def __init__(self, name, score):
self.name = name
self.score = score
def get_grade(self):
return 'A'
p1 = Person('Bob', 90)
print p1.get_grade
# => <bound method Person.get_grade of <__main__.Person object at 0x109e58510>>
print p1.get_grade()
# => A
也就是說,p1.get_grade
返回的是一個(gè)函數(shù)對(duì)象,但這個(gè)函數(shù)是一個(gè)綁定到實(shí)例的函數(shù)跳芳,p1.get_grade()
才是方法調(diào)用竹勉。
因?yàn)榉椒ㄒ彩且粋€(gè)屬性,所以次乓,它也可以動(dòng)態(tài)地添加到實(shí)例上,只是需要用types.MethodType()
把一個(gè)函數(shù)變?yōu)橐粋€(gè)方法:
import types
def fn_get_grade(self):
if self.score >= 80:
return 'A'
if self.score >= 60:
return 'B'
return 'C'
class Person(object):
def __init__(self, name, score):
self.name = name
self.score = score
p1 = Person('Bob', 90)
p1.get_grade = types.MethodType(fn_get_grade, p1, Person)
print p1.get_grade()
# => A
p2 = Person('Alice', 65)
print p2.get_grade()
# ERROR: AttributeError: 'Person' object has no attribute 'get_grade'
# 因?yàn)閜2實(shí)例并沒有綁定get_grade
python中定義類方法
和屬性類似城看,方法也分實(shí)例方法和類方法杏慰。
在class
中定義的全部是實(shí)例方法,實(shí)例方法第一個(gè)參數(shù)self
是實(shí)例本身轰胁。
要在class中定義類方法朝扼,需要這么寫:
class Person(object):
count = 0
@classmethod
def how_many(cls):
return cls.count
def __init__(self, name):
self.name = name
Person.count = Person.count + 1
print Person.how_many()
p1 = Person('Bob')
print Person.how_many()
通過標(biāo)記一個(gè) @classmethod
,該方法將綁定到Person
類上榛斯,而非類的實(shí)例肠仪。類方法的第一個(gè)參數(shù)將傳入類本身,通常將參數(shù)名命名為cls
异旧,上面的cls.count
實(shí)際上相當(dāng)于Person.count
。
因?yàn)槭窃陬惿险{(diào)用荤崇,而非實(shí)例上調(diào)用,因此類方法無法獲得任何實(shí)例變量倚喂,只能獲得類的引用瓣戚。
例子:
如果將類屬性 count
改為私有屬性__count
,則外部無法讀取__score
子库,但可以通過一個(gè)類方法獲取,請(qǐng)編寫類方法獲得__count
值宴倍。
注意類方法需要添加 @classmethod
參考代碼:
class Person(object):
__count = 0
@classmethod
def how_many(cls):
return cls.__count
def __init__(self, name):
self.name = name
Person.__count = Person.__count + 1
print Person.how_many()
p1 = Person('Bob')
print Person.how_many()