详解Python常用的魔法方法


Posted in Python onJune 03, 2021

一、python魔法方法

Python的魔法方法会在特定的情况下自动调用,且他们的方法名通常被双下划线包裹,之前我们学习的构造函数和析构函数就属于魔法方法

二、运算符重载

Python中同样有运算符重载,其实所有的运算符都是使用了对应的魔法方法来处理的对象的,魔法方法对应的操作符如下

详解Python常用的魔法方法

我们来举一个简单的例子

class A:
    def __init__(self,x):
        self.x = x
    def __add__(self,other):
        return int(self.x)+int(other.x)
a = A(3.3)
b = A(5.2)
print(a+b)

类似的还有反运算重载和增量复制运算,用处较少,不再解释

详解Python常用的魔法方法
详解Python常用的魔法方法

三、打印操作的魔法方法

__str__(self):返回值是str类型的,当我们需要以字符串的形式输出对象时(调用print时),就会自动调用该方法,举个例子

class A:
    def __str__(self):
        return '我真帅'

a = A()
print(a)# 我真帅

__repr__(self):返回值是str类型的,当我们直接在shell中输入对象名并按下回车,就会自动调用该方法,他也有和__str__一样的功能,但如果两者你都重写了,在使用print时,__str__的优先级高,__repr__是给机器看的,__str__是给人看的,举个例子

>>> class A:
    def __str__(self):
        return '我真帅'
    def __repr__(self):
        return '我是世界第一帅'

>>> a = A()
>>> a
我是世界第一帅
>>> print(a)
我真帅

四、属性操作的魔法方法

  • __getattr__(self, name):定义当用户试图获取一个不存在的属性时的行为,其中name是属性名,是一个字符串,下同
  • __getattribute__(self, name):定义当该类的属性被访问时的行为,该方法默认返回该属性的值
  • __setattr__(self, name, value):定义当一个属性被设置时的行为,value是给该属性的值
  • __delattr__(self, name):定义当一个属性被删除时的行为

例如:

class A:
    def __init__(self):
        self.id = "Pyhon"
    def __getattr__(self,name):
        print(name+"这个属性不存在")
    def __getattribute__(self,name):
        print("我访问了"+name+"这个属性")
        return super().__getattribute__(name)
    def __setattr__(self,name,value):
        print("将属性"+name+"置为"+value)
        super().__setattr__(name,value)
    def __delattr__(self,name):
        print("将属性"+name+"删除了");
        super().__delattr__(name)
    def fun(self):
        pass
a = A()
a.name
a.name = "老师"
del a.name
a.fun()
# output:
# 将属性id置为Pyhon
# 我访问了name这个属性
# name这个属性不存在
# 将属性name置为老师
# 将属性name删除了
# 我访问了fun这个属性

结果可以看出,当我们访问一个属性的时候,先是调用了__getattribute__,如果该属性不存在,则再调用__getattr__

使用这几个的方法的时候,要注意不要陷入无限递归,运算符重载的时候也容易犯这种错误,例如下面的错误

class A:
    def __init__(self):
        self.id = "Pyhon"
    def __setattr__(self,name,value):
        print("将属性"+name+"置为"+value)
        if(name == "id"):
            self.id = value

a = A()

执行这段程序的时候将陷入无限递归,原因是在__setattr__中,直接给self对象的属性赋值,而这又会调用__setattr__方法。

所以在__setattr__中,我们通常会使用父类的__setattr__方法来给self对象的属性赋值,这不会陷入无限递归,其他几个方法和运算符重载也是同理,上面程序订正后如下

class A:
    def __init__(self):
        self.id = "Pyhon"
    def __setattr__(self,name,value):
        print("将属性"+name+"置为"+value)
        if(name == "id"):
            super().__setattr__(name,value)

a = A()
# output
# 将属性id置为Pyhon

五、描述符

  • __get__(self, instance, owner):通过其他实例对象来访问该类的实例对象时会调用该方法,返回该实例对象的引用。其中instance是访问该对象的实例对象的引用,下同,owner是访问该对象的类对象
  • __set__(self, instance, value):通过其他实例对象来给该类的实例对象赋值时会调用该方法。其中value是给该对象赋的值
  • __delete__(self, instance):通过其他实例对象来删除该类的实例对象时会调用该方法
class Fit:
    def __init__(self):
        self.height = 180
        self.weight = 80
    def __get__(self,instance,owner):
        print("get:",instance,owner)
        return [self.height,self.weight]
    def __set__(self,instance,value):
        print("set:",instance,value)
        self.height = value
        self.weight = value/2
    def __delete__(self,instance):
        del self.height
        del self.weight
        print("delete:",instance)

class Test:
    fit = Fit()
        
t = Test()
print (t.fit)
t.fit = 190
del t.fit
# output:
# get: <__main__.Test object at 0x0000023EFFA738C8> <class '__main__.Test'>
# [180, 80]
# set: <__main__.Test object at 0x0000023EFFA738C8> 190
# delete: <__main__.Test object at 0x0000023EFFA738C8>

通常情况下,上面几个魔法方法,当我们需要定义一个属性,且希望可以直接对该属性进行相应的操作,而不是通过调用方法的方式来进行操作时,我们可以定义一个该属性的类,实现上面几个魔法方法,将需要用到的属性作为其实例对象,这样就完成了,例如上面的Fit,其实就是体型类,而Test中有一个体型属性叫fit,我们在Fit中定义了一些对Fit的实例对象操作时执行的操作。

六、定制序列

  • __len__(self):定义当该类的实例对象被len()调用时的行为
  • __getitem__(self, key):定义获取该类的实例对象中指定元素的行为,也就是说执行self[key]时的行为
  • __setitem__(self, key, value):定义设置该类的实例对象中指定元素的行为,相当于self[key] = value
  • __delitem__(self, key):定义删除该类的实例对象中指定元素的新闻,相当于del self[key]
class CountList:
    def __init__(self,*args):
        self.values = [x for x in args]#这是一个列表推导式,把args里的元素作为values的元素
        self.count = {}.fromkeys(range(len(self.values)),0)

    def __len__(self):
        return len(self.values)

    def __getitem__(self,key):
        self.count[key] += 1;
        return self.values[key]

c = CountList(1,3,5,7,9,11)
print(c[1])
print(c[1]+c[2])
print(c.count)
# output:
# 3
# 8
# {0: 0, 1: 2, 2: 1, 3: 0, 4: 0, 5: 0}

该类中的count是记录对应元素被访问的次数,其他两个也差不多,不再举例了

七、迭代器

迭代器,就是提供了迭代方法的容器,而所谓的迭代方法,就是下面这两个__iter____next__
可迭代,就是提供了__iter__方法的容器,我们之前讲的字符串,列表,元组,字典,集合都是可迭代的,但他们不是迭代器,可以使用Python的内置函数iter(iterable)来获取他们相应的迭代器,而迭代器使用next(iterator)可以获取下一个元素,而这两个方法其实就是调用了迭代器的__iter____next__

  • __iter__(self):定义获取迭代器时的行为
  • __next__(self):定义获取迭代器对应的下一个元素时的行为
class Fb:
    def __init__(self,n = 20):
        self.a = 0
        self.b = 1
        self.n = n
    def __iter__(self):
        return self
    def __next__(self):
        t = self.a
        self.a = self.b
        self.b = t + self.b
        if(self.a <= self.n):
            return self.a
        else:
            raise StopIteration

f = Fb()
for i in f:
    print(i,end=' ')
# output:1 1 2 3 5 8 13

其中 raise 是返回一个异常,上面的程序等价于下面这个

class Fb:
    def __init__(self,n = 20):
        self.a = 0
        self.b = 1
        self.n = n
    def __iter__(self):
        return self
    def __next__(self):
        t = self.a
        self.a = self.b
        self.b = t + self.b
        if(self.a <= self.n):
            return self.a
        else:
            raise StopIteration

f = Fb()
it = iter(f)
while True:
    try:
        i = next(it)
        print(i, end=' ')
    except StopIteration:
        break;

这样我们就很清楚Python中for循环的原理了,先通过iter来获取迭代器对象,然后不断调用next来获取下一个元素赋值给i,直到遇到StopIteration异常

到此这篇关于详解Python常用的魔法方法的文章就介绍到这了,更多相关python魔法方法内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python3实现并发检验代理池地址的方法
Sep 18 Python
Python中django学习心得
Dec 06 Python
python+selenium实现163邮箱自动登陆的方法
Dec 31 Python
python单例模式实例解析
Aug 28 Python
浅述python2与python3的简单区别
Sep 19 Python
在IPython中执行Python程序文件的示例
Nov 01 Python
python实现局域网内实时通信代码
Dec 22 Python
将tensorflow模型打包成PB文件及PB文件读取方式
Jan 23 Python
基于python生成英文版词云图代码实例
May 16 Python
利用python下载scihub成文献为PDF操作
Jul 09 Python
python和C/C++混合编程之使用ctypes调用 C/C++的dll
Apr 29 Python
python+opencv实现目标跟踪过程
Jun 21 Python
Python自动化之批量处理工作簿和工作表
Jun 03 #Python
Jupyter Notebook内使用argparse报错的解决方案
Python实现机器学习算法的分类
Jupyter Notebook 如何修改字体和大小以及更改字体样式
Python数据可视化之用Matplotlib绘制常用图形
使用numpy实现矩阵的翻转(flip)与旋转
Jun 03 #Python
详解Python生成器和基于生成器的协程
You might like
重置版游戏视频
2020/04/09 魔兽争霸
php 文件状态缓存带来的问题
2008/12/14 PHP
基于wordpress主题制作的具体实现步骤
2013/05/10 PHP
php使用百度天气接口示例
2014/04/22 PHP
php输出金字塔的2种实现方法
2014/12/16 PHP
用php守护另一个php进程的例子
2015/02/13 PHP
几个优化WordPress中JavaScript加载体验的插件介绍
2015/12/17 PHP
yii实现model添加默认值的方法(2种方法)
2016/01/06 PHP
WordPress的文章自动添加关键词及关键词的SEO优化
2016/03/01 PHP
PHP如何通过带尾指针的链表实现'队列'
2020/10/22 PHP
取得传值的函数
2006/10/27 Javascript
Add Formatted Data to a Spreadsheet
2007/06/12 Javascript
Span元素的width属性无效果原因及解决方案
2010/01/15 Javascript
createElement与createDocumentFragment的点点区别小结
2011/12/19 Javascript
JQuery.closest(),parent(),parents()寻找父结点
2012/02/17 Javascript
JS操作Cookie写入和读取实例代码
2013/10/20 Javascript
jquery动感漂浮导航菜单代码分享
2020/04/15 Javascript
js判断手机系统是android还是ios
2017/03/07 Javascript
搭建简单的nodejs http服务器详解
2017/03/09 NodeJs
微信小程序--onShareAppMessage分享参数用处(页面分享)
2017/04/18 Javascript
JavaScript实现三级联动效果
2017/07/15 Javascript
详解为Bootstrap Modal添加拖拽的方法
2018/01/05 Javascript
vue实现前进刷新后退不刷新效果
2018/01/26 Javascript
微信小程序中使用wxss加载图片并实现动画效果
2018/08/13 Javascript
开发用到的js封装方法(20种)
2018/10/12 Javascript
node实现分片下载的示例代码
2018/10/17 Javascript
Promise扫盲贴
2019/06/24 Javascript
python实现的简单窗口倒计时界面实例
2015/05/05 Python
关于tf.nn.dynamic_rnn返回值详解
2020/01/20 Python
美国婴童服装市场上的领先品牌:Carter’s
2018/02/08 全球购物
Linux面试题LINUX系统类
2015/11/25 面试题
建筑工程专业大学生求职信
2014/04/23 职场文书
会计岗位说明书
2014/07/29 职场文书
怎样写离婚协议书
2014/09/10 职场文书
学习十八届四中全会依法治国心得体会
2014/11/03 职场文书
js中Object.create实例用法详解
2021/10/05 Javascript