热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Python类与元类的深度挖掘II【经验】

元类在Python中属于比较深层的黑魔法,在一般的日常应用中可能并不常用,但理解其背后的原理对于理解Python面向对象编程以及一切皆为对象的理念很有帮助;如果你需要对类进行深度改造,至少要知道从何入手。
  上一篇解决了通过调用类对象生成实例对象过程中可能遇到的命名空间相关的一些问题,这次我们向上回溯一层,看看类对象本身是如何产生的。

  我们知道 type() 方法可以查看一个对象的类型,或者说判断这个对象是由那个类产生的:

  print(type(12))

  print(type('python'))

  class A:

  pass

  print(type(A))

  通过这段代码可以看出,类对象 A 是由type() 产生的,也就是说 type 也可以用来产生新的对象,而且产生的是类对象,因此它是所有类对象的类:

  print(type.__doc__)

  type(object_or_name, bases, dict)

  type(object) -> the object's type

  type(name, bases, dict) -> a new type

  class 定义类的语法实际上转化为 type(name, bases, dict),其中 name 参数为类的名字,bases 为继承父类的元组,dict 为类的属性和方法:

class A:
  pass
# 实际上等于
B = type('A', (), {})

print(A.__name__ == B.__name__)
True

  理论上说这就是元类的意义,但从实际的角度出发显然使用 class 语法更方便、合理,而元类的实际意义则是通过继承 type 类来构造一个新的元类,并进行特定的操作以产生具有特定行为的类对象。这样看来它的本质与普通的类对象没有差异,只不过继承的是 type 类。

  在生成实例时是通过调用 __init__ 方法进行初始化的,而实际上在此之前会先调用 __new__ 方法用于创建实例,再通过 __init__ 初始化,就好像 __new__ 负责声明变量,而 __init__ 负责对声明的变量进行初始化一样。这里有一个规则是 __new__(cls,) 的返回值必须是 cls 参数的实例,否则 __init__ 将不会触发,例如在 enum.Enum 的定义中,由于枚举类型是单例模式,因此在定义 __new__ 的时候没有返回其实例,也就不会进行初始化:

class Enum:

  def __new__(cls, value):

  print(cls, value)

  return value

  def __init__(self):

  print("Will not be called!")

  e = Enum(1)

    1

  通常情况下自己定义 __new__ 需要通过调用父类的 __new__ 方法创建一个 cls 的实例,同样在定义元类的时候则是调用上面提到的 type 的用法(因为元类继承自 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 中定义元类的方法是使用 __metaclass__ 变量

  # [PEP 3115](https://www.python.org/dev/peps/pep-3115/)

  # 将 Python 3.0 以后语法改为 class Cls(metaclass=Meta)

  test = 0

  Metaclass: 

  Name: Enum

  Parents: ()

  Attributes: {'__qualname__': 'Enum', '__module__': '__main__', 'test': 0}

  此时我们再来看 Enum 的类,已经不再是 type 而是其元类 MetaEnum:

  type(Enum)

  __main__.MetaEnum

  除了 __new__ 方法之外,PEP 3115 还定义了 __prepare__ 属性,用于设定初始化的命名空间(即 type 的第 3 个参数),还是以 enum.Enum 为例,我们需要限制枚举类型中属性名称不得重复使用,则可以通过元类限制类的行为:

  # 定义新的字典类,在赋值新的 dict[k] = v 时

  # 检查 k 是否重复

 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:# 这里没有使用 as err: 的原因是?

  print("TypeError catched")

  TypeError catched

  Python 中一切皆为对象,所有的对象都是某一类的实例,或是某一元类的实例,type 是自己的元类也是自己的实例

推荐阅读
  • 安装mysqlclient失败解决办法
    本文介绍了在MAC系统中,使用django使用mysql数据库报错的解决办法。通过源码安装mysqlclient或将mysql_config添加到系统环境变量中,可以解决安装mysqlclient失败的问题。同时,还介绍了查看mysql安装路径和使配置文件生效的方法。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • 打开文件管理器_【教程】模组管理器3.1食用指南
    文编:byakko最近有部分小伙伴反应还不会使用unity模组管理器,现在我就给大家讲一下unity模组管理器——从下载到使用。完整视频版以下是无WiF ... [详细]
  • 本文详细介绍了C语言中的格式化字符串类型,包括浮点数、十六进制数字、p-计数法、十进制整数、小数形式、实数、有符号整数和输出字符串等。对于每种类型,都给出了详细的解释和示例。通过本文的学习,读者将对C语言中的格式化字符串类型有更深入的理解。 ... [详细]
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • 本文详细解析了JavaScript中相称性推断的知识点,包括严厉相称和宽松相称的区别,以及范例转换的规则。针对不同类型的范例值,如差别范例值、统一类的原始范例值和统一类的复合范例值,都给出了具体的比较方法。对于宽松相称的情况,也解释了原始范例值和对象之间的比较规则。通过本文的学习,读者可以更好地理解JavaScript中相称性推断的概念和应用。 ... [详细]
  • Linuxchmod目录权限命令图文详解在Linux文件系统模型中,每个文件都有一组9个权限位用来控制谁能够读写和执行该文件的内容。对于目录来说,执行位的作用是控制能否进入或者通过 ... [详细]
  • Skywalking系列博客1安装单机版 Skywalking的快速安装方法
    本文介绍了如何快速安装单机版的Skywalking,包括下载、环境需求和端口检查等步骤。同时提供了百度盘下载地址和查询端口是否被占用的命令。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • Lodop中特殊符号打印设计和预览样式不同的问题解析
    本文主要解析了在Lodop中使用特殊符号打印设计和预览样式不同的问题。由于调用的本机ie引擎版本可能不同,导致在不同浏览器下样式解析不同。同时,未指定文字字体和样式设置也会导致打印设计和预览的差异。文章提出了通过指定具体字体和样式来解决问题的方法,并强调了以打印预览和虚拟打印机测试为准。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • Final关键字的含义及用法详解
    本文详细介绍了Java中final关键字的含义和用法。final关键字可以修饰非抽象类、非抽象类成员方法和变量。final类不能被继承,final类中的方法默认是final的。final方法不能被子类的方法覆盖,但可以被继承。final成员变量表示常量,只能被赋值一次,赋值后值不再改变。文章还讨论了final类和final方法的应用场景,以及使用final方法的两个原因:锁定方法防止修改和提高执行效率。 ... [详细]
  • 本文介绍了求解gcdexgcd斐蜀定理的迭代法和递归法,并解释了exgcd的概念和应用。exgcd是指对于不完全为0的非负整数a和b,gcd(a,b)表示a和b的最大公约数,必然存在整数对x和y,使得gcd(a,b)=ax+by。此外,本文还给出了相应的代码示例。 ... [详细]
author-avatar
火立者
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有