Python自学成才之路 元类中的__new__和__init__方法
前面一节留了点悬念,这一节来做解释,相信看完这节你会对元类有更加深刻的认识。
元类其实和普通类一样,普通类的__new__方法是创建实例,__init__方法是初始化实例,说是初始化,其实就是可以给实例添加一些属性。在元类中也是一样,只是元类__new__创建的是类实例,__init__是对类实例做修改。
元类__init__中的第一个参数是cls(普通类是self)表示的是类实例本身,有了类实例本身,当然能对类做一些修改。那么在__new__和__init__方法中都可以对类实例做什么样的修改?
建议:在看本节之前建议先debug下一节的最后一个案例(传送门)
元类中,__new__和__init__中除了第一个参数不一样,其它参数都是一样的,参数都是类名,基类,类属性字典。这是元类创建一个类的三要素。在下面这个案例中,我将分别在__new__和__init__方法中做一些手术。
from pprint import pprint
import inspect
class Tag1: pass
class Tag2: pass
class Tag3:
def tag3_method(self): pass
class MetaNewVSInit(type):
def __new__(mcs, *args, **kwargs):
print('MetaNewVSInit.__new__')
name, bases, nmspc = args[0], args[1], args[2]
for x in (mcs, name, bases, nmspc): pprint(x)
print('')
if 'foo' in nmspc: nmspc.pop('foo')
nmspc['__slots__'] += ('z',)
name += '_x'
bases += (Tag1,)
nmspc['baz'] = 42
args = (name, bases, nmspc)
return super(MetaNewVSInit, mcs).__new__(mcs, *args, **kwargs)
def __init__(cls, *args, **kwargs):
name, bases, nmspc = args[0], args[1], args[2]
print('MetaNewVSInit.__init__')
for x in (cls, name, bases, nmspc): pprint(x)
print('')
nmspc['__slots__'] += ('m',) # 无效
if 'bar' in nmspc: nmspc.pop('bar') # 无效
name += '_y' # 无效
bases += (Tag2,) # 无效
nmspc['pi'] = 3.14159 # 无效
args = (name, bases, nmspc)
super(MetaNewVSInit, cls).__init__(*args, **kwargs)
class Test(metaclass=MetaNewVSInit):
__slots__ = ('x', 'y')
def __init__(self):
print('Test.__init__')
def foo(self): print('foo still here')
def bar(self): print('bar still here')
print(inspect.getmro(Test))
print(Test.mro())
print(Test.__mro__)
t = Test()
print('class name: ' + Test.__name__)
print('base classes: ', [c.__name__ for c in Test.__bases__])
print([m for m in dir(t) if not m.startswith("__")])
t.bar()
print(t.e)
输出:
AttributeError: 'Test_x' object has no attribute 'e'
MetaNewVSInit.__new__
<class '__main__.MetaNewVSInit'>
'Test'
()
{'__init__': <function Test.__init__ at 0x000001E37BEB1160>,
'__module__': '__main__',
'__qualname__': 'Test',
'__slots__': ('x', 'y'),
'bar': <function Test.bar at 0x000001E37BEB1280>,
'foo': <function Test.foo at 0x000001E37BEB11F0>}
MetaNewVSInit.__init__
<class '__main__.Test'>
'Test'
()
{'__init__': <function Test.__init__ at 0x000001E37BEB1160>,
'__module__': '__main__',
'__qualname__': 'Test',
'__slots__': ('x', 'y', 'z'),
'bar': <function Test.bar at 0x000001E37BEB1280>,
'baz': 42}
(<class '__main__.Test'>, <class '__main__.Tag1'>, <class 'object'>)
[<class '__main__.Test'>, <class '__main__.Tag1'>, <class 'object'>]
(<class '__main__.Test'>, <class '__main__.Tag1'>, <class 'object'>)
Test.__init__
class name: Test_x
base classes: ['Tag1']
['bar', 'baz', 'x', 'y', 'z']
bar still here
如果对创建类的三要素做了修改,那么是不是就修改了类实例。在这个案例中分别在__new__和__init__方法对三要素做了修改,在__new__方法中改了__slots__属性,加了基类tag2,改了类名,加了属性,删除了bar方法。在__init__中做了类似的操作。从最后输出结果可以看出__new__的修改起作用了,__init__的操作并没其作用。
主要原因是创建类实例是在__new__方法中执行的,在__init__方法中实例已经生成了,改三要素也不会再一次生成类实例。所以要想在__init__方法中起到作用,只能修改实例本身。这就好比普通类中的__new__是创建对象实例,__init__只是修改这个实例,添加一些属性。将__init__改成下面这样,修改就能起到效果了。
def __init__(cls, *args, **kwargs):
cls.__name__ += '_z'
cls.__bases__ += (Tag3,)
cls.e = 2.718
在__init__中只有直接对cls做修改才有效。看完这些是不是对元类有了进一步的认识,实际上元类和普通类是一样的,只是元类创建的实例是类,普通类创建的实例是对象。
- Nginx软件部署配置过程
- iptables网络安全服务详细使用
- 未来哪个行业能赚钱,看百度、阿里、腾讯投资的企业你就知道了!
- Augmate公司应用分布式账本技术,将IOTA整合为物联网设备管理平台
- 一域名一年前六位数终端易主 是为了......
- 黑客通过Facebook Messenger传播加密货币挖掘恶意软件
- linq to sql的多条件动态查询(下)
- iptables网络安全服务详细使用
- iptables网络安全服务详细使用
- linq to sql的多条件动态查询(上)
- 极简区块链手册:什么是区块链?什么是比特币?
- Nginx+keepalived实现高可用
- 来一波Linux中查看cpu、磁盘、内存、网络的命令
- PXE+kickstart网络安装CentOS7.4系统及过程中各种报错
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 简单了解如何封装自己的Python包
- python求解汉诺塔游戏
- Python第三方包PrettyTable安装及用法解析
- 如何让python的运行速度得到提升
- 在keras中对单一输入图像进行预测并返回预测结果操作
- python中数字是否为可变类型
- ThinkPHP3.2.3框架实现的空模块、空控制器、空操作,跳转到错误404页面图文详解
- PHP示例演示发送邮件给某个邮箱
- PHP设计模式之观察者模式定义与用法分析
- PHP实现数组向任意位置插入,删除,替换数据操作示例
- 实例讲解Python 迭代器与生成器
- opencv 图像轮廓的实现示例
- 基于python实现可视化生成二维码工具
- Python word实现读取及导出代码解析
- Python项目跨域问题解决方案