当前位置: 首页 > 后端技术 > Python

Python类内置方法介绍

时间:2023-03-26 12:46:31 Python

相对于其他语言,Python中的类提供了很多以双下划线开头,以__xxx__结尾的方法。这些方法是Python运行的基础。很多功能背后都是调用这些内置方法来实现的。例如len()函数调用了对象的__len__方法;print(obj)函数调用对象的__str__方法,而foriteminiterable_obj调用对象的__next__和__iter__方法。注意:由于一个类的所有内置方法都是以双下划线开头和结尾,所以在下面的描述中,为了简洁起见,在不引起歧义的情况下,有些描述中会去掉方法前后的双下划线。__new__和__init__new和init这两个方法很容易混淆。定义一个类时,通常使用init方法,很少使用new方法,但它们的作用完全不同。new是静态(@staticmethod)方法,用于创建实例对象,该方法必须返回一个对象;而init是执行实例初始化的实例方法,在new返回的对象中执行。__new__()主要用于允许不可变类型(如int、str或tuple)的子类自定义实例创建。它也通常在自定义元类中被覆盖以自定义类创建。init在创建实例之后调用(通过__new__()),但在它返回给调用者之前。正常的类实例化过程如下。类Obj(object):def__new__(cls):print("__new__in")returnobject.__new__(Obj)def__init__(self):print("__init__in")obj=Obj()print(type(obj))#执行输出>>__new__in>>__init__in>>如果我们在类的new方法中返回其他类型,那么你以新类型结束。classOldObj(object):def__new__(cls):print("__new__in")返回对象.__new__(NewObj)def__init__(self):print("__init__in")classNewObj(object):def__init__(self):print("__init__in")obj=OldObj()print(type(obj))#执行输出>>__new__in>>here这里有一个疑问,为什么new执行完后,OldObj的init方法和NewObj的init方法都不执行。PythonDoc中的描述是:如果__new__()没有返回cls的实例,那么新实例的__init__()方法将不会被调用。在应用中,可以通过重写new方法来实现单例模式。classSingleton(object):instance=Nonedef__new__(cls):如果不是cls.instance:cls.instance=object.__new__(cls)returncls.instances1=Singleton()s2=Singleton()print(s1==s2)#输出:True__str__和__repr__str和repr都返回一个对象的字符串描述,但两者的用途不同。可以这样理解,str是给人看的,repr是给程序看的。官网对repr的描述如下:repr(obj)返回对象的规范字符串表示。对于许多对象类型,包括大多数内置函数,eval(repr(obj))==obj。即:在很多情况下,通过repr(obj)输出的数据,可以重构obj对象。print(obj)和str(obj)方法调用对象的str方法;交互式CLI、repr(obj)、gdb调试返回对象的值以查看repr,但大多数情况下,程序员将str和repr设置为相同的__str__==__repr__。__call__call方法将对象变成可调用对象,即当直接通过obj()调用对象时,系统执行对象的call方法,callable(obj)为实现调用的对象返回True方法。classCallObj(object):def__call__(self,*args,**kwargs):print("__call__")obj=CallObj()print(callable(obj))obj()#调用对象的__call__方法应用上另一方面,调用语法糖可用于简化对象调用;用户也可以使用实现call方法的对象来代替函数式装饰器,简化代码结构。__iter__和__next__在Java等强类型语言中,对象的功能特性必须通过继承或实现接口来实现。例如,可迭代类必须继承自Iterator或者实现Iterable接口并实现相关方法。对于动态语言Python,属于鸭子类型。只要一个类实现了iter和next方法,它就是一个可迭代对象。鸭子类型是编程中的一种动态类型类型。在这种风格中,对象的有效语义不是由从特定类继承或实现特定接口决定的,而是由“当前的方法和属性集”决定的。该概念的名称来源于JamesWhitcombRiley提出的鸭子测试。“鸭考”可以这样表述:“当一只鸟走路像鸭子,游起来像鸭子,叫声像鸭子,鸭子,那么这只鸟就可以称为鸭子。“类Iter:def__init__(self,max):self.max=maxself.current=0def__iter__(self):returnselfdef__next__(self):ifself.current>getitem,args:1#slice读取操作item_test[1:10]#>>getitem,args:slice(1,10,None)#用Step的slice读操作item_test[1:10:2]#>>getitem,args:slice(1,10,2)#通过键值读取操作item_test["str_key"]#>>getitem,args:str_key#切片为键值,非标准操作,方法会如实告知传递参数item_test["key1":"key2"]#>>getitem,args:slice('key1','key2',None)setitemoperation:#下标赋值操作item_test[1]=99#>>setitem,args:199#切片替换操作item_test[1:2]=[10,20]#>>setitem,args:slice(1,2,None)[10,20]delitemoperation:#下标删除操作delitem_test[5]#>>delitem,args:5#切片删除操作delitem_test[5:10]#>>delitem,args:slice(5,10,None)__getattr__,__setattr__,__delattr__这组方法在框架开发中必不可少Artifact当访问对象中不存在的属性时,系统会调用对象的getattr方法。通过这个方法的处理,系统可以凭空创造出原本系统不支持的功能。比如访问obj.attr1,而obj中不存在attr1时,就会触发getattr方法。为对象属性设置值时会触发setattr,例如obj.attr1=100;delattr在删除某个属性时触发,例如delobj.attr1。例如,在ORM模型中,我们需要将与数据库字段相关的属性保存在对象中的dict类型的字段字段中,实现与对象其他字段的隔离,可以通过以上三种方式实现。classUser:def__init__(self):#保存数据库表映射字段self.__dict__['fields']=dict()#数据库字段列表self.__dict__["fields_list"]=["name","age","address"]def__setattr__(self,key,value):#通过user.name=xxxifkeyinself.fields_list实现赋值:self.__dict__["fields"][key]=valuereturnself.__dict__[key]=valuedef__getattr__(self,key):#通过user.name获取valueifkeyinself.fields_list:returnself.__dict__["fields"][key]returnself.__dict__[key]user=User()user.name="zhangsan"print(user.__dict__['fields']['name'])#>>zhangsan在实现getattr方法的类内部给对象属性赋值时需要特别注意,容易造成getattr死循环。在init中,对象的所有自定义属性都没有被初始化。这时如果给属性赋值,就会触发setattr方法,访问属性时会触发getattr方法。因此,在类内部,最好通过self.__dict__来赋值和获取对象属性,避免点(.)运算符(self.attrandself.attr=xxx)造成的死循环。在getattr方法中,如果获取的属性不存在应该抛出什么错误?答案是AttributeError,因为系统内置函数hasattr和getattr(这两个函数没有双下划线),只有捕获到AttributeError时,hasattr才会返回False,而getattr(obj,attr,default)可以返回默认值(default)值。__getatrribute__getatrribute是一个属性访问拦截器,拦截所有对对象属性的访问请求,不管该属性是否存在,访问查询顺序最高。对象属性的查找顺序为:实例的getattribute→实例对象字典→实例的类字典→实例类的父类(MRO顺序)字典→实例类的getattrinstance→error无条件调用以实现类实例的属性访问。如果该类还定义了__getattr__(),则不会调用后者,除非__getattribute__()显式调用它或引发AttributeError。此方法应返回(计算的)属性值或将AttributeError引发为无效异常。在此方法中递归,其实现应始终调用具有相同名称的基类方法来访问它需要的任何属性,例如,object.__getattribute__(self,name)。既然getattribute会拦截所有的属性访问,那么如果我们对于一个不存在的属性想调用getattr怎么办呢?答案是raiseAttributeError,只要抛出这个错误,就会触发getattr方法的调用。在getattr例子中,我们通过直接访问self.__dict__来避免触发getattr方法,但是getattribute在访问对象已有的属性时会触发,甚至self.__dict__会造成死循环,所以在getattribute方法中不能使用self.__dict__,解决方法是调用父类的getattribute方法,即通过getattribute中的super().__getattribute__(item)读取对象的属性。__getattribute__方法看起来很强大,但是由于它比较敏感,容易造成死循环,一般情况下不建议重写这个方法。只有当你清楚地知道通过这种方法需要达到什么效果时才使用它。__enter__和__exit__enter和exit允许通过with关键字使用对象,提供进入with块前的初始化工作和退出with块后的清理工作,常用于文件和数据库操作。classDbConnect:defconnect(self):print("Initandconnecttodb.")defexecute(self):print("ExecuteSQLstatement.")defdisconnect(self):print("Disconnectfromdb.")def__enter__(self):self.connect()returnselfdef__exit__(self,exc_type,exc_val,exc_tb):self.disconnect()withDbConnect()asconn:conn.execute()#output#>>初始化和连接到db.#>>执行SQL语句。#>>断开与db的连接。参考资料Python类的内置方法一篇文章让你全面了解__getattr__、__getattribute__、__getitem__的使用和执行原理