阿里天池:Task 03:Python基礎進階:從函式到高級魔法方法(第3部分)
學習內容:從函式到高級魔法方法
Python訓練營內容:

Python入門(下)
-
簡介
-
函式
1. 函式的定義
2. 函式的呼叫
3. 函式檔案
4. 函式引數
5. 函式的回傳值
6. 變數作用域 -
Lambda-運算式
1. 匿名函式的定義
2. 匿名函式的應用 -
類與物件
1. 屬性和方法組成物件
2. self是什么?
3. Python的魔法方法
4. 公有和私有
5. 繼承
6. 組合
7. 類、類物件和實體物件
8. 什么是系結?
9. 一些相關的內置函式(BIF) -
魔法方法
1. 基本的魔法方法
2. 算術運算子
3. 反算術運算子
4. 增量賦值運算
5. 一元運算子
6. 屬性訪問
7. 描述符
8. 定制序列
9. 迭代器
簡介
Python 是一種通用編程語言,其在科學計算和機器學習領域具有廣泛的應用,如果我們打算利用 Python 來執行機器學習,那么對 Python 有一些基本的了解就是至關重要的,本 Python 入門系列體驗就是為這樣的初學者精心準備的,
本實驗包括以下內容:
- 函式
- 函式的定義
- 函式的呼叫
- 函式檔案
- 函式引數
- 函式的回傳值
- 變數作用域
- Lambda 運算式
- 匿名函式的定義
- 匿名函式的應用
- 類與物件
- 物件 = 屬性 + 方法
- self 是什么?
- Python 的魔法方法
- 公有和私有
- 繼承
- 組合
- 類、類物件和實體物件
- 什么是系結?
- 一些相關的內置函式(BIF)
- 魔法方法
- 基本的魔法方法
- 算術運算子
- 反算術運算子
- 增量賦值運算子
- 一元運算子
- 屬性訪問
- 描述符
- 定制序列
- 迭代器
函式
函式的定義
還記得 Python 里面“萬物皆物件”么?Python 把函式也當成物件,可以從另一個函式中回傳出來而去構建高階函式,比如: 引數是函式、回傳值是函式,
我們首先來介紹函式的定義,
- 函式以
def關鍵詞開頭,后接函式名和圓括號(), - 函式執行的代碼以冒號起始,并且縮進,
- return [運算式] 結束函式,選擇性地回傳一個值給呼叫方,不帶運算式的return相當于回傳
None,
def functionname (parameters):
"函式_檔案字串"
function_suite
return [expression]
函式的呼叫
def printme(str):
print(str)
printme("我要呼叫用戶自定義函式!") # 我要呼叫用戶自定義函式!
printme("再次呼叫同一函式") # 再次呼叫同一函式
temp = printme('hello') # hello
print(temp) # None
函式檔案
def MyFirstFunction(name):
"函式定義程序中name是形參"
# 因為Ta只是一個形式,表示占據一個引數位置
print('傳遞進來的{0}叫做實參,因為Ta是具體的引數值!'.format(name))
MyFirstFunction('老馬的程式人生')
# 傳遞進來的老馬的程式人生叫做實參,因為Ta是具體的引數值!
print(MyFirstFunction.__doc__)
# 函式定義程序中name是形參
help(MyFirstFunction)
# Help on function MyFirstFunction in module __main__:
# MyFirstFunction(name)
# 函式定義程序中name是形參
函式引數
Python 的函式具有非常靈活多樣的引數形態,既可以實作簡單的呼叫,又可以傳入非常復雜的引數,從簡到繁的引數形態如下:
- 位置引數 (positional argument)
- 默認引數 (default argument)
- 可變引數 (variable argument)
- 關鍵字引數 (keyword argument)
- 命名關鍵字引數 (name keyword argument)
- 引陣列合
1. 位置引數
def functionname(arg1):
"函式_檔案字串"
function_suite
return [expression]
arg1- 位置引數 ,這些引數在呼叫函式 (call function) 時位置要固定,
2. 默認引數
def functionname(arg1, arg2=v):
"函式_檔案字串"
function_suite
return [expression]
arg2 = v- 默認引數 = 默認值,呼叫函式時,默認引數的值如果沒有傳入,則被認為是默認值,- 默認引數一定要放在位置引數 后面,不然程式會報錯,
- Python 允許函式呼叫時引數的順序與宣告時不一致,因為 Python 解釋器能夠用引數名匹配引數值,
3. 可變引數
顧名思義,可變引數就是傳入的引數個數是可變的,可以是 0, 1, 2 到任意個,是不定長的引數,
def functionname(arg1, arg2=v, *args):
"函式_檔案字串"
function_suite
return [expression]
*args- 可變引數,可以是從零個到任意個,自動組裝成元組,- 加了星號(*)的變數名會存放所有未命名的變數引數,
4. 關鍵字引數
def functionname(arg1, arg2=v, args, *kw):
"函式_檔案字串"
function_suite
return [expression]
5. 命名關鍵字引數
def functionname(arg1, arg2=v, args, *, nkw, *kw):
"函式_檔案字串"
function_suite
return [expression]
**kw- 關鍵字引數,可以是從零個到任意個,自動組裝成字典,- 可變引數允許傳入零個到任意個引數,它們在函式呼叫時自動組裝為一個元組 (tuple),
- 關鍵字引數允許傳入零個到任意個引數,它們在函式內部自動組裝為一個字典 (dict),
*, nkw- 命名關鍵字引數,用戶想要輸入的關鍵字引數,定義方式是在nkw 前面加個分隔符*,- 如果要限制關鍵字引數的名字,就可以用「命名關鍵字引數」
- 使用命名關鍵字引數時,要特別注意不能缺少引數名,
- 沒有寫引數名
nwk,因此 10 被當成「位置引數」,而原函式只有 1 個位置函式,現在呼叫了 2 個,因此程式會報錯,
6. 引陣列合
在 Python 中定義函式,可以用位置引數、默認引數、可變引數、命名關鍵字引數和關鍵字引數,這 5 種引數中的 4 個都可以一起使用,但是注意,引數定義的順序必須是:
- 位置引數、默認引數、可變引數和關鍵字引數,
- 位置引數、默認引數、命名關鍵字引數和關鍵字引數,
要注意定義可變引數和關鍵字引數的語法:
*args是可變引數,args接收的是一個tuple**kw是關鍵字引數,kw接收的是一個dict
命名關鍵字引數是為了限制呼叫者可以傳入的引數名,同時可以提供默認值,定義命名關鍵字引數不要忘了寫分隔符 *,否則定義的是位置引數,
警告:雖然可以組合多達 5 種引數,但不要同時使用太多的組合,否則函式很難懂,
函式的回傳值
def add(a, b):
return a + b
print(add(1, 2)) # 3
print(add([1, 2, 3], [4, 5, 6])) # [1, 2, 3, 4, 5, 6]
變數作用域
- Python 中,程式的變數并不是在哪個位置都可以訪問的,訪問權限決定于這個變數是在哪里賦值的,
- 定義在函式內部的變數擁有區域作用域,該變數稱為區域變數,
- 定義在函式外部的變數擁有全域作用域,該變數稱為全域變數,
- 區域變數只能在其被宣告的函式內部訪問,而全域變數可以在整個程式范圍內訪問,
- 當內部作用域想修改外部作用域的變數時,就要用到
global和nonlocal關鍵字了,
閉包
- 是函式式編程的一個重要的語法結構,是一種特殊的內嵌函式,
- 如果在一個內部函式里對外層非全域作用域的變數進行參考,那么內部函式就被認為是閉包,
- 通過閉包可以訪問外層非全域作用域的變數,這個作用域稱為 閉包作用域,
- 如果一個函式在內部呼叫自身本身,這個函式就是遞回函式,
Lambda 運算式
匿名函式的定義
在 Python 里有兩類函式:
- 第一類:用
def關鍵詞定義的正規函式 - 第二類:用
lambda關鍵詞定義的匿名函式
Python 使用 lambda 關鍵詞來創建匿名函式,而非def關鍵詞,它沒有函式名,其語法結構如下:
lambda argument_list: expression
lambda- 定義匿名函式的關鍵詞,argument_list- 函式引數,它們可以是位置引數、默認引數、關鍵字引數,和正規函式里的引數型別一樣,:- 冒號,在函式引數和運算式中間要加個冒號,expression- 只是一個運算式,輸入函式引數,輸出一些值,
注意:
expression中沒有 return 陳述句,因為 lambda 不需要它來回傳,運算式本身結果就是回傳值,- 匿名函式擁有自己的命名空間,且不能訪問自己引數串列之外或全域命名空間里的引數,
匿名函式的應用
函式式編程 是指代碼中每一塊都是不可變的,都由純函式的形式組成,這里的純函式,是指函式本身相互獨立、互不影響,對于相同的輸入,總會有相同的輸出,沒有任何副作用,
匿名函式 常常應用于函式式編程的高階函式 (high-order function)中,主要有兩種形式:
- 引數是函式 (filter, map)
- 回傳值是函式 (closure)
如,在 filter和map函式中的應用:
filter(function, iterable)過濾序列,過濾掉不符合條件的元素,回傳一個迭代器物件,如果要轉換為串列,可以使用list()來轉換,map(function, *iterables)根據提供的函式對指定序列做映射,
類與物件
物件 = 屬性 + 方法
物件是類的實體,換句話說,類主要定義物件的結構,然后我們以類為模板創建物件,類不但包含方法定義,而且還包含所有實體共享的資料,
- 封裝:資訊隱蔽技術
我們可以使用關鍵字 class 定義 Python 類,關鍵字后面緊跟類的名稱、分號和類的實作,
- 繼承:子類自動共享父類之間資料和方法的機制
- 多型:不同物件對同一方法回應不同的行動
self 是什么?
Python 的 self 相當于 C++ 的 this 指標,
類的方法與普通的函式只有一個特別的區別 —— 它們必須有一個額外的第一個引數名稱(對應于該實體,即該物件本身),按照慣例它的名稱是 self,在呼叫方法時,我們無需明確提供與引數 self 相對應的引數,
Python 的魔法方法
據說,Python 的物件天生擁有一些神奇的方法,它們是面向物件的 Python 的一切...
它們是可以給你的類增加魔力的特殊方法...
如果你的物件實作了這些方法中的某一個,那么這個方法就會在特殊的情況下被 Python 所呼叫,而這一切都是自動發生的...
類有一個名為__init__(self[, param1, param2...])的魔法方法,該方法在類實體化時會自動呼叫,
公有和私有
在 Python 中定義私有變數只需要在變數名或函式名前加上“__”兩個下劃線,那么這個函式或變數就會為私有的了,
如果子類中定義與父類同名的方法或屬性,則會自動覆寫父類對應的方法或屬性,
# 類定義
class people:
# 定義基本屬性
name = ''
age = 0
# 定義私有屬性,私有屬性在類外部無法直接進行訪問
__weight = 0
# 定義構造方法
def __init__(self, n, a, w):
self.name = n
self.age = a
self.__weight = w
def speak(self):
print("%s 說: 我 %d 歲," % (self.name, self.age))
# 單繼承示例
class student(people):
grade = ''
def __init__(self, n, a, w, g):
# 呼叫父類的構函
people.__init__(self, n, a, w)
self.grade = g
# 覆寫父類的方法
def speak(self):
print("%s 說: 我 %d 歲了,我在讀 %d 年級" % (self.name, self.age, self.grade))
s = student('小馬的程式人生', 10, 60, 3)
s.speak()
# 小馬的程式人生 說: 我 10 歲了,我在讀 3 年級
注意:如果上面的程式去掉:people.__init__(self, n, a, w),則輸出:說: 我 0 歲了,我在讀 3 年級,因為子類的構造方法把父類的構造方法覆寫了,
import random
class Fish:
def __init__(self):
self.x = random.randint(0, 10)
self.y = random.randint(0, 10)
def move(self):
self.x -= 1
print("我的位置", self.x, self.y)
class GoldFish(Fish): # 金魚
pass
class Carp(Fish): # 鯉魚
pass
class Salmon(Fish): # 三文魚
pass
class Shark(Fish): # 鯊魚
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print("吃貨的夢想就是天天有得吃!")
self.hungry = False
else:
print("太撐了,吃不下了!")
self.hungry = True
g = GoldFish()
g.move() # 我的位置 9 4
s = Shark()
s.eat() # 吃貨的夢想就是天天有得吃!
s.move()
# AttributeError: 'Shark' object has no attribute 'x'
我的位置 3 8
吃貨的夢想就是天天有得吃!
解決該問題可用以下兩種方式:
- 呼叫未系結的父類方法
Fish.__init__(self) - 使用super函式
super().__init__()
Python 雖然支持多繼承的形式,但我們一般不使用多繼承,因為容易引起混亂,
class DerivedClassName(Base1, Base2, Base3):
statement-1
.
.
.
statement-N
需要注意圓括號中父類的順序,若是父類中有相同的方法名,而在子類使用時未指定,Python 從左至右搜索,即方法在子類中未找到時,從左到右查找父類中是否包含方法,
類、類物件和實體物件

