阿里云天池 Python训练营Task3: Python基础进阶:从函数到高级魔法方法 学习笔记
本學(xué)習(xí)筆記為阿里云天池龍珠計(jì)劃Python訓(xùn)練營的學(xué)習(xí)內(nèi)容,學(xué)習(xí)鏈接為:https://tianchi.aliyun.com/specials/promotion/aicamppython?spm=5176.22758685.J_6770933040.1.6f103da1tESyzu
目錄
一、學(xué)習(xí)知識(shí)點(diǎn)概要
二、學(xué)習(xí)內(nèi)容
I.函數(shù)
1.定義自己的函數(shù)
2.函數(shù)文檔
3.函數(shù)參數(shù)
4.變量作用域
II.Lambda-表達(dá)式
1.定義
2.匿名函數(shù)的運(yùn)用
III.類與對(duì)象
1.定義自己的類
2.self
?3.類的構(gòu)造函數(shù)
4.公有和私有
5.類的繼承與多態(tài)
6.組合
7.類對(duì)象和實(shí)例對(duì)象
8.綁定
9.相關(guān)的內(nèi)置函數(shù)
IV.魔法方法
1.基本的魔法方法
2.算術(shù)運(yùn)算符
3.反算術(shù)運(yùn)算符
4.增量賦值運(yùn)算符
5.一元運(yùn)算符
6.屬性訪問
7.描述符
8.定制序列
9.迭代器
三、學(xué)習(xí)問題與解答
四、學(xué)習(xí)思考與總結(jié)
一、學(xué)習(xí)知識(shí)點(diǎn)概要
本次學(xué)習(xí)的主要知識(shí)有:
- 函數(shù)
- Lambda-表達(dá)式
- 類與對(duì)象
- 魔法方法
二、學(xué)習(xí)內(nèi)容
I.函數(shù)
我在之前的文章中把函數(shù)稱為方法,其實(shí)就是封裝在一起的能夠完成一些操作的代碼塊。和其他語言相同,Python不知有許許多多內(nèi)置的函數(shù),也能夠定義自己的函數(shù)。和其他語言有些不同的是,Python中的函數(shù)也是對(duì)象,其他函數(shù)的返回值也可以是一個(gè)函數(shù),進(jìn)而去構(gòu)造一些高階的函數(shù)。
1.定義自己的函數(shù)
在Python中定義自己的函數(shù)的語法結(jié)構(gòu)為:
def 函數(shù)名 (參數(shù)):"函數(shù)文檔字符串"函數(shù)體return [返回值]- 定義函數(shù)時(shí)以def關(guān)鍵詞為開頭,后面為函數(shù)名和用小括號(hào)括起來的參數(shù),參數(shù)可以不止一個(gè)
- 函數(shù)執(zhí)行的是冒號(hào)之后的代碼
- return [返回值]結(jié)束函數(shù),中括號(hào)[]里面可以填寫返回值返回給函數(shù),返回值也可以是一個(gè)表達(dá)式。如果不帶表達(dá)式則默認(rèn)返回None
例:
def mean(x):"求序列的均值"return [sum(x)/len(x)] #此處返回的是一個(gè)列表a=list(range(4,15)) print(a) #[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] print('a的均值為:',mean(a)) #a的均值為: [9.0]2.函數(shù)文檔
函數(shù)文檔是在函數(shù)定義中,冒號(hào)下一行的字符串,用于解釋函數(shù)的用法。在命令窗口中準(zhǔn)備使用函數(shù)時(shí)編譯器會(huì)顯示該函數(shù)的函數(shù)文檔??梢杂媚Хǚ椒╢unction.__doc__返回函數(shù)的函數(shù)文檔,也可以用help(functionname)的方式查看到函數(shù)名為functionname的函數(shù)的函數(shù)文檔。
例:
def mean(x):"求序列的均值"return [sum(x)/len(x)]print(mean.__doc__)#'求序列的均值' help(mean)#Help on function mean in module __main__:#mean(x)# 求序列的均值3.函數(shù)參數(shù)
Python 的函數(shù)具有非常靈活多樣的參數(shù)形態(tài),既可以實(shí)現(xiàn)簡單的調(diào)用,又可以傳入非常復(fù)雜的參數(shù)。從簡到繁的參數(shù)形態(tài)如下:
- 位置參數(shù) (positional argument)
- 默認(rèn)參數(shù) (default argument)
- 可變參數(shù) (variable argument)
- 關(guān)鍵字參數(shù) (keyword argument)
- 命名關(guān)鍵字參數(shù) (name keyword argument)
- 參數(shù)組合
(1)位置參數(shù)
定義函數(shù)時(shí)直接放在函數(shù)形參表中,沒有任何操作符的參數(shù)。這種參數(shù)在調(diào)用函數(shù)時(shí),位置要固定,除非調(diào)用時(shí)寫出參數(shù)名進(jìn)行賦值。
(2)默認(rèn)參數(shù)
定義函數(shù)時(shí)在形參表中完成賦值的參數(shù),這類參數(shù)稱為默認(rèn)參數(shù)。若調(diào)用函數(shù)時(shí)傳入了參數(shù),則使用傳入的參數(shù)值;如果沒有傳入?yún)?shù)值,則使用默認(rèn)的參數(shù)值。
注:定義函數(shù)時(shí)默認(rèn)參數(shù)一定要放在位置參數(shù)的后面
def printinfo(name, age=8): #name是位置參數(shù),age是默認(rèn)參數(shù)print('Name:{0},Age:{1}'.format(name, age))printinfo('江總',20) #Name:江總,Age:20 printinfo('小王') #Name:小王,Age:8(3)可變參數(shù)
可變參數(shù)意為可傳入的參數(shù)的個(gè)數(shù)是可變的,為任意個(gè)
- 在形參表中寫入 *vararg 表示vararg是可變參數(shù)
- 傳入到vararg里的參數(shù)會(huì)被打包成元組
- 定義時(shí)要將可變參數(shù)放到位置參數(shù)的后面,否則在調(diào)用時(shí)會(huì)因無法給位置參數(shù)傳值而報(bào)錯(cuò)
(4)關(guān)鍵字參數(shù)
關(guān)鍵字參數(shù)也可以傳入任意個(gè)
- 在形參表中寫入 **keyarg 表示keyarg是關(guān)鍵字參數(shù)
- 傳入到keyarg里的參數(shù)會(huì)被打包成字典
- 調(diào)用函數(shù)給關(guān)鍵字參數(shù)傳值時(shí)以類似?function(…… ,key1=value1,key2=value2,……)賦值的方式將傳入關(guān)鍵字參數(shù)的鍵值對(duì)打包
(5)命名關(guān)鍵字參數(shù)
由于傳入關(guān)鍵字參數(shù)時(shí),用戶傳入的鍵值對(duì)內(nèi)容不受限制,如果要限制傳入的關(guān)鍵字的名字可以用命名關(guān)鍵字參數(shù)
- 在形參表中輸入 *, 分隔符,會(huì)將分隔符后面的參數(shù)都視為命名關(guān)鍵字參數(shù)
- 調(diào)用函數(shù)時(shí),必須要寫出命名關(guān)鍵字參數(shù)的參數(shù)名,否則會(huì)報(bào)錯(cuò)
- 命名關(guān)鍵字參數(shù)可以有默認(rèn)值
例:例程來源:https://blog.51cto.com/u_11317783/1953276
def person(name, age, **kw):if 'city' in kw:#關(guān)鍵字參數(shù)kw中是否有city參數(shù)passif 'job' in kw:#關(guān)鍵字參數(shù)kw中是否有job參數(shù)passprint('name:', name, 'age:', age, 'other:', kw)#調(diào)用者仍可以傳入不受限制的關(guān)鍵字參數(shù) person('Jack', 24, city='Beijing', addr='朝陽', zipcode=123456) # 如果要限制關(guān)鍵字參數(shù)的名字,就可以用命名關(guān)鍵字參數(shù),例如,只接收city和job作為關(guān)鍵字參數(shù)def person(name, age, *, city, job):print(name, age, city, job) # 和關(guān)鍵字參數(shù)**kw不同,命名關(guān)鍵字參數(shù)需要一個(gè)特殊分隔符*,*后面的參數(shù)被視為命名關(guān)鍵字參數(shù) # 調(diào)用方式如下 person('Jack', 24, city='Beijing', job='Engineer')(6)參數(shù)組合
定義函數(shù)時(shí)可以使用以上5種參數(shù)的任意組合,只要符合相關(guān)規(guī)定即可。但建議不要用太多的參數(shù),否則可能會(huì)導(dǎo)致函數(shù)代碼的可讀性降低。
4.變量作用域
與其他熱門語言相同,Python中的變量可以分為局部變量和全局變量
- 定義在函數(shù)內(nèi)部的變量擁有局部作用域,該變量稱為局部變量
- 定義在函數(shù)外部的變量擁有全局作用域,該變量稱為全局變量
- 局部變量只能在其被聲明的函數(shù)內(nèi)部訪問,而全局變量可以在整個(gè)程序范圍內(nèi)訪問
- 若內(nèi)部作用域想修改外部作用域的變量時(shí),需要用global或nonlocal關(guān)鍵字,例:
- 函數(shù)內(nèi)部可以調(diào)用其他函數(shù),稱函數(shù)的嵌套
(1)閉包
- 是函數(shù)式編程的一個(gè)重要的語法結(jié)構(gòu),是一種特殊的內(nèi)嵌函數(shù)。
- 如果在一個(gè)內(nèi)部函數(shù)里對(duì)外層非全局作用域的變量進(jìn)行引用,那么內(nèi)部函數(shù)就被認(rèn)為是閉包。
- 通過閉包可以訪問外層非全局作用域的變量,這個(gè)作用域稱為?閉包作用域
- 閉包的返回值通常是函數(shù)
例:
def make_counter(init):counter = [init]def inc(): counter[0] += 1def dec(): counter[0] -= 1def get(): return counter[0]def reset(): counter[0] = initreturn inc, dec, get, resetinc, dec, get, reset = make_counter(0) #內(nèi)部的inc,dec,get,reset都是閉包的,可以訪問counter inc() #外面的inc,dec,get,reset由于獲取的是函數(shù)make_counter inc() #返回的內(nèi)部的四個(gè)inc,dec,get,reset, inc() #外部的inc,dec,get,reset函數(shù)依然可以對(duì)局部變量counter print(get()) # 3 #進(jìn)行操作 dec() print(get()) # 2 reset() print(get()) # 0global關(guān)鍵字聲明的是全局變量,如果要在函數(shù)里的函數(shù)的局部變量的作用域,可用nonlocal關(guān)鍵字。
定義函數(shù)時(shí),函數(shù)體里面還定義了函數(shù),相當(dāng)于有兩層函數(shù),內(nèi)層的函數(shù)是閉包的,可以訪問外層函數(shù)的變量,但如果內(nèi)層函數(shù)出現(xiàn)了和外層函數(shù)同名的變量(假如稱為x)時(shí),內(nèi)層函數(shù)內(nèi)的代碼會(huì)用內(nèi)層函數(shù)的同名變量,這表明在兩個(gè)函數(shù)的作用域里有兩個(gè)不同的x。如果想在內(nèi)部函數(shù)修改外層的變量,則需要在內(nèi)部函數(shù)的變量聲明前加上nonlocal關(guān)鍵字,表明內(nèi)外函數(shù)中的這個(gè)同名變量是同一個(gè)。例:
def outer():num = 10def inner():nonlocal num # nonlocal關(guān)鍵字聲明,表明inner內(nèi)部的num和outer的num是同一個(gè)num = 100print(num)inner()print(num)outer()# 100 # 100(2)遞歸
函數(shù)在內(nèi)部調(diào)用自己,稱這個(gè)函數(shù)為遞歸函數(shù)。遞歸是一種重要的操作。例:
# 利用循環(huán) n = 5 for k in range(1, 5):n = n * k print(n) # 120# 利用遞歸 def factorial(n):if n == 1:return 1return n * factorial(n - 1)print(factorial(5)) # 120?加入包sys,用sys.setrecursionlimit(n)可以設(shè)置遞歸的最大次數(shù)為n,默認(rèn)是1000次
II.Lambda-表達(dá)式
1.定義
lambda關(guān)鍵詞用于定義匿名的函數(shù),匿名函數(shù)沒有函數(shù)名,語法結(jié)構(gòu)為:
lambda argument_list: expression- argument_list為形參表,lambda定義的匿名函數(shù)和def定義的函數(shù)所用參數(shù)相同,但是不用小括號(hào)括起來
- expression為表達(dá)式,相當(dāng)于函數(shù)的返回值
- expression中沒有return語句,因?yàn)閑xpression本身就是返回值
- 匿名函數(shù)有自己的命名空間,匿名函數(shù)內(nèi)部不能訪問形參表之外和全局命名空間里的參數(shù)
例:
def sqr(x):return x ** 2print(sqr) # <function sqr at 0x000000BABD3A4400>y = [sqr(x) for x in range(10)] #運(yùn)用def定義的普通函數(shù) print(y) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]lbd_sqr = lambda x: x ** 2 print(lbd_sqr) # <function <lambda> at 0x000000BABB6AC1E0>y = [lbd_sqr(x) for x in range(10)] #運(yùn)用lambda定義的匿名函數(shù) print(y) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]sumary = lambda arg1, arg2: arg1 + arg2 print(sumary(10, 20)) # 30func = lambda *args: sum(args) print(func(1, 2, 3, 4, 5)) # 152.匿名函數(shù)的運(yùn)用
匿名函數(shù)常常應(yīng)用于高階函數(shù)中,有兩種運(yùn)用方式:
- 參數(shù)是函數(shù)
- 返回值是函數(shù)
例如Python內(nèi)置的filter(function,iterable)函數(shù)和map(function,*iterable)函數(shù)中,都有函數(shù)作為參數(shù),如果用先用def定義一個(gè)常常會(huì)有些麻煩,但如果用lambda定義的匿名函數(shù)就會(huì)簡潔不少。
odd = lambda x: x % 2 == 1 #filter()過濾序列,過濾掉不符合條件的templist = filter(odd, [1, 2, 3, 4, 5, 6, 7, 8, 9]) #元素,返回一個(gè)迭代器對(duì)象,如果要轉(zhuǎn)換為列 print(list(templist)) #表,可以使用list() 來轉(zhuǎn)換 # [1, 3, 5, 7, 9]m1 = map(lambda x: x ** 2, [1, 2, 3, 4, 5]) #map()根據(jù)提供的函數(shù)對(duì)指定序列做映射 print(list(m1)) # [1, 4, 9, 16, 25]m2 = map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10]) print(list(m2)) # [3, 7, 11, 15, 19]III.類與對(duì)象
與C++和Java類似,Python也可以定義自己的類。類是對(duì)象的模板,對(duì)象是類的實(shí)例,類中包含屬性和方法(函數(shù))。
1.定義自己的類
我們可以用關(guān)鍵字class定義類,關(guān)鍵字后面跟類名和冒號(hào),以及類的內(nèi)容,Python中的類名以大寫字母開頭。例:
class Turtle: # 屬性color = 'green'weight = 10legs = 4shell = Truemouth = '大嘴'# 方法def climb(self):print('我正在很努力的向前爬...')def run(self):print('我正在飛快的向前跑...')def bite(self):print('咬死你咬死你!!')def eat(self):print('有得吃,真滿足...')def sleep(self):print('困了,睡了,晚安,zzz')tt = Turtle() print(tt) # <__main__.Turtle object at 0x0000007C32D67F98>print(type(tt)) # <class '__main__.Turtle'>print(tt.__class__) # <class '__main__.Turtle'>print(tt.__class__.__name__) # Turtlett.climb() # 我正在很努力的向前爬...tt.run() # 我正在飛快的向前跑...tt.bite() # 咬死你咬死你!!# Python類也是對(duì)象。它們是type的實(shí)例 print(type(Turtle)) # <class 'type'>2.self
Python 中的 self 相當(dāng)于 C++和Java中的 this 指針。
例:
class Ball:def setName(self, name):self.name = namedef kick(self):print("我叫%s,該死的,誰踢我..." % self.name)a = Ball() a.setName("球A") b = Ball() b.setName("球B") c = Ball() c.setName("球C") a.kick() # 我叫球A,該死的,誰踢我... b.kick() # 我叫球B,該死的,誰踢我...?3.類的構(gòu)造函數(shù)
類的構(gòu)造函數(shù)是一種魔法方法,這個(gè)魔法方法為__init__(self[,param1,param2…])
Python中的構(gòu)造函數(shù)可以自己定義,但是函數(shù)名必須是__init__(self[,param1,param2…])。在類外給類實(shí)例化(產(chǎn)生對(duì)象)時(shí)會(huì)自動(dòng)調(diào)用,調(diào)用方法是? 類名(參數(shù)),參數(shù)是__init__()中除self之外的參數(shù)。
在定義子類的構(gòu)造函數(shù)時(shí)應(yīng)調(diào)用父類的構(gòu)造函數(shù)。如果不調(diào)用父類的構(gòu)造函數(shù),可能會(huì)因子類的構(gòu)造函數(shù)覆蓋了父類的構(gòu)造函數(shù)而導(dǎo)致從父類繼承過來的屬性不是自己想要的值。當(dāng)然,你也可以通過在子類的構(gòu)造函數(shù)里給所有屬性賦值來避免這種情況,但是這樣在類的屬性較多時(shí)就會(huì)比較繁瑣了。
例子可見下文“類的繼承”部分
4.公有和私有
在 Python 中定義私有變量只需要在變量名或函數(shù)名前加上“__”兩個(gè)下劃線,那么這個(gè)函數(shù)或變量就會(huì)為私有的了。
其余的與C++和Java中的公有私有類似,此處不作贅述。但是要提的是Python中的私有是偽私有,只要用以下形式仍然可以在類外直接訪問或調(diào)用對(duì)象的私有屬性或私有方法。
對(duì)象._類名__私有屬性 #只需在對(duì)象的.和私有數(shù)據(jù)或方法的雙下劃線__ 對(duì)象._類名__私有方法 #之間插入下劃線 _ 加上類名即可5.類的繼承與多態(tài)
定義的類可以用以下語法形式繼承父類:
class 子類名(父類名1,父類名2,……,父類名n):類的實(shí)現(xiàn)子類可以繼承父類的數(shù)據(jù)和函數(shù)。子類必須與派生類定義在一個(gè)作用域內(nèi)。除了類,還可以用表達(dá)式,基類定義在另一個(gè)模塊中時(shí)這一點(diǎn)非常有用。
與Java類似,子類可以重寫父類的函數(shù)。但是子類在重寫構(gòu)造函數(shù)時(shí),由于構(gòu)造函數(shù)名不能變化,子類的構(gòu)造函數(shù)就會(huì)覆蓋父類的構(gòu)造函數(shù),這樣可能會(huì)出現(xiàn)以下狀況:
import randomclass Fish:def __init__(self):self.x = random.randint(0, 10)self.y = random.randint(0, 10)def move(self):self.x -= 1print("我的位置", self.x, self.y)class GoldFish(Fish): # 金魚passclass Carp(Fish): # 鯉魚passclass Salmon(Fish): # 三文魚passclass Shark(Fish): # 鯊魚def __init__(self):self.hungry = Truedef eat(self):if self.hungry:print("吃貨的夢(mèng)想就是天天有得吃!")self.hungry = Falseelse:print("太撐了,吃不下了!")self.hungry = Trueg = GoldFish() g.move() # 我的位置 9 4 s = Shark() s.eat() # 吃貨的夢(mèng)想就是天天有得吃! s.move() # AttributeError: 'Shark' object has no attribute 'x' ''' Shark類的構(gòu)造函數(shù)沒有調(diào)用父類Fish的構(gòu)造函數(shù) 導(dǎo)致對(duì)象s在實(shí)例化時(shí)沒有x和y兩個(gè)屬性 從而在調(diào)用move()函數(shù)時(shí)訪問x和y就會(huì)出錯(cuò) '''?要在子類的構(gòu)造函數(shù)中調(diào)用父類的構(gòu)造函數(shù),可以有以下兩種方法:
class Shark(Fish): # 鯊魚def __init__(self):Fish.__init__(self) #調(diào)用未綁定的父類方法Fish.__init__(self)self.hungry = Truedef eat(self):if self.hungry:print("吃貨的夢(mèng)想就是天天有得吃!")self.hungry = Falseelse:print("太撐了,吃不下了!")self.hungry = True'''或是用以下方法''' class Shark(Fish): # 鯊魚def __init__(self):super().__init__() #用super函數(shù)super().__init__()self.hungry = Truedef eat(self):if self.hungry:print("吃貨的夢(mèng)想就是天天有得吃!")self.hungry = Falseelse:print("太撐了,吃不下了!")self.hungry = True?其中super函數(shù)和Java中的super關(guān)鍵字類似,但是在Python的子類中用super()來表示父類的對(duì)象。
在多繼承時(shí)需要注意圓括號(hào)中父類的順序,若是父類中有相同的函數(shù)名,而在子類使用時(shí)未指定,Python 從左至右搜索,即函數(shù)在子類中未找到時(shí),從左到右查找父類中是否包含該函數(shù)。
與Java類似,子類也可以通過重寫父類的函數(shù),再通過用子類的構(gòu)造方法構(gòu)造父類的對(duì)象來實(shí)現(xiàn)多態(tài)。Java中稱用子類構(gòu)造方法構(gòu)造的父類對(duì)象為上轉(zhuǎn)型對(duì)象,調(diào)用同名的函數(shù)時(shí)會(huì)調(diào)用子類中的,但是子類有而父類中沒有的屬性和函數(shù)會(huì)丟失。
多態(tài):不同對(duì)象對(duì)同一方法響應(yīng)不同的行動(dòng)
例:
class Animal:def run(self):raise AttributeError('子類必須實(shí)現(xiàn)這個(gè)方法')class People(Animal):def run(self):print('人正在走')class Pig(Animal):def run(self):print('pig is walking')class Dog(Animal):def run(self):print('dog is running')def func(animal):animal.run()func(Pig()) #Pig()是Pig類的構(gòu)造函數(shù) # pig is walking6.組合
即其他類的對(duì)象可以成為另一個(gè)類的屬性,例:
class Turtle:def __init__(self, x):self.num = xclass Fish:def __init__(self, x):self.num = xclass Pool:def __init__(self, x, y):self.turtle = Turtle(x)self.fish = Fish(y)def print_num(self):print("水池里面有烏龜%s只,小魚%s條" % (self.turtle.num, self.fish.num))p = Pool(2, 3) p.print_num() # 水池里面有烏龜2只,小魚3條7.類對(duì)象和實(shí)例對(duì)象
- 在Python中“萬物皆對(duì)象”,所以我們創(chuàng)建的類也是一個(gè)對(duì)象,也在內(nèi)存中開辟了一塊空間,這種對(duì)象叫類對(duì)象,類對(duì)象只能有一個(gè)。
- 通過實(shí)例化類創(chuàng)建的對(duì)象稱為實(shí)例對(duì)象,實(shí)例對(duì)象可以有多個(gè)。
類屬性和實(shí)例屬性:
類屬性和實(shí)例屬性的說法和Java類似,就是靜態(tài)屬性和非靜態(tài)屬性的區(qū)別,但是Python中的類屬性和實(shí)例屬性的聲明不一樣。
- 在類里面方法外聲明的屬性是類屬性,需要賦值,所有實(shí)例對(duì)象都可以訪問且共享。
- 實(shí)例屬性是和具體的實(shí)例有關(guān),并且一個(gè)實(shí)例對(duì)象和另外一個(gè)實(shí)例對(duì)象是不共享屬性的,說白了實(shí)例屬性只能在自己的對(duì)象里面使用,其他的對(duì)象不能直接使用??梢酝ㄟ^類內(nèi)的函數(shù)或類外賦值創(chuàng)建。
- 注:在類外賦值創(chuàng)建實(shí)例屬性時(shí),如果屬性和函數(shù)同名,屬性會(huì)覆蓋函數(shù)。
8.綁定
Python 嚴(yán)格要求方法需要有實(shí)例才能被調(diào)用,這種限制其實(shí)就是 Python 所謂的綁定概念。
Python 對(duì)象的數(shù)據(jù)屬性通常存儲(chǔ)在名為 .__dict__的字典中,我們可以直接訪問 .__dict__ ,或者利用 Python 的內(nèi)置函數(shù) vars()獲取 .__dict__ 。
9.相關(guān)的內(nèi)置函數(shù)
- issubclass(class,classinfo)? 方法用于判斷參數(shù) class 是否是類型參數(shù) classinfo 的子類
- 一個(gè)類被認(rèn)為是其自身的子類
- classinfo?可以是類對(duì)象的元組,只要class是其中任何一個(gè)候選類的子類,則返回True
?
- isinstance(object,classinfo)? 方法用于判斷一個(gè)對(duì)象是否是classinfo的對(duì)象,類似type()
- type()不會(huì)認(rèn)為子類是一種父類類型,不考慮繼承關(guān)系
- isinstance()會(huì)認(rèn)為子類是一種父類類型,考慮繼承關(guān)系
- 如果第一個(gè)參數(shù)不是對(duì)象,則永遠(yuǎn)返回False
- 如果第二個(gè)參數(shù)不是類或者由類對(duì)象組成的元組,會(huì)拋出一個(gè)TypeError異常
?
- hasattr(object,name)? 用于判斷對(duì)象是否包含對(duì)應(yīng)的屬性
- getattr(object,name[,default])? 用于返回一個(gè)對(duì)象屬性值
- setattr(object,name,value)對(duì)應(yīng) getattr(),用于設(shè)置屬性值,該屬性不一定是存在的
- delattr(object,name)? 用于刪除屬性
?
- class property([fget[,fset[,fdel[,doc]]]])??于在新式類中返回屬性值
- fget? ? ? ? ? ?獲取屬性值的函數(shù)
- fset? ? ? ? ? ?設(shè)置屬性值的函數(shù)
- fdel? ? ? ? ? ?刪除屬性值函數(shù)
- doc? ? ? ? ? ?屬性描述信息
IV.魔法方法
Python的魔法方法是一些特殊的方法,又叫魔術(shù)方法,例如上文提到的:__init__()。魔法方法是面向?qū)ο蟮?Python 的一切,有著強(qiáng)大的作用。魔法方法有以下特點(diǎn):
- 魔法方法總是被雙下劃線包圍
- 魔法方法總是能夠在適當(dāng)?shù)臅r(shí)候被自動(dòng)調(diào)用
魔法方法的第一個(gè)參數(shù)應(yīng)為 cls (類方法)? 或者 self(實(shí)例方法)。cls代表一個(gè)類名稱,self代表一個(gè)實(shí)例對(duì)象的名稱。
1.基本的魔法方法
- __init__(self[,……]) 構(gòu)造器,即各個(gè)類的構(gòu)造方法,當(dāng)一個(gè)實(shí)例被創(chuàng)建的時(shí)候調(diào)用的初始化方法。
- __new__(cls[,……])?在一個(gè)對(duì)象實(shí)例化的時(shí)候所調(diào)用的第一個(gè)方法,系統(tǒng)會(huì)先調(diào)用__new__再調(diào)用__init__。
- 注:__new__()至少要有一個(gè)參數(shù)cls,代表要實(shí)例化的類,此參數(shù)在實(shí)例化時(shí)由 Python 解釋器自動(dòng)提供,后面的參數(shù)直接傳遞給__init__()
- 注:__new__()對(duì)當(dāng)前類進(jìn)行了實(shí)例化,即產(chǎn)生了一個(gè)對(duì)象,再將對(duì)象返回,傳給__init__中的self。但是__new__返回的實(shí)例必須是當(dāng)前類的實(shí)例才會(huì)繼續(xù)執(zhí)行__init__。也就是說,如果通過在類中定義了__new__(),使得__new__返回的不是當(dāng)前類的對(duì)象,那當(dāng)前類中的__init__是不會(huì)被調(diào)用的,即使返回的是父類的實(shí)例也不行。
- 例:
- __new__()方法主要是當(dāng)你繼承一些不可變的 class 時(shí)(如:int , str ,tuple), 提供給你一個(gè)自定義這些類的實(shí)例化過程的途徑。
- __del__(self) 析構(gòu)器,相當(dāng)于C++中的析構(gòu)函數(shù),當(dāng)一個(gè)對(duì)象將要被系統(tǒng)回收之時(shí)調(diào)用的方法。在類中也可以自己定義__del__()的內(nèi)容,與C++相似。
- __str__(self) 當(dāng)我們打印(print)一個(gè)對(duì)象的時(shí)候、當(dāng)你使用%s格式化的時(shí)候、str()強(qiáng)轉(zhuǎn)數(shù)據(jù)類型的時(shí)候會(huì)調(diào)用。
- __repr__(self)? __repr__是__str__的備用函數(shù),如果沒有實(shí)現(xiàn)__str__的時(shí)候才會(huì)執(zhí)行__repr__()
- 注:__repr__(obj)內(nèi)置函數(shù)對(duì)應(yīng)的結(jié)果是__repr__的返回值
- 注:當(dāng)使用%r格式化的時(shí)候會(huì)調(diào)用__repr__
__str__(self)的返回結(jié)果可讀性強(qiáng)。也就是說,__str__的意義是得到便于閱讀的信息,就像下面的 '2019-10-11' 一樣。
__repr__(self)的返回結(jié)果應(yīng)更準(zhǔn)確。也就是說,__repr__存在的目的在于調(diào)試,便于開發(fā)者使用。
例:
import datetimetoday = datetime.date.today() print(str(today)) # 2019-10-11 print(repr(today)) # datetime.date(2019, 10, 11) print('%s' %today) # 2019-10-11 print('%r' %today) # datetime.date(2019, 10, 11)2.算術(shù)運(yùn)算符
Python中可以在類中定義運(yùn)算符對(duì)應(yīng)的魔法方法,進(jìn)而實(shí)現(xiàn)類對(duì)象的算術(shù)運(yùn)算的操作。
- __add__(self,other)? ? ? ? ? ? ? ? ? ? ? ? ? ? 定義加法 :+
- __sub__(self,other)? ? ? ? ? ? ? ? ? ? ? ? ? ? 定義減法 :-
- __mul__(self,other)? ? ? ? ? ? ? ? ? ? ? ? ? ? 定義乘法 :*
- __truediv__(self,other)? ? ? ? ? ? ? ? ? ? ? ?定義真除法 :/
- __floordiv__(self,other)? ? ? ? ? ? ? ? ? ? ? 定義整除 ://
- __mod__(self,other)? ? ? ? ? ? ? ? ? ? ? ? ? ?定義取余 :%
- __divmod__(self,other)? ? ? ? ? ? ? ? ? ? ? 定義被divmod()調(diào)用時(shí)的行為
- 注:divmod(a,b)把除數(shù)和余數(shù)運(yùn)算結(jié)果結(jié)合起來,返回一個(gè)包含商和余數(shù)的元組(a//b , a%b)
- __pow__(self,other[,module])? ? ? ? ? ? 定義乘方 :** ,或被power()調(diào)用時(shí)的行為。
- 注:未定義時(shí)power()與**操作結(jié)果相同
- __lshift__(self,other)? ? ? ? ? ? ? ? ? ? ? ? ? 定義左移運(yùn)算符 :<<
- __rshift__(self,other)? ? ? ? ? ? ? ? ? ? ? ? ? 定義右移運(yùn)算符 :>>
- __and__(self,other)? ? ? ? ? ? ? ? ? ? ? ? ? ? 定義按位與 :&
- __xor__(self,other)? ? ? ? ? ? ? ? ? ? ? ? ? ? ?定義按位異或 :^
- __or__(self,other)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?定義按位或 :|
例:
class MyClass:def __init__(self, height, weight):self.height = heightself.weight = weight# 兩個(gè)對(duì)象的身高相加,體重相加。返回一個(gè)新的對(duì)象def __add__(self, others):return MyClass(self.height + others.height, self.weight + others.weight)# 兩個(gè)對(duì)象的身高相減,體重相減。返回一個(gè)新的對(duì)象def __sub__(self, others):return MyClass(self.height - others.height, self.weight - others.weight)# 說一下自己的參數(shù)def intro(self):print("高為", self.height, " 重為", self.weight)def main():a = MyClass(height=10, weight=5)a.intro()b = MyClass(height=20, weight=10)b.intro()c = b - ac.intro()d = a + bd.intro()if __name__ == '__main__':main()# 高為 10 重為 5 # 高為 20 重為 10 # 高為 10 重為 5 # 高為 30 重為 153.反算術(shù)運(yùn)算符
反運(yùn)算魔方方法,與算術(shù)運(yùn)算符保持一一對(duì)應(yīng),不同之處就是反運(yùn)算的魔法方法多了一個(gè)“r”。當(dāng)文件左操作不支持相應(yīng)的操作時(shí)被調(diào)用。
以定義加法的魔法方法為例,定義算術(shù)運(yùn)算符+用__add__(self,other),反算術(shù)運(yùn)算符+用__radd__(self,other)。上文所提所有運(yùn)算符都有對(duì)應(yīng)的反算術(shù)運(yùn)算符。
反算術(shù)運(yùn)算符的用處可以用a+b舉例,這里a是加數(shù),b是被加數(shù),當(dāng)使用__add__(self,other)定義的+運(yùn)算時(shí),會(huì)將a傳給self,b傳給other,也就是調(diào)用對(duì)象a的__add__()方法。如果定義的__add__(self,other)不支持當(dāng)前操作,或者_(dá)_add__(self,other)沒有實(shí)現(xiàn),Python就會(huì)調(diào)用對(duì)象b的__radd__(self,other)。換句話說就是如果a+b實(shí)現(xiàn)不了,就計(jì)算b+a。
例:
class Nint(int):def __radd__(self, other):return int.__sub__(other, self) # 注意 self 在后面a = Nint(5) b = Nint(3) print(a + b) # 8 print(1 + b) # -2 '''代碼解釋: 運(yùn)算1 + b時(shí),1是int類型的對(duì)象,b是Nint類型的對(duì)象 一開始先嘗試調(diào)用1.__add__(),由于不支持此類操作 系統(tǒng)便嘗試調(diào)用b.__radd__(),將b傳入self,1傳入other 返回的是int.__sub__(),1 + b的運(yùn)算結(jié)果就變成1 - 3 '''4.增量賦值運(yùn)算符
Python中有增量賦值運(yùn)算符,例如 += ,a += 2 與 a = a + 2等價(jià)。定義增量賦值運(yùn)算符的魔法方法與算術(shù)運(yùn)算符的魔法方法一一對(duì)應(yīng),不同之處在于名字前面多了一個(gè)“ i ”
- __iadd__(self,other)? ? ? ? ? ? ? ? ? ? ? ?定義賦值加法:+=
- __isub__(self,other)? ? ? ? ? ? ? ? ? ? ? ?定義賦值減法:-=
- __imul__(self,other)? ? ? ? ? ? ? ? ? ? ? ?定義賦值乘法:*=
- __itruediv__(self,other)? ? ? ? ? ? ? ? ??定義賦值真除法:/=
- __ifloordiv__(self,other)? ? ? ? ? ? ? ? ?定義賦值整數(shù)除法://=
- __imod__(self,other)? ? ? ? ? ? ? ? ? ? ? 定義賦值取模算法:%=
- __ipow__(self,other[,module])? ? ? ?定義賦值冪運(yùn)算:**=
- __ilshift__(self,other)? ? ? ? ? ? ? ? ? ? ?定義賦值按位左移位:<<=
- __irshift__(self,other)? ? ? ? ? ? ? ? ? ??定義賦值按位右移位:>>=
- __iand__(self,other)? ? ? ? ? ? ? ? ? ? ??定義賦值按位與操作:&=
- __ixor__(self,other)? ? ? ? ? ? ? ? ? ? ? ?定義賦值按位異或操作:^=
- __ior__(self,other)? ? ? ? ? ? ? ? ? ? ? ? ?定義賦值按位或操作:|=
5.一元運(yùn)算符
- __neg__(self)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?定義正號(hào):+x
- __pos__(self)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?定義負(fù)號(hào):-x
- __abs__(self)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?定義被abs()調(diào)用時(shí)的行為,即絕對(duì)值
- __invert__(self)? ? ? ? ? ? ? ? ? ? ? ? ? ? 定義按位取反:~x
6.屬性訪問
- __getattr__(self,name)??? ? ? ? ? ? ? ?定義當(dāng)用戶試圖獲取一個(gè)不存在的屬性時(shí)的行為。
- __getattribute__(self,name)? ?定義當(dāng)該類的屬性被訪問時(shí)的行為(先調(diào)用該方法,查看是否存在該屬性,若不存在,接著去調(diào)用__getattr__)。
- __setattr__(self,name,value)? ? ? ? 定義當(dāng)一個(gè)屬性被設(shè)置時(shí)的行為。
- __delattr__(self,name)? ? ? ? ? ? ? ? ? 定義當(dāng)一個(gè)屬性被刪除時(shí)的行為。
例:
class C:def __getattribute__(self, item):print('__getattribute__')return super().__getattribute__(item)def __getattr__(self, item):print('__getattr__')def __setattr__(self, key, value):print('__setattr__')super().__setattr__(key, value)def __delattr__(self, item):print('__delattr__')super().__delattr__(item)c = C() c.x # __getattribute__ # __getattr__c.x = 1 # __setattr__del c.x # __delattr__7.描述符
描述符就是將某種特殊類型的類的實(shí)例指派給另一個(gè)類的屬性。
- __get__(self,instance,owner)? ? ? ? 用于訪問屬性,它返回屬性的值。
- __set__(self,instance,value)? ? ? ? ?將在屬性分配操作中調(diào)用,不返回任何內(nèi)容。
- __delete__(self,instance)? ? ? ? ? ? ?控制刪除操作,不返回任何內(nèi)容。
例:
class MyDecriptor:def __get__(self, instance, owner):print('__get__', self, instance, owner)def __set__(self, instance, value):print('__set__', self, instance, value)def __delete__(self, instance):print('__delete__', self, instance)class Test:x = MyDecriptor()t = Test() t.x # __get__ <__main__.MyDecriptor object at 0x000000CEAAEB6B00> <__main__.Test object at 0x000000CEABDC0898> <class '__main__.Test'>t.x = 'x-man' # __set__ <__main__.MyDecriptor object at 0x00000023687C6B00> <__main__.Test object at 0x00000023696B0940> x-mandel t.x # __delete__ <__main__.MyDecriptor object at 0x000000EC9B160A90> <__main__.Test object at 0x000000EC9B160B38>8.定制序列
Python有一種類叫協(xié)議(Protocols),協(xié)議與其它編程語言中的接口很相似,它規(guī)定你哪些方法必須要定義。然而,在 Python 中的協(xié)議就顯得不那么正式。事實(shí)上,在 Python 中,協(xié)議更像是一種指南。
容器類型相關(guān)的魔法方法有:
- __len__(self)? ? ? ? ? ? ? ? ? ? ? ? ? ? 定義當(dāng)被len()調(diào)用時(shí)的行為(返回容器中元素的個(gè)數(shù))
- __getitem__(self,key)? ? ? ? ? ? ? 定義 獲取容器中元素 的行為
- __setitem__(self,key,value)? ? ?定義設(shè)置容器中指定元素的行為,相當(dāng)于self[key] = value
- __delitem__(self,key)? ? ? ? ? ? ? ?定義刪除容器中指定元素的行為,相當(dāng)于del self[key]
而容器類型的協(xié)議有:
- 如果希望定制的容器是不可變的,則只需要在類中定義__len__()和__getitem__()方法
- 如果希望定制的容器是可變的,則除了要定義__len__()和__getitem__()方法外,還需要定義__setitem__()和__delitem__()方法
例:
class CountList: #定義可變類型的容器類def __init__(self, *args):self.values = [x for x in args]self.count = {}.fromkeys(range(len(self.values)), 0)def __len__(self):return len(self.values)def __getitem__(self, item):self.count[item] += 1 #統(tǒng)計(jì)容器內(nèi)元素被訪問的次數(shù)return self.values[item]def __setitem__(self, key, value):self.values[key] = valuedef __delitem__(self, key):del self.values[key]for i in range(0, len(self.values)):if i >= key:self.count[i] = self.count[i + 1]self.count.pop(len(self.values))c1 = CountList(1, 3, 5, 7, 9) c2 = CountList(2, 4, 6, 8, 10) print(c1[1]) # 3 print(c2[2]) # 6 c2[2] = 12 print(c1[1] + c2[2]) # 15 print(c1.count) # {0: 0, 1: 2, 2: 0, 3: 0, 4: 0} print(c2.count) # {0: 0, 1: 0, 2: 2, 3: 0, 4: 0} del c1[1] print(c1.count) # {0: 0, 1: 0, 2: 0, 3: 0}9.迭代器
- 迭代是 Python 最強(qiáng)大的功能之一,是訪問集合元素的一種方式。
- 迭代器是一個(gè)可以記住遍歷的位置的對(duì)象。
- 迭代器對(duì)象從集合的第一個(gè)元素開始訪問,直到所有的元素被訪問完結(jié)束。
- 迭代器只能往前不會(huì)后退。
- 字符串,列表或元組對(duì)象都可用于創(chuàng)建迭代器
迭代器有兩個(gè)基本方法:iter() 和 next()
- iter(obj)? ? ? ? ? ? ? ? ? ? ? ? ?用于生成迭代器
- next(iterator[,default])? ?返回迭代器的下一個(gè)項(xiàng)目。iterator指可迭代對(duì)象,即一個(gè)迭代器。default是可選參數(shù),如果沒有下一個(gè)元素時(shí)返回默認(rèn)值default,如果沒設(shè)置,又沒有下一個(gè)元素則會(huì)報(bào)錯(cuò)
例:
links = {'B': '百度', 'A': '阿里', 'T': '騰訊'}it = iter(links) while True:try:each = next(it)except StopIteration:breakprint(each)# B # A # Tit = iter(links) print(next(it)) # B print(next(it)) # A print(next(it)) # T print(next(it)) # StopIteration如果要定義一個(gè)類來當(dāng)?shù)?#xff0c;則需要在類中實(shí)現(xiàn)兩個(gè)魔法方法__iter__()和__next__()
- __iter__(self)? ? ? ? ?定義迭代容器中的元素的行為,返回一個(gè)特殊的迭代器對(duì)象, 這個(gè)迭代器對(duì)象實(shí)現(xiàn)了__next__()方法并通過StopIteration異常標(biāo)識(shí)迭代的完成
- __next__()? ? ? ? ? ? ?返回下一個(gè)迭代器對(duì)象
- StopIteration?異常用于標(biāo)識(shí)迭代的完成,防止出現(xiàn)無限循環(huán)的情況,在__next__()方法中我們可以設(shè)置在完成指定循環(huán)次數(shù)后觸發(fā)StopIteration異常來結(jié)束迭代
例:
class Fibs:def __init__(self, n=10):self.a = 0self.b = 1self.n = ndef __iter__(self):return selfdef __next__(self):self.a, self.b = self.b, self.a + self.bif self.a > self.n:raise StopIterationreturn self.afibs = Fibs(100) for each in fibs:print(each, end=' ')# 1 1 2 3 5 8 13 21 34 55 89生成器
- 在 Python 中,使用了 yield 的函數(shù)被稱為生成器(generator)。
- 跟普通函數(shù)不同的是,生成器是一個(gè)返回迭代器的函數(shù),只能用于迭代操作,更簡單點(diǎn)理解生成器就是一個(gè)迭代器。
- 在調(diào)用生成器運(yùn)行的過程中,每次遇到 yield 時(shí)函數(shù)會(huì)暫停并保存當(dāng)前所有的運(yùn)行信息,返回 yield 的值, 并在下一次執(zhí)行?next()?方法時(shí)從當(dāng)前位置繼續(xù)運(yùn)行。
- 調(diào)用一個(gè)生成器函數(shù),返回的是一個(gè)迭代器對(duì)象
? 例:
def myGen():print('生成器執(zhí)行!')yield 1yield 2myG = myGen() for each in myG:print(each)''' 生成器執(zhí)行! 1 2 '''myG = myGen() print(next(myG)) # 生成器執(zhí)行! # 1print(next(myG)) # 2 print(next(myG)) # StopIteration三、學(xué)習(xí)問題與解答
本次學(xué)習(xí)所遇到的問題還是對(duì)大多數(shù)方法的不熟悉。尤其本次學(xué)習(xí)的主要就是方法,包括Python中最重要的魔法方法,在代碼運(yùn)行時(shí)什么時(shí)候會(huì)調(diào)用魔法方法并不熟悉。主要還是需要多上手練習(xí)才能知道。
四、學(xué)習(xí)思考與總結(jié)
本次學(xué)習(xí)主要是學(xué)學(xué)習(xí)Python中的函數(shù),或叫方法。從定義普通的函數(shù),匿名函數(shù)和魔法函數(shù)都有很大的用處。魔法方法和類的運(yùn)用可以做出很多簡潔又包括多方面功能的操作。主要是要多上手才能有更深的了解。
?
總結(jié)
以上是生活随笔為你收集整理的阿里云天池 Python训练营Task3: Python基础进阶:从函数到高级魔法方法 学习笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿里云天池 Python训练营Task
- 下一篇: 阿里云天池 Python训练营Task4