最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python中的 enum 模塊源碼詳析

 更新時間:2019年01月09日 08:29:31   作者:棲遲于一丘  
這篇文章主要給大家介紹了關(guān)于Python中 enum 模塊的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用python具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

起步

上一篇 《Python 的枚舉類型》 文末說有機會的話可以看看它的源碼。那就來讀一讀,看看枚舉的幾個重要的特性是如何實現(xiàn)的。

要想閱讀這部分,需要對元類編程有所了解。

成員名不允許重復(fù)

這部分我的第一個想法是去控制 __dict__ 中的 key 。但這樣的方式并不好,__dict__ 范圍大,它包含該類的所有屬性和方法。而不單單是枚舉的命名空間。我在源碼中發(fā)現(xiàn) enum 使用另一個方法。通過 __prepare__ 魔術(shù)方法可以返回一個類字典實例,在該實例 使用 __prepare__ 魔術(shù)方法自定義命名空間,在該空間內(nèi)限定成員名不允許重復(fù)。

# 自己實現(xiàn)
class _Dict(dict):
 def __setitem__(self, key, value):
 if key in self:
  raise TypeError('Attempted to reuse key: %r' % key)
 super().__setitem__(key, value)

class MyMeta(type):
 @classmethod
 def __prepare__(metacls, name, bases):
 d = _Dict()
 return d

class Enum(metaclass=MyMeta):
 pass

class Color(Enum):
 red = 1
 red = 1  # TypeError: Attempted to reuse key: 'red'

再看看 Enum 模塊的具體實現(xiàn):

class _EnumDict(dict):
 def __init__(self):
 super().__init__()
 self._member_names = []
 ...

 def __setitem__(self, key, value):
 ...
 elif key in self._member_names:
  # descriptor overwriting an enum?
  raise TypeError('Attempted to reuse key: %r' % key)
 ...
 self._member_names.append(key)
 super().__setitem__(key, value)

class EnumMeta(type):
 @classmethod
 def __prepare__(metacls, cls, bases):
 enum_dict = _EnumDict()
 ...
 return enum_dict

class Enum(metaclass=EnumMeta):
 ...

模塊中的 _EnumDict 創(chuàng)建了 _member_names 列表來存儲成員名,這是因為不是所有的命名空間內(nèi)的成員都是枚舉的成員。比如 __str__, __new__ 等魔術(shù)方法就不是了,所以這邊的 __setitem__ 需要做一些過濾:

def __setitem__(self, key, value):
 if _is_sunder(key): # 下劃線開頭和結(jié)尾的,如 _order__
 raise ValueError('_names_ are reserved for future Enum use')
 elif _is_dunder(key): # 雙下劃線結(jié)尾的, 如 __new__
 if key == '__order__':
  key = '_order_'
 elif key in self._member_names: # 重復(fù)定義的 key
 raise TypeError('Attempted to reuse key: %r' % key)
 elif not _is_descriptor(value): # value得不是描述符
 self._member_names.append(key)
 self._last_values.append(value)
 super().__setitem__(key, value)

模塊考慮的會更全面。

每個成員都有名稱屬性和值屬性

上述的代碼中,Color.red 取得的值是 1。而 eumu 模塊中,定義的枚舉類中,每個成員都是有名稱和屬性值的;并且細心的話還會發(fā)現(xiàn) Color.red 是 Color 的實例。這樣的情況是如何來實現(xiàn)的呢。

還是用元類來完成,在元類的 __new__ 中實現(xiàn),具體的思路是,先創(chuàng)建目標(biāo)類,然后為每個成員都創(chuàng)建一樣的類,再通過 setattr 的方式將后續(xù)的類作為屬性添加到目標(biāo)類中,偽代碼如下:

def __new__(metacls, cls, bases, classdict):
 __new__ = cls.__new__
 # 創(chuàng)建枚舉類
 enum_class = super().__new__()
 # 每個成員都是cls的示例,通過setattr注入到目標(biāo)類中
 for name, value in cls.members.items():
 member = super().__new__()
 member.name = name
 member.value = value
 setattr(enum_class, name, member)
 return enum_class

來看下一個可運行的demo:

class _Dict(dict):
 def __init__(self):
 super().__init__()
 self._member_names = []

 def __setitem__(self, key, value):
 if key in self:
  raise TypeError('Attempted to reuse key: %r' % key)

 if not key.startswith("_"):
  self._member_names.append(key)
 super().__setitem__(key, value)

