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

Python入门之面向对象编程(三)Python类的魔术方法

python类中有一些方法前后都有两个下划线,这类函数统称为魔术方法。这些方法有特殊的用途,有的不需要我们自己定义,有的则通过一些简单的定义可以实现比较神奇的功能我主要把它们分为三个部分,下文也是

python类中有一些方法前后都有两个下划线,这类函数统称为魔术方法。这些方法有特殊的用途,有的不需要我们自己定义,有的则通过一些简单的定义可以实现比较神奇的功能

我主要把它们分为三个部分,下文也是分为这三个部分来讲解的

  • 简单,功能性,一般不修改直接调用查看结果
  • 实现简单功能,定义后使用
  • 实现比较复杂功能,常常是一些我们常见类的特性的实现原理,具体分为
    • 实例成为可迭代对象(列表的特性)
    • 实例索引取值(列表的特性)
    • 上下文管理(文件的特性)等

 

简单功能

这部分包括上面列出的前两个点

不需要修改直接调用的,主要有如下几个

  • __dict__ 类和实例都可以调用这个方法
    • 类调用返回这个类中已经定义了的属性和方法,包括特殊方法
    • 实例调用返回属性的字典
  • __module__ 类和实例都可以调用,类所在的模块,比如aa文件夹下的bb.py文件中则返回 aa.bb
  • __class__ 只有实例可以调用,表明实例属于哪个类,内容包括了__module__的信息
  • dir()函数 作用于类和实例上,返回它所有的属性和方法,实际上相当于调用了__dir__()函数

一般进行简单定义之后使用的方法

  • __doc__ 返回定义类时标注的字符串,标注位置必须在定义的方法属性之前,默认是None
  • __slots__ = ('name', 'age') 加一句这个可以只允许定义这两个属性,无法在实例中添加,这条命令只对当前类起作用,对子类无效
  • __init__ 用于定义实例属性
  • __call__ 输入 实例() 或者 类()() 触发,只有定义了这个,实例才可以像函数一样后面加括号
  • __str__ print实例时打印出来的内容,不定义可以调用,重新定义后则可以定制打印内容
  • __repr__ 直接输出实例名打印出来的内容,不定义可以调用,重新定义后则可以定制打印内容
  • __new__ 创建实例时自动调用 __del__ 对象在内存中被释放时触发(后面不设置例子)
  • __len__ 定义 len(实例) 返回的内容,比如字符串类就定义的是字符串的字符个数,当然也可以改成其他的
  • __eq__ 改变==运算符的行为,定义实例使用==number时,是拿实例的什么与number相比
  • 和eq类似的还有ne lt le gt ge
  • 还有一些用于实例之间数值计算的 __add__ __abs__等方法,定义方式和__eq__差不多,这些应用的典型类就是数值类,定义这些其实定义了某些运算符的行为,比如__iadd__定义了+=的行为一样。除此之外还有转化为整数、浮点,
  • __getattr__ 当实例访问的属性未被定义时,原来会报错,定义了这个之后就会按照这里定义的来输出. setattr 则可以设置属性 delattr则删除属性

下面是上面方法的使用展示

class Special:
    '''描述类的信息,__doc__返回放在这里的字符串结果'''
    
    __slots__ = ('name', 'age','__weight') # 限制属性的取值
    
    def __init__(self, name, age, weight):
        self.name = name
        self.age = age
        self.__weight = weight
    
    def __call__(self, content): # 定义 实例() 会返回什么结果
        print(content)
        
    def __str__(self): # 定义打印实例会返回什么结果
        return 'Special object (name:%s)' % self.name
    
    __repr__ = __str__  # 这样简单赋值即可
    
    def __len__(self): # 定义len函数返回的结果
        return len(self.name)
    
    def __eq__(self, num): # 实例和数字比
        return len(self.name) == num
    
    def __gt__(self, other): # 这样可以实现实例和实例比
        return len(self.name) >= len(other)
    
    def __getattr__(self, attr):
        if attr=='score':
            return "You can't see the score"

创建实例

s = Special("Bob", 5, 4)
# s.hobby = "running" # 报错,因为__slots__限制了属性取值
s.__doc__ # 返回类定义时下方写的字符串描述内容
# '描述类的信息,__doc__返回放在这里的字符串结果'
s("__call__ is used") # 调用了__call__
# __call__ is used
print(s) # 调用__str__
# Special object (name:Bob)
s # 调用__repr__
# Special object (name:Bob)
len(s) # 调用了__len__
# 3
s==3 # 调用了__eq__
# True
Special("Mary", 4,5) > Special("Bob",5,4) # 调用__gt__
# True
s.score # 无score参数,调用__getattr__
# "You can't see the score"

s.__dict__ # 要把__slots__注释掉才能看见属性的字典
# {'_Special__weight': 4, 'age': 5, 'name': 'Bob'}
s.__module__ # 查看所属模块
# '__main__'
s.__class__ # 查看所属类
# __main__.Special
dir(s) # 返回实例的所有属性和方法,调用了__dir__()方法

 

