–Python3新增–
9. 枚舉
實際生活中的“類”,在Python中給出了一個“枚舉”類型,用來表示它。
比如我們平時玩的大型遊戲,有各種段位之分,青銅,白銀,黃金等。我們在數據庫中可能會以序號(ID)來簡單標識,而在Python3中新增了一個“枚舉”類,就用於表示這些“類”。(Python2是沒有枚舉類的)
from enum import Enum
class Level(Enum):
BRONZE = 1 # 青銅
SILVER= 2 # 白銀
GOLD= 3 # 黃金
PLATINUM= 4 # 鉑金
DIAMOND= 5 # 鑽石
之後,我們就可以直接用Level.BRONZE這種形式來表示各個類了,在繼承枚舉的類中,一般把每個常量大寫。
(1)枚舉類和普通類的區別
直觀上看起來枚舉類和普通類沒有區別,然而你打印一下print(Level.BRONZE)就發現打印結果依然是Level.BRONZE。因爲我們關注的是名,而不是值。
如果我們用字典或普通類,可以輕易地修改值,而枚舉中值是不可更改的。各位可以自己親自實驗一下,結果會報異常。而且,枚舉還防止了相同標籤的錯誤。
由於Python3中的枚舉是單例模式設計,因此無法像普通類那樣被實例化。
(2)對枚舉的簡單操作
- 獲取枚舉中類型的值:print(Level.DIAMOND.value)
- 獲取枚舉中類型標籤:print(Level.DIAMOND.name),得到的是字符串類型
- 遍歷枚舉:
for i in Level:
print(i)
=======================
Level.BRONZE
Level.SILVER
Level.GOLD
Level.PLATINUM
Level.DIAMOND
- 比較運算:Level.GOLD == Level.DIAMOND——False
Level.GOLD == Level.GOLD——True
Level.GOLD is Level.GOLD——True
Level.GOLD == 3——報錯,無法比較
另外,枚舉類型之間是無法比較大小的,“>”和“<”是不行的。
兩個枚舉類,Level和Level1(複製了Level中的成員)比較也是False:
Level.GOLD == Level1.GOLD——False
幾點注意:枚舉中不同標籤的值可以設置爲相同值,但其實代表的是同一種類型,只是起了一個別名:
class Level(Enum):
BRONZE = 1 # 青銅
QINGTONG = 1 # 青銅的別名
SILVER= 2 # 白銀
GOLD= 3 # 黃金
PLATINUM= 4 # 鉑金
DIAMOND= 5 # 鑽石
print(Level.QINGTONG)
===============================
Level.BRONZE
且遍歷時不會打印出“QINGTONG”。如果想打印出來,大家可以自己試一下:for i in Level.__members__ :和for i in Level.__members__.item():
一個轉換:當惡魔需要將枚舉類型存儲到數據庫時,我們一般使用數字,而可以通過這種方法將數字轉換成枚舉:
a = 1
print(Level(a))
結果:Level.BRONZE
(3)enum中其他模塊
- IntEnum
如果是繼承的Enum,那麼每個類型的值可以設置爲字符串,但若繼承自IntEnum,就只能爲整型。
from enum import IntEnum
class Level(IntEnum):
GOLD = 'g'
上面這樣就會報錯。
- unique
from enum import IntEnum, unique
@unique
class Level(IntEnum):
如果導入unique,則在定義枚舉類時,就不能出現不同類型的定義相同值了。
====================================================
現在,我們正式開始Python高級語法--------->
Python的高級語法與函數式編程有很大關係,但我自己之前並沒有接觸過函數式編程,所以歡迎大家及時提出意見和建議。
10. 閉包
Python中一切皆對象,因此,Python中的函數也屬於對象的範疇。
在其他語言中,函數定義時必須顯式地指出返回值類型,比如:
void add() {}、int main() {}而Python中完全不需要考慮這點,Python甚至可以將一個函數當作參數傳入另一個函數中,一個函數的返回值還能爲函數。一切的原因就是他們都是對象。
def add():
pass
print(type(add))
============================
<class 'function'>
這裏打印出的是“class”,說明是一個類。
再演示一個用法,這個用法如果是C或Java,看起來會覺得非常不可思議。
def out():
a = 1
def inner():
print('This is inner' + str(a))
return inner
fun = out()
fun()
=============================
This is inner 1
百度百科對於閉包的定義是:閉包可以理解成“定義在一個函數內部的函數“。在本質上,閉包是將函數內部和函數外部連接起來的橋樑。
在這裏,閉包就是inner函數部分加上a = 1。(在學習閉包的同時要注意思考一下變量作用域)
def out():
a = 1
def inner(b):
print('This is inner')
return a + b
return inner
fun = out()
print(fun.__closure__)
==================================
(<cell at 0x00000179BD76B558: int object at 0x0000000064AD8070>,)
而下面這兩種情況並不是閉包:
# 第一種
def out():
a = 1
def inner(b):
return b
return inner
fun = out()
print(fun.__closure__)
===================================
None
# 第二種
def out():
a = 1
def inner(b):
return b
fun = out()
print(fun.__closure__)
===================================
AttributeError: 'NoneType' object has no attribute '__closure__'
也就是說,閉包必須確保外層函數返回內層函數,且內層函數返回時必須用到外層函數定義的局部變量(即必須return a或者含有a的其他表達式)
說一下全局變量和局部變量注意點
看一下這段代碼:
variable = 0
def add(x):
global variable
temp = variable + x
variable = temp
return variable
print(add(3))
=============================
3
如果將第4行代碼刪除,則會報錯:
UnboundLocalError: local variable ‘variable’ referenced before assignment
這是因爲Python在執行時,函數內部的變量默認都是在內部定義的局部變量,如果不事先聲明,那麼Python將會在函數內部找變量的定義。
而在閉包中,有另一個類似於global的關鍵字:nonlocal,它聲明的變量可以記憶上一步的執行結果。在閉包內部使用。
def a(x):
def b(y):
nonlocal z
z = x * y
......
閉包是Python高級編程的一個難點,只有在不斷編程中自己慢慢體會才能學懂。