本學習筆記為阿里云天池龍珠計劃Python訓練營的學習內容,學習鏈接為:https://tianchi.aliyun.com/specials/promotion/aicamppython?spm=5176.22758685.J_6770933040.1.6f103da1tESyzu
目錄
一、學習知識點概要
二、學習內容
I.函數
1.定義自己的函數
2.函數文檔
3.函數參數
4.變量作用域
II.Lambda-表達式
1.定義
2.匿名函數的運用
III.類與對象
1.定義自己的類
2.self
?3.類的構造函數
4.公有和私有
5.類的繼承與多態
6.組合
7.類對象和實例對象
8.綁定
9.相關的內置函數
IV.魔法方法
1.基本的魔法方法
2.算術運算符
3.反算術運算符
4.增量賦值運算符
5.一元運算符
6.屬性訪問
7.描述符
8.定制序列
9.迭代器
三、學習問題與解答
四、學習思考與總結
一、學習知識點概要
本次學習的主要知識有:
- 函數
- Lambda-表達式
- 類與對象
- 魔法方法
二、學習內容
I.函數
我在之前的文章中把函數稱為方法,其實就是封裝在一起的能夠完成一些操作的代碼塊。和其他語言相同,Python不知有許許多多內置的函數,也能夠定義自己的函數。和其他語言有些不同的是,Python中的函數也是對象,其他函數的返回值也可以是一個函數,進而去構造一些高階的函數。
1.定義自己的函數
在Python中定義自己的函數的語法結構為:
def 函數名 (參數):"函數文檔字符串"函數體return [返回值]
- 定義函數時以def關鍵詞為開頭,后面為函數名和用小括號括起來的參數,參數可以不止一個
- 函數執行的是冒號之后的代碼
- return [返回值]結束函數,中括號[]里面可以填寫返回值返回給函數,返回值也可以是一個表達式。如果不帶表達式則默認返回None
例:
def mean(x):"求序列的均值"return [sum(x)/len(x)] #此處返回的是一個列表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.函數文檔
函數文檔是在函數定義中,冒號下一行的字符串,用于解釋函數的用法。在命令窗口中準備使用函數時編譯器會顯示該函數的函數文檔。可以用魔法方法function.__doc__返回函數的函數文檔,也可以用help(functionname)的方式查看到函數名為functionname的函數的函數文檔。
例:
def mean(x):"求序列的均值"return [sum(x)/len(x)]print(mean.__doc__)#'求序列的均值'
help(mean)#Help on function mean in module __main__:#mean(x)# 求序列的均值
3.函數參數
Python 的函數具有非常靈活多樣的參數形態,既可以實現簡單的調用,又可以傳入非常復雜的參數。從簡到繁的參數形態如下:
- 位置參數 (positional argument)
- 默認參數 (default argument)
- 可變參數 (variable argument)
- 關鍵字參數 (keyword argument)
- 命名關鍵字參數 (name keyword argument)
- 參數組合
(1)位置參數
定義函數時直接放在函數形參表中,沒有任何操作符的參數。這種參數在調用函數時,位置要固定,除非調用時寫出參數名進行賦值。
(2)默認參數
定義函數時在形參表中完成賦值的參數,這類參數稱為默認參數。若調用函數時傳入了參數,則使用傳入的參數值;如果沒有傳入參數值,則使用默認的參數值。
注:定義函數時默認參數一定要放在位置參數的后面
def printinfo(name, age=8): #name是位置參數,age是默認參數print('Name:{0},Age:{1}'.format(name, age))printinfo('江總',20) #Name:江總,Age:20
printinfo('小王') #Name:小王,Age:8
(3)可變參數
可變參數意為可傳入的參數的個數是可變的,為任意個
- 在形參表中寫入 *vararg 表示vararg是可變參數
- 傳入到vararg里的參數會被打包成元組
- 定義時要將可變參數放到位置參數的后面,否則在調用時會因無法給位置參數傳值而報錯
(4)關鍵字參數
關鍵字參數也可以傳入任意個
- 在形參表中寫入 **keyarg 表示keyarg是關鍵字參數
- 傳入到keyarg里的參數會被打包成字典
- 調用函數給關鍵字參數傳值時以類似?function(…… ,key1=value1,key2=value2,……)賦值的方式將傳入關鍵字參數的鍵值對打包
(5)命名關鍵字參數
由于傳入關鍵字參數時,用戶傳入的鍵值對內容不受限制,如果要限制傳入的關鍵字的名字可以用命名關鍵字參數
- 在形參表中輸入 *, 分隔符,會將分隔符后面的參數都視為命名關鍵字參數
- 調用函數時,必須要寫出命名關鍵字參數的參數名,否則會報錯
- 命名關鍵字參數可以有默認值
例:例程來源:https://blog.51cto.com/u_11317783/1953276
def person(name, age, **kw):if 'city' in kw:#關鍵字參數kw中是否有city參數passif 'job' in kw:#關鍵字參數kw中是否有job參數passprint('name:', name, 'age:', age, 'other:', kw)#調用者仍可以傳入不受限制的關鍵字參數
person('Jack', 24, city='Beijing', addr='朝陽', zipcode=123456)
# 如果要限制關鍵字參數的名字,就可以用命名關鍵字參數,例如,只接收city和job作為關鍵字參數def person(name, age, *, city, job):print(name, age, city, job)
# 和關鍵字參數**kw不同,命名關鍵字參數需要一個特殊分隔符*,*后面的參數被視為命名關鍵字參數
# 調用方式如下
person('Jack', 24, city='Beijing', job='Engineer')
(6)參數組合
定義函數時可以使用以上5種參數的任意組合,只要符合相關規定即可。但建議不要用太多的參數,否則可能會導致函數代碼的可讀性降低。
4.變量作用域
與其他熱門語言相同,Python中的變量可以分為局部變量和全局變量
- 定義在函數內部的變量擁有局部作用域,該變量稱為局部變量
- 定義在函數外部的變量擁有全局作用域,該變量稱為全局變量
- 局部變量只能在其被聲明的函數內部訪問,而全局變量可以在整個程序范圍內訪問
- 若內部作用域想修改外部作用域的變量時,需要用global或nonlocal關鍵字,例:
num = 1def fun1():global num # 需要使用 global 關鍵字聲明print(num) # 1num = 123print(num) # 123fun1()
print(num) # 123
- 函數內部可以調用其他函數,稱函數的嵌套
(1)閉包
- 是函數式編程的一個重要的語法結構,是一種特殊的內嵌函數。
- 如果在一個內部函數里對外層非全局作用域的變量進行引用,那么內部函數就被認為是閉包。
- 通過閉包可以訪問外層非全局作用域的變量,這個作用域稱為?閉包作用域
- 閉包的返回值通常是函數
例:
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) #內部的inc,dec,get,reset都是閉包的,可以訪問counter
inc() #外面的inc,dec,get,reset由于獲取的是函數make_counter
inc() #返回的內部的四個inc,dec,get,reset,
inc() #外部的inc,dec,get,reset函數依然可以對局部變量counter
print(get()) # 3 #進行操作
dec()
print(get()) # 2
reset()
print(get()) # 0
global關鍵字聲明的是全局變量,如果要在函數里的函數的局部變量的作用域,可用nonlocal關鍵字。
定義函數時,函數體里面還定義了函數,相當于有兩層函數,內層的函數是閉包的,可以訪問外層函數的變量,但如果內層函數出現了和外層函數同名的變量(假如稱為x)時,內層函數內的代碼會用內層函數的同名變量,這表明在兩個函數的作用域里有兩個不同的x。如果想在內部函數修改外層的變量,則需要在內部函數的變量聲明前加上nonlocal關鍵字,表明內外函數中的這個同名變量是同一個。例:
def outer():num = 10def inner():nonlocal num # nonlocal關鍵字聲明,表明inner內部的num和outer的num是同一個num = 100print(num)inner()print(num)outer()# 100
# 100
(2)遞歸
函數在內部調用自己,稱這個函數為遞歸函數。遞歸是一種重要的操作。例:
# 利用循環
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)可以設置遞歸的最大次數為n,默認是1000次
II.Lambda-表達式
1.定義
lambda關鍵詞用于定義匿名的函數,匿名函數沒有函數名,語法結構為:
lambda argument_list: expression
- argument_list為形參表,lambda定義的匿名函數和def定義的函數所用參數相同,但是不用小括號括起來
- expression為表達式,相當于函數的返回值
- expression中沒有return語句,因為expression本身就是返回值
- 匿名函數有自己的命名空間,匿名函數內部不能訪問形參表之外和全局命名空間里的參數
例:
def sqr(x):return x ** 2print(sqr)
# <function sqr at 0x000000BABD3A4400>y = [sqr(x) for x in range(10)] #運用def定義的普通函數
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)] #運用lambda定義的匿名函數
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)) # 15
2.匿名函數的運用
匿名函數常常應用于高階函數中,有兩種運用方式:
- 參數是函數
- 返回值是函數
例如Python內置的filter(function,iterable)函數和map(function,*iterable)函數中,都有函數作為參數,如果用先用def定義一個常常會有些麻煩,但如果用lambda定義的匿名函數就會簡潔不少。
odd = lambda x: x % 2 == 1 #filter()過濾序列,過濾掉不符合條件的templist = filter(odd, [1, 2, 3, 4, 5, 6, 7, 8, 9]) #元素,返回一個迭代器對象,如果要轉換為列
print(list(templist)) #表,可以使用list() 來轉換
# [1, 3, 5, 7, 9]m1 = map(lambda x: x ** 2, [1, 2, 3, 4, 5]) #map()根據提供的函數對指定序列做映射
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.類與對象
與C++和Java類似,Python也可以定義自己的類。類是對象的模板,對象是類的實例,類中包含屬性和方法(函數)。
1.定義自己的類
我們可以用關鍵字class定義類,關鍵字后面跟類名和冒號,以及類的內容,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類也是對象。它們是type的實例
print(type(Turtle))
# <class 'type'>
2.self
Python 中的 self 相當于 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.類的構造函數
類的構造函數是一種魔法方法,這個魔法方法為__init__(self[,param1,param2…])
Python中的構造函數可以自己定義,但是函數名必須是__init__(self[,param1,param2…])。在類外給類實例化(產生對象)時會自動調用,調用方法是? 類名(參數),參數是__init__()中除self之外的參數。
在定義子類的構造函數時應調用父類的構造函數。如果不調用父類的構造函數,可能會因子類的構造函數覆蓋了父類的構造函數而導致從父類繼承過來的屬性不是自己想要的值。當然,你也可以通過在子類的構造函數里給所有屬性賦值來避免這種情況,但是這樣在類的屬性較多時就會比較繁瑣了。
例子可見下文“類的繼承”部分
4.公有和私有
在 Python 中定義私有變量只需要在變量名或函數名前加上“__”兩個下劃線,那么這個函數或變量就會為私有的了。
其余的與C++和Java中的公有私有類似,此處不作贅述。但是要提的是Python中的私有是偽私有,只要用以下形式仍然可以在類外直接訪問或調用對象的私有屬性或私有方法。
對象._類名__私有屬性 #只需在對象的.和私有數據或方法的雙下劃線__
對象._類名__私有方法 #之間插入下劃線 _ 加上類名即可
5.類的繼承與多態
定義的類可以用以下語法形式繼承父類:
class 子類名(父類名1,父類名2,……,父類名n):類的實現
子類可以繼承父類的數據和函數。子類必須與派生類定義在一個作用域內。除了類,還可以用表達式,基類定義在另一個模塊中時這一點非常有用。
與Java類似,子類可以重寫父類的函數。但是子類在重寫構造函數時,由于構造函數名不能變化,子類的構造函數就會覆蓋父類的構造函數,這樣可能會出現以下狀況:
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("吃貨的夢想就是天天有得吃!")self.hungry = Falseelse:print("太撐了,吃不下了!")self.hungry = Trueg = GoldFish()
g.move() # 我的位置 9 4
s = Shark()
s.eat() # 吃貨的夢想就是天天有得吃!
s.move()
# AttributeError: 'Shark' object has no attribute 'x'
'''
Shark類的構造函數沒有調用父類Fish的構造函數
導致對象s在實例化時沒有x和y兩個屬性
從而在調用move()函數時訪問x和y就會出錯
'''
?要在子類的構造函數中調用父類的構造函數,可以有以下兩種方法:
class Shark(Fish): # 鯊魚def __init__(self):Fish.__init__(self) #調用未綁定的父類方法Fish.__init__(self)self.hungry = Truedef eat(self):if self.hungry:print("吃貨的夢想就是天天有得吃!")self.hungry = Falseelse:print("太撐了,吃不下了!")self.hungry = True'''或是用以下方法'''
class Shark(Fish): # 鯊魚def __init__(self):super().__init__() #用super函數super().__init__()self.hungry = Truedef eat(self):if self.hungry:print("吃貨的夢想就是天天有得吃!")self.hungry = Falseelse:print("太撐了,吃不下了!")self.hungry = True
?其中super函數和Java中的super關鍵字類似,但是在Python的子類中用super()來表示父類的對象。
在多繼承時需要注意圓括號中父類的順序,若是父類中有相同的函數名,而在子類使用時未指定,Python 從左至右搜索,即函數在子類中未找到時,從左到右查找父類中是否包含該函數。
與Java類似,子類也可以通過重寫父類的函數,再通過用子類的構造方法構造父類的對象來實現多態。Java中稱用子類構造方法構造的父類對象為上轉型對象,調用同名的函數時會調用子類中的,但是子類有而父類中沒有的屬性和函數會丟失。
多態:不同對象對同一方法響應不同的行動
例:
class Animal:def run(self):raise AttributeError('子類必須實現這個方法')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類的構造函數
# pig is walking
6.組合
即其他類的對象可以成為另一個類的屬性,例:
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.類對象和實例對象
- 在Python中“萬物皆對象”,所以我們創建的類也是一個對象,也在內存中開辟了一塊空間,這種對象叫類對象,類對象只能有一個。
- 通過實例化類創建的對象稱為實例對象,實例對象可以有多個。
類屬性和實例屬性:
類屬性和實例屬性的說法和Java類似,就是靜態屬性和非靜態屬性的區別,但是Python中的類屬性和實例屬性的聲明不一樣。
- 在類里面方法外聲明的屬性是類屬性,需要賦值,所有實例對象都可以訪問且共享。
- 實例屬性是和具體的實例有關,并且一個實例對象和另外一個實例對象是不共享屬性的,說白了實例屬性只能在自己的對象里面使用,其他的對象不能直接使用。可以通過類內的函數或類外賦值創建。
- 注:在類外賦值創建實例屬性時,如果屬性和函數同名,屬性會覆蓋函數。
8.綁定
Python 嚴格要求方法需要有實例才能被調用,這種限制其實就是 Python 所謂的綁定概念。
Python 對象的數據屬性通常存儲在名為 .__dict__的字典中,我們可以直接訪問 .__dict__ ,或者利用 Python 的內置函數 vars()獲取 .__dict__ 。
9.相關的內置函數
- issubclass(class,classinfo)? 方法用于判斷參數 class 是否是類型參數 classinfo 的子類
- 一個類被認為是其自身的子類
- classinfo?可以是類對象的元組,只要class是其中任何一個候選類的子類,則返回True
?
- isinstance(object,classinfo)? 方法用于判斷一個對象是否是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? ? ? ? ? ?屬性描述信息
IV.魔法方法
Python的魔法方法是一些特殊的方法,又叫魔術方法,例如上文提到的:__init__()。魔法方法是面向對象的 Python 的一切,有著強大的作用。魔法方法有以下特點:
- 魔法方法總是被雙下劃線包圍
- 魔法方法總是能夠在適當的時候被自動調用
魔法方法的第一個參數應為 cls (類方法)? 或者 self(實例方法)。cls代表一個類名稱,self代表一個實例對象的名稱。
1.基本的魔法方法
- __init__(self[,……]) 構造器,即各個類的構造方法,當一個實例被創建的時候調用的初始化方法。
- __new__(cls[,……])?在一個對象實例化的時候所調用的第一個方法,系統會先調用__new__再調用__init__。
- 注:__new__()至少要有一個參數cls,代表要實例化的類,此參數在實例化時由 Python 解釋器自動提供,后面的參數直接傳遞給__init__()
- 注:__new__()對當前類進行了實例化,即產生了一個對象,再將對象返回,傳給__init__中的self。但是__new__返回的實例必須是當前類的實例才會繼續執行__init__。也就是說,如果通過在類中定義了__new__(),使得__new__返回的不是當前類的對象,那當前類中的__init__是不會被調用的,即使返回的是父類的實例也不行。
- 例:
class A(object):def __init__(self, value):print("into A __init__")self.value = valuedef __new__(cls, *args, **kwargs):print("into A __new__")print(cls)return object.__new__(cls)class B(A):def __init__(self, value):print("into B __init__")self.value = valuedef __new__(cls, *args, **kwargs):print("into B __new__")print(cls)return super().__new__(A, *args, **kwargs) #返回的__new__中的cls參數為A類b = B(10)# 結果:
# into B __new__
# <class '__main__.B'>
# into A __new__
# <class '__main__.A'>
- __new__()方法主要是當你繼承一些不可變的 class 時(如:int , str ,tuple), 提供給你一個自定義這些類的實例化過程的途徑。
- __del__(self) 析構器,相當于C++中的析構函數,當一個對象將要被系統回收之時調用的方法。在類中也可以自己定義__del__()的內容,與C++相似。
- __str__(self) 當我們打印(print)一個對象的時候、當你使用%s格式化的時候、str()強轉數據類型的時候會調用。
- __repr__(self)? __repr__是__str__的備用函數,如果沒有實現__str__的時候才會執行__repr__()
- 注:__repr__(obj)內置函數對應的結果是__repr__的返回值
- 注:當使用%r格式化的時候會調用__repr__
__str__(self)的返回結果可讀性強。也就是說,__str__的意義是得到便于閱讀的信息,就像下面的 '2019-10-11' 一樣。
__repr__(self)的返回結果應更準確。也就是說,__repr__存在的目的在于調試,便于開發者使用。
例:
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.算術運算符
Python中可以在類中定義運算符對應的魔法方法,進而實現類對象的算術運算的操作。
- __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()調用時的行為。
- 注:未定義時power()與**操作結果相同
- __lshift__(self,other)? ? ? ? ? ? ? ? ? ? ? ? ? 定義左移運算符 :<<
- __rshift__(self,other)? ? ? ? ? ? ? ? ? ? ? ? ? 定義右移運算符 :>>
- __and__(self,other)? ? ? ? ? ? ? ? ? ? ? ? ? ? 定義按位與 :&
- __xor__(self,other)? ? ? ? ? ? ? ? ? ? ? ? ? ? ?定義按位異或 :^
- __or__(self,other)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?定義按位或 :|
例:
class MyClass:def __init__(self, height, weight):self.height = heightself.weight = weight# 兩個對象的身高相加,體重相加。返回一個新的對象def __add__(self, others):return MyClass(self.height + others.height, self.weight + others.weight)# 兩個對象的身高相減,體重相減。返回一個新的對象def __sub__(self, others):return MyClass(self.height - others.height, self.weight - others.weight)# 說一下自己的參數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 重為 15
3.反算術運算符
反運算魔方方法,與算術運算符保持一一對應,不同之處就是反運算的魔法方法多了一個“r”。當文件左操作不支持相應的操作時被調用。
以定義加法的魔法方法為例,定義算術運算符+用__add__(self,other),反算術運算符+用__radd__(self,other)。上文所提所有運算符都有對應的反算術運算符。
反算術運算符的用處可以用a+b舉例,這里a是加數,b是被加數,當使用__add__(self,other)定義的+運算時,會將a傳給self,b傳給other,也就是調用對象a的__add__()方法。如果定義的__add__(self,other)不支持當前操作,或者__add__(self,other)沒有實現,Python就會調用對象b的__radd__(self,other)。換句話說就是如果a+b實現不了,就計算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
'''代碼解釋:
運算1 + b時,1是int類型的對象,b是Nint類型的對象
一開始先嘗試調用1.__add__(),由于不支持此類操作
系統便嘗試調用b.__radd__(),將b傳入self,1傳入other
返回的是int.__sub__(),1 + b的運算結果就變成1 - 3 '''
4.增量賦值運算符
Python中有增量賦值運算符,例如 += ,a += 2 與 a = a + 2等價。定義增量賦值運算符的魔法方法與算術運算符的魔法方法一一對應,不同之處在于名字前面多了一個“ i ”
- __iadd__(self,other)? ? ? ? ? ? ? ? ? ? ? ?定義賦值加法:+=
- __isub__(self,other)? ? ? ? ? ? ? ? ? ? ? ?定義賦值減法:-=
- __imul__(self,other)? ? ? ? ? ? ? ? ? ? ? ?定義賦值乘法:*=
- __itruediv__(self,other)? ? ? ? ? ? ? ? ??定義賦值真除法:/=
- __ifloordiv__(self,other)? ? ? ? ? ? ? ? ?定義賦值整數除法://=
- __imod__(self,other)? ? ? ? ? ? ? ? ? ? ? 定義賦值取模算法:%=
- __ipow__(self,other[,module])? ? ? ?定義賦值冪運算:**=
- __ilshift__(self,other)? ? ? ? ? ? ? ? ? ? ?定義賦值按位左移位:<<=
- __irshift__(self,other)? ? ? ? ? ? ? ? ? ??定義賦值按位右移位:>>=
- __iand__(self,other)? ? ? ? ? ? ? ? ? ? ??定義賦值按位與操作:&=
- __ixor__(self,other)? ? ? ? ? ? ? ? ? ? ? ?定義賦值按位異或操作:^=
- __ior__(self,other)? ? ? ? ? ? ? ? ? ? ? ? ?定義賦值按位或操作:|=
5.一元運算符
- __neg__(self)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?定義正號:+x
- __pos__(self)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?定義負號:-x
- __abs__(self)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?定義被abs()調用時的行為,即絕對值
- __invert__(self)? ? ? ? ? ? ? ? ? ? ? ? ? ? 定義按位取反:~x
6.屬性訪問
- __getattr__(self,name)??? ? ? ? ? ? ? ?定義當用戶試圖獲取一個不存在的屬性時的行為。
- __getattribute__(self,name)
? ?
定義當該類的屬性被訪問時的行為(先調用該方法,查看是否存在該屬性,若不存在,接著去調用__getattr__)。 - __setattr__(self,name,value)? ? ? ? 定義當一個屬性被設置時的行為。
- __delattr__(self,name)? ? ? ? ? ? ? ? ? 定義當一個屬性被刪除時的行為。
例:
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.描述符
描述符就是將某種特殊類型的類的實例指派給另一個類的屬性。
- __get__(self,instance,owner)? ? ? ? 用于訪問屬性,它返回屬性的值。
- __set__(self,instance,value)? ? ? ? ?將在屬性分配操作中調用,不返回任何內容。
- __delete__(self,instance)? ? ? ? ? ? ?控制刪除操作,不返回任何內容。
例:
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有一種類叫協議(Protocols),協議與其它編程語言中的接口很相似,它規定你哪些方法必須要定義。然而,在 Python 中的協議就顯得不那么正式。事實上,在 Python 中,協議更像是一種指南。
容器類型相關的魔法方法有:
- __len__(self)? ? ? ? ? ? ? ? ? ? ? ? ? ? 定義當被len()調用時的行為(返回容器中元素的個數)
- __getitem__(self,key)? ? ? ? ? ? ? 定義 獲取容器中元素 的行為
- __setitem__(self,key,value)? ? ?定義設置容器中指定元素的行為,相當于self[key] = value
- __delitem__(self,key)? ? ? ? ? ? ? ?定義刪除容器中指定元素的行為,相當于del self[key]
而容器類型的協議有:
- 如果希望定制的容器是不可變的,則只需要在類中定義__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 #統計容器內元素被訪問的次數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 最強大的功能之一,是訪問集合元素的一種方式。
- 迭代器是一個可以記住遍歷的位置的對象。
- 迭代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。
- 迭代器只能往前不會后退。
- 字符串,列表或元組對象都可用于創建迭代器
迭代器有兩個基本方法:iter() 和 next()
- iter(obj)? ? ? ? ? ? ? ? ? ? ? ? ?用于生成迭代器
- next(iterator[,default])? ?返回迭代器的下一個項目。iterator指可迭代對象,即一個迭代器。default是可選參數,如果沒有下一個元素時返回默認值default,如果沒設置,又沒有下一個元素則會報錯
例:
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
如果要定義一個類來當迭代器,則需要在類中實現兩個魔法方法__iter__()和__next__()
- __iter__(self)? ? ? ? ?定義迭代容器中的元素的行為,返回一個特殊的迭代器對象, 這個迭代器對象實現了__next__()方法并通過StopIteration異常標識迭代的完成
- __next__()? ? ? ? ? ? ?返回下一個迭代器對象
- StopIteration?異常用于標識迭代的完成,防止出現無限循環的情況,在__next__()方法中我們可以設置在完成指定循環次數后觸發StopIteration異常來結束迭代
例:
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 的函數被稱為生成器(generator)。
- 跟普通函數不同的是,生成器是一個返回迭代器的函數,只能用于迭代操作,更簡單點理解生成器就是一個迭代器。
- 在調用生成器運行的過程中,每次遇到 yield 時函數會暫停并保存當前所有的運行信息,返回 yield 的值, 并在下一次執行?next()?方法時從當前位置繼續運行。
- 調用一個生成器函數,返回的是一個迭代器對象
? 例:
def myGen():print('生成器執行!')yield 1yield 2myG = myGen()
for each in myG:print(each)'''
生成器執行!
1
2
'''myG = myGen()
print(next(myG))
# 生成器執行!
# 1print(next(myG)) # 2
print(next(myG)) # StopIteration
三、學習問題與解答
本次學習所遇到的問題還是對大多數方法的不熟悉。尤其本次學習的主要就是方法,包括Python中最重要的魔法方法,在代碼運行時什么時候會調用魔法方法并不熟悉。主要還是需要多上手練習才能知道。
四、學習思考與總結
本次學習主要是學學習Python中的函數,或叫方法。從定義普通的函數,匿名函數和魔法函數都有很大的用處。魔法方法和類的運用可以做出很多簡潔又包括多方面功能的操作。主要是要多上手才能有更深的了解。
?