類物件:創建一個類,其實也是一個物件也在記憶體開辟了一塊空間,稱為類物件,類物件只有一個,
類屬性:類里面方法外面定義的變數稱為類屬性,類屬性所屬于類物件并且多個實體物件之間共享同一個類屬性,說白了就是類屬性所有的通過該類實體化的物件都能共享,
實體屬性:實體屬性和具體的某個實體物件有關系,并且一個實體物件和另外一個實體物件是不共享屬性的,說白了實體屬性只能在自己的物件里面使用,其他的物件不能直接使用,因為self是誰呼叫,它的值就屬于該物件,
注意:屬性與方法名相同,屬性會覆寫方法,
什么是系結?
Python 嚴格要求方法需要有實體才能被呼叫,這種限制其實就是 Python 所謂的系結概念,
Python 物件的資料屬性通常存盤在名為.__ dict__的字典中,我們可以直接訪問__dict__,或利用 Python 的內置函式vars()獲取.__ dict__,
什么是系結?
Python 嚴格要求方法需要有實體才能被呼叫,這種限制其實就是 Python 所謂的系結概念,
Python 物件的資料屬性通常存盤在名為.__ dict__的字典中,我們可以直接訪問__dict__,或利用 Python 的內置函式vars()獲取.__ dict__,
isinstance(object, classinfo)方法用于判斷一個物件是否是一個已知的型別,類似type(),type()不會認為子類是一種父型別別,不考慮繼承關系,isinstance()會認為子類是一種父型別別,考慮繼承關系,- 如果第一個引數不是物件,則永遠回傳
False, - 如果第二個引數不是類或者由類物件組成的元組,會拋出一個
TypeError例外, hasattr(object, name)用于判斷物件是否包含對應的屬性,getattr(object, name[, default])用于回傳一個物件屬性值,setattr(object, name, value)對應函式getattr(),用于設定屬性值,該屬性不一定是存在的,delattr(object, name)用于洗掉屬性,class property([fget[, fset[, fdel[, doc]]]])用于在新式類中回傳屬性值,fget-- 獲取屬性值的函式fset-- 設定屬性值的函式fdel-- 洗掉屬性值函式doc-- 屬性描述資訊
魔法方法
魔法方法總是被雙下劃線包圍,例如__init__,
魔法方法是面向物件的 Python 的一切,如果你不知道魔法方法,說明你還沒能意識到面向物件的 Python 的強大,
魔法方法的“魔力”體現在它們總能夠在適當的時候被自動呼叫,
魔法方法的第一個引數應為cls(類方法) 或者self(實體方法),
cls:代表一個類的名稱self:代表一個實體物件的名稱
基本的魔法方法
__init__(self[, ...])構造器,當一個實體被創建的時候呼叫的初始化方法__new__(cls[, ...])在一個物件實體化的時候所呼叫的第一個方法,在呼叫__init__初始化前,先呼叫__new__,__new__至少要有一個引數cls,代表要實體化的類,此引數在實體化時由 Python 解釋器自動提供,后面的引數直接傳遞給__init__,__new__對當前類進行了實體化,并將實體回傳,傳給__init__的self,但是,執行了__new__,并不一定會進入__init__,只有__new__回傳了,當前類cls的實體,當前類的__init__才會進入,
- 若
__new__沒有正確回傳當前類cls的實體,那__init__是不會被呼叫的,即使是父類的實體也不行,將沒有__init__被呼叫, __new__方法主要是當你繼承一些不可變的 class 時(比如int, str, tuple), 提供給你一個自定義這些類的實體化程序的途徑,__del__(self)析構器,當一個物件將要被系統回收之時呼叫的方法,
Python 采用自動參考計數(ARC)方式來回收物件所占用的空間,當程式中有一個變數參考該 Python 物件時,Python 會自動保證該物件參考計數為 1;當程式中有兩個變數參考該 Python 物件時,Python 會自動保證該物件參考計數為 2,依此類推,如果一個物件的參考計數變成了 0,則說明程式中不再有變數參考該物件,表明程式不再需要該物件,因此 Python 就會回收該物件,
大部分時候,Python 的 ARC 都能準確、高效地回收系統中的每個物件,但如果系統中出現回圈參考的情況,比如物件 a 持有一個實體變數參考物件 b,而物件 b 又持有一個實體變數參考物件 a,此時兩個物件的參考計數都是 1,而實際上程式已經不再有變數參考它們,系統應該回收它們,此時 Python 的垃圾回收器就可能沒那么快,要等專門的回圈垃圾回收器(Cyclic Garbage Collector)來檢測并回收這種參考回圈,
-
__str__(self):- 當你列印一個物件的時候,觸發
__str__ - 當你使用
%s格式化的時候,觸發__str__ str強轉資料型別的時候,觸發__str__
- 當你列印一個物件的時候,觸發
-
__repr__(self):repr是str的備胎- 有
__str__的時候執行__str__,沒有實作__str__的時候,執行__repr__ repr(obj)內置函式對應的結果是__repr__的回傳值- 當你使用
%r格式化的時候 觸發__repr__
__str__(self) 的回傳結果可讀性強,也就是說,__str__ 的意義是得到便于人們閱讀的資訊,就像下面的 '2019-10-11' 一樣,
__repr__(self) 的回傳結果應更準確,怎么說,__repr__ 存在的目的在于除錯,便于開發者使用,
算術運算子
型別工廠函式,指的是“不通過類而是通過函式來創建物件”,
反算術運算子
反運算魔方方法,與算術運算子保持一一對應,不同之處就是反運算的魔法方法多了一個“r”,當檔案左操作不支持相應的操作時被呼叫,
a + b
這里加數是a,被加數是b,因此是a主動,反運算就是如果a物件的__add__()方法沒有實作或者不支持相應的操作,那么 Python 就會呼叫b的__radd__()方法,
__add__(self, other)定義加法的行為:+__sub__(self, other)定義減法的行為:-__mul__(self, other)定義乘法的行為:*__truediv__(self, other)定義真除法的行為:/__floordiv__(self, other)定義整數除法的行為://__mod__(self, other)定義取模演算法的行為:%__divmod__(self, other)定義當被divmod()呼叫時的行為divmod(a, b)把除數和余數運算結果結合起來,回傳一個包含商和余數的元組(a // b, a % b),__pow__(self, other[, module])定義當被power()呼叫或**運算時的行為__lshift__(self, other)定義按位左移位的行為:<<__rshift__(self, other)定義按位右移位的行為:>>__and__(self, other)定義按位與操作的行為:&__xor__(self, other)定義按位異或操作的行為:^__or__(self, other)定義按位或操作的行為:|__radd__(self, other)定義加法的行為:+__rsub__(self, other)定義減法的行為:-__rmul__(self, other)定義乘法的行為:*__rtruediv__(self, other)定義真除法的行為:/__rfloordiv__(self, other)定義整數除法的行為://__rmod__(self, other)定義取模演算法的行為:%__rdivmod__(self, other)定義當被 divmod() 呼叫時的行為__rpow__(self, other[, module])定義當被 power() 呼叫或**運算時的行為__rlshift__(self, other)定義按位左移位的行為:<<__rrshift__(self, other)定義按位右移位的行為:>>__rand__(self, other)定義按位與操作的行為:&__rxor__(self, other)定義按位異或操作的行為:^__ror__(self, other)定義按位或操作的行為:|
增量賦值運算子
__iadd__(self, other)定義賦值加法的行為:+= __isub__(self, other)定義賦值減法的行為:-= __imul__(self, other)定義賦值乘法的行為:*= __itruediv__(self, other)定義賦值真除法的行為:/=
屬性訪問
__neg__(self)定義正號的行為:+x__pos__(self)定義負號的行為:-x__abs__(self)定義當被abs()呼叫時的行為__invert__(self)定義按位求反的行為:~x__getattr__(self, name): 定義當用戶試圖獲取一個不存在的屬性時的行為,__getattribute__(self, name):定義當該類的屬性被訪問時的行為(先呼叫該方法,查看是否存在該屬性,若不存在,接著去呼叫__getattr__),__setattr__(self, name, value):定義當一個屬性被設定時的行為,__delattr__(self, name):定義當一個屬性被洗掉時的行為,__ifloordiv__(self, other)定義賦值整數除法的行為://=__imod__(self, other)定義賦值取模演算法的行為:%=__ipow__(self, other[, modulo])定義賦值冪運算的行為:**=__ilshift__(self, other)定義賦值按位左移位的行為:<<=__irshift__(self, other)定義賦值按位右移位的行為:>>=__iand__(self, other)定義賦值按位與操作的行為:&=__ixor__(self, other)定義賦值按位異或操作的行為:^=__ior__(self, other)定義賦值按位或操作的行為:|=
描述符
描述符就是將某種特殊型別的類的實體指派給另一個類的屬性,
__get__(self, instance, owner)用于訪問屬性,它回傳屬性的值,__set__(self, instance, value)將在屬性分配操作中呼叫,不回傳任何內容,__del__(self, instance)控制洗掉操作,不回傳任何內容,
定制序列
協議(Protocols)與其它編程語言中的介面很相似,它規定你哪些方法必須要定義,然而,在 Python 中的協議就顯得不那么正式,事實上,在 Python 中,協議更像是一種指南,
容器型別的協議
- 如果說你希望定制的容器是不可變的話,你只需要定義
__len__()和__getitem__()方法, - 如果你希望定制的容器是可變的話,除了
__len__()和__getitem__()方法,你還需要定義__setitem__()和__delitem__()兩個方法, __len__(self)定義當被len()呼叫時的行為(回傳容器中元素的個數),__getitem__(self, key)定義獲取容器中元素的行為,相當于self[key],__setitem__(self, key, value)定義設定容器中指定元素的行為,相當于self[key] = value,__delitem__(self, key)定義洗掉容器中指定元素的行為,相當于del self[key],
迭代器
- 迭代是 Python 最強大的功能之一,是訪問集合元素的一種方式,
- 迭代器是一個可以記住遍歷的位置的物件,
- 迭代器物件從集合的第一個元素開始訪問,直到所有的元素被訪問完結束,
- 迭代器只能往前不會后退,
- 字串,串列或元組物件都可用于創建迭代器:
- 迭代器有兩個基本的方法:
iter()和next(), iter(object)函式用來生成迭代器,next(iterator[, default])回傳迭代器的下一個專案,iterator-- 可迭代物件default-- 可選,用于設定在沒有下一個元素時回傳該默認值,如果不設定,又沒有下一個元素則會觸發StopIteration例外,
把一個類作為一個迭代器使用需要在類中實作兩個魔法方法 __iter__() 與 __next__() ,
__iter__(self)定義當迭代容器中的元素的行為,回傳一個特殊的迭代器物件, 這個迭代器物件實作了__next__()方法并通過StopIteration例外標識迭代的完成,__next__()回傳下一個迭代器物件,StopIteration例外用于標識迭代的完成,防止出現無限回圈的情況,在__next__()方法中我們可以設定在完成指定回圈次數后觸發StopIteration例外來結束迭代,
生成器
- 在 Python 中,使用了
yield的函式被稱為生成器(generator), - 跟普通函式不同的是,生成器是一個回傳迭代器的函式,只能用于迭代操作,更簡單點理解生成器就是一個迭代器,
- 在呼叫生成器運行的程序中,每次遇到
yield時函式會暫停并保存當前所有的運行資訊,回傳yield的值, 并在下一次執行next()方法時從當前位置繼續運行, - 呼叫一個生成器函式,回傳的是一個迭代器物件,
著作權宣告:本文為博主原創文章,遵循 CC 4.0 BY-SA 著作權協議,轉載請附上原文出處鏈接和本宣告,
本文鏈接:https://mp.csdn.net/console/editor/html/108481518
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/4813.html
標籤:其他
下一篇:Python語法學習