可迭代对象

使实例成为可迭代对象(可以被for循环的)

  • 实现 __iter__ __next__ 方法
  • 如果只实现__iter__则这个方法的返回值必须是一个迭代器
  • 也可以__iter__返回一个self,再定义__next__方法,接受self为参数,在里面具体实现如何取得下一个值以及迭代器何时结束
  • 原理是当对实例调用for循环时,相当于每次对__iter__的返回结果作用一次next()函数,所以要想迭代必须定义__iter__方法。第一种就是next每次正常调用__iter__返回的值,这就要求它的返回值是一个迭代器;第二种则是把next函数改掉,使其功能不再是找到下一个,而是定制我们想要的一些操作

第一种

class Ite1:
    
    def __init__(self, a):
        self.a = a
        
    def __iter__(self):
        return iter(range(2*self.a)) # iter函数将一个可迭代对象变成迭代器
     
i1 = Ite1(3)
for i in i1:
    print(i) # 0到 5

第二种

class Ite2:
    
    def __init__(self, a, b):
        self.a = a
        self.b = b
        
    def __iter__(self):
        return self
    
    def __next__(self):
        self.a += 1
        if self.a > self.b+1: # 条件成立则迭代器结束
            raise StopIteration()
        return self.a-1
    
for i in Ite2(2,5):
    print(i) # 返回2-5

 

索引取值

这里实现使用中括号索引取值,或者像字典一样操作

  • 实现 __getitem__方法
  • 这个方法的参数除了self,还可以指定一个index,之后return一个和index相关的结果,其实相当于把实例定义成了一个函数,但是是用中括号调用的
  • 结合 __setitem__ __delitem__ 即可让实例像字典一样操作

只定义__getitem__

class Index1:
    
    def __getitem__(self, index):
        return 2*index # 如果定义和__next__中内容类似则实现既可以循环又可以[]取值了
    
i = Index1()
i[2] # 4

全部定义

class Index2:
    
    def __init__(self,**kw):
        self.dict = kw 
            
    def __getitem__(self, key):
        return self.dict[key]
    
    def __setitem__(self, key, value):
        self.dict[key] = value
        
    def __delitem__(self, key):
        del self.dict[key]
        
i = Index2(name="Bob")
i['name'] # 'Bob'
i['age'] = 13
i['age'] # 13
del i['age']
# i['age'] #报错,删掉就没有这个属性了

 

上下文管理

实现上下文管理,即可以和with结合使用

  • 要实现 __enter__ __exit__ 两个方法
  • __enter__会返回一个值,并赋值给as关键词之后的变量
  • __exit__ 定义了处理结束后要做的事情,比如文件的关闭,socket的断开等
  • 更深入地使用:__exit__中可以处理异常。
    • 在上下文管理中运行的代码如果报错,会将三个值自动传入__exit__方法中,分别为 异常的类型,异常的值,异常的追踪栈
    • 通过定义__exit__的返回值可以进行不同的处理,共有两种返回形式,返回True则这个异常忽略,返回None则正常抛出异常

简单实现上下文管理

class Manager:
    
    def __init__(self, text):
        self.text = text
    
    def __enter__(self):
        self.text = "It has started " + self.text
        return self # 还是让这个类的实例进行下面的处理
        
    def __exit__(self, ex_type, ex_value, ex_tb):
        self.text = "It is over"
        
        
with Manager("machine") as m:
    print(m.text)
    
print(m.text) # as定义的m仍然可以调用
# It has started machine
# It is over
# 发现最后执行了__exit__ 把这个属性改变了

异常处理

class DemoManager:

    def __enter__(self):
        pass

    def __exit__(self, ex_type, ex_value, ex_tb):
        if ex_type is IndexError:
            print(ex_value.__class__)
            return True
        if ex_type is TypeError:
            print(ex_value.__class__)
            return  # return None

# 下面故意制造两种错误
with DemoManager() as nothing:
    data = [1, 2, 3]
    data[4]  # raise IndexError, 该异常被__exit__处理了

with DemoManager() as nothing:
    data = [1, 2, 3]
    data['a']  # raise TypeError, 该异常没有被__exit__处理

 

 

参考

 


推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了Python对Excel文件的读取方法,包括模块的安装和使用。通过安装xlrd、xlwt、xlutils、pyExcelerator等模块,可以实现对Excel文件的读取和处理。具体的读取方法包括打开excel文件、抓取所有sheet的名称、定位到指定的表单等。本文提供了两种定位表单的方式,并给出了相应的代码示例。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • Python如何调用类里面的方法
    本文介绍了在Python中调用同一个类中的方法需要加上self参数,并且规范写法要求每个函数的第一个参数都为self。同时还介绍了如何调用另一个类中的方法。详细内容请阅读剩余部分。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
author-avatar
阿Love静_999
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有