Python元类和新型类-对象是类的实例,那类又是谁的实例?
文章目录
- 元类
- 类工厂
- 初始元类
- 元类属性
- 元类作用
- 面向方面和元类
- 小结
- 新型类
- 新型类VS传统类
- 静态方法和类方法
- 特定方法
- 特定属性
- super()方法
- 小结
元类
既然对象是以类为模板生成的,那么类又是以什么为模板生成的?
事实上绝大部分情况下都都不是必须使用元类才能完成开发,但是元类动态地生成类的能力能更方便地解决下面情景的难题:
- 类在设计时不是所有细节都能确定,有些细节需要程序运行时得到的信息才能决定。
- 类比实例更重要的情况,如用声明性语言在类声明中直接表示了它的程序逻辑,使用元类来影响类的创建过程就相当有用。
类工厂
在Python老版本中,可以使用类工厂函数来创建类,返回在函数体内动态创建的类。
类工厂的方法是通过一个函数来生产不同的类。类工厂可以是类,就像它们可以是函数一样容易。
例如:
def class_with_method(func):class klass: passsetattr(klass, func.__name__, func)return klass
def say_tip(self):print('记得一键三连~')
Tip = class_with_method(say_tip)
tip = Tip()
tip.say_tip()
函数class_with_method
是一个类工厂函数,通过setattr()
方法来设置类的成员函数,并且返回该类,这个类的成员方法可以通过class_with_method
的func
参数来指定。
初始元类
在Python2.2之后,type特殊类就是这样的类工厂,即所谓的元类,元类是类的类,类是元类的实例,对象是类的实例。
元类type使用方法:
def say_tip(self):print('记得一键三连~')
Tip = type('Tip',(),{'say_tip':say_tip})
tip = Tip()
tip.say_tip()
元类type首先是一个类,所以比类工厂的方法梗灵活多变,可以自由的创建子类来继承扩展元类的能力。例如:
class ChattyTypr(type):def __new__(cls, name, bases, dct):print("分配内存空间给类",name)return type.__new__(cls, name, bases, dct)def __init__(cls, name, bases, dct):print("初始化类", name)super(ChattyTypr, cls).__init__(name, bases, dct)
a = ChattyTypr('Test',(),{})
其中,__new__
分配创建类和__init__
方法配置类是类type内置的基本方法,需要注意的是,第一个蚕食是cls
(特指类本身)而非self
(类的实例)。
元类实例化一个类时,类将会获得元类所拥有方法,就像类实例化对象时对象获得类所拥有方法一样,但是注意多次实例化和多次继承的区别:
元类属性
Python中每一个类都是经过元类实例化而来,只不过这个实例化过程在很多情况下都是由Python解释器自动完成的。那么怎么设置元类的属性?
每个类都有一个属性__metaclass__
用来说明该类的元类,该属性一般由解释器自动设置,不过用户也可以更改该属性来更改类的元类。可以在类的内部直接设置__metaclass__
属性,也可以设置全局变量,那么该命名空间下定义所有类的元类都将是全局变量__metaclass__
所指定的元类。
class ChattyTypr(type):def __new__(cls, name, bases, dct):print("分配内存空间给类",name)return type.__new__(cls, name, bases, dct)def __init__(cls, name, bases, dct):print("初始化类", name)super(ChattyTypr, cls).__init__(name, bases, dct)
class example(metaclass=ChattyTypr):def __init__(self):print('初始化')
元类作用
改变全局变量__metaclass
就能改变类的元类,而类又是元类的实例化结果,所以元类可以改变类的定义过程。换句话说,只要改变全局变量__metaclass__
就能改变类的定义,这就是元类的作用了。
class example:def __init__(self):print('类example初始化')def say_tip(self):print('记得一键三连')
a = example()
a.say_tip()
class change(type):def __new__(cls, name, bases, dict):def say_tip(self):print('记得点赞关注收藏~')dict['say_tip']=say_tipreturn type.__new__(cls, name ,bases, dict)
class example(metaclass=change):def __init__(self):print('类example初始化')def say_tip(self):print('记得一键三连')
a = example()
a.say_tip()
面向方面和元类
元类的作用能带来什么实用价值吗?
实际用途确实有的,接近于面向方面编程(Aspect Oriented Programming,AOP)的核心内容,即所谓的“横切关注点”。
使用面向对象方法构建软件系统,我们可以利用OO的特性很好地解决纵向问题,因为OO的核心概念(如继承等)都是纵向结构的。
但是软件系统中往往很多模块/类共享某个行为,或者说某个行为存在于软件的各个部分中,看作是横向 存在于软件之中,它所关注的是软件个部分共有的一些行为,而且很多情况下这种行为不属于业务逻辑的一部分。
一个软件系统的业务逻辑很大一部分代码都是AOP里所说的横切关注点。例如日志处理、安全检测、事务处理、权限检测等占比很大,几乎每个地方都要调用。AOP的思想就是把这些横切关注点代码都抽取出来,不再在各个软件模块中显示使用。
以日志处理为例,一般习惯在做一些操作前写上开始模块处理的每个步骤都需要由正常日志和异常日志,那么这个软件光是写日志的代码就要成千上万行了,维护起来相当困难。
如果部分代码不需要手工写到各个业务逻辑处理的地方,而是把这部分代码独立出来,那么在各个业务逻辑处理的地方,会在运行的时候自动调用这些横切关注点功能,这样代码量就少很多,这就是AOP的核心思想。
要实现AOP所说的自动调用,有的语言使用AspectJ编译器,Python则使用元类。
小结
元类具有动态改变类的能力,给编程带来了更方便的动态性和能力。
实际使用过程中,需要防止过度使用元类来改变类,过于复杂的元类通常会带来代码难以和可读性差的问题,所以一定要在确实需要使用是再使用元类。
新型类
Python在2.2版本后,新引入了两种不同的类:新型类和传统类/经典类。Python的对象世界相比也发生了重大变化。
新型类VS传统类
老版本的Python中不是所有的元素都是对象,内置的数值类型都不能被继承,而在版本2.2后,任何内建类型也都是继承自object类的类,凡是继承自类object或者object子类的类都是新型类,而不是继承自object或object子类的都成为传统类。
新的对象模型于传统模型相比有小但是很重要的优势,Python版本对传统类的支持主要是为了兼容性,所以使用类的时候推荐从现在开始直接使用新型类。在Python3版本将放弃兼容性,即Python3.X版本中只存在新型类。
新型类继承自object或object子类,实际上所有的内建类型都是从object继承而来,可以用issubclass()
函数验证,当存在子类和父类关系时返回True,否则返回False。
(插播反爬信息 )博主CSDN地址:https://wzlodq.blog.csdn.net/
静态方法和类方法
新的对象模型提供了两种类的方法:静态方法和类方法。
静态方法可以直接被类或类的实例调用,没有常规方法的那样限制(绑定、非绑定、默认第一个参数规则等),即静态函数的第一个参数不需要指定为self
,也不需要只有对象(类的实例)才能调用。使用关键字@staticmethod
定义。
如下定义静态方法、常规方法(第一个参为self
和不带self
两种)
class Test(object):@staticmethoddef static_tip(str):print(str)def normal_tip(str):print(str)def normal_tip2(self,str):print(str)
- 使用类调用
直接使用类调用时,不需要传入self表示具体的类的实例,即报错只传了一个参数。
- 使用对象(类的实例)调用
使用对象调用时,自动将类实例对象作为第一个参数传给该方法,即报错给了两个参数。
类方法不管是使用类来调用还是使用对象(类的实例)来调用,都是将类作为第一个参数传入。使用关键字@classmethod
定义。
特定方法
__new__
方法
当一个类C调用C(*args,**kwds)
创建一个C类实例时,Python内部实际上调用的是C.__new__(C,*args,**kwds)
。new方法的返回值x就是该类的实例对象,new即用来分配内存生成类的实例。
注意第一个参数是cls
(即这里写的类C),用来接受一个类参数,然后才能返回该类的实例。
使用new方法可以实现一些传统类无法做到的功能,例如让类只能实例化一次:
__init__
方法
当调用new方法分配内存创建一个类C对象后,Python判断该实例是该类的实例,然后会调用C.__init__(x,*args,**kwds)
来初始化这个实例,x就是new方法的返回值,init即对类实例对象做初始化操作。
注意第一个参数是self
(即这里写的x)表示接受类的实例对象。
上述实例化对象代码c = C()
就等价于:
__getattribute__
方法
__getattribute__
负责实现对象属性引用的全部细节。新型类在调用它自身的类或方法是,实际上都是先通过该方法来调用。
因为新型类调用自身属性和方法时都会先调用__getattribute__
方法,所以可以实现一些新功能,如隐藏父类的方法:
特定属性
内建property
类用来绑定类实例的方法,并将其返回值绑定为一个类属性,语法:
attrib = property(fget=None, fset=None, fdel=None, doc=None)
设类C通过property
创建了属性attrib
,x是类C的一个实例。
- 当引用
x.attrib
时,会调用fget()
方法取值; - 当为
x.attrib
赋值时,会调用fset()
方法; - 当执行删除
del x.attrib
时,会调用fdel()
方法; doc
参数为该属性的文档字符串。
如果不定义fset()
和fdel()
方法,那么该属性将是一个只读属性。
property
可以方便地将一个函数的返回值转换为属性,这下操作就很灵活方便了。
比如定义一个长方形类,如果要将它的面积也作为一个属性,就可以用property
将计算面积的方法绑定为一个属性:
class Rectangle(object):def __init__(self,width,height):self.width=widthself.height=heightdef getArea(self):return self.width*self.heightarea = property(getArea(),doc='长方形的面积')
上述代码中,getArea()
是计算面积的方法,使用property
将该方法的返回值转换为属性area
,这样引用Rectangle的area
是,Python会自动使用getArea()
计算出面积。同时由于该例中只定义了fget()
方法,所以area
是一个只读属性。
super()方法
新型类提供了一个特殊的方法super()
。super(aclass,obj)
返回对象obj是一个特殊的超对象(superobject)。当我们调用该超对象的一个属性或方法时,就保证了每个父类的实现均被调用且仅仅调用了一次。
以下时直接调用父类的同名方法,无法避免类A的方法被重复调用:
class A(object):def test(self):print('A')
class B(A):def test(self):print('B')A.test(self)
class C(A):def test(self):print('C')A.test(self)
class D(B,C):def test(self):print('D')B.test(self)C.test(self)
d = D()
d.test()
以下时使用super()
方法,保证父类方法均调用一次:
class A(object):def test(self):print('A')
class B(A):def test(self):print('B')super(B, self).test()
class C(A):def test(self):print('C')super(C, self).test()
class D(B,C):def test(self):print('D')super(D, self).test()
d = D()
d.test()
小结
新型类相比于传统类,支持更多特性和机制,有更多的弹性。例如可以定制实例化的过程,尤其时在多重继承的情况下能避免传统类存在的缺陷。而事实上Python3.X版本中已经不存在传统类了,目前传统类存在的意义主要是为了保持之前的兼容性。
Python系列博客持续更新中
原创不易,请勿转载(本不富裕的访问量雪上加霜 )
博主首页:https://wzlodq.blog.csdn.net/
微信公众号:唔仄lo咚锵
如果文章对你有帮助,记得一键三连❤
Python元类和新型类-对象是类的实例,那类又是谁的实例?相关推荐
- 深入理解 python 元类
一.什么的元类 # 思考: # Python 中对象是由实例化类得来的,那么类又是怎么得到的呢? # 疑问: # python 中一切皆对象,那么类是否也是对象?如果是,那么它又是那个类实例化而来的呢 ...
- python元类、反射及双线方法
元类 print(type('abc')) print(type(True)) print(type(100)) print(type([1, 2, 3])) print(type({'name': ...
- python元类的简单了解
一.什么是元类? 在python中一切皆对象,那么类是否也是对象呢?通过class关键字产生的类的实例,我们已经很熟悉了,但是通过class关键字产生的类的类就是元类. class Bar:passb ...
- Python元类详解
文章目录 Python元类详解 Python谜团 元类的本质 调用一个类时发生了什么 再探元类 自定义元类 彩蛋:跳过python解释器 Python元类详解 元类比99%的用户所担心的魔法要更深,如 ...
- 深入理解Python元类(原创)
同样效果的代码: def __init__(cls,cls_name,cls_bases,cls_dict):type.__init__(cls,cls_name,cls_bases,cls_dict ...
- 深入理解python元类
类和对象 什么是元类 __metaclass属性 定制元类 为什么要使用元类 总结 类和对象 在理解什么是元类之前,有必要先理解下,什么是类. 什么是类?通俗的讲,类就是用来创建对象的代码片.在pyt ...
- python——元类、元类实现orm
元类 1. 类也是对象 在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段.在Python中这一点仍然成立: >>> class ObjectCreator(object ...
- Python元类编程
文章目录 property动态属性 \__getattr\_\_ \__getattribute__ 属性描述符 属性的查找顺序 自定义元类 metaclass 元类实现简单orm property动 ...
- python元类深入理解
1.python 中的类 在python中,类也是一个对象,只不过这个对象拥有生成实例的能力,我们一般使用class XXX来定义一个类,在python解释器执行到这个地方的时候会自动创建出这个对象, ...
最新文章
- <读书笔记> Thinking in python (Python 设计模式) 1. Singlton的c++与python的实现
- java接口方法默认权限_java 接口内定义方法的权限是什么?
- C#刷剑指Offer | 【常考题】最小的k个数
- PHP 函数:intval()
- 华为鸿蒙还会不会推出,华为如果把鸿蒙独立出来,让小米、魅族和蓝绿厂参股进来,会不会超越安卓?...
- 现代操作系统原理与实践02:硬件结构
- 为什么你应该在 OpenResty 项目中使用 lua-resty-core
- openstack实例启动失败_Nokia NESC,这是世界上最大的OpenStack私有云之一
- mycat是什么_MyCat
- Funcode实现打飞虫1
- Saved Blogs
- Android一键锁屏与抬手亮屏的实现
- dell计算机的硬盘如何分区,戴尔电脑分盘怎么分区
- GBase 8a Mpp Cluster集群产品性能优化篇之行列混存优化
- 高德地图怎么画圈_高德地图 Javascript API 入门(二)
- 全新的 Uber 应用设计
- 【中亦安图】运维无小事之一次导致数据丢失的小变更(10)
- 百度网盘下载限速解决
- 群晖docker部署带web容器的端口配置
- 数据库原理与应用--数据库系统概述