国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > Python > 正文

Python 類(lèi)與元類(lèi)的深度挖掘 II【經(jīng)驗(yàn)】

2019-11-25 16:48:24
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

  上一篇解決了通過(guò)調(diào)用類(lèi)對(duì)象生成實(shí)例對(duì)象過(guò)程中可能遇到的命名空間相關(guān)的一些問(wèn)題,這次我們向上回溯一層,看看類(lèi)對(duì)象本身是如何產(chǎn)生的。

  我們知道 type() 方法可以查看一個(gè)對(duì)象的類(lèi)型,或者說(shuō)判斷這個(gè)對(duì)象是由那個(gè)類(lèi)產(chǎn)生的:

  print(type(12))  print(type('python'))  class A:  pass  print(type(A))

  通過(guò)這段代碼可以看出,類(lèi)對(duì)象 A 是由type() 產(chǎn)生的,也就是說(shuō) type 也可以用來(lái)產(chǎn)生新的對(duì)象,而且產(chǎn)生的是類(lèi)對(duì)象,因此它是所有類(lèi)對(duì)象的類(lèi):

  print(type.__doc__)  type(object_or_name, bases, dict)  type(object) -> the object's type  type(name, bases, dict) -> a new type

 

  class 定義類(lèi)的語(yǔ)法實(shí)際上轉(zhuǎn)化為 type(name, bases, dict),其中 name 參數(shù)為類(lèi)的名字,bases 為繼承父類(lèi)的元組,dict 為類(lèi)的屬性和方法:

class A:  pass# 實(shí)際上等于B = type('A', (), {})print(A.__name__ == B.__name__)True

  理論上說(shuō)這就是元類(lèi)的意義,但從實(shí)際的角度出發(fā)顯然使用 class 語(yǔ)法更方便、合理,而元類(lèi)的實(shí)際意義則是通過(guò)繼承 type 類(lèi)來(lái)構(gòu)造一個(gè)新的元類(lèi),并進(jìn)行特定的操作以產(chǎn)生具有特定行為的類(lèi)對(duì)象。這樣看來(lái)它的本質(zhì)與普通的類(lèi)對(duì)象沒(méi)有差異,只不過(guò)繼承的是 type 類(lèi)。

  在生成實(shí)例時(shí)是通過(guò)調(diào)用 __init__ 方法進(jìn)行初始化的,而實(shí)際上在此之前會(huì)先調(diào)用 __new__ 方法用于創(chuàng)建實(shí)例,再通過(guò) __init__ 初始化,就好像 __new__ 負(fù)責(zé)聲明變量,而 __init__ 負(fù)責(zé)對(duì)聲明的變量進(jìn)行初始化一樣。這里有一個(gè)規(guī)則是 __new__(cls,) 的返回值必須是 cls 參數(shù)的實(shí)例,否則 __init__ 將不會(huì)觸發(fā),例如在 enum.Enum 的定義中,由于枚舉類(lèi)型是單例模式,因此在定義 __new__ 的時(shí)候沒(méi)有返回其實(shí)例,也就不會(huì)進(jìn)行初始化:

class Enum:  def __new__(cls, value):  print(cls, value)  return value  def __init__(self):  print("Will not be called!")  e = Enum(1)   <class '__main__.Enum'> 1

  通常情況下自己定義 __new__ 需要通過(guò)調(diào)用父類(lèi)的 __new__ 方法創(chuàng)建一個(gè) cls 的實(shí)例,同樣在定義元類(lèi)的時(shí)候則是調(diào)用上面提到的 type 的用法(因?yàn)樵?lèi)繼承自 type):

  

class MetaEnum(type):  def __new__(metaclass, name, base, attrs):  print("Metaclass: {}/nName: {}/nParents: {}/nAttributes: {}".format(metaclass, name, base, attrs))  return super().__new__(metaclass, name, base, attrs)  class Enum(metaclass=MetaEnum):  # Python 2.7 中定義元類(lèi)的方法是使用 __metaclass__ 變量  # [PEP 3115](https://www.python.org/dev/peps/pep-3115/)  # 將 Python 3.0 以后語(yǔ)法改為 class Cls(metaclass=Meta)  test = 0  Metaclass:   Name: Enum  Parents: ()  Attributes: {'__qualname__': 'Enum', '__module__': '__main__', 'test': 0}  此時(shí)我們?cè)賮?lái)看 Enum 的類(lèi),已經(jīng)不再是 type 而是其元類(lèi) MetaEnum:  type(Enum)  __main__.MetaEnum

  除了 __new__ 方法之外,PEP 3115 還定義了 __prepare__ 屬性,用于設(shè)定初始化的命名空間(即 type 的第 3 個(gè)參數(shù)),還是以 enum.Enum 為例,我們需要限制枚舉類(lèi)型中屬性名稱不得重復(fù)使用,則可以通過(guò)元類(lèi)限制類(lèi)的行為:

  # 定義新的字典類(lèi),在賦值新的 dict[k] = v 時(shí)

  # 檢查 k 是否重復(fù)

 class _EnumDict(dict):  def __init__(self):  super().__init__()  self.members = []  def __setitem__(self, k, v):  if k in self.members:  raise TypeError("Attempted to reuse key: '{}'".format(k))  else:  self.members.append(k)  super().__setitem__(k, v)  class MetaEnum(type):  @classmethod  def __prepare__(metaclass, cls, bases):  return _EnumDict()  def __new__(metaclass, name, base, attrs):  return super().__new__(metaclass, name, base, attrs)  class Enum(metaclass=MetaEnum):  pass  class Color(Enum):  try:  red = 1  red = 2  except TypeError:# 這里沒(méi)有使用 as err: 的原因是?  print("TypeError catched")  TypeError catched

  Python 中一切皆為對(duì)象,所有的對(duì)象都是某一類(lèi)的實(shí)例,或是某一元類(lèi)的實(shí)例,type 是自己的元類(lèi)也是自己的實(shí)例

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 岱山县| 通山县| 剑川县| 通道| 灵宝市| 石林| 乌兰察布市| 河源市| 苏尼特右旗| 抚远县| 吐鲁番市| 玉屏| 西华县| 鹿泉市| 双牌县| 北辰区| 贵溪市| 汕尾市| 平南县| 周至县| 东丽区| 阿合奇县| 新竹县| 洪洞县| 绩溪县| 安吉县| 五河县| 通州市| 沙雅县| 芦山县| 龙岩市| 天祝| 贞丰县| 海阳市| 开阳县| 赤峰市| 淮滨县| 罗山县| 慈溪市| 万载县| 周宁县|