史上最全 Python 面向對象編程

面向對象編程和函數式編程(面向過程編程)都是程序設計的方法,不過稍有區別。

面向過程編程:

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')

class studetn:
    # 定義一個類名爲studetn
    def __init__(self,idx):
    # 定義初始化構造,這裏使用init,還有別的屬性比如reversed,iter之類的
        self.idx=idx
        # 初始化變量,方便繼承
    def runx(self):
    # 定義運行函數,從上面繼承變量
        print self.idx
        # 打印出idx的值,或者做一些別的處理
        time.sleep(1)
a=studetn('a')
a.runx()
# 這是類的調用,一定要記得類的使用方法,首先傳入參數,類賦值給一個變量a
# 然後調用這個類下面定義的函數

一些專業術語概念,既然有面向對象編程這個高大上的定義了,自然要搭配一些高大上的概念。

  1. 類(Class): 用來描述具有相同屬性和方法的對象的集合。它定義了該集合中每個對象所共有的屬性和方法。其中的對象被稱作類的實例。

  2. 實例:也稱對象。通過類定義的初始化方法,賦予具體的值,成爲一個”有血有肉的實體”。

  3. 實例化:創建類的實例的過程或操作。

  4. 實例變量:定義在實例中的變量,只作用於當前實例。

  5. 類變量:類變量是所有實例公有的變量。類變量定義在類中,但在方法體之外。

  6. 數據成員:類變量、實例變量、方法、類方法、靜態方法和屬性等的統稱。

  7. 方法:類中定義的函數。

  8. 靜態方法:不需要實例化就可以由類執行的方法

  9. 類方法:類方法是將類本身作爲對象進行操作的方法。

  10. 方法重寫:如果從父類繼承的方法不能滿足子類的需求,可以對父類的方法進行改寫,這個過程也稱override。

  11. 封裝:將內部實現包裹起來,對外透明,提供api接口進行調用的機制

  12. 繼承:即一個派生類(derived class)繼承父類(base class)的變量和方法。

  13. 多態:根據對象類型的不同以不同的方式進行處理。

類與實例

# -*- 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')

