當(dāng)我們需要定義常量時(shí),一個(gè)辦法是用大寫變量通過整數(shù)來定義,例如月份:
JAN = 1
FEB = 2
MAR = 3
...
NOV = 11
DEC = 12
好處是簡單,缺點(diǎn)是類型是int,并且仍然是變量。
更好的方法是為這樣的枚舉類型定義一個(gè)class類型,然后,每個(gè)常量都是class的一個(gè)唯一實(shí)例。Python提供了Enum類來實(shí)現(xiàn)這個(gè)功能:
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
這樣我們就獲得了Month類型的枚舉類,可以直接使用Month.Jan來引用一個(gè)常量,或者枚舉它的所有成員:
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
value屬性則是自動(dòng)賦給成員的int常量,默認(rèn)從1開始計(jì)數(shù)。
如果需要更精確地控制枚舉類型,可以從Enum派生出自定義類:
from enum import Enum, unique
@unique
class Weekday(Enum):
Sun = 0 # Sun的value被設(shè)定為0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
@unique裝飾器可以幫助我們檢查保證沒有重復(fù)值。
訪問這些枚舉類型可以有若干種方法:
>>> day1 = Weekday.Mon
>>> print(day1)
Weekday.Mon
>>> print(Weekday.Tue)
Weekday.Tue
>>> print(Weekday['Tue'])
Weekday.Tue
>>> print(Weekday.Tue.value)
2
>>> print(day1 == Weekday.Mon)
True
>>> print(day1 == Weekday.Tue)
False
>>> print(Weekday(1))
Weekday.Mon
>>> print(day1 == Weekday(1))
True
>>> Weekday(7)
Traceback (most recent call last):
...
ValueError: 7 is not a valid Weekday
>>> for name, member in Weekday.__members__.items():
... print(name, '=>', member)
...
Sun => Weekday.Sun
Mon => Weekday.Mon
Tue => Weekday.Tue
Wed => Weekday.Wed
Thu => Weekday.Thu
Fri => Weekday.Fri
Sat => Weekday.Sat
可見,既可以用成員名稱引用枚舉常量,又可以直接根據(jù)value的值獲得枚舉常量。
Enum可以把一組相關(guān)常量定義在一個(gè)class中,且class不可變,而且成員可以直接比較。
比較好的講解
https://segmentfault.com/a/1190000017327003
元類
動(dòng)態(tài)語言和靜態(tài)語言最大的不同,就是函數(shù)和類的定義,不是編譯時(shí)定義的,而是運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建的。
比方說我們要定義一個(gè)Hello的class,就寫一個(gè)hello.py模塊:
class Hello(object):
def hello(self, name='world'):
print('Hello, %s.' % name)
當(dāng)Python解釋器載入hello模塊時(shí),就會(huì)依次執(zhí)行該模塊的所有語句,執(zhí)行結(jié)果就是動(dòng)態(tài)創(chuàng)建出一個(gè)Hello的class對象,測試如下:
>>> from hello import Hello
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class 'hello.Hello'>
type()函數(shù)可以查看一個(gè)類型或變量的類型,Hello是一個(gè)class,它的類型就是type,而h是一個(gè)實(shí)例,它的類型就是class Hello。
我們說class的定義是運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建的,而創(chuàng)建class的方法就是使用type()函數(shù)。
type()函數(shù)既可以返回一個(gè)對象的類型,又可以創(chuàng)建出新的類型,比如,我們可以通過type()函數(shù)創(chuàng)建出Hello類,而無需通過class Hello(object)…的定義:
>>> def fn(self, name='world'): # 先定義函數(shù)
... print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 創(chuàng)建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>
要?jiǎng)?chuàng)建一個(gè)class對象,type()函數(shù)依次傳入3個(gè)參數(shù):
class的名稱;
繼承的父類集合,注意Python支持多重繼承,如果只有一個(gè)父類,別忘了tuple的單元素寫法;
class的方法名稱與函數(shù)綁定,這里我們把函數(shù)fn綁定到方法名hello上。
通過type()函數(shù)創(chuàng)建的類和直接寫class是完全一樣的,因?yàn)镻ython解釋器遇到class定義時(shí),僅僅是掃描一下class定義的語法,然后調(diào)用type()函數(shù)創(chuàng)建出class。
正常情況下,我們都用class Xxx…來定義類,但是,type()函數(shù)也允許我們動(dòng)態(tài)創(chuàng)建出類來,也就是說,動(dòng)態(tài)語言本身支持運(yùn)行期動(dòng)態(tài)創(chuàng)建類,這和靜態(tài)語言有非常大的不同,要在靜態(tài)語言運(yùn)行期創(chuàng)建類,必須構(gòu)造源代碼字符串再調(diào)用編譯器,或者借助一些工具生成字節(jié)碼實(shí)現(xiàn),本質(zhì)上都是動(dòng)態(tài)編譯,會(huì)非常復(fù)雜。
metaclass
除了使用type()動(dòng)態(tài)創(chuàng)建類以外,要控制類的創(chuàng)建行為,還可以使用metaclass。
metaclass,直譯為元類,簡單的解釋就是:
當(dāng)我們定義了類以后,就可以根據(jù)這個(gè)類創(chuàng)建出實(shí)例,所以:先定義類,然后創(chuàng)建實(shí)例。
但是如果我們想創(chuàng)建出類呢?那就必須根據(jù)metaclass創(chuàng)建出類,所以:先定義metaclass,然后創(chuàng)建類。
連接起來就是:先定義metaclass,就可以創(chuàng)建類,最后創(chuàng)建實(shí)例。
所以,metaclass允許你創(chuàng)建類或者修改類。換句話說,你可以把類看成是metaclass創(chuàng)建出來的“實(shí)例”。
metaclass是Python面向?qū)ο罄镒铍y理解,也是最難使用的魔術(shù)代碼。正常情況下,你不會(huì)碰到需要使用metaclass的情況,所以,以下內(nèi)容看不懂也沒關(guān)系,因?yàn)榛旧夏悴粫?huì)用到。
我一個(gè)小白看這些真是有夠?yàn)殡y的,直接貼鏈接了,以后有機(jī)會(huì)用到再來看
https://www./wiki/1016959663602400/1017592449371072
來源:https://www./content-1-451551.html
|