class MyMeta(type):
 @classmethod
 def __prepare__(metacls, name, bases):
 d = _Dict()
 return d

 def __new__(metacls, cls, bases, classdict):
 __new__ = bases[0].__new__ if bases else object.__new__
 # 創(chuàng)建枚舉類
 enum_class = super().__new__(metacls, cls, bases, classdict)

 # 創(chuàng)建成員
 for member_name in classdict._member_names:
  value = classdict[member_name]
  enum_member = __new__(enum_class)
  enum_member.name = member_name
  enum_member.value = value
  setattr(enum_class, member_name, enum_member)

 return enum_class

class MyEnum(metaclass=MyMeta):
 pass

class Color(MyEnum):
 red = 1
 blue = 2

 def __str__(self):
 return "%s.%s" % (self.__class__.__name__, self.name)

print(Color.red) # Color.red
print(Color.red.name) # red
print(Color.red.value) # 1

enum 模塊在讓每個成員都有名稱和值的屬性的實現(xiàn)思路是一樣的(代碼我就不貼了)。EnumMeta.__new__ 是該模塊的重點,幾乎所有枚舉的特性都在這個函數(shù)實現(xiàn)。

當(dāng)成員值相同時,第二個成員是第一個成員的別名

從這節(jié)開始就不再使用自己實現(xiàn)的類的說明了,而是通過拆解 enum 模塊的代碼來說明其實現(xiàn)了,從模塊的使用特性中可以知道,如果成員值相同,后者會是前者的一個別名:

from enum import Enum
class Color(Enum):
 red = 1
 _red = 1

print(Color.red is Color._red) # True

從這可以知道,red和_red是同一對象。這又要怎么實現(xiàn)呢?

元類會為枚舉類創(chuàng)建 _member_map_ 屬性來存儲成員名與成員的映射關(guān)系,如果發(fā)現(xiàn)創(chuàng)建的成員的值已經(jīng)在映射關(guān)系中了,就會用映射表中的對象來取代:

class EnumMeta(type):
 def __new__(metacls, cls, bases, classdict):
 ...
 # create our new Enum type
 enum_class = super().__new__(metacls, cls, bases, classdict)
 enum_class._member_names_ = []  # names in definition order
 enum_class._member_map_ = OrderedDict() # name->value map

 for member_name in classdict._member_names:
  enum_member = __new__(enum_class)

  # If another member with the same value was already defined, the
  # new member becomes an alias to the existing one.
  for name, canonical_member in enum_class._member_map_.items():
  if canonical_member._value_ == enum_member._value_:
   enum_member = canonical_member # 取代
   break
  else:
  # Aliases don't appear in member names (only in __members__).
  enum_class._member_names_.append(member_name) # 新成員,添加到_member_names_中

  enum_class._member_map_[member_name] = enum_member
  ...

從代碼上來看,即使是成員值相同,還是會先為他們都創(chuàng)建對象,不過后創(chuàng)建的很快就會被垃圾回收掉了(我認(rèn)為這邊是有優(yōu)化空間的)。通過與 _member_map_ 映射表做對比,用以創(chuàng)建該成員值的成員取代后續(xù),但兩者成員名都會在 _member_map_ 中,如例子中的 red 和 _red 都在該字典,但他們指向的是同一個對象。

屬性 _member_names_ 只會記錄第一個,這將會與枚舉的迭代有關(guān)。

可以通過成員值來獲取成員

print(Color['red']) # Color.red 通過成員名來獲取成員
print(Color(1)) # Color.red 通過成員值來獲取成員

枚舉類中的成員都是單例模式,元類創(chuàng)建的枚舉類中還維護了值到成員的映射關(guān)系 _value2member_map_ :

class EnumMeta(type):
 def __new__(metacls, cls, bases, classdict):
 ...
 # create our new Enum type
 enum_class = super().__new__(metacls, cls, bases, classdict)
 enum_class._value2member_map_ = {}

 for member_name in classdict._member_names:
  value = enum_members[member_name]
  enum_member = __new__(enum_class)

  enum_class._value2member_map_[value] = enum_member
  ...

然后在 Enum 的 __new__ 返回該單例即可:

class Enum(metaclass=EnumMeta):
 def __new__(cls, value):
 if type(value) is cls:
  return value

 # 嘗試從 _value2member_map_ 獲取
 try:
  if value in cls._value2member_map_:
  return cls._value2member_map_[value]
 except TypeError:
  # 從 _member_map_ 映射獲取
  for member in cls._member_map_.values():
  if member._value_ == value:
   return member

 raise ValueError("%r is not a valid %s" % (value, cls.__name__))

迭代的方式遍歷成員

枚舉類支持迭代的方式遍歷成員,按定義的順序,如果有值重復(fù)的成員,只獲取重復(fù)的第一個成員。對于重復(fù)的成員值只獲取第一個成員,正好屬性 _member_names_ 只會記錄第一個:

class Enum(metaclass=EnumMeta):
 def __iter__(cls):
 return (cls._member_map_[name] for name in cls._member_names_)

總結(jié)

enum 模塊的核心特性的實現(xiàn)思路就是這樣,幾乎都是通過元類黑魔法來實現(xiàn)的。對于成員之間不能做比較大小但可以做等值比較。這反而不需要講,這其實繼承自 object 就是這樣的,不用額外做什么就有的“特性”了。

總之,enum 模塊相對獨立,且代碼量不多,對于想知道元類編程可以閱讀一下,教科書式教學(xué),還有單例模式等,值得一讀。

好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • Python讀取pdf文件的簡單代碼示例

    Python讀取pdf文件的簡單代碼示例

    PDF文件的數(shù)據(jù)主要是文本、圖片、表格,這三部分組成,但是也會穿插流程圖、各種柱狀圖等,這篇文章主要給大家介紹了關(guān)于Python讀取pdf文件的簡單代碼示例,需要的朋友可以參考下
    2024-02-02
  • 翻轉(zhuǎn)數(shù)列python實現(xiàn),求前n項和,并能輸出整個數(shù)列的案例

    翻轉(zhuǎn)數(shù)列python實現(xiàn),求前n項和,并能輸出整個數(shù)列的案例

    這篇文章主要介紹了翻轉(zhuǎn)數(shù)列python實現(xiàn),求前n項和,并能輸出整個數(shù)列的案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-05-05
  • Python爬蟲文件下載圖文教程

    Python爬蟲文件下載圖文教程

    在本篇內(nèi)容里小編給大家分享的是關(guān)于Python爬蟲文件下載的相關(guān)知識點內(nèi)容,有需要的朋友們學(xué)習(xí)下。
    2018-12-12
  • python Django編寫接口并用Jmeter測試的方法

    python Django編寫接口并用Jmeter測試的方法

    這篇文章主要介紹了python Django編寫接口并用Jmeter測試,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • 網(wǎng)易2016研發(fā)工程師編程題 獎學(xué)金(python)

    網(wǎng)易2016研發(fā)工程師編程題 獎學(xué)金(python)

    這篇文章主要為大家詳細介紹了網(wǎng)易2016研發(fā)工程師編程題:獎學(xué)金(python),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-06-06
  • wxPython窗體拆分布局基礎(chǔ)組件

    wxPython窗體拆分布局基礎(chǔ)組件

    這篇文章主要為大家詳細介紹了wxPython窗體拆分布局基礎(chǔ)組件,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-11-11
  • 使用Python進行時間序列分析的8種繪圖類型

    使用Python進行時間序列分析的8種繪圖類型

    時間序列數(shù)據(jù)是按時間順序按固定時間間隔排列的觀測值的集合,每個觀察對應(yīng)于一個特定的時間點,并且可以以各種頻率(例如,每天、每月、每年)記錄數(shù)據(jù),本文介紹了幾種類型的繪圖,可幫助您使用 Python 進行時間序列分析,并提供使用可免費訪問的數(shù)據(jù)集的詳細示例
    2023-09-09
  • python yield關(guān)鍵詞案例測試

    python yield關(guān)鍵詞案例測試

    這篇文章主要介紹了python yield關(guān)鍵詞案例測試,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10
  • pandas數(shù)值排序的實現(xiàn)實例

    pandas數(shù)值排序的實現(xiàn)實例

    篩選和排序是Excel中使用頻率最多的功能,本文主要介紹了pandas數(shù)值排序的實現(xiàn)實例,文中通過示例代碼介紹的非常詳細,,感興趣的可以了解一下
    2021-07-07
  • Python深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)殘差塊

    Python深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)殘差塊

    這篇文章主要為大家介紹了Python深度學(xué)習(xí)中的神經(jīng)網(wǎng)絡(luò)殘差塊示例詳解有需要的 朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2021-10-10

最新評論

梧州市| 滦平县| 乌鲁木齐市| 江门市| 朝阳市| 石台县| 壤塘县| 沙田区| 朔州市| 太谷县| 克什克腾旗| 定安县| 综艺| 安新县| 海宁市| 双鸭山市| 长汀县| 鹤峰县| 平江县| 康定县| 宜州市| 金川县| 监利县| 绥滨县| 嵊泗县| 武宁县| 集安市| 博湖县| 神农架林区| 株洲县| 镇赉县| 江孜县| 徐闻县| 扶余县| 水富县| 大竹县| 南汇区| 双桥区| 胶州市| 罗山县| 古浪县|