class cc:
    ccc = 'ccc'
    # cc就是類名 如果想要繼承別的類 就class cc(threading) 意思就是從threading繼承
    def __init__(self,a,b,c):
        self.a=a
        self.b=b
        self.c=c
        # 定義構造的過程就是實例化
    def runx(self):
        print self.a*10
        print self.b*5
        print self.c*2
    def runy(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')

class dd:
    def __init__(self,url):
        self.url=url
    def runx(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')
class ff:
    @staticmethod
    def runx():
        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')
class gg:
    def __init__(self,url,stat):
        self.url=url
        self.stat=stat
    def outer(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')
class gg:
    url = 0
    stat = 0
    # 因爲使用classmethod後會傳入新的變量,所以一開始是需要自己先定義類變量
    def __init__(self,url=0,stat=0):
    # 這裏按照正常的定義構造函數
        self.url=url
        self.stat=stat
    @classmethod
    # 裝飾器,立馬執行下面的函數
    def split(cls,info):
        # 這個函數接受兩個參數,默認的cls就是這個類的init函數,info就是外面傳入進來的
        url,stat=map(str,info.split('-'))
        # 這裏轉換成了格式化的結構
        data = cls(url,stat)
        # 然後執行這個類第一個方法,這個類構造函數需要傳入兩個參數,於是就傳入了兩個參數
        return data
        # 這裏就直接返回了函數結果
    def outer(self):
        print self.url
        print self.stat

r = gg.split(('langzi-200'))
r.outer()
# 這裏是調用類方法,與調用實例方法一樣

類的特性

封裝

封裝是指將數據與具體操作的實現代碼放在某個對象內部,外部無法訪問。必須要先調用類的方法才能啓動。

案例

class cc:
    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()方法可以直接打印:

class Animal(object):
    def run(self):
        print 'Animal is running...'

當我們需要編寫Dog和Cat類時,就可以直接從Animal類繼承:

class Dog(Animal):
    pass
class Cat(Animal):
    pass

繼承有什麼好處?最大的好處是子類獲得了父類的全部功能。由於Animial實現了run()方法,因此,Dog和Cat作爲它的子類,什麼事也沒幹,就自動擁有了run()方法:

dog = Dog()
dog.run()
cat = Cat()
cat.run()

當子類和父類都存在相同的run()方法時,我們說,子類的run()覆蓋了父類的run(),在代碼運行的時候,總是會調用子類的run()。這樣,我們就獲得了繼承的另一個好處:多態。

多態

要理解多態的好處,我們還需要再編寫一個函數,這個函數接受一個Animal類型的變量:

def run_twice(animal):
    animal.run()
    animal.run()

當我們傳入Animal的實例時,run_twice()就打印出:

run_twice(Animal())
運行結果:
Animal is running...
Animal is running...

當我們傳入Dog的實例時,run_twice()就打印出:

run_twice(Dog())
運行結果:
Dog is running...
Dog is running...

當我們傳入Cat的實例時,run_twice()就打印出:

run_twice(Cat())
運行結果:
Cat is running...
Cat is running...

看上去沒啥意思,但是仔細想想,現在,如果我們再定義一個Tortoise類型,也從Animal派生:

class Tortoise(Animal):
    def run(self):
        print 'Tortoise is running slowly...'

當我們調用run_twice()時,傳入Tortoise的實例:

run_twice(Tortoise())
運行結果:
Tortoise is running slowly...
Tortoise is running 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自建,無需自定義。

class Foo:
    """ 描述類信息,可被自動收集 """
    def func(self):
        pass
# 打印類的說明文檔 
print(Foo.__doc__)

2. init()

實例化方法,通過類創建實例時,自動觸發執行。

class Foo:
    def __init__(self, name):
        self.name = name
        self.age = 18
obj = Foo(jack') # 自動執行類中的 __init__ 方法

3. module__ 和 __class

module 表示當前操作的對象在屬於哪個模塊。
class 表示當前操作的對象屬於哪個類。
這兩者也是Python內建,無需自定義。

class Foo:
    pass
obj = Foo()
print(obj.__module__)
print(obj.__class__)
運行結果:
main

4. del()

析構方法,當對象在內存中被釋放時,自動觸發此方法。

注:此方法一般無須自定義,因爲Python自帶內存分配和釋放機制,除非你需要在釋放的時候指定做一些動作。析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。

class Foo:
    def __del__(self):
        print("我被回收了!")

obj = Foo()
del obj

5. call()

如果爲一個類編寫了該方法,那麼在該類的實例後面加括號,可會調用這個方法。

注:構造方法的執行是由類加括號執行的,即:對象 = 類名(),而對於call() 方法,是由對象後加括號觸發的,即:對象() 或者 類()()

class Foo:
    def __init__(self):
        pass
    def __call__(self, *args, **kwargs):
        print('__call__')
obj = Foo()     # 執行 __init__
obj()       # 執行 __call__

可以用Python內建的callable()函數進行測試,判斷一個對象是否可以被執行。

callable(Student())

運行結果:

True

6. dict

列出類或對象中的所有成員!非常重要和有用的一個屬性,Python自建,無需用戶自己定義。

class Province:
    country = 'China'
    def __init__(self, name, count):
        self.name = name
        self.count = count
    def func(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()方法,打印結果是:

class Foo:
    pass
obj = Foo()
print(obj)
定義了__str__()方法後,打印結果是:'jack'。
class Foo:
    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__方法

如果有一個類同時定義了這三個魔法方法,那麼這個類的實例的行爲看起來就像一個字典一樣,如下例所示:

class Foo:
    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__
del obj['k1']             # 自動觸發執行 __delitem__

9. iter()

這是迭代器方法!列表、字典、元組之所以可以進行for循環,是因爲其內部定義了 iter()這個方法。如果用戶想讓自定義的類的對象可以被迭代,那麼就需要在類中定義這個方法,並且讓該方法的返回值是一個可迭代的對象。當在代碼中利用for循環遍歷對象時,就會調用類的這個iter()方法。

普通的類:

class Foo:
    pass
obj = Foo()
for i in obj:
    print(i)
# 報錯:TypeError: 'Foo' object is not iterable<br># 原因是Foo對象不可迭代
添加一個__iter__(),但什麼都不返回:
class Foo:
    def __iter__(self):
        pass
obj = Foo()
for i in obj:
    print(i)
# 報錯:TypeError: iter() returned non-iterator of type 'NoneType'
#原因是 __iter__方法沒有返回一個可迭代的對象

返回一個個迭代對象:

class Foo:
    def __init__(self, sq):
        self.sq = sq
    def __iter__(self):
        return iter(self.sq)
obj = Foo([11,22,33,44])
for i in obj:
    print(i)

最好的方法是使用生成器:

class Foo:
    def __init__(self):
        pass
    def __iter__(self):
        yield 1
        yield 2
        yield 3
obj = Foo()
for i in obj:
    print(i)

10、len()

在Python中,如果你調用內置的len()函數試圖獲取一個對象的長度,在後臺,其實是去調用該對象的len()方法,所以,下面的代碼是等價的:

len('ABC')
3
'ABC'.__len__()
3

Python的list、dict、str等內置數據類型都實現了該方法,但是你自定義的類要實現len方法需要好好設計。

11. repr()

這個方法的作用和str()很像,兩者的區別是str()返回用戶看到的字符串,而repr()返回程序開發者看到的字符串,也就是說,repr()是爲調試服務的。通常兩者代碼一樣。

class Foo:
    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支持運算符的重載,也就是重寫。

class Vector:
   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):
      return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2,10)
v2 = Vector(5,-2)
print (v1 + v2)

13. author作者信息

__author__ = "Jack"
def show():
    print(__author__)
show()

14. slots

Python作爲一種動態語言,可以在類定義完成和實例化後,給類或者對象繼續添加隨意個數或者任意類型的變量或方法,這是動態語言的特性。例如:

def print_doc(self):
    print("haha")

class Foo:
    pass

obj1 = Foo()
obj2 = Foo()
# 動態添加實例變量
obj1.name = "jack"
obj2.age = 18
# 動態的給類添加實例方法
Foo.show = print_doc
obj1.show()
obj2.show()

但是!如果我想限制實例可以添加的變量怎麼辦?可以使slots限制實例的變量,比如,只允許Foo的實例添加name和age屬性。

def print_doc(self):
    print("haha")
class Foo:
    __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。

成員保護與訪問機制

有些對象你不想外部訪問,即使是通過調用類對象也無法訪問,那就請認真學完本章節。

私有成員

class obj:
    def __init__(self,name):
        self.name=name
    def pri(self):
        print self.name
    __age = 18
    # 加上雙下劃線的就是私有變量,只能在類的內部訪問,外部無法訪問
a = obj('zhao')
a.pri()

運行結果:

zhao

如果要在類中調用這個私有成員,可以這麼用

class obj:
    def __init__(self,name):
        self.name=name
    def prin(self):
        print self.name
    __age = 18
    # 加上雙下劃線的就是私有變量,只能在類的內部訪問,外部無法訪問
    @classmethod
    # 如果要在類中調用,首先調用類方法
    def pri(cls):
        print cls.__age
        # 然後在使用
a = obj('zhao')
a.prin()
obj.pri()
# 通過這樣直接調用類中的私有變量

運行結果:

zhao
18

使用get-set-del方法操作私有成員

class obj:
    def __init__(self,name):
        self.name=name
    def prin(self):
        print self.name
    __age = 18
    # 加上雙下劃線的就是私有變量,只能在類的內部訪問,外部無法訪問
    @classmethod
    # 如果要在類中調用,首先調用類方法
    def pri(cls):
        print cls.__age
        # 然後在使用
    @classmethod
    def set_age(cls,value):
        cls.__age = value
        return cls.__age
        # 這個用法就是改變__age的值
    @classmethod
    def get_age(cls):
        return cls.__age
        # 這個用法就是直接返回__age的值
    @classmethod
    def del_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裝飾器

把類的方法僞裝成屬性調用的方式,就是把類裏面的一個函數,變成一個屬性一樣的東西~
一開始調用類的方法要使用圓括號,現在變成了屬性進行讀取設置存儲。
舉個例子來說明:

常用的調用方法

class obj:
    def __init__(self,name,age):
        self.__name=name
        self.__age=age
        # 講這些設置成私有變量
    def get_age(self):
        return self.__age
    def set_age(self,value):
        if isinstance(value,int):
            self.__age=value
        else:
            raise ValueError('非整數類型')
    def del_age(self):
        print 'delete over'
a = obj('langzi',18)
print a.get_age()
a.set_age(20)
print a.get_age()

使用裝飾器

class obj:
    def __init__(self,name,age):
        self.__name=name
        self.__age=age
        # 把這些設置成私有變量
    @property
    def age(self):
        return self.__age
    @age.setter
    def age(self,value):
        if isinstance(value,int):
            self.__age=value
        else:
            raise ValueError('非整數類型')
    @age.deleter
    def age(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()函數,爲我們提供了第二種設置類屬性的手段。

class People:

    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    def get_age(self):
        return self.__age

    def set_age(self, age):
        if isinstance(age, int):
            self.__age = age
        else:
            raise ValueError

    def del_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__時的描述信息。



本文參考於:magedu.com/79132.html


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章