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