诺,你们要的Python进阶来咯!【函数、类进阶必备】
Python基礎(chǔ)及進(jìn)階內(nèi)容持續(xù)更新中!歡迎小伙伴們一起關(guān)注學(xué)習(xí)!
目錄
一、Python進(jìn)階初體驗(yàn)——內(nèi)置函數(shù)
1、數(shù)據(jù)類(lèi)型相關(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ù)更好用——類(lèi)進(jìn)階
1、類(lèi)屬性和類(lèi)方法
(1)類(lèi)屬性的定義
(2)類(lèi)方法的定義
2、靜態(tài)方法
3、私有屬性、方法
4、特殊方法
5、類(lèi)的繼承
(1)類(lèi)的簡(jiǎn)單繼承
(2)類(lèi)的繼承鏈
(3)類(lèi)的多繼承
四、從小獨(dú)棟升級(jí)為別墅區(qū)——函數(shù)式編程
1、函數(shù)賦值給變量
2、函數(shù)作為函數(shù)參數(shù)
3、lambda 表達(dá)式
寫(xiě)在前面
Hello,你好呀,我是灰小猿!一個(gè)超會(huì)寫(xiě)bug的程序猿!
最近和大家總結(jié)了幾期有關(guān)Python基礎(chǔ)入門(mén)和常見(jiàn)報(bào)錯(cuò)解決的相關(guān)文章,得到了很多小伙伴的支持,同時(shí)Python基礎(chǔ)入門(mén)相關(guān)的內(nèi)容也算是和大家總結(jié)得差不多了,有想學(xué)習(xí)或參考的小伙伴可以看以下幾篇文章:
Python基礎(chǔ)入門(mén):
【全網(wǎng)力薦】堪稱(chēng)最易學(xué)的Python基礎(chǔ)入門(mén)教程
萬(wàn)字長(zhǎng)文爆肝Python基礎(chǔ)入門(mén)【第二彈、超詳細(xì)數(shù)據(jù)類(lèi)型總結(jié)】
常見(jiàn)報(bào)錯(cuò)及解決:
全網(wǎng)最值得收藏的Python常見(jiàn)報(bào)錯(cuò)及其解決方案,再也不用擔(dān)心遇到BUG了!
今天就繼續(xù)來(lái)和大家分享有關(guān)Python進(jìn)階中函數(shù)和類(lèi)使用的相關(guān)內(nèi)容,同時(shí)之后還會(huì)繼續(xù)更新,感興趣的小伙伴可以關(guān)注一起學(xué)習(xí)呀!
?
一、Python進(jìn)階初體驗(yàn)——內(nèi)置函數(shù)
Python 中內(nèi)置有很多常用的函數(shù),這些函數(shù)無(wú)需從模塊中導(dǎo)入,可直接使用。由于內(nèi)置函數(shù)有六七十個(gè)之多,
故這里不一一介紹,只介紹一些最常用的,有關(guān)其他詳細(xì)的內(nèi)置函數(shù)大家可以參考這里“菜鳥(niǎo)教程—Python內(nèi)置函數(shù)”。
1、數(shù)據(jù)類(lèi)型相關(guān)
| dict() | 將參數(shù)轉(zhuǎn)換為字典類(lèi)型 | 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() | 將元組、字符串等可迭代對(duì)象轉(zhuǎn)換為列表 | list('abc') | ['a', 'b', 'c'] |
| tuple() | 將列表、字符串等可迭代對(duì)象轉(zhuǎn)換為元組 | tuple([1, 2, 3]) | (1, 2, 3) |
| set() | 1.創(chuàng)建空集合;2.將可迭代對(duì)象轉(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)格來(lái)講并不是函數(shù),而是類(lèi),只是其命名風(fēng)格和使用方式和函數(shù)類(lèi)似。
可迭代對(duì)象:如列表、元組、字符串、集合、字典等。關(guān)于可迭代對(duì)象的使用計(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() | 求絕對(duì)值 | 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() | 如果可迭代對(duì)象中的所有值,在逐一應(yīng)用 bool(值) 后結(jié)果都為 True,則返回 True,否則返回 False |
| any() | 如果可迭代對(duì)象中的任意一個(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)容寫(xiě)入標(biāo)準(zhǔn)輸出中 |
| open() | 打開(kāi)一個(gè)文件。之后便可以對(duì)文件做讀寫(xiě)操作。詳見(jiàn) IO 操作章節(jié) |
5、元數(shù)據(jù)相關(guān)
| type() | 獲取對(duì)象的類(lèi)型 |
| isinstance() | 判斷對(duì)象是否是某個(gè)類(lèi)(或其子類(lèi))的對(duì)象 |
| dir() | 獲取類(lèi)或?qū)ο笾械乃蟹椒ê蛯傩?#xff1b;無(wú)參數(shù)時(shí)獲取當(dāng)前作用域下的所有名字 |
| id() | 返回一個(gè)對(duì)象的唯一標(biāo)識(shí)。在我們所使用的 CPython 中這個(gè)唯一標(biāo)識(shí)實(shí)際為該對(duì)象在內(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è)類(lèi)型放在元組中,其中一個(gè)與對(duì)象的類(lèi)型相符即為 True,若無(wú)相符則為 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ù)、類(lèi)的幫助信息,非常實(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 鍵退出上述界面。
對(duì)于這個(gè)章節(jié)中的內(nèi)置函數(shù),如果你有不清楚的地方,便可以用 help() 來(lái)查看使用說(shuō)明。
7、sorted()函數(shù)
對(duì)可迭代對(duì)象中的數(shù)據(jù)進(jìn)行排序,返回一個(gè)新的列表。
>>> numbers = (4, 5, 2, 8, 9, 1, 0)
>>> sorted(numbers)
[0, 1, 2, 4, 5, 8, 9]
通過(guò)參數(shù) reverse=True 指定倒序:
>>> numbers = (4, 5, 2, 8, 9, 1, 0)
>>> sorted(numbers, reverse=True)
[9, 8, 5, 4, 2, 1, 0]
通過(guò)參數(shù) key 指定排序時(shí)所使用的字段:
>>> codes = [(‘上海’, ‘021’), (‘北京’, ‘010’), (‘成都’, ‘028’), (‘廣州’, ‘020’)]
>>> sorted(codes, key=lambda x: x[1])
[(‘北京’, ‘010’), (‘廣州’, ‘020’), (‘上海’, ‘021’), (‘成都’, ‘028’)]
說(shuō)明:指定 key 排序需要用到 lambda 表達(dá)式。有關(guān) lambda 表達(dá)式的內(nèi)容將在函數(shù)式編程章節(jié)中介紹。
8、range()函數(shù)
獲取一個(gè)整數(shù)序列。可指定起始數(shù)值,結(jié)束數(shù)值,增長(zhǎng)步長(zhǎng)。
在 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ù)值范圍為左閉右開(kāi)區(qū)間,即不包括所指定的結(jié)束數(shù)值。
-
只指定結(jié)束數(shù)值,此時(shí)起始數(shù)值默認(rèn)為 0
>>> for i in range(4):
… print(i)
…
0
1
2
3 -
指定步長(zhǎng)(第三個(gè)參數(shù))
>>> for i in range(3, 15, 3):
… print(i)
…
3
6
9
12
?
二、給代碼安個(gè)家——函數(shù)進(jìn)階
1、位置參數(shù)
位置參數(shù)這個(gè)名稱(chēng)其實(shí)我們并不陌生,之前所編寫(xiě)的函數(shù)使用的就是位置參數(shù)。位置參數(shù),顧名思義,傳入函數(shù)時(shí)每個(gè)參數(shù)都是通過(guò)位置來(lái)作區(qū)分的。函數(shù)調(diào)用時(shí),傳入的值需按照位置與參數(shù)一一對(duì)應(yīng)。
比如下面這個(gè)程序:
def overspeed_rate(current, max, min):if current > max:return (current - max) / max # 超過(guò)最大時(shí)速,結(jié)果為正elif current < min:return (current - min) / min # 超過(guò)最小時(shí)速,結(jié)果為負(fù)else:return 0 # 不超速,結(jié)果為 0這個(gè)函數(shù)用來(lái)判斷車(chē)輛在高速上行駛時(shí)超速的比例。它接受三個(gè)參數(shù),current 表示當(dāng)前時(shí)速,max 參數(shù)表示當(dāng)前路段的允許的最大時(shí)速,min 表示所允許的最小時(shí)速。
位置參數(shù)需要按位置順序來(lái)傳遞,否則結(jié)果不可預(yù)期。
>>> overspeed_rate(150, 120, 90)
0.25 # 超過(guò)最大時(shí)速 25%
>>> overspeed_rate(80, 100, 60)
0 # 不超速
>>> overspeed_rate(60, 120, 90)
-0.3333333333333333 # 超過(guò)最小時(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)值來(lái)填充參數(shù)。如果一個(gè)有默認(rèn)值的參數(shù)依然被傳入了值,那么默認(rèn)值將會(huì)被覆蓋。
函數(shù)定義時(shí),以 參數(shù)=值 來(lái)指定參數(shù)默認(rèn)值。如下:
def 函數(shù)(參數(shù)1, 參數(shù)2=默認(rèn)值):pass例如上面的 overspeed_rate 函數(shù), max 和 min 通常比較固定,我們可以使用一個(gè)常用值來(lái)作為默認(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ù)
對(duì)于 overspeed_rate 函數(shù),我們還可以在函數(shù)調(diào)用時(shí),以 參數(shù)名=值 的形式來(lái)向指定的參數(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)鍵字的形式來(lái)使用的參數(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)我們?cè)诙x函數(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
顯然,我們并沒(méi)有在函數(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ù)。類(lèi)似的,參數(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 上。來(lái)試驗(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í),用對(duì)應(yīng)返回值數(shù)量的變量來(lái)分別接收它們。
>>> year, month, day = date()
>>> year
2019
>>> month
9
>>> day
4
函數(shù)返回多個(gè)返回值是什么原理呢?其實(shí)多返回值時(shí),Python 將這些返回值包裝成了元組,然后將元組返回。來(lái)驗(yàn)證下:
>>> date()
(2019, 9, 4)
接收返回值時(shí),year, month, day = date(),這樣賦值寫(xiě)法,會(huì)將元組解包,分別將元素賦予單獨(dú)的變量中。即:
>>> year, month, day = (2019, 9, 4)
>>> year
2019
>>> month
9
>>> day
4
?
三、讓你函數(shù)更好用——類(lèi)進(jìn)階
1、類(lèi)屬性和類(lèi)方法
之前介紹類(lèi)的時(shí)候,我們學(xué)習(xí)了對(duì)象屬性和對(duì)象方法。對(duì)象屬性和對(duì)象方法是綁定在對(duì)象這個(gè)層次上的,也就是說(shuō)需要先創(chuàng)建對(duì)象,然后才能使用對(duì)象的屬性和方法。
即:
對(duì)象 = 類(lèi)()對(duì)象.屬性 對(duì)象.方法()除此之外,還有一種綁定在類(lèi)這個(gè)層面的屬性和方法,叫作類(lèi)屬性和類(lèi)方法。使用類(lèi)屬性和類(lèi)方法時(shí),不用創(chuàng)建對(duì)象,直接通過(guò)類(lèi)來(lái)使用。
類(lèi)屬性和類(lèi)方法的使用方式:
類(lèi).屬性 類(lèi).方法()?
(1)類(lèi)屬性的定義
類(lèi)屬性如何定義呢?
只要將屬性定義在類(lèi)之中方法之外即可。如下面的 屬性1 和 屬性2:
class 類(lèi):屬性1 = X屬性2 = Ydef 某方法():pass舉個(gè)例子:
class Char:letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'digits = '0123456789'這里定義了類(lèi) Char,有兩個(gè)類(lèi)屬性,這兩個(gè)類(lèi)屬性分別包含所有大寫(xiě)字母和所有數(shù)字。可以通過(guò)類(lèi)名來(lái)使用這兩個(gè)類(lèi)屬性,此時(shí)無(wú)需創(chuàng)建對(duì)象:
>>> Char.letters
’ABCDEFGHIJKLMNOPQRSTUVWXYZ’
>>> Char.digits
’0123456789’
當(dāng)然,類(lèi)所創(chuàng)建出來(lái)的對(duì)象也能使用類(lèi)屬性:
>>> char = Char()
>>> char.letters
’ABCDEFGHIJKLMNOPQRSTUVWXYZ’
>>> char.digits
’0123456789’
?
(2)類(lèi)方法的定義
再來(lái)看下類(lèi)方法的定義方法。類(lèi)方法的定義需要借助于裝飾器,裝飾器具體是什么后續(xù)文章中會(huì)介紹,目前只要知道用法即可。
定義類(lèi)方法時(shí),需要在方法的前面加上裝飾器 @classmethod。如下:
class 類(lèi):@classmethoddef 類(lèi)方法(cls):pass注意與對(duì)象方法不同,類(lèi)方法的第一個(gè)參數(shù)通常命名為 cls,表示當(dāng)前這個(gè)類(lèi)本身。我們可以通過(guò)該參數(shù)來(lái)引用類(lèi)屬性,或類(lèi)中其它類(lèi)方法。
類(lèi)方法中可以使用該類(lèi)的類(lèi)屬性,但不能使用該類(lèi)的對(duì)象屬性。因?yàn)轭?lèi)方法隸屬于類(lèi),而對(duì)象屬性隸屬于對(duì)象,使用類(lèi)方法時(shí)可能還沒(méi)有對(duì)象被創(chuàng)建出來(lái)。
在之前 Char 類(lèi)的基礎(chǔ)上,我們加上隨機(jī)獲取任意字符的類(lèi)方法。代碼如下:
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è)大寫(xiě)字母;方法 random_digits() 可以從屬性 digits 隨機(jī)獲取一個(gè)數(shù)字。它們函數(shù)體中的 random.choice() 可從指定序列中隨機(jī)獲取一個(gè)元素。
>>> Char.random_digits()
‘8’
>>> Char.random_letter()
‘X’
擴(kuò)展:import 語(yǔ)句不僅可用于模塊的開(kāi)頭,也可用于模塊的任意位置,如函數(shù)中。
?
2、靜態(tài)方法
與類(lèi)方法有點(diǎn)相似的是靜態(tài)方法,靜態(tài)方法也可直接通過(guò)類(lèi)名來(lái)調(diào)用,不必先創(chuàng)建對(duì)象。不同在于類(lèi)方法的第一個(gè)參數(shù)是類(lèi)自身(cls),而靜態(tài)方法沒(méi)有這樣的參數(shù)。如果方法需要和其它類(lèi)屬性或類(lèi)方法交互,那么可以將其定義成類(lèi)方法;如果方法無(wú)需和其它類(lèi)屬性或類(lèi)方法交互,那么可以將其定義成靜態(tài)方法。
定義靜態(tài)方法時(shí),需要在方法的前面加上裝飾器 @staticmethod。如下:
class 類(lèi):@staticmethoddef 靜態(tài)方法():pass之前的例子中,我們可以從類(lèi)屬性 letters 和 digits 中隨機(jī)獲取字符,如果想要自己來(lái)指定字符的范圍,并從中獲取一個(gè)隨機(jī)字符,可以再來(lái)定義一個(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(wú)需與類(lèi)屬性交互。
>>> Char.random_char(‘imooc2019’)
‘0’
>>> Char.random_char(‘imooc2019’)
‘m’
?
3、私有屬性、方法
類(lèi)屬性 letters 和 digits 是為了提供給同一個(gè)類(lèi)中的類(lèi)方法使用,但我們可以通過(guò)類(lèi)或?qū)ο髲念?lèi)的外部直接訪(fǎng)問(wèn)它們。比如:
Char.letters Char.digits>>> Char.letters
’ABCDEFGHIJKLMNOPQRSTUVWXYZ’
>>> Char.digits
’0123456789’
有時(shí)我們不想把過(guò)多的信息暴露出去,有沒(méi)有什么方法來(lái)限制屬性不被類(lèi)外部所訪(fǎng)問(wèn),而是只能在類(lèi)中使用?
答案是有的,我們只需要在命名上動(dòng)動(dòng)手腳,將屬性或方法的名稱(chēng)用 __(兩個(gè)下劃線(xiàn))開(kāi)頭即可。如:
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)從類(lèi)外部訪(fǎng)問(wèn)這兩個(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’
可以看到,修改過(guò)后的屬性不能直接被訪(fǎng)問(wèn)了,解釋器拋出 AttributeError 異常,提示類(lèi)中沒(méi)有這個(gè)屬性。
但位于同一個(gè)類(lèi)中的方法還是可以正常使用這些屬性:
>>> Char.random_letter()
‘N’
>>> Char.random_digits()
‘4’
像這樣以 __(兩個(gè)下劃線(xiàn))開(kāi)頭的屬性我們稱(chēng)為私有屬性。顧名思義,它是類(lèi)所私有的,不能在類(lèi)外部使用。
上述是以類(lèi)屬性作為示例,該規(guī)則對(duì)類(lèi)方法、對(duì)象屬性、對(duì)象方法同樣適用。只需在名稱(chēng)前加上 __(兩個(gè)下劃線(xiàn))即可。
我們也可以使用 _(一個(gè)下劃線(xiàn))前綴來(lái)聲明某屬性或方法是私有的,但是這種形式只是一種使用者間的約定,并不在解釋器層面作限制。如:
class Char:_letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'_digits = '0123456789'上面的 _letters 和 _digits 也可看作私有屬性,只不過(guò)是約定上的私有,通過(guò)名稱(chēng)前綴 _(一個(gè)下滑線(xiàn))向使用者告知這是私有的。但你如果非要使用,依然可以用。
>>> Char._letters
’ABCDEFGHIJKLMNOPQRSTUVWXYZ’
>>> Char._digits
’0123456789’
?
4、特殊方法
類(lèi)中以 __ 開(kāi)頭并以 __ 結(jié)尾的方法是特殊方法,特殊方法有特殊的用途。它們可以直接調(diào)用,也可以通過(guò)一些內(nèi)置函數(shù)或操作符來(lái)間接調(diào)用,如之前學(xué)習(xí)過(guò)的 __init__()、__next__()。
特殊方法很多,在這里我們簡(jiǎn)單例舉幾個(gè):
-
__init__()
__init__() 是非常典型的一個(gè)特殊方法,它用于對(duì)象的初始化。在實(shí)例化類(lèi)的過(guò)程中,被自動(dòng)調(diào)用。
-
__next__()
在迭代器章節(jié)中我們講過(guò),對(duì)迭代器調(diào)用 next() 函數(shù),便能生成下一個(gè)值。這個(gè)過(guò)程的背后,next() 調(diào)用了迭代器的 __next__() 方法。
-
__len__()
你可能會(huì)好奇,為什么調(diào)用 len() 函數(shù)時(shí),便能返回一個(gè)容器的長(zhǎng)度?原因就是容器類(lèi)中實(shí)現(xiàn)了 __len__() 方法,調(diào)用 len() 函數(shù)時(shí)將自動(dòng)調(diào)用容器的 __len__() 方法。
-
__str__()
在使用 print() 函數(shù)時(shí)將自動(dòng)調(diào)用類(lèi)的 __str__() 方法。如:
class A:def __str__(self):return '這是 A 的對(duì)象'>>> a = A()
>>> print(a)
這是 A 的對(duì)象` -
__getitem__()
諸如列表、元素、字符串這樣的序列,我們可以通過(guò)索引的方式來(lái)獲取其中的元素,這背后便是 __getitem__() 在起作用。
'abc'[2] 即等同于 'abc'.__getitem__(2)。
>>> ‘a(chǎn)bc’[2]
‘c’
>>> ‘a(chǎn)bc’.__getitem__(2)
‘c’
?
5、類(lèi)的繼承
(1)類(lèi)的簡(jiǎn)單繼承
如果想基于一個(gè)現(xiàn)有的類(lèi),獲取其全部能力,并以此擴(kuò)展出一個(gè)更強(qiáng)大的類(lèi),此時(shí)可以使用類(lèi)的繼承。被繼承的類(lèi)叫作父類(lèi)(或基類(lèi)),繼承者叫作子類(lèi)(或派生類(lèi))。關(guān)于類(lèi)的簡(jiǎn)單繼承可以看下圖就是一個(gè)典型的例子:
在類(lèi)的繼承的定義時(shí),子類(lèi)名稱(chēng)的后面加上括號(hào)并寫(xiě)入父類(lèi)。如下:
class 父類(lèi):父類(lèi)的實(shí)現(xiàn)class 子類(lèi)(父類(lèi)):子類(lèi)的實(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
可以看到,雖然類(lèi) B 中什么都沒(méi)定義,但由于 B 繼承自 A,所以它擁有 A 的屬性和方法。
子類(lèi) B 中當(dāng)然也可以定義自己的屬性。
class B(A):def __init__(self):super().__init__()self.banana = 'banana'>>> b = B()
>>> b.banana
’banana’
我們?cè)?B 中定義 __init__() 方法,并在其中定義了 B 自己的屬性 banana。
super().__init__() 這一句代碼是什么作用?由于我們?cè)谧宇?lèi)中定義了 __init__() 方法,這會(huì)導(dǎo)致子類(lèi)無(wú)法再獲取父類(lèi)的屬性,加上這行代碼就能在子類(lèi)初始化的同時(shí)初始化父類(lèi)。super() 用在類(lèi)的方法中時(shí),返回父類(lèi)對(duì)象。
子類(lèi)中出現(xiàn)和父類(lèi)同名的方法會(huì)怎么樣?答案是子類(lèi)會(huì)覆蓋父類(lèi)的同名方法。
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)類(lèi)的繼承鏈
子類(lèi)可以繼承父類(lèi),同樣的,父類(lèi)也可以繼承它自己的父類(lèi),如此一層一層繼承下去。
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 都是它的子類(lèi)(孫子類(lèi))。
其實(shí) A 也有繼承,它繼承自 object。任何類(lèi)的根源都是 object 類(lèi)。如果一個(gè)類(lèi)沒(méi)有指定所繼承的類(lèi),那么它默認(rèn)繼承 object。
A 中也可以顯式指明其繼承于 object :
class A(object):def have(self):print('I hava an apple')如果想要判斷一個(gè)類(lèi)是否是另一個(gè)類(lèi)的子類(lèi),可以使用內(nèi)置函數(shù) issubclass() 。用法如下:
>>> issubclass(C, A)
True
>>> issubclass(B, A)
True
>>> issubclass(C, B)
True
(3)類(lèi)的多繼承
子類(lèi)可以同時(shí)繼承多個(gè)父類(lèi),這樣它便擁有了多份能力。如下圖,步兵類(lèi)就同時(shí)擁有士兵類(lèi)和人類(lèi)的屬性,就步兵類(lèi)屬于多繼承。
定義時(shí),子類(lèi)名稱(chēng)后面加上括號(hào)并寫(xiě)入多個(gè)父類(lèi)。如下:
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ú)棟升級(jí)為別墅區(qū)——函數(shù)式編程
1、函數(shù)賦值給變量
在 Python 中,所有的對(duì)象都可以賦值給變量,包括函數(shù)。這可能有點(diǎn)出乎意料,我們不妨來(lái)試一試:
def say_hello(name):return name + ', hello!'f = say_hello>>> f(‘開(kāi)發(fā)者’)
‘開(kāi)發(fā)者, hello!’
>>> f
<function say_hello at 0x10befec80>
注意,這里被賦值的是函數(shù)本身,而不是函數(shù)的結(jié)果。賦值后,變量 f 與函數(shù) say_hello 綁定,f 也就相當(dāng)于是 say_hello 的別名,完全可以用調(diào)用 say_hello 的方式來(lái)調(diào)用 f。
擴(kuò)展:類(lèi)也可以賦值給變量。如:
class Apple:who_am_i = 'apple'banana = Apple>>> banana.who_am_i
’apple’
注意,被賦值的是類(lèi)本身,而不是類(lèi)實(shí)例化后的對(duì)象。賦值后,變量 banana 與類(lèi) Apple 綁定,banana 也就相當(dāng)于是 Apple 的別名,使用 banana 就相當(dāng)于使用 Apple。
?
2、函數(shù)作為函數(shù)參數(shù)
一切對(duì)象都可以作為函數(shù)的參數(shù),包括另一個(gè)函數(shù)。接受函數(shù)作為參數(shù)的函數(shù),稱(chēng)為高階函數(shù)。這和數(shù)學(xué)中的高階函數(shù)有些相似。
來(lái)看一個(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 用來(lái)篩選數(shù)字,它接受兩個(gè)參數(shù),nums 是包含所有待篩選數(shù)字的列表,want_it 是一個(gè)函數(shù),用來(lái)決定某個(gè)數(shù)字是否保留。
我們選定一個(gè)簡(jiǎn)單的策略來(lái)實(shí)現(xiàn)下 want_it 參數(shù)所對(duì)應(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)過(guò) want_it() 函數(shù)的判斷,而 want_it() 是以 filter_num() 第二個(gè)參數(shù)的形式傳遞進(jìn)去,供 filter_num() 調(diào)用。
?
3、lambda 表達(dá)式
在 Python 中,可以通過(guò) lambda 表達(dá)式來(lái)便捷地定義一個(gè)功能簡(jiǎn)單的函數(shù),這個(gè)函數(shù)只有實(shí)現(xiàn)沒(méi)有名字,所以叫作匿名函數(shù)。
lambda 表達(dá)式的寫(xiě)法如下:
lambda 參數(shù)1, 參數(shù)2, 參數(shù)N: 函數(shù)實(shí)現(xiàn)使用上述表達(dá)式將定義一個(gè)匿名函數(shù),這個(gè)匿名函數(shù)可接受若干參數(shù),參數(shù)寫(xiě)在冒號(hào)前(:),多個(gè)參數(shù)時(shí)用逗號(hào)分隔,其實(shí)現(xiàn)寫(xiě)在冒號(hào)后(:)。
舉個(gè)例子:
f = lambda x: x ** 2這個(gè) lambda 表達(dá)式定義了一個(gè)匿名函數(shù),這個(gè)匿名函數(shù)接受一個(gè)參數(shù) x,返回 x ** 2 的計(jì)算結(jié)果。同時(shí)賦值語(yǔ)句將這個(gè)匿名函數(shù)賦值給了變量 f。注意 f 保存的是函數(shù),而不是函數(shù)結(jié)果。
>>> f
<function at 0x10bcba0d0>
>>> f(4)
16
>>> f(9)
81
通過(guò)觀(guān)察上述示例可以發(fā)現(xiàn),lambda 表達(dá)式中并沒(méi)有 return 關(guān)鍵字,但結(jié)果被返回出來(lái)。是的,匿名函數(shù)的 函數(shù)實(shí)現(xiàn) 的執(zhí)行結(jié)果就會(huì)作為它的返回值,無(wú)需使用 return 關(guān)鍵字。
從功能上來(lái)看,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á)式,這樣就無(wú)需在函數(shù)調(diào)用前去定義另外一個(gè)函數(shù)了。
如我們剛才寫(xiě)的函數(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á)式便能方便的解決問(wèn)題。可以像這樣來(lái)使用:
>>> filter_nums([11, 12, 13, 14, 15, 16, 17, 18], lambda x: x % 2 == 0)
[12, 14, 16, 18]
以前講內(nèi)置函數(shù)的時(shí)候,我們介紹過(guò)排序函數(shù) sorted()。它有一個(gè)參數(shù) key,用來(lái)在排序復(fù)雜元素時(shí),指定排序所使用的字段,這個(gè)參數(shù)需要是個(gè)函數(shù),同樣可以用 lambda 表達(dá)式來(lái)解決:
>>> codes = [(‘上海’, ‘021’), (‘北京’, ‘010’), (‘成都’, ‘028’), (‘廣州’, ‘020’)]
>>> sorted(codes, key=lambda x: x[1]) # 以區(qū)號(hào)字典來(lái)排序
[(‘北京’, ‘010’), (‘廣州’, ‘020’), (‘上海’, ‘021’), (‘成都’, ‘028’)]
?
關(guān)于Python進(jìn)階的第一部分內(nèi)容就和大家分享到這里。評(píng)論區(qū)留言你們的問(wèn)題和見(jiàn)解,我們一起學(xué)習(xí)!
之后還會(huì)和大家繼續(xù)更新更多關(guān)于Python技術(shù)干貨,
感興趣的小伙伴別忘了點(diǎn)個(gè)關(guān)注哈!
灰小猿陪你一起進(jìn)步呀!
同時(shí)給大家推薦一個(gè)CSDN官方的Python全棧知識(shí)圖譜學(xué)習(xí)路線(xiàn),涵蓋Python六大模塊,100+知識(shí)點(diǎn),內(nèi)容梳理全面,難點(diǎn),痛點(diǎn)羅列齊全,可以說(shuō)這本知識(shí)圖譜上的每一句話(huà),都價(jià)值千金,這是CSDN聯(lián)合6位一線(xiàn)Python工程師,花費(fèi)3個(gè)月反復(fù)打磨,旨在幫助大家Python知識(shí)體系,具備實(shí)戰(zhàn)經(jīng)驗(yàn),破解開(kāi)發(fā)和面試難題!非常適合學(xué)習(xí)Python的小伙伴們!原價(jià)129,CSDN官方限時(shí)限量29元!
總結(jié)
以上是生活随笔為你收集整理的诺,你们要的Python进阶来咯!【函数、类进阶必备】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 北京的林书豪,像一把小李飞刀
- 下一篇: 来看看比尔盖茨当年写的BASIC解释器源