原标題:一篇非常全的Python 面向對象程式設計
轉自:浪子燕青
from: Python程式設計開發
http://www.langzi.fun/Python面向對象程式設計.html
面向對象程式設計和函數式程式設計(面向過程程式設計)都是程式設計的方法,不過稍有差別。
面向過程程式設計:
1.導入各種外部庫
2.設計各種全局變量
3.寫一個函數完成某個功能
4.寫一個函數完成某個功能
5.寫一個函數完成某個功能
6.寫一個函數完成某個功能
7.寫一個函數完成某個功能
8.......
9.寫一個main函數作為程式入口
在多函數程式中,許多重要的資料被放置在全局資料區,這樣它們可以被所有的函數通路。每個函數都可以具有它們自己的局部資料,将某些功能代碼封裝到函數中,日後便無需重複編寫,僅調用函數即可。從代碼的組織形式來看就是根據業務邏輯從上到下壘代碼 。
面向對象程式設計:
1.導入各種外部庫
2.設計各種全局變量
3.決定你要的類
4.給每個類提供完整的一組操作
5.明确地使用繼承來表現不同類之間的共同點
6.根據需要,決定是否寫一個main函數作為程式入口
面向對象程式設計中,将函數和變量進一步封裝成類,類才是程式的基本元素,它将資料和操作緊密地連結在一起,并保護資料不會被外界的函數意外地改變。類和和類的執行個體(也稱對象)是面向對象的核心概念,是和面向過程程式設計、函數式程式設計的根本差別。
并不是非要用面向對象程式設計,要看你的程式怎麼設計友善,但是就目前來說,基本上都是在使用面向對象程式設計。
類的基本用法
面向對象是通過定義class類來定義,這麼說面向對象程式設計就是隻使用class類,在class類中有封裝,繼承的功能,并且還可以構造要傳入的參數,友善控制。
案例一import sys
import time
reload(sys)
sys.setdefaultencoding( 'utf-8')
classstudetn:
# 定義一個類名為studetn
def__init__(self,idx):
# 定義初始化構造,這裡使用init,還有别的屬性比如reversed,iter之類的
self.idx=idx
# 初始化變量,友善繼承
defrunx(self):
# 定義運作函數,從上面繼承變量
print self.idx
# 列印出idx的值,或者做一些别的處理
time.sleep( 1)
a=studetn( 'a')
a.runx()
# 這是類的調用,一定要記得類的使用方法,首先傳入參數,類指派給一個變量a
# 然後調用這個類下面定義的函數
一些專業術語概念,既然有面向對象程式設計這個高大上的定義了,自然要搭配一些高大上的概念。
類(Class): 用來描述具有相同屬性和方法的對象的集合。它定義了該集合中每個對象所共有的屬性和方法。其中的對象被稱作類的執行個體。
執行個體:也稱對象。通過類定義的初始化方法,賦予具體的值,成為一個”有血有肉的實體”。
執行個體化:建立類的執行個體的過程或操作。
執行個體變量:定義在執行個體中的變量,隻作用于目前執行個體。
類變量:類變量是所有執行個體公有的變量。類變量定義在類中,但在方法體之外。
資料成員:類變量、執行個體變量、方法、類方法、靜态方法和屬性等的統稱。
方法:類中定義的函數。
靜态方法:不需要執行個體化就可以由類執行的方法
類方法:類方法是将類本身作為對象進行操作的方法。
方法重寫:如果從父類繼承的方法不能滿足子類的需求,可以對父類的方法進行改寫,這個過程也稱override。
封裝:将内部實作包裹起來,對外透明,提供api接口進行調用的機制
繼承:即一個派生類(derived class)繼承父類(base class)的變量和方法。
多态:根據對象類型的不同以不同的方式進行處理。
類與執行個體
# -*- coding: utf-8 -*-
# @Time: 2018/5/3 0003 17:02
# @Author: Langzi
# @Blog: www.langzi.fun
# @File: 面向對象2.py
# @Software: PyCharm
import sys
import time
import requests
reload(sys)
sys.setdefaultencoding( 'utf-8')
classcc:
ccc = 'ccc'
# cc就是類名 如果想要繼承别的類 就class cc(threading) 意思就是從threading繼承
def__init__(self,a,b,c):
self.a=a
self.b=b
self.c=c
# 定義構造的過程就是執行個體化
defrunx(self):
print self.a* 10
print self.b* 5
print self.c* 2
defruny(self):
print requests.get( 'http://www.langzi.fun').headers
e = cc( 'AAA', 'CCC', 'EEE')
e.runx()
e.runy()
# 這兩個就是調用類裡面的方法
print e.c
#執行個體變量指的是執行個體本身擁有的變量。每個執行個體的變量在記憶體中都不一樣。
print e.ccc
#類變量,在類裡面找到定義的變量。
調用類的三種方法執行個體方法# -*- coding: utf-8 -*-
# @Time: 2018/5/3 0003 17:16
# @Author: Langzi
# @Blog: www.langzi.fun
# @File: 面向對象3.py
# @Software: PyCharm
import sys
import time
import requests
reload(sys)
sys.setdefaultencoding( 'utf-8')
classdd:
def__init__(self,url):
self.url=url
defrunx(self):
print requests.get( self.url).status_code
a = dd( 'http://www.langzi.fun')
a.runx()
# 這種調用方法就是執行個體方法
靜态方法
靜态方法由類調用,無預設參數。将執行個體方法參數中的self去掉,然後在方法定義上方加上@staticmethod,就成為靜态方法。它屬于類,和執行個體無關。建議隻使用類名.靜态方法的調用方式。(雖然也可以使用執行個體名.靜态方法的方式調用)
# -*- coding: utf-8 -*-
# @Time: 2018/5/3 0003 17:21
# @Author: Langzi
# @Blog: www.langzi.fun
# @File: 面向對象4.py
# @Software: PyCharm
import sys
import requests
reload(sys)
sys.setdefaultencoding( 'utf-8')
classff:
@staticmethod
defrunx():
print requests.get( 'http://www.langzi.fun').status_code
ff.runx()
#這裡就直接調用了類的變量,隻在類中運作而不在執行個體中運作的方法
經常有一些跟類有關系的功能但在運作時又不需要執行個體和類參與的情況下需要用到靜态方法. 比如更改環境變量或者修改其他類的屬性等能用到靜态方法. 這種情況可以直接用函數解決, 但這樣同樣會擴散類内部的代碼,造成維護困難。
類方法
類方法由類調用,采用@classmethod裝飾,至少傳入一個cls(代指類本身,類似self)參數。執行類方法時,自動将調用該方法的類指派給cls。建議隻使用類名.類方法的調用方式。(雖然也可以使用執行個體名.類方法的方式調用)
實際案例
如果要構造一個類,接受一個網站和這個網站的狀态碼,然後列印出來。就像這樣:
import sys
import requests
reload(sys)
sys.setdefaultencoding( 'utf-8')
classgg:
def__init__(self,url,stat):
self.url=url
self.stat=stat
defouter(self):
print self.url
print self.stat
a = gg( 'langzi', 200)
a.outer()
這樣就是使用執行個體方法,雖然可以實作,但是有的時候傳入的參數并不是(‘langzi’,200)這樣的格式,而是(‘langzi-200’)這樣的,那該怎麼做?首先要把這個拆分,但是要使用執行個體方法實作起來很麻煩,這個時候就可以使用類方法。
# -*- coding: utf-8 -*-
# @Time: 2018/5/3 0003 17:27
# @Author: Langzi
# @Blog: www.langzi.fun
# @File: 面向對象5.py
# @Software: PyCharm
import sys
import requests
reload(sys)
sys.setdefaultencoding( 'utf-8')
classgg:
url = 0
stat = 0
# 因為使用classmethod後會傳入新的變量,是以一開始是需要自己先定義類變量
def__init__(self,url=0,stat=0):
# 這裡按照正常的定義構造函數
self.url=url
self.stat=stat
@classmethod
# 裝飾器,立馬執行下面的函數
defsplit(cls,info):
# 這個函數接受兩個參數,預設的cls就是這個類的init函數,info就是外面傳入進來的
url,stat=map(str,info.split( '-'))
# 這裡轉換成了格式化的結構
data = cls(url,stat)
# 然後執行這個類第一個方法,這個類構造函數需要傳入兩個參數,于是就傳入了兩個參數
returndata
# 這裡就直接傳回了函數結果
defouter(self):
print self.url
print self.stat
r = gg.split(( 'langzi-200'))
r.outer()
# 這裡是調用類方法,與調用執行個體方法一樣
類的特性封裝
封裝是指将資料與具體操作的實作代碼放在某個對象内部,外部無法通路。必須要先調用類的方法才能啟動。
案例classcc:
ccc = 'ccc'
# cc就是類名 如果想要繼承别的類 就class cc(threading) 意思就是從threading繼承
def__init__(self,a,b,c):
self.a=a
self.b=b
self.c=c
print e.ccc
#類變量,在類裡面找到定義的變量。
print ccc
# 這裡會報錯,這就是封裝。類中的函數同理。
繼承
當我們定義一個class的時候,可以從某個現有的class繼承,新的class稱為子類(Subclass),而被繼承的class稱為基類、父類或超類(Base class、Super class)。
比如,我們已經編寫了一個名為Animal的class,有一個run()方法可以直接列印:
classAnimal(object):
defrun(self):
print'Animal is running...'
當我們需要編寫Dog和Cat類時,就可以直接從Animal類繼承:
classDog(Animal):
pass
classCat(Animal):
pass
繼承有什麼好處?最大的好處是子類獲得了父類的全部功能。由于Animial實作了run()方法,是以,Dog和Cat作為它的子類,什麼事也沒幹,就自動擁有了run()方法:
dog = Dog()
dog.run()
cat = Cat()
cat.run()
當子類和父類都存在相同的run()方法時,我們說,子類的run()覆寫了父類的run(),在代碼運作的時候,總是會調用子類的run()。這樣,我們就獲得了繼承的另一個好處:多态。
多态
要了解多态的好處,我們還需要再編寫一個函數,這個函數接受一個Animal類型的變量:
defrun_twice(animal):
animal.run()
animal.run()
當我們傳入Animal的執行個體時,run_twice()就列印出:
run_twice(Animal())
運作結果:
Animal isrunning...
Animal isrunning...
當我們傳入Dog的執行個體時,run_twice()就列印出:
run_twice(Dog())
運作結果:
Dog isrunning...
Dog isrunning...
當我們傳入Cat的執行個體時,run_twice()就列印出:
run_twice(Cat())
運作結果:
Cat isrunning...
Cat isrunning...
看上去沒啥意思,但是仔細想想,現在,如果我們再定義一個Tortoise類型,也從Animal派生:
classTortoise(Animal):
defrun(self):
print'Tortoise is running slowly...'
當我們調用run_twice()時,傳入Tortoise的執行個體:
run_twice(Tortoise())
運作結果:
Tortoise isrunning slowly...
Tortoise isrunning slowly...
你會發現,新增一個Animal的子類,不必對run_twice()做任何修改,實際上,任何依賴Animal作為參數的函數或者方法都可以不加修改地正常運作,原因就在于多态。
多态的好處就是,當我們需要傳入Dog、Cat、Tortoise……時,我們隻需要接收Animal類型就可以了,因為Dog、Cat、Tortoise……都是Animal類型,然後,按照Animal類型進行操作即可。由于Animal類型有run()方法,是以,傳入的任意類型,隻要是Animal類或者子類,就會自動調用實際類型的run()方法,這就是多态的意思:
對于一個變量,我們隻需要知道它是Animal類型,無需确切地知道它的子類型,就可以放心地調用run()方法,而具體調用的run()方法是作用在Animal、Dog、Cat還是Tortoise對象上,由運作時該對象的确切類型決定,這就是多态真正的威力:調用方隻管調用,不管細節,而當我們新增一種Animal的子類時,隻要確定run()方法編寫正确,不用管原來的代碼是如何調用的。這就是著名的“開閉”原則:
對擴充開放:允許新增Animal子類;
對修改封閉:不需要修改依賴Animal類型的run_twice()等函數。
總結:繼承可以把父類的所有功能都直接拿過來,這樣就不必重零做起,子類隻需要新增自己特有的方法,也可以把父類不适合的方法覆寫重寫;
有了繼承,才能有多态。在調用類執行個體方法的時候,盡量把變量視作父類類型,這樣,所有子類類型都可以正常被接收;
舊的方式定義Python類允許不從object類繼承,但這種程式設計方式已經嚴重不推薦使用。任何時候,如果沒有合适的類可以繼承,就繼承自object類。
魔法方法
在上面有提到除了init之外還有iter,reverse的方法,這裡就詳細說下除了init初始化還有哪些别的方法。
__init__: 構造函數,在生成對象時調用
__del__: 析構函數,釋放對象時使用
__repr__: 列印,轉換
__setitem__: 按照索引指派
__getitem__: 按照索引擷取值
__len__: 獲得長度
__cmp__: 比較運算
__call__: 調用
__add__: 加運算
__sub__: 減運算
__mul__: 乘運算
__div__: 除運算
__mod__: 求餘運算
__pow__: 幂
具體使用1. doc
說明性文檔和資訊。Python自建,無需自定義。
classFoo:
""" 描述類資訊,可被自動收集 """
deffunc(self):
pass
# 列印類的說明文檔
print(Foo.__doc__)
2. init()
執行個體化方法,通過類建立執行個體時,自動觸發執行。
classFoo:
def__init__(self, name):
self.name = name
self.age = 18
obj = Foo(jack ') # 自動執行類中的 __init__ 方法
3. module__ 和 __class
module 表示目前操作的對象在屬于哪個子產品。
class 表示目前操作的對象屬于哪個類。
這兩者也是Python内建,無需自定義。
classFoo:
pass
obj = Foo()
print(obj.__module__)
print(obj.__class__)
運作結果:
main
4. del()
析構方法,當對象在記憶體中被釋放時,自動觸發此方法。
注:此方法一般無須自定義,因為Python自帶記憶體配置設定和釋放機制,除非你需要在釋放的時候指定做一些動作。析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。
classFoo:
def__del__(self):
print( "我被回收了!")
obj = Foo()
delobj
5. call()
如果為一個類編寫了該方法,那麼在該類的執行個體後面加括号,可會調用這個方法。
注:構造方法的執行是由類加括号執行的,即:對象 = 類名(),而對于call() 方法,是由對象後加括号觸發的,即:對象() 或者 類()()
classFoo:
def__init__(self):
pass
def__call__(self, *args, **kwargs):
print( '__call__')
obj = Foo() # 執行 __init__
obj() # 執行 __call__
可以用Python内建的callable()函數進行測試,判斷一個對象是否可以被執行。
callable(Student())
運作結果:
True
6. dict
列出類或對象中的所有成員!非常重要和有用的一個屬性,Python自建,無需使用者自己定義。
classProvince:
country = 'China'
def__init__(self, name, count):
self.name = name
self.count = count
deffunc(self, *args, **kwargs):
print( 'func')
# 擷取類的成員
print(Province.__dict__)
# 擷取 對象obj1 的成員
obj1 = Province( 'HeBei', 10000)
print(obj1.__dict__)
# 擷取 對象obj2 的成員
obj2 = Province( 'HeNan', 3888)
print(obj2.__dict__)
7. str()
如果一個類中定義了str()方法,那麼在列印對象時,預設輸出該方法的傳回值。這也是一個非常重要的方法,需要使用者自己定義。
下面的類,沒有定義str()方法,列印結果是:
classFoo:
pass
obj = Foo()
print(obj)
定義了__str__()方法後,列印結果是: 'jack'。
classFoo:
def__str__(self):
return'jack'
obj = Foo()
print(obj)
8、getitem__()、_setitem_()、__delitem()
取值、指派、删除這“三劍客”的套路,在Python中,我們已經見過很多次了,比如前面的@property裝飾器。
Python中,辨別符後面加圓括号,通常代表執行或調用方法的意思。而在辨別符後面加中括号[],通常代表取值的意思。Python設計了getitem()、setitem()、delitem()這三個特殊成員,用于執行與中括号有關的動作。它們分别表示取值、指派、删除資料。
也就是如下的操作:
a = 辨別符[] : 執行 __getitem__方法
辨別符[] = a : 執行 __setitem__方法
del 辨別符[] : 執行 __delitem__方法
如果有一個類同時定義了這三個魔法方法,那麼這個類的執行個體的行為看起來就像一個字典一樣,如下例所示:
classFoo:
def__getitem__(self, key):
print( '__getitem__',key)
def__setitem__(self, key, value):
print( '__setitem__',key,value)
def__delitem__(self, key):
print( '__delitem__',key)
obj = Foo()
result = obj[ 'k1'] # 自動觸發執行 __getitem__
obj[ 'k2'] = 'jack'# 自動觸發執行 __setitem__
delobj[ 'k1'] # 自動觸發執行 __delitem__
9. iter()
這是疊代器方法!清單、字典、元組之是以可以進行for循環,是因為其内部定義了 iter()這個方法。如果使用者想讓自定義的類的對象可以被疊代,那麼就需要在類中定義這個方法,并且讓該方法的傳回值是一個可疊代的對象。當在代碼中利用for循環周遊對象時,就會調用類的這個iter()方法。
普通的類:
classFoo:
pass
obj = Foo()
fori inobj:
print(i)
# 報錯:TypeError: 'Foo' object is not iterable
# 原因是Foo對象不可疊代
添加一個__iter__(),但什麼都不傳回:
classFoo:
def__iter__(self):
pass
obj = Foo()
fori inobj:
print(i)
# 報錯:TypeError: iter() returned non-iterator of type 'NoneType'
#原因是 __iter__方法沒有傳回一個可疊代的對象
傳回一個個疊代對象:
classFoo:
def__init__(self, sq):
self.sq = sq
def__iter__(self):
returniter( self.sq)
obj = Foo([ 11, 22, 33, 44])
fori inobj:
print(i)
最好的方法是使用生成器:
classFoo:
def__init__(self):
pass
def__iter__(self):
yield1
yield2
yield3
obj = Foo()
fori inobj:
print(i)
10、len()
在Python中,如果你調用内置的len()函數試圖擷取一個對象的長度,在背景,其實是去調用該對象的len()方法,是以,下面的代碼是等價的:
len( 'ABC')
3
'ABC'.__len__()
3
Python的list、dict、str等内置資料類型都實作了該方法,但是你自定義的類要實作len方法需要好好設計。
11. repr()
這個方法的作用和str()很像,兩者的差別是str()傳回使用者看到的字元串,而repr()傳回程式開發者看到的字元串,也就是說,repr()是為調試服務的。通常兩者代碼一樣。
classFoo:
def__init__(self, name):
self.name = name
def__str__(self):
return"this is %s"% self.name
__repr_ _= __str_ _
12. add__: 加運算 _sub_: 減運算 _mul_: 乘運算 _div_: 除運算 _mod_: 求餘運算 __pow: 幂運算
這些都是算術運算方法,需要你自己為類設計具體運算代碼。有些Python内置資料類型,比如int就帶有這些方法。Python支援運算符的重載,也就是重寫。
classVector:
def__init__(self, a, b):
self.a = a
self.b = b
def__str__(self):
return'Vector (%d, %d)'% ( self.a, self.b)
def__add__(self,other):
returnVector( self.a + other.a, self.b + other.b)
v1 = Vector( 2, 10)
v2 = Vector( 5,- 2)
print (v1 + v2)
13. author作者資訊__author__ = "Jack"
defshow():
print(__author__)
show()
14. slots
Python作為一種動态語言,可以在類定義完成和執行個體化後,給類或者對象繼續添加随意個數或者任意類型的變量或方法,這是動态語言的特性。例如:
defprint_doc(self):
print( "haha")
classFoo:
pass
obj1 = Foo()
obj2 = Foo()
# 動态添加執行個體變量
obj1.name = "jack"
obj2.age = 18
# 動态的給類添加執行個體方法
Foo.show = print_doc
obj1.show()
obj2.show()
但是!如果我想限制執行個體可以添加的變量怎麼辦?可以使slots限制執行個體的變量,比如,隻允許Foo的執行個體添加name和age屬性。
defprint_doc(self):
print( "haha")
classFoo:
__slots_ _= ( "name", "age")
pass
obj1 = Foo()
obj2 = Foo()
# 動态添加執行個體變量
obj1.name = "jack"
obj2.age = 18
obj1.sex = "male"# 這一句會彈出錯誤
# 但是無法限制給類添加方法
Foo.show = print_doc
obj1.show()
obj2.show()
由于 'sex'不在__slots_ _的清單中,是以不能綁定sex屬性,試圖綁定sex将得到AttributeError的錯誤。
Traceback (most recent call last):
File "F:/Python/pycharm/201705/1.py", line 14, in< module>
obj1.sex = "male"
AttributeError:'Foo'object has no attribute 'sex'
需要提醒的是,slots定義的屬性僅對目前類的執行個體起作用,對繼承了它的子類是不起作用的。想想也是這個道理,如果你繼承一個父類,卻莫名其妙發現有些變量無法定義,那不是大問題麼?如果非要子類也被限制,除非在子類中也定義slots,這樣,子類執行個體允許定義的屬性就是自身的slots加上父類的slots。
成員保護與通路機制
有些對象你不想外部通路,即使是通過調用類對象也無法通路,那就請認真學完本章節。
私有成員classobj:
def__init__(self,name):
self.name=name
defpri(self):
print self.name
__age = 18
# 加上雙下劃線的就是私有變量,隻能在類的内部通路,外部無法通路
a = obj( 'zhao')
a.pri()
運作結果:
zhao
如果要在類中調用這個私有成員,可以這麼用
classobj:
def__init__(self,name):
self.name=name
defprin(self):
print self.name
__age = 18
# 加上雙下劃線的就是私有變量,隻能在類的内部通路,外部無法通路
@classmethod
# 如果要在類中調用,首先調用類方法
defpri(cls):
print cls.__age
# 然後在使用
a = obj( 'zhao')
a.prin()
obj.pri()
# 通過這樣直接調用類中的私有變量
運作結果:
zhao
18
使用get-set-del方法操作私有成員classobj:
def__init__(self,name):
self.name=name
defprin(self):
print self.name
__age = 18
# 加上雙下劃線的就是私有變量,隻能在類的内部通路,外部無法通路
@classmethod
# 如果要在類中調用,首先調用類方法
defpri(cls):
print cls.__age
# 然後在使用
@classmethod
defset_age(cls,value):
cls.__age = value
returncls.__age
# 這個用法就是改變__age的值
@classmethod
defget_age(cls):
returncls.__age
# 這個用法就是直接傳回__age的值
@classmethod
defdel_age(cls):
del cls.__age
# 這個用法就是直接删除__age的值
print obj.get_age()
# 這裡是直接調用出__age的值 傳回值18
print obj.set_age( 20)
# 這裡是直接改變__age的值 傳回值20
obj.del_age()
# 這裡是直接删除__age的值
思考: 既然是私有變量,不讓外部通路,為何有要在後面調用又改變呢?因為可以對私有變量進行額外的檢測,處理,加工等等。比如判斷value的值,使用isinstance然後做if-else判斷。
使用私有變量可以對内部變量進行保護,外部無法改變,但是可以對它進行檢測處理。
這裡引申一下私有成員的保護機制,使用__age對私有變量其實就是—>obj._obj__age的樣子進行保護,說白了你直接使用obj._obj__age就可以直接調用内部私有變量age了。
Propety裝飾器
把類的方法僞裝成屬性調用的方式,就是把類裡面的一個函數,變成一個屬性一樣的東西~
一開始調用類的方法要使用圓括号,現在變成了屬性進行讀取設定存儲。
舉個例子來說明:
常用的調用方法classobj:
def__init__(self,name,age):
self.__name=name
self.__age=age
# 講這些設定成私有變量
defget_age(self):
returnself.__age
defset_age(self,value):
ifisinstance(value,int):
self.__age=value
else:
raise ValueError( '非整數類型')
defdel_age(self):
print 'delete over'
a = obj( 'langzi', 18)
print a.get_age()
a.set_age( 20)
print a.get_age()
使用裝飾器classobj:
def__init__(self,name,age):
self.__name=name
self.__age=age
# 把這些設定成私有變量
@property
defage(self):
returnself.__age
@age.setter
defage(self,value):
ifisinstance(value,int):
self.__age=value
else:
raise ValueError( '非整數類型')
@age.deleter
defage(self):
print 'delete over'
a = obj( 'langzi', 18)
# 使用這些裝飾器,可以使用類與對象的方法直接調用
print a.age
# 這裡就是直接調用傳回age的值
a.age= 20
# 這裡就是直接使用setter把值轉換
print a.age
del a.age
# 删除age
當然這種調用方法有些麻煩,每次都是一個一個去執行個體類與對象,有個更加簡單直覺的方法。
更加減半的使用property()函數
除了使用裝飾器的方式将一個方法僞裝成屬性外,Python内置的builtins子產品中的property()函數,為我們提供了第二種設定類屬性的手段。
classPeople:
def__init__(self, name, age):
self.__name = name
self.__age = age
defget_age(self):
returnself.__age
defset_age(self, age):
ifisinstance(age, int):
self.__age = age
else:
raise ValueError
defdel_age(self):
print( "删除年齡資料!")
# 核心在這句
age = property(get_age, set_age, del_age, "年齡")
obj = People( "jack", 18)
print(obj.age)
obj.age = 19
print( "obj.age: ", obj.age)
del obj.ag
通過語句age = property(get_age, set_age, del_age, “年齡”)将一個方法僞裝成為屬性。其效果和裝飾器的方法是一樣的。
property()函數的參數:
第一個參數是方法名,調用 執行個體.屬性 時自動執行的方法
第二個參數是方法名,調用 執行個體.屬性 = XXX時自動執行的方法
第三個參數是方法名,調用 del執行個體.屬性 時自動執行的方法
第四個參數是字元串,調用 執行個體.屬性 .__doc__時的描述資訊。 傳回搜狐,檢視更多
責任編輯: