第十二篇:面向对象系列之魔法方法(四)
本篇主要介绍面向对象编程中类的魔法属性,这些魔法属性均有其特殊的功能,即执行某些操作时,会自动触发这些魔法方法的执行,了解这些魔法方法可以更好的了解面向对象编程的实现原理;
在学习魔法方法之前,我们先了解其他两个方法:
isinstance() 和 issubclass()
class A(object): pass class B(A): pass b = B() # isinstance(o,c) -- 判断前者是否为后者所创建的对象 print(isinstance(b,A)) # True print(isinstance(b,B)) # True # issubclass --判断前者是否为后者的子类 print(issubclass(B,A)) # True
反射:hasattr、getattr、setattr、delattr四个内置函数
反射,又称自省(自我反省),该概念是由Smith于1982首次提出的,是指主要是指程序可以访问、检测和修改它本身状态或行为的一种能力;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Programmer(object): """定义一个程序员类""" character = "intelligent" # 特点:聪明 def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender def write_code(self): # 写代码 pass def debug(self): #调试 pass p = Programmer("little-five",22,"male") # hasttr - 查看该对象是否可以使用该属性或者方法 print(hasattr(p,"name")) print(hasattr(p,"debug")) # getattr - 获取对象属性 print(getattr(p,"write_code")) #<bound method Programmer.write_code of <__main__.Programmer object at 0x0000016F87ABBFD0>> print(getattr(p,"character")) # == p.character # 可以指定第三个参数,当属性不存在时返回该值,而不会报错 # print(getattr(p,"balabala")) # 报错 print(getattr(p,"balabala","不存在的")) # setattr -修改对象属性 setattr(Programmer,"character","handsome") # == Programmer.character = "handsome" print(p.character) # handsome # delattr - 删除该属性 delattr(p,"name") # == del p.name print(p.__dict__) #{'age': 22, 'gender': 'male'},name没啦 delattr(Programmer,"debug") print(Programmer.__dict__) #debug属性没啦
反射的优点:可实现一种可插拔机制,例如:在实际项目的开发过程中,通常是多个程序员配合区完成一个大的项目,假如程序员A在开发过程中需要用到程序员B定义的类,但是程序员B请假陪老婆生孩子了,代码没有完成,这时候程序员B可以使用反射的机制继续去完成自己的代码,例如:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 程序员A --moduleA class A(object): def __init__(self,x,y): self.x = x self.y = y # ---溜了----- # --------------------------------------------------------- # 程序员B from moduleA import A a = A(10,20) if hasattr(a,"cal"): # 判断是否有该计算方法,不管有没有不影响我其他逻辑的书写和调试 print(a.cal()) else: print("该计算方法没有定义") print("继续执行其他的逻辑")
一、魔法方法
1、__doc__ 查看类的描述信息
class Foo(object): """这是一段关于该类的介绍和描述""" pass print(Foo.__doc__) #这是一段关于该类的介绍和描述
2、__module__ 和 __class__
前者查看当前操作的类所在模块,后者查看当前对象的类是什么;
class Bar(object): pass b1 = Bar() print(Bar.__module__) # __main__ ----》当前文件所在模块 print(Bar.__class__) # <class 'type'> print(b1.__class__) # <class '__main__.Bar'> # 注:从上述结果可以看出,类也是对象,该Bar类对象其是由type类实例化得到的,而对象b1是由当前模块的Bar类实例化得到的
3、__init__ 构造方法
当通过类实例化成对象时,自动触发执行__init__方法。该方法下定义的属性即为实例属性,即会自动开辟一个内存空间,用来存放该实例对象的属性和类方法的指针。
class Foo(object): # 构造方法 def __init__(self,name): self.name =name print("in the init") foo = Foo("alex") # in the init
4、__del__ 方法
当由该类创建的实例对象,被删除或者说在内存中被释放时,将会自动触发执行。
class Foo(object): # 析构方法 def __del__(self): print("in the __del__") foo = Foo() del foo # in the __del__
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。当然我上面重新了析构方法时,则无法真正内存回收,底层会有很多实现的功能被封装。
5、__call__ 对象后面加括号,触发执行。
class Bar(object): pass bar = Bar() # bar() # TypeError: 'Bar' object is not callable # 当一个类中有__call__,其实例化得到的对象便是可调用的即callable class Foo(object): def __call__(self): print("in the call") foo = Foo() foo() # in the call
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
6、__dict__ 类或者实例的属性字典
类或这实例的属性字典,用来存放类或者实例的属性和方法;
class Bar(object): gender = "male" def __init__(self,name): self.name = name def tt(self): pass bar = Bar("alex") print(Bar.__dict__) # 输出为:{'__module__': '__main__', 'gender': 'male', '__init__': <function Bar.__init__ at 0x0000020F94F10B70>, 'tt': <function Bar.tt at 0x0000020F94F10BF8>, '__dict__': # <attribute '__dict__' of 'Bar' objects>, '__weakref__': <attribute '__weakref__' of 'Bar' objects>, '__doc__': None} print(bar.__dict__) #{'name': 'alex'}
该属性字典中以键值对的形式,存放类对象的属性和其对应的值 、方法和其对应的方法指针。
7、__str__方法
class Bar(object): def __str__(self): return "<bar object <Bar> at 000000xxxx>" # 如果一个类中定义了__str__方法,那么在打印对象 时,默认输出该方法的返回值。 bar = Bar() print(bar) # <bar object <Bar> at 000000xxxx> # 其实在其他的对象中也是这样实现,当打印该对象给用户一些提示信息 import threading def test(): pass t = threading.Thread(target=test) print(t) #<Thread(Thread-1, initial)>
该方法当用户不清楚该实例对象是什么时,通过打印的方式自动执行该类的内部的__str__方法,用来给用户一定的提示信息。
8、__getattr__、__setattr__、__delattr__
在学习这三个魔法方法时,我们先了解一下一个概念叫反射,其通过四个内置函数来实现的效果,即hasattr、getattr、setattr、delattr;其实当我们调用后面三个方法时,便是执行了类中的魔法方法;例如:
#---------------- copy--------------- class Foo: x=1 def __init__(self,y): self.y=y def __getattr__(self, item): print('----> from getattr:你找的属性不存在') def __setattr__(self, key, value): print('----> from setattr') # self.key=value #这就无限递归了,你好好想想 # self.__dict__[key]=value #应该使用它 def __delattr__(self, item): print('----> from delattr') # del self.item #无限递归了 self.__dict__.pop(item) #__setattr__添加/修改属性会触发它的执行 f1=Foo(10) print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值 f1.z=3 print(f1.__dict__) #__delattr__删除属性的时候会触发 f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作 del f1.a print(f1.__dict__) #__getattr__只有在使用点调用属性且属性不存在的时候才会触发 f1.xxxxxx
9、__getitem__、__setitem__、delitem__
用法与上面三种十分相似,只是触发的方式不太相同,上面的attr魔法方法是通过点" . "的方式调用触发,而后者item魔法方法是通过字典"[ ]"的方式调用触发;例如:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print(self.__dict__[item]) def __setitem__(self, key, value): print("in the setitem.") self.__dict__[key]=value def __delitem__(self, key): print('in the delitem..') self.__dict__.pop(key) def __delattr__(self, item): print('in the delattr..') self.__dict__.pop(item) # obj.["key"]的方式触发__xxxitem__魔法方法 f1=Foo("alex") ret = f1["name"] # 触发__getitem__执行 f1["age"]=18 # 触发__setattr__执行 del f1['age'] # 触发__delitem__执行 f1['name']='alex' del f1.name # 触发__delattr__执行 print(f1.__dict__)
10、__iter__和__next__方法
若一个对象有__iter__方法,则为可迭代对象;而同时拥有__iter__和__next__方法,则为迭代器对象;详情请见 另外一篇博客----------------》迭代器和生成器;
over~~~~~,当然还有其他的魔法方法,但是我也不是很懂就不误人子弟了。。。
本文来自博客园,作者:Little_five,转载请注明原文链接:https://www.cnblogs.com/littlefivebolg/p/9357526.html