中文學(xué)習(xí)網(wǎng)站: 廖雪峰的官網(wǎng)網(wǎng)站https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000
2. OOP 面向?qū)ο缶幊?/h2>
類(lèi)的訪問(wèn)限制
- class中以雙下劃線開(kāi)頭的變量(如:_private_val)是私有變量, 不能被外部直接訪問(wèn), 但以雙下劃線開(kāi)頭和結(jié)尾的變量除外(如_name),這種是特殊變量, 能夠被外部直接訪問(wèn)
- 私有變量的實(shí)現(xiàn)原理是: python解釋器將私有變量改成了_Classname__private_val. 所以如果直接訪問(wèn) _Classname__private_val是可以訪問(wèn)的, 但強(qiáng)烈不推薦這樣做, 因?yàn)椴煌姹镜腜ython解釋器可能會(huì)把__private_val改成不同的變量名.
- 然而直接用a.__private_val=1, 這種賦值是能通過(guò)的, 但是注意:這是新建了一個(gè)名為_(kāi)_private_val的成員變量, 并不是原來(lái)的私有變量(已經(jīng)被改名成了_Classname__private_val)
- _slots_ 限制用戶能對(duì)類(lèi)添加的屬性.
class Student(object):
__slots__ = ('name', 'age') # 用tuple定義允許綁定的屬性名稱
>>> s.age = 25 # 綁定屬性'age'
>>> s.score = 99 # 綁定屬性'score'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'
注意:_slots_定義的屬性僅對(duì)當(dāng)前類(lèi)實(shí)例起作用饮笛,對(duì)繼承的子類(lèi)是不起作用的
給實(shí)例綁定方法
- 通常是直接對(duì)類(lèi)添加方法, 但python支持只對(duì)實(shí)例綁定方法
>>> def set_age(self, age): # 定義一個(gè)函數(shù)作為實(shí)例方法
... self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s) # 給實(shí)例綁定一個(gè)方法
>>> s.set_age(25) # 調(diào)用實(shí)例方法
>>> s.age # 測(cè)試結(jié)果
25
- 實(shí)例屬性只對(duì)當(dāng)前實(shí)例生效, 類(lèi)屬性對(duì)類(lèi)和所有類(lèi)的實(shí)例生效
@property裝飾器
- 讀接口用@property裝飾, 寫(xiě)接口用@score.setter裝飾,score是需要裝飾的接口名字.
- 盡管是函數(shù), 但調(diào)用時(shí)不需要帶括號(hào), 把它當(dāng)做一個(gè)變量進(jìn)行訪問(wèn). 如果沒(méi)有定義寫(xiě)接口, 則該"變量"只讀, 不可寫(xiě).
- 注意: 必須要先定義@property才能定義寫(xiě)接口.
class Student(object):
#get接口
@property
def score(self):
return self._score
#set接口
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
>>> s = Student()
>>> s.score = 60 # OK截珍,實(shí)際轉(zhuǎn)化為s.set_score(60)
>>> s.score # OK瓶逃,實(shí)際轉(zhuǎn)化為s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!
實(shí)例屬性與類(lèi)屬性的區(qū)別
class Student(object):
__slots__ = ('name', 'age') # 用tuple定義允許綁定的屬性名稱
>>> s.age = 25 # 綁定屬性'age'
>>> s.score = 99 # 綁定屬性'score'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'
注意:_slots_定義的屬性僅對(duì)當(dāng)前類(lèi)實(shí)例起作用饮笛,對(duì)繼承的子類(lèi)是不起作用的
>>> def set_age(self, age): # 定義一個(gè)函數(shù)作為實(shí)例方法
... self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s) # 給實(shí)例綁定一個(gè)方法
>>> s.set_age(25) # 調(diào)用實(shí)例方法
>>> s.age # 測(cè)試結(jié)果
25
class Student(object):
#get接口
@property
def score(self):
return self._score
#set接口
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
>>> s = Student()
>>> s.score = 60 # OK截珍,實(shí)際轉(zhuǎn)化為s.set_score(60)
>>> s.score # OK瓶逃,實(shí)際轉(zhuǎn)化為s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!
實(shí)例屬性是在類(lèi)中定義,可以通過(guò)類(lèi)或?qū)嵗L問(wèn)炭分,但如果給實(shí)例綁定一個(gè)同名的屬性,實(shí)例屬性會(huì)把類(lèi)屬性覆蓋掉剑肯。
如下例子中捧毛,當(dāng)調(diào)用s.name = 'Michael'時(shí),s中其實(shí)有兩個(gè)同名的name屬性让网,一個(gè)是類(lèi)屬型("Student")呀忧,一個(gè)是實(shí)例屬性("Michael"),實(shí)例屬性會(huì)將類(lèi)屬性覆蓋掉溃睹,直到它被刪掉而账。
>>> class Student(object):
... name = 'Student'
...
>>> s = Student() # 創(chuàng)建實(shí)例s
>>> print(s.name) # 打印name屬性,因?yàn)閷?shí)例并沒(méi)有name屬性因篇,所以會(huì)繼續(xù)查找class的name屬性
Student
>>> print(Student.name) # 打印類(lèi)的name屬性
Student
>>> s.name = 'Michael' # 給實(shí)例綁定name屬性
>>> print(s.name) # 由于實(shí)例屬性優(yōu)先級(jí)比類(lèi)屬性高泞辐,因此,它會(huì)屏蔽掉類(lèi)的name屬性
Michael
>>> print(Student.name) # 但是類(lèi)屬性并未消失竞滓,用Student.name仍然可以訪問(wèn)
Student
>>> del s.name # 如果刪除實(shí)例的name屬性
>>> print(s.name) # 再次調(diào)用s.name咐吼,由于實(shí)例的name屬性沒(méi)有找到,類(lèi)的name屬性就顯示出來(lái)了
Student
綁定方法與_slots_
給實(shí)例綁定一個(gè)方法商佑,注意:
- 綁定實(shí)例方法不能直接進(jìn)行函數(shù)賦值汽烦,因?yàn)樾枰獋魅雽?duì)象實(shí)例。
- 另外莉御,實(shí)例方法只對(duì)單個(gè)實(shí)例起作用撇吞。
>>> def set_age(self, age): # 定義一個(gè)函數(shù)作為實(shí)例方法
... self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s) # 給實(shí)例綁定一個(gè)方法
給類(lèi)綁定方法:直接賦值,但函數(shù)第一個(gè)參數(shù)必須為self
- 類(lèi)綁定方法對(duì)所有實(shí)例起作用礁叔,而且不需要重新實(shí)例化牍颈,之前創(chuàng)建的實(shí)例也起作用
>>> def set_score(self, score):
... self.score = score
...
>>> Student.set_score = set_score
使用_slots_可以限制添加實(shí)例的屬性(包括變量 和方法)
類(lèi)中的特殊函數(shù)
- _init_:類(lèi)的初始化函數(shù)/構(gòu)造函數(shù)
- _slot_:限制類(lèi)及其實(shí)例的可添加屬性
- _len_: 返回長(zhǎng)度,支持len()函數(shù)琅关,它自動(dòng)去調(diào)用該對(duì)象的_len_()方法
- _str_: print()一個(gè)實(shí)例時(shí)煮岁,會(huì)調(diào)用該函數(shù)
- _repr_:直接顯示變量調(diào)用該函數(shù)讥蔽,為調(diào)試服務(wù)的
s = Student()
print(s) #調(diào)用__str__
s #調(diào)用__repr__
- _iter_: 如果一個(gè)類(lèi)想被用于for ... in循環(huán),類(lèi)似list或tuple那樣画机,就必須實(shí)現(xiàn)一個(gè)_iter_()方法冶伞,該方法返回一個(gè)迭代對(duì)象,然后步氏,Python的for循環(huán)就會(huì)不斷調(diào)用該迭代對(duì)象的_next_()方法拿到循環(huán)的下一個(gè)值响禽,直到遇到StopIteration錯(cuò)誤時(shí)退出循環(huán)。
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化兩個(gè)計(jì)數(shù)器a荚醒,b
def __iter__(self):
return self # 實(shí)例本身就是迭代對(duì)象芋类,故返回自己
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 計(jì)算下一個(gè)值
if self.a > 100000: # 退出循環(huán)的條件
raise StopIteration()
return self.a # 返回下一個(gè)值
>>> for n in Fib():
... print(n)
...
1
1
2
3
5
...
46368
75025
- _getitem_: 支持list那樣按照下標(biāo)取出元素
class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
>>> f = Fib()
>>> f[0]
1
- _getattr_: 調(diào)用對(duì)象屬性時(shí),如果找不到某個(gè)屬性界阁,會(huì)調(diào)用該函數(shù)侯繁。
- _call_: 支持實(shí)例化方法,直接用實(shí)例調(diào)用方法
class Student(object):
def __init__(self, name):
self.name = name
def __call__(self):
print('My name is %s.' % self.name)
>>> s = Student('Michael')
>>> s() # self參數(shù)不要傳入泡躯, 調(diào)用s.__call__()
My name is Michael.
>>> callable(Student())
True
通過(guò)callable()函數(shù)贮竟,我們就可以判斷一個(gè)對(duì)象是否是“可調(diào)用”對(duì)象。