诺,你们要的Python进阶来咯!【函数、类进阶必备】
Python基礎(chǔ)及進(jìn)階內(nèi)容持續(xù)更新中!歡迎小伙伴們一起關(guān)注學(xué)習(xí)!
目錄
一、Python進(jìn)階初體驗(yàn)——內(nèi)置函數(shù)
1、數(shù)據(jù)類型相關(guān)
2、數(shù)值計(jì)算相關(guān)
3、bool 值判斷相關(guān)
4、IO 相關(guān)
5、元數(shù)據(jù)相關(guān)
6、help()函數(shù)
7、sorted()函數(shù)
8、range()函數(shù)
二、給代碼安個(gè)家——函數(shù)進(jìn)階
1、位置參數(shù)
2、參數(shù)默認(rèn)值
3、關(guān)鍵字參數(shù)
4、任意參數(shù)列表
5、多返回值
三、讓你函數(shù)更好用——類進(jìn)階
1、類屬性和類方法
(1)類屬性的定義
(2)類方法的定義
2、靜態(tài)方法
3、私有屬性、方法
4、特殊方法
5、類的繼承
(1)類的簡單繼承
(2)類的繼承鏈
(3)類的多繼承
四、從小獨(dú)棟升級為別墅區(qū)——函數(shù)式編程
1、函數(shù)賦值給變量
2、函數(shù)作為函數(shù)參數(shù)
3、lambda 表達(dá)式
寫在前面
Hello,你好呀,我是灰小猿!一個(gè)超會(huì)寫bug的程序猿!
最近和大家總結(jié)了幾期有關(guān)Python基礎(chǔ)入門和常見報(bào)錯(cuò)解決的相關(guān)文章,得到了很多小伙伴的支持,同時(shí)Python基礎(chǔ)入門相關(guān)的內(nèi)容也算是和大家總結(jié)得差不多了,有想學(xué)習(xí)或參考的小伙伴可以看以下幾篇文章:
Python基礎(chǔ)入門:
【全網(wǎng)力薦】堪稱最易學(xué)的Python基礎(chǔ)入門教程
萬字長文爆肝Python基礎(chǔ)入門【第二彈、超詳細(xì)數(shù)據(jù)類型總結(jié)】
常見報(bào)錯(cuò)及解決:
全網(wǎng)最值得收藏的Python常見報(bào)錯(cuò)及其解決方案,再也不用擔(dān)心遇到BUG了!
今天就繼續(xù)來和大家分享有關(guān)Python進(jìn)階中函數(shù)和類使用的相關(guān)內(nèi)容,同時(shí)之后還會(huì)繼續(xù)更新,感興趣的小伙伴可以關(guān)注一起學(xué)習(xí)呀!
?
一、Python進(jìn)階初體驗(yàn)——內(nèi)置函數(shù)
Python 中內(nèi)置有很多常用的函數(shù),這些函數(shù)無需從模塊中導(dǎo)入,可直接使用。由于內(nèi)置函數(shù)有六七十個(gè)之多,
故這里不一一介紹,只介紹一些最常用的,有關(guān)其他詳細(xì)的內(nèi)置函數(shù)大家可以參考這里“菜鳥教程—Python內(nèi)置函數(shù)”。
1、數(shù)據(jù)類型相關(guān)
| dict() | 將參數(shù)轉(zhuǎn)換為字典類型 | dict(a=1, b=2, c=3) | {'a': 1, 'b': 2, 'c': 3} |
| float() | 將字符串或數(shù)字轉(zhuǎn)換為浮點(diǎn)型 | float('0.22') | 0.22 |
| int() | 將字符串或數(shù)字轉(zhuǎn)換為整數(shù)型 | int(1.23) | 1 |
| list() | 將元組、字符串等可迭代對象轉(zhuǎn)換為列表 | list('abc') | ['a', 'b', 'c'] |
| tuple() | 將列表、字符串等可迭代對象轉(zhuǎn)換為元組 | tuple([1, 2, 3]) | (1, 2, 3) |
| set() | 1.創(chuàng)建空集合;2.將可迭代對象轉(zhuǎn)換為列表集合 | set('abc') | {'b', 'a', 'c'} |
| str() | 將參數(shù)轉(zhuǎn)換為字符串 | str(3.14) | '3.14' |
| bytes() | 將參數(shù)轉(zhuǎn)換為字節(jié)序列 | bytes(4) | b'\x00\x00\x00\x00 |
擴(kuò)展:上表中的函數(shù)嚴(yán)格來講并不是函數(shù),而是類,只是其命名風(fēng)格和使用方式和函數(shù)類似。
可迭代對象:如列表、元組、字符串、集合、字典等。關(guān)于可迭代對象的使用計(jì)劃在下一篇和大家分享。
2、數(shù)值計(jì)算相關(guān)
| max() | 求最大值 | max([13, 2, 0.6, -51, 7]) | 13 |
| min() | 求最小值 | min([13, 2, 0.6, -51, 7]) | -51 |
| sum() | 求和 | sum([13, 2, 0.6, -51, 7]) | -28.4 |
| abs() | 求絕對值 | abs(-51) | 51 |
| pow() | 求次方 | pow(2, 10) | 1024 |
| bin() | 轉(zhuǎn)換為二進(jìn)制 | bin(77) | '0b1001101' (注意結(jié)果為字符串) |
| hex() | 轉(zhuǎn)換為十六進(jìn)制 | hex(77) | '0x4d' (注意結(jié)果為字符串) |
| round() | 浮點(diǎn)數(shù)四舍五入 | round(4.5678, 2) (第二個(gè)參數(shù)為小數(shù)精度) | 4.57 |
3、bool 值判斷相關(guān)
| bool() | 判斷參數(shù)是否為真,為真則返回 True,否則返回 False。「為真」指的是,表達(dá)式的結(jié)果為布爾值 True,或非零數(shù)字,或非空字符串,或非空列表 |
| all() | 如果可迭代對象中的所有值,在逐一應(yīng)用 bool(值) 后結(jié)果都為 True,則返回 True,否則返回 False |
| any() | 如果可迭代對象中的任意一個(gè)或多個(gè)值,在應(yīng)用 bool(值) 后結(jié)果為 True,則返回 True,否則返回 False |
關(guān)于上述三個(gè)函數(shù)的使用可以看下面的實(shí)例:
>>> bool(2)
True
>>> bool(0)
False
>>> bool([1, 2, 3])
True
>>> bool([])
False
>>> bool(‘a(chǎn)bc’)
True
>>> bool(’’)
False
>>> all([‘a(chǎn)’, 1, [1]])
True
>>> all([‘a(chǎn)’, 0, [1]])
False
>>> any([’’, 0, []])
False
>>> any([‘a(chǎn)’, 0, []])
True
4、IO 相關(guān)
IO 即輸入輸出。
| input() | 從標(biāo)準(zhǔn)輸入中讀取字符串 |
| print() | 將內(nèi)容寫入標(biāo)準(zhǔn)輸出中 |
| open() | 打開一個(gè)文件。之后便可以對文件做讀寫操作。詳見 IO 操作章節(jié) |
5、元數(shù)據(jù)相關(guān)
| type() | 獲取對象的類型 |
| isinstance() | 判斷對象是否是某個(gè)類(或其子類)的對象 |
| dir() | 獲取類或?qū)ο笾械乃蟹椒ê蛯傩?#xff1b;無參數(shù)時(shí)獲取當(dāng)前作用域下的所有名字 |
| id() | 返回一個(gè)對象的唯一標(biāo)識(shí)。在我們所使用的 CPython 中這個(gè)唯一標(biāo)識(shí)實(shí)際為該對象在內(nèi)存中的地址 |
type() 示例:
>>> numbers = [1, 2, 3]
>>> type(numbers)
<class ‘list’>
isinstance() 示例:
>>> numbers = [1, 2, 3]
>>> isinstance(numbers, list)
True
>>> isinstance(numbers, str)
False
也可以把多個(gè)類型放在元組中,其中一個(gè)與對象的類型相符即為 True,若無相符則為 False。如:
>>> numbers = [1, 2, 3]
>>> isinstance(numbers, (list, str))
True
dir() 示例:
>>> dir(list)
[’__add__’, ‘__class__’, ‘__contains__’, ‘__delattr__’, ‘__delitem__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__getitem__’, ‘__gt__’, ‘__hash__’, ‘__iadd__’, '__imul__, ‘__init__’, ‘__init_subclass__’, ‘__iter__’, ‘__le__’, ‘__len__’, ‘__lt__’, ‘__mul__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__reversed__’, ‘__rmul__’, ‘__setattr__’, ‘__setitem__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘a(chǎn)ppend’, ‘clear’, ‘copy’, ‘count’, ‘extend’, ‘index’, ‘insert’, ‘pop’, ‘remove’, ‘reverse’, ‘sort’]
id() 示例:
>>> number = 1
>>> id(number)
4411695232
>>> numbers = [1, 2, 3, 4]
>>> id(numbers)
4417622792
6、help()函數(shù)
解釋器交互模式下獲取某個(gè)函數(shù)、類的幫助信息,非常實(shí)用。
比如查看內(nèi)置函數(shù) any() 的用法:
>>> help(any) # 只需使用函數(shù)名字
將顯示出 any() 的幫助信息:
Help on built-in function any in module builtins:any(iterable, /)Return True if bool(x) is True for any x in the iterable.If the iterable is empty, return False. (END)按下 q 鍵退出上述界面。
對于這個(gè)章節(jié)中的內(nèi)置函數(shù),如果你有不清楚的地方,便可以用 help() 來查看使用說明。
7、sorted()函數(shù)
對可迭代對象中的數(shù)據(jù)進(jìn)行排序,返回一個(gè)新的列表。
>>> numbers = (4, 5, 2, 8, 9, 1, 0)
>>> sorted(numbers)
[0, 1, 2, 4, 5, 8, 9]
通過參數(shù) reverse=True 指定倒序:
>>> numbers = (4, 5, 2, 8, 9, 1, 0)
>>> sorted(numbers, reverse=True)
[9, 8, 5, 4, 2, 1, 0]
通過參數(shù) key 指定排序時(shí)所使用的字段:
>>> codes = [(‘上海’, ‘021’), (‘北京’, ‘010’), (‘成都’, ‘028’), (‘廣州’, ‘020’)]
>>> sorted(codes, key=lambda x: x[1])
[(‘北京’, ‘010’), (‘廣州’, ‘020’), (‘上海’, ‘021’), (‘成都’, ‘028’)]
說明:指定 key 排序需要用到 lambda 表達(dá)式。有關(guān) lambda 表達(dá)式的內(nèi)容將在函數(shù)式編程章節(jié)中介紹。
8、range()函數(shù)
獲取一個(gè)整數(shù)序列。可指定起始數(shù)值,結(jié)束數(shù)值,增長步長。
在 for 循環(huán)中想要指定循環(huán)次數(shù)時(shí)非常有用。
-
指定起始數(shù)值和結(jié)束數(shù)值,獲取一個(gè)連續(xù)的整數(shù)序列
for i in range(2, 6):print(i)>>> for i in range(2, 6):
… print(i)
…
2
3
4
5注意,生成的數(shù)值范圍為左閉右開區(qū)間,即不包括所指定的結(jié)束數(shù)值。
-
只指定結(jié)束數(shù)值,此時(shí)起始數(shù)值默認(rèn)為 0
>>> for i in range(4):
… print(i)
…
0
1
2
3 -
指定步長(第三個(gè)參數(shù))
>>> for i in range(3, 15, 3):
… print(i)
…
3
6
9
12
?
二、給代碼安個(gè)家——函數(shù)進(jìn)階
1、位置參數(shù)
位置參數(shù)這個(gè)名稱其實(shí)我們并不陌生,之前所編寫的函數(shù)使用的就是位置參數(shù)。位置參數(shù),顧名思義,傳入函數(shù)時(shí)每個(gè)參數(shù)都是通過位置來作區(qū)分的。函數(shù)調(diào)用時(shí),傳入的值需按照位置與參數(shù)一一對應(yīng)。
比如下面這個(gè)程序:
def overspeed_rate(current, max, min):if current > max:return (current - max) / max # 超過最大時(shí)速,結(jié)果為正elif current < min:return (current - min) / min # 超過最小時(shí)速,結(jié)果為負(fù)else:return 0 # 不超速,結(jié)果為 0這個(gè)函數(shù)用來判斷車輛在高速上行駛時(shí)超速的比例。它接受三個(gè)參數(shù),current 表示當(dāng)前時(shí)速,max 參數(shù)表示當(dāng)前路段的允許的最大時(shí)速,min 表示所允許的最小時(shí)速。
位置參數(shù)需要按位置順序來傳遞,否則結(jié)果不可預(yù)期。
>>> overspeed_rate(150, 120, 90)
0.25 # 超過最大時(shí)速 25%
>>> overspeed_rate(80, 100, 60)
0 # 不超速
>>> overspeed_rate(60, 120, 90)
-0.3333333333333333 # 超過最小時(shí)速 33.33%
?
2、參數(shù)默認(rèn)值
前面的函數(shù)中,如果最大時(shí)速和最小時(shí)速比較固定,那么每次函數(shù)調(diào)用時(shí)都輸入這個(gè)兩個(gè)參數(shù)就顯得有些繁瑣,這時(shí)我們可以使用參數(shù)默認(rèn)值。
參數(shù)默認(rèn)值也就是給參數(shù)設(shè)置默認(rèn)值,之后函數(shù)調(diào)用時(shí)便可以不傳入這個(gè)參數(shù),Python 自動(dòng)以默認(rèn)值來填充參數(shù)。如果一個(gè)有默認(rèn)值的參數(shù)依然被傳入了值,那么默認(rèn)值將會(huì)被覆蓋。
函數(shù)定義時(shí),以 參數(shù)=值 來指定參數(shù)默認(rèn)值。如下:
def 函數(shù)(參數(shù)1, 參數(shù)2=默認(rèn)值):pass例如上面的 overspeed_rate 函數(shù), max 和 min 通常比較固定,我們可以使用一個(gè)常用值來作為默認(rèn)值。
def overspeed_rate(current, max=120, min=90):if current > max:return (current - max) / maxelif current < min:return (current - min) / minelse:return 0>>> overspeed_rate(192)
0.6
>>> overspeed_rate(45)
-0.5
?
3、關(guān)鍵字參數(shù)
對于 overspeed_rate 函數(shù),我們還可以在函數(shù)調(diào)用時(shí),以 參數(shù)名=值 的形式來向指定的參數(shù)傳入值。
如:
overspeed_rate(100, min=80)或者
overspeed_rate(current=100, min=80)或者
overspeed_rate(current=100, max=100, min=80)在調(diào)用函數(shù)時(shí)以 參數(shù)名=值 指明要傳遞的參數(shù),這種以關(guān)鍵字的形式來使用的參數(shù)叫做關(guān)鍵字參數(shù)。
使用關(guān)鍵字時(shí)甚至可以打亂參數(shù)傳遞次序:
overspeed_rate(min=80, max=100, current=100)>>> overspeed_rate(min=80, max=100, current=100)
0
但要注意,關(guān)鍵字參數(shù)需要出現(xiàn)在位置參數(shù)之后,否則將拋出 SyntaxError 異常:
>>> overspeed_rate(100, max=100, 80)
???? File “”, line 1
SyntaxError: positional argument follows keyword argument
關(guān)鍵字參數(shù)的用法還不止如此。
當(dāng)我們在定義函數(shù)時(shí),如果參數(shù)列表中某個(gè)參數(shù)使用 **參數(shù)名 形式,那么這個(gè)參數(shù)可以接受一切關(guān)鍵字參數(shù)。如下:
def echo(string, **keywords):print(string)for kw in keywords:print(kw, ":", keywords[kw])>>> echo(‘hello’, today=‘2019-09-04’, content=‘function’, section=3.6)
hello
today : 2019-09-04
content : function
section : 3.6
顯然,我們并沒有在函數(shù)定義時(shí)定義 today、content、section 參數(shù),但是我們卻能接收到它們,這正是 **keywords 發(fā)揮了作用。函數(shù)會(huì)將所有接收到的關(guān)鍵字參數(shù)組裝成一個(gè)字典,并綁定到 keywords 上。驗(yàn)證一下:
>>> def foo(**keywords):
…???? print(keywords)
…
>>> foo(a=1, b=2, c=3)
{‘a(chǎn)’: 1, ‘b’: 2, ‘c’: 3}
?
4、任意參數(shù)列表
定義函數(shù)時(shí),在參數(shù)列表中使用 **參數(shù)名,可以接收一切關(guān)鍵字參數(shù)。類似的,參數(shù)列表中使用 *參數(shù)名,就可以接受任意數(shù)量的非關(guān)鍵字參數(shù),也就是可變參數(shù)。
如,計(jì)算任意個(gè)數(shù)的乘積:
def multiply(*nums):result = 1for n in nums:result *= nreturn result>>> multiply(1,3,5,7)
105
這個(gè)函數(shù)能接收任意個(gè)參數(shù),這正是 *nums 所發(fā)揮的作用。函數(shù)所有接收到的非關(guān)鍵字參數(shù)組裝成一個(gè)元組,并綁定到 nums 上。來試驗(yàn)一下:
>>> def multiply(*nums):
…???? print(nums)
…
>>> multiply(1, 2, 3, 4, 5)
(1, 2, 3, 4, 5)
?
5、多返回值
典型情況下,函數(shù)只有一個(gè)返回值,但是 Python 也支持函數(shù)返回多個(gè)返回值。
要返回多個(gè)返回值,只需在 return 關(guān)鍵字后跟多個(gè)值(依次用逗號(hào)分隔)。
例如:
def date():import datetimed = datetime.date.today()return d.year, d.month, d.daydate() 返回了今天的日期的年、月、日。
接收函數(shù)返回值時(shí),用對應(yīng)返回值數(shù)量的變量來分別接收它們。
>>> year, month, day = date()
>>> year
2019
>>> month
9
>>> day
4
函數(shù)返回多個(gè)返回值是什么原理呢?其實(shí)多返回值時(shí),Python 將這些返回值包裝成了元組,然后將元組返回。來驗(yàn)證下:
>>> date()
(2019, 9, 4)
接收返回值時(shí),year, month, day = date(),這樣賦值寫法,會(huì)將元組解包,分別將元素賦予單獨(dú)的變量中。即:
>>> year, month, day = (2019, 9, 4)
>>> year
2019
>>> month
9
>>> day
4
?
三、讓你函數(shù)更好用——類進(jìn)階
1、類屬性和類方法
之前介紹類的時(shí)候,我們學(xué)習(xí)了對象屬性和對象方法。對象屬性和對象方法是綁定在對象這個(gè)層次上的,也就是說需要先創(chuàng)建對象,然后才能使用對象的屬性和方法。
即:
對象 = 類()對象.屬性 對象.方法()除此之外,還有一種綁定在類這個(gè)層面的屬性和方法,叫作類屬性和類方法。使用類屬性和類方法時(shí),不用創(chuàng)建對象,直接通過類來使用。
類屬性和類方法的使用方式:
類.屬性 類.方法()?
(1)類屬性的定義
類屬性如何定義呢?
只要將屬性定義在類之中方法之外即可。如下面的 屬性1 和 屬性2:
class 類:屬性1 = X屬性2 = Ydef 某方法():pass舉個(gè)例子:
class Char:letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'digits = '0123456789'這里定義了類 Char,有兩個(gè)類屬性,這兩個(gè)類屬性分別包含所有大寫字母和所有數(shù)字。可以通過類名來使用這兩個(gè)類屬性,此時(shí)無需創(chuàng)建對象:
>>> Char.letters
’ABCDEFGHIJKLMNOPQRSTUVWXYZ’
>>> Char.digits
’0123456789’
當(dāng)然,類所創(chuàng)建出來的對象也能使用類屬性:
>>> char = Char()
>>> char.letters
’ABCDEFGHIJKLMNOPQRSTUVWXYZ’
>>> char.digits
’0123456789’
?
(2)類方法的定義
再來看下類方法的定義方法。類方法的定義需要借助于裝飾器,裝飾器具體是什么后續(xù)文章中會(huì)介紹,目前只要知道用法即可。
定義類方法時(shí),需要在方法的前面加上裝飾器 @classmethod。如下:
class 類:@classmethoddef 類方法(cls):pass注意與對象方法不同,類方法的第一個(gè)參數(shù)通常命名為 cls,表示當(dāng)前這個(gè)類本身。我們可以通過該參數(shù)來引用類屬性,或類中其它類方法。
類方法中可以使用該類的類屬性,但不能使用該類的對象屬性。因?yàn)轭惙椒`屬于類,而對象屬性隸屬于對象,使用類方法時(shí)可能還沒有對象被創(chuàng)建出來。
在之前 Char 類的基礎(chǔ)上,我們加上隨機(jī)獲取任意字符的類方法。代碼如下:
import randomclass Char:letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'digits = '0123456789'@classmethoddef random_letter(cls):return random.choice(cls.letters)@classmethoddef random_digits(cls):return random.choice(cls.digits)方法 random_letter() 可以從屬性 letters 隨機(jī)獲取一個(gè)大寫字母;方法 random_digits() 可以從屬性 digits 隨機(jī)獲取一個(gè)數(shù)字。它們函數(shù)體中的 random.choice() 可從指定序列中隨機(jī)獲取一個(gè)元素。
>>> Char.random_digits()
‘8’
>>> Char.random_letter()
‘X’
擴(kuò)展:import 語句不僅可用于模塊的開頭,也可用于模塊的任意位置,如函數(shù)中。
?
2、靜態(tài)方法
與類方法有點(diǎn)相似的是靜態(tài)方法,靜態(tài)方法也可直接通過類名來調(diào)用,不必先創(chuàng)建對象。不同在于類方法的第一個(gè)參數(shù)是類自身(cls),而靜態(tài)方法沒有這樣的參數(shù)。如果方法需要和其它類屬性或類方法交互,那么可以將其定義成類方法;如果方法無需和其它類屬性或類方法交互,那么可以將其定義成靜態(tài)方法。
定義靜態(tài)方法時(shí),需要在方法的前面加上裝飾器 @staticmethod。如下:
class 類:@staticmethoddef 靜態(tài)方法():pass之前的例子中,我們可以從類屬性 letters 和 digits 中隨機(jī)獲取字符,如果想要自己來指定字符的范圍,并從中獲取一個(gè)隨機(jī)字符,可以再來定義一個(gè)靜態(tài)方法 random_char()。如:
import randomclass Char:letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'digits = '0123456789'@classmethoddef random_letter(cls):return random.choice(cls.letters)@classmethoddef random_digits(cls):return random.choice(cls.digits)@staticmethoddef random_char(string):if not isinstance(string, str):raise TypeError('需要字符串參數(shù)')return random.choice(string)靜態(tài)方法 random_char 從傳入的字符串中隨機(jī)挑選出一個(gè)字符。之所以定義成靜態(tài)方法,是因?yàn)樗鼰o需與類屬性交互。
>>> Char.random_char(‘imooc2019’)
‘0’
>>> Char.random_char(‘imooc2019’)
‘m’
?
3、私有屬性、方法
類屬性 letters 和 digits 是為了提供給同一個(gè)類中的類方法使用,但我們可以通過類或?qū)ο髲念惖耐獠恐苯釉L問它們。比如:
Char.letters Char.digits>>> Char.letters
’ABCDEFGHIJKLMNOPQRSTUVWXYZ’
>>> Char.digits
’0123456789’
有時(shí)我們不想把過多的信息暴露出去,有沒有什么方法來限制屬性不被類外部所訪問,而是只能在類中使用?
答案是有的,我們只需要在命名上動(dòng)動(dòng)手腳,將屬性或方法的名稱用 __(兩個(gè)下劃線)開頭即可。如:
import randomclass Char:__letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'__digits = '0123456789'@classmethoddef random_letter(cls):return random.choice(cls.__letters)@classmethoddef random_digits(cls):return random.choice(cls.__digits)從類外部訪問這兩個(gè)屬性看看:
>>> Char.__letters
Traceback (most recent call last):
???? File “”, line 1, in
AttributeError: type object ‘Char’ has no attribute ‘__letters’
>>> Char.__digits
Traceback (most recent call last):
???? File “”, line 1, in
AttributeError: type object ‘Char’ has no attribute ‘__digits’
可以看到,修改過后的屬性不能直接被訪問了,解釋器拋出 AttributeError 異常,提示類中沒有這個(gè)屬性。
但位于同一個(gè)類中的方法還是可以正常使用這些屬性:
>>> Char.random_letter()
‘N’
>>> Char.random_digits()
‘4’
像這樣以 __(兩個(gè)下劃線)開頭的屬性我們稱為私有屬性。顧名思義,它是類所私有的,不能在類外部使用。
上述是以類屬性作為示例,該規(guī)則對類方法、對象屬性、對象方法同樣適用。只需在名稱前加上 __(兩個(gè)下劃線)即可。
我們也可以使用 _(一個(gè)下劃線)前綴來聲明某屬性或方法是私有的,但是這種形式只是一種使用者間的約定,并不在解釋器層面作限制。如:
class Char:_letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'_digits = '0123456789'上面的 _letters 和 _digits 也可看作私有屬性,只不過是約定上的私有,通過名稱前綴 _(一個(gè)下滑線)向使用者告知這是私有的。但你如果非要使用,依然可以用。
>>> Char._letters
’ABCDEFGHIJKLMNOPQRSTUVWXYZ’
>>> Char._digits
’0123456789’
?
4、特殊方法
類中以 __ 開頭并以 __ 結(jié)尾的方法是特殊方法,特殊方法有特殊的用途。它們可以直接調(diào)用,也可以通過一些內(nèi)置函數(shù)或操作符來間接調(diào)用,如之前學(xué)習(xí)過的 __init__()、__next__()。
特殊方法很多,在這里我們簡單例舉幾個(gè):
-
__init__()
__init__() 是非常典型的一個(gè)特殊方法,它用于對象的初始化。在實(shí)例化類的過程中,被自動(dòng)調(diào)用。
-
__next__()
在迭代器章節(jié)中我們講過,對迭代器調(diào)用 next() 函數(shù),便能生成下一個(gè)值。這個(gè)過程的背后,next() 調(diào)用了迭代器的 __next__() 方法。
-
__len__()
你可能會(huì)好奇,為什么調(diào)用 len() 函數(shù)時(shí),便能返回一個(gè)容器的長度?原因就是容器類中實(shí)現(xiàn)了 __len__() 方法,調(diào)用 len() 函數(shù)時(shí)將自動(dòng)調(diào)用容器的 __len__() 方法。
-
__str__()
在使用 print() 函數(shù)時(shí)將自動(dòng)調(diào)用類的 __str__() 方法。如:
class A:def __str__(self):return '這是 A 的對象'>>> a = A()
>>> print(a)
這是 A 的對象` -
__getitem__()
諸如列表、元素、字符串這樣的序列,我們可以通過索引的方式來獲取其中的元素,這背后便是 __getitem__() 在起作用。
'abc'[2] 即等同于 'abc'.__getitem__(2)。
>>> ‘a(chǎn)bc’[2]
‘c’
>>> ‘a(chǎn)bc’.__getitem__(2)
‘c’
?
5、類的繼承
(1)類的簡單繼承
如果想基于一個(gè)現(xiàn)有的類,獲取其全部能力,并以此擴(kuò)展出一個(gè)更強(qiáng)大的類,此時(shí)可以使用類的繼承。被繼承的類叫作父類(或基類),繼承者叫作子類(或派生類)。關(guān)于類的簡單繼承可以看下圖就是一個(gè)典型的例子:
在類的繼承的定義時(shí),子類名稱的后面加上括號(hào)并寫入父類。如下:
class 父類:父類的實(shí)現(xiàn)class 子類(父類):子類的實(shí)現(xiàn)例如:
class A:def __init__(self):self.apple = 'apple'def have(self):print('I hava an', self.apple)class B(A):def who(self):print('I am an object of B')>>> b = B()
>>> b.who()
I am an object of B
>>> b.apple
’apple’
>>> b.have()
I hava an apple
可以看到,雖然類 B 中什么都沒定義,但由于 B 繼承自 A,所以它擁有 A 的屬性和方法。
子類 B 中當(dāng)然也可以定義自己的屬性。
class B(A):def __init__(self):super().__init__()self.banana = 'banana'>>> b = B()
>>> b.banana
’banana’
我們在 B 中定義 __init__() 方法,并在其中定義了 B 自己的屬性 banana。
super().__init__() 這一句代碼是什么作用?由于我們在子類中定義了 __init__() 方法,這會(huì)導(dǎo)致子類無法再獲取父類的屬性,加上這行代碼就能在子類初始化的同時(shí)初始化父類。super() 用在類的方法中時(shí),返回父類對象。
子類中出現(xiàn)和父類同名的方法會(huì)怎么樣?答案是子類會(huì)覆蓋父類的同名方法。
class A:def __init__(self):self.apple = 'apple'def have(self):print('I hava an', self.apple)class B(A):def __init__(self):super().__init__()self.banana = 'banana'def have(self):print('I hava an', self.banana)>>> b = B()
>>> b.have()
I hava an banana
?
(2)類的繼承鏈
子類可以繼承父類,同樣的,父類也可以繼承它自己的父類,如此一層一層繼承下去。
class A:def have(self):print('I hava an apple')class B(A):passclass C(B):pass>>> c = C()
>>> c.have()
I hava an apple
在這里 A 是繼承鏈的頂端,B 和 C 都是它的子類(孫子類)。
其實(shí) A 也有繼承,它繼承自 object。任何類的根源都是 object 類。如果一個(gè)類沒有指定所繼承的類,那么它默認(rèn)繼承 object。
A 中也可以顯式指明其繼承于 object :
class A(object):def have(self):print('I hava an apple')如果想要判斷一個(gè)類是否是另一個(gè)類的子類,可以使用內(nèi)置函數(shù) issubclass() 。用法如下:
>>> issubclass(C, A)
True
>>> issubclass(B, A)
True
>>> issubclass(C, B)
True
(3)類的多繼承
子類可以同時(shí)繼承多個(gè)父類,這樣它便擁有了多份能力。如下圖,步兵類就同時(shí)擁有士兵類和人類的屬性,就步兵類屬于多繼承。
定義時(shí),子類名稱后面加上括號(hào)并寫入多個(gè)父類。如下:
class A:def get_apple(self):return 'apple'class B:def get_banana(self):return 'banana'class C(A, B):pass>>> c = C()
>>> c.get_apple()
‘a(chǎn)pple’
>>> c.get_banana()
‘banana’
此時(shí) C 便同時(shí)擁有了 A 和 B 的能力。
?
四、從小獨(dú)棟升級為別墅區(qū)——函數(shù)式編程
1、函數(shù)賦值給變量
在 Python 中,所有的對象都可以賦值給變量,包括函數(shù)。這可能有點(diǎn)出乎意料,我們不妨來試一試:
def say_hello(name):return name + ', hello!'f = say_hello>>> f(‘開發(fā)者’)
‘開發(fā)者, hello!’
>>> f
<function say_hello at 0x10befec80>
注意,這里被賦值的是函數(shù)本身,而不是函數(shù)的結(jié)果。賦值后,變量 f 與函數(shù) say_hello 綁定,f 也就相當(dāng)于是 say_hello 的別名,完全可以用調(diào)用 say_hello 的方式來調(diào)用 f。
擴(kuò)展:類也可以賦值給變量。如:
class Apple:who_am_i = 'apple'banana = Apple>>> banana.who_am_i
’apple’
注意,被賦值的是類本身,而不是類實(shí)例化后的對象。賦值后,變量 banana 與類 Apple 綁定,banana 也就相當(dāng)于是 Apple 的別名,使用 banana 就相當(dāng)于使用 Apple。
?
2、函數(shù)作為函數(shù)參數(shù)
一切對象都可以作為函數(shù)的參數(shù),包括另一個(gè)函數(shù)。接受函數(shù)作為參數(shù)的函數(shù),稱為高階函數(shù)。這和數(shù)學(xué)中的高階函數(shù)有些相似。
來看一個(gè)函數(shù)作為參數(shù)的例子。
這個(gè)例子中,我們實(shí)現(xiàn)了一個(gè)函數(shù),它從給定的數(shù)字列表中篩選數(shù)字,而具體的篩選策略由另一個(gè)函數(shù)決定并以參數(shù)的形式存在:
def filter_nums(nums, want_it):return [n for n in nums if want_it(n)]函數(shù) filter_nums 用來篩選數(shù)字,它接受兩個(gè)參數(shù),nums 是包含所有待篩選數(shù)字的列表,want_it 是一個(gè)函數(shù),用來決定某個(gè)數(shù)字是否保留。
我們選定一個(gè)簡單的策略來實(shí)現(xiàn)下 want_it 參數(shù)所對應(yīng)的函數(shù)(其函數(shù)名不必為 want_it):
def want_it(num):return num % 2 == 0這里 want_it 接受一個(gè)數(shù)字作為參數(shù),如果這個(gè)數(shù)字是 2 的倍數(shù),則返回 True,否則返回 False。
調(diào)用一下 filter_nums 試試:
>>> def filter_nums(nums, want_it):
…???? return [n for n in nums if want_it(n)]
…
>>> def want_it(num):
…???? return num % 2 == 0
…
>>> filter_nums([11, 12, 13, 14, 15, 16, 17, 18], want_it)
[12, 14, 16, 18]
這里每個(gè)數(shù)字都經(jīng)過 want_it() 函數(shù)的判斷,而 want_it() 是以 filter_num() 第二個(gè)參數(shù)的形式傳遞進(jìn)去,供 filter_num() 調(diào)用。
?
3、lambda 表達(dá)式
在 Python 中,可以通過 lambda 表達(dá)式來便捷地定義一個(gè)功能簡單的函數(shù),這個(gè)函數(shù)只有實(shí)現(xiàn)沒有名字,所以叫作匿名函數(shù)。
lambda 表達(dá)式的寫法如下:
lambda 參數(shù)1, 參數(shù)2, 參數(shù)N: 函數(shù)實(shí)現(xiàn)使用上述表達(dá)式將定義一個(gè)匿名函數(shù),這個(gè)匿名函數(shù)可接受若干參數(shù),參數(shù)寫在冒號(hào)前(:),多個(gè)參數(shù)時(shí)用逗號(hào)分隔,其實(shí)現(xiàn)寫在冒號(hào)后(:)。
舉個(gè)例子:
f = lambda x: x ** 2這個(gè) lambda 表達(dá)式定義了一個(gè)匿名函數(shù),這個(gè)匿名函數(shù)接受一個(gè)參數(shù) x,返回 x ** 2 的計(jì)算結(jié)果。同時(shí)賦值語句將這個(gè)匿名函數(shù)賦值給了變量 f。注意 f 保存的是函數(shù),而不是函數(shù)結(jié)果。
>>> f
<function at 0x10bcba0d0>
>>> f(4)
16
>>> f(9)
81
通過觀察上述示例可以發(fā)現(xiàn),lambda 表達(dá)式中并沒有 return 關(guān)鍵字,但結(jié)果被返回出來。是的,匿名函數(shù)的 函數(shù)實(shí)現(xiàn) 的執(zhí)行結(jié)果就會(huì)作為它的返回值,無需使用 return 關(guān)鍵字。
從功能上來看,lambda x: x ** 2 等同于:
def no_name(x):return x ** 2>>> no_name(4)
16
一般情況下,我們不會(huì)像 f = lambda x: x ** 2 這樣直接將匿名函數(shù)賦值給變量,然后去用這個(gè)變量。而是在需要將函數(shù)作為參數(shù)時(shí),才去使用 lambda 表達(dá)式,這樣就無需在函數(shù)調(diào)用前去定義另外一個(gè)函數(shù)了。
如我們剛才寫的函數(shù) filter_nums:
def filter_nums(nums, want_it):return [n for n in nums if want_it(n)]它的 want_it 參數(shù)需要是一個(gè)函數(shù) ,這時(shí)用 lambda 表達(dá)式便能方便的解決問題。可以像這樣來使用:
>>> filter_nums([11, 12, 13, 14, 15, 16, 17, 18], lambda x: x % 2 == 0)
[12, 14, 16, 18]
以前講內(nèi)置函數(shù)的時(shí)候,我們介紹過排序函數(shù) sorted()。它有一個(gè)參數(shù) key,用來在排序復(fù)雜元素時(shí),指定排序所使用的字段,這個(gè)參數(shù)需要是個(gè)函數(shù),同樣可以用 lambda 表達(dá)式來解決:
>>> codes = [(‘上海’, ‘021’), (‘北京’, ‘010’), (‘成都’, ‘028’), (‘廣州’, ‘020’)]
>>> sorted(codes, key=lambda x: x[1]) # 以區(qū)號(hào)字典來排序
[(‘北京’, ‘010’), (‘廣州’, ‘020’), (‘上海’, ‘021’), (‘成都’, ‘028’)]
?
關(guān)于Python進(jìn)階的第一部分內(nèi)容就和大家分享到這里。評論區(qū)留言你們的問題和見解,我們一起學(xué)習(xí)!
之后還會(huì)和大家繼續(xù)更新更多關(guān)于Python技術(shù)干貨,
感興趣的小伙伴別忘了點(diǎn)個(gè)關(guān)注哈!
灰小猿陪你一起進(jìn)步呀!
同時(shí)給大家推薦一個(gè)CSDN官方的Python全棧知識(shí)圖譜學(xué)習(xí)路線,涵蓋Python六大模塊,100+知識(shí)點(diǎn),內(nèi)容梳理全面,難點(diǎn),痛點(diǎn)羅列齊全,可以說這本知識(shí)圖譜上的每一句話,都價(jià)值千金,這是CSDN聯(lián)合6位一線Python工程師,花費(fèi)3個(gè)月反復(fù)打磨,旨在幫助大家Python知識(shí)體系,具備實(shí)戰(zhàn)經(jīng)驗(yàn),破解開發(fā)和面試難題!非常適合學(xué)習(xí)Python的小伙伴們!原價(jià)129,CSDN官方限時(shí)限量29元!
總結(jié)
以上是生活随笔為你收集整理的诺,你们要的Python进阶来咯!【函数、类进阶必备】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 北京的林书豪,像一把小李飞刀
- 下一篇: python 定时任务 web管理_py