(点击上方公众号,可快速关注一起学Python)

作者:浪子燕青       链接:

http://www.langzi.fun/Python魔法函数.html

魔法函数概念

魔法函数是以双下划线开头并且以双下划线结尾的功能函数,可以用来定义自己类的新特性。

举一个例子:

class magic:def __init__(self,num):        self.num = num

def __getitem__(self, item):return self.num[item]

a = magic(['1','2','3'])for x in a:print x

返回结果:

123

简直鹅妹子嘤!

这里的__getitem__是魔法函数中的其中一个,具有的功能是返回一个有序化数组的值。在定义的类magic中,你引用了一个魔法函数,这个magic类就拥有了该魔法函数的功能。

当使用for循环的时候,因为getitem基于你的magic类一个可迭代的功能,所以magic类具有可迭代功能。

魔法函数对类的影响

如上若是,仅仅在类中添加了getitem这个魔法函数,就能直接使用for循环,也就是说魔法函数在一定程度上可以影响python自定义类的语法,或者说是增强了你这个类的类型。

通过python内置的大量魔法函数,你可以创造出具有独特个性的数据类型,符合业务的需求。

举个例子:

class magic:'''    这是功能性注释    使用__doc__就可以看到啦    '''def __init__(self,num):        self.num = num

def __len__(self):return 6666666

a = magic(5)print len(a)print a.__doc__

返回结果:

6666666

    这是功能性注释    使用__doc__就可以看到啦

通过魔法函数__len__实现获取,len本来是获取字符串或者列表数量长度,但是通过自定义类就实现了返回6666666.

内置魔法函数

python中内置了大量的魔法函数,尝试理解和记下这些魔法函数在以后的业务需求中可以如鱼得水,所以说还是要背啊~~

字符串表示

1. __repr__ 格式化字符串式样,主用开发模式下2. __str__    常用的字符串,格式化字符串

这连个魔法函数的作用都是和字符串相关,一般来说在print打印中会调用这个魔法函数

class magic:def __init__(self,num):        self.num = num

def __str__(self):return (self.num + '\n')*5

a = magic('浪子好帅啊')print a

输出结果:

浪子好帅啊浪子好帅啊浪子好帅啊浪子好帅啊浪子好帅啊

这里使用print a和使用 print a.__str__()效果是一样的。同理repr(a)和a.__repr__

区别:

__repr__ 目的是为了表示清楚,是为开发者准备的。

__str__ 目的是可读性好,是为使用者准备的。

__repr__ 应该尽可能的表示出一个对象来源的类以及继承关系,方便程序员们了解这个对象。而 __str__ 就简单的表示对象,而不要让不懂编程的以为输出的是 bug。

同时定义 repr 方法和 str 方法时,print() 方法会调用 str 方法。

集合序列

1. __len__2. __getitem__3. __setitem__4. __delitem__5. __contains__

迭代

1. __iter__2. __next__

在存储数据的数据结构中有list,set,tuple,dict,当使用for循环他们的时候,本质上是做了两件事。

  1. 获得一个迭代对象,调用__iter__魔法函数

  2. 循环的时候,循环调用__next__魔法函数

举个例子:

class magic:def __init__(self,num):        self.num = num

def __iter__(self):# 使用__iter__,magic类就变成了可迭代对象return self

def __next__(self):# __next__魔法函数的作用是在循环的时候,无限提供输出下一个值,直到没有数据后抛出异常if self.num >5:# 设置上限raise StopIterationelse:            self.num += 1return self.num

a = magic(-5)for x in a:    print(x)

返回结果:

-4-3-2-10123456

可能有些难理解,这是第一次自己做出来的一个数据类型,他的作用是提供一个原始值,自增长到6就停止。

如果这样做也可以的:

a = magic(-5)print(a.__next__())print(next(a))

返回结果:

-4-3

总的来说,使用iter魔法函数,这个类就变成了可迭代对象,但是如何调用这个可迭代对象的数值呢?这个时候就需要使用next来循环调用了。

注意:含有__next__()函数的对象都是一个迭代器(Iterator),也就是说__next__要继承Iterator,__iter__要继承Iterable,继承的类来自与collections

可调用

1. __call__

一个类实例变成一个可调用对象,只需要实现一个特殊方法__call__()。在创建类的时候,只要使用了call这个魔法函数,那么这个类就是可调用的。

函数之所以可以被直接调用,原因在于他的底层是用call实现的。

举个例子:

class magic:def __init__(self):passdef __call__(self, num):return '浪子:' + num

a = magic()print(a('admin'))

运行结果:

浪子:admin

可以看到结果直接运行出来了。这样的例子不够深刻,尝试把场景转移到业务需求中来。

浪子餐馆卖馒头,每次买出一个,店小二就会大喊’xxx买了一个馒头~花了1块钱~~’

使用类来实现:

class ao:def __init__(self,name):        self.name =name

def __call__(self, money):return self.name + '大老板买了一个馒头~~花了%s元~~让我们感谢这位老铁~~'%money

a = ao('小桃红')print(a(15))b = ao('猫饼饼')print(b(666))

运行结果:

小桃红大老板买了一个馒头~~花了15元~~让我们感谢这位老铁~~猫饼饼大老板买了一个馒头~~花了666元~~让我们感谢这位老铁~~

总的来说,就是你定义类中只要有call,那么就可以直接调用,注意call是对象不是类。他和new以及init的关系如下(可以先不看)

  1. new: 对象的创建,是一个静态方法,第一个参数是cls。(想想也是,不可能是self,对象还没创建,哪来的self)

  2. init : 对象的初始化, 是一个实例方法,第一个参数是self。

  3. call : 对象可call,注意不是类,是对象。

问题:为了让下面这段代码运行,需要增加哪些代码?

class A(object):def __init__(self,a,b):        self.__a = a        self.__b = bdef myprint(self):print 'a=', self.__a, 'b=', self.__b

a1=A(10,20)a1.myprint()

a1(80)

为了能让对象实例能被直接调用,需要实现call方法

class A(object):def __init__(self,a,b):        self.__a = a        self.__b = bdef myprint(self):        print('a=', self.__a, 'b=', self.__b)def __call__(self, *args, **kwargs):        print(args)

with上下文管理

1. __enter__2. __exit__

with上下文管理器,对于那些需要必须成对打开关闭的操作是非常方便的,比如打开关闭文件。他的实现原理就是通过enter和exit这两个魔法函数来实现的。

先看看常规的实现一个with上下文管理器的步骤,Pymysql with 操作

import contextlib@contextlib.contextmanagerdef mysql(host='127.0.0.1',user='root',passwd='root',db='meizi',port=3306,charset='utf8'):    conn = pymysql.connect(host='127.0.0.1',user='root',passwd='root',db='meizi',port=3306,charset='utf8')    cursor = conn.cursor()try:yield cursorfinally:        conn.commit()        cursor.close()        conn.close()# # 执行sql# with mysql() as cursor:#    print(cursor)#    row_count = cursor.execute("select * from tb7")#    row_1 = cursor.fetchone()#    print row_count, row_1

想要自己实现这种类的话,就必须要使用到enter和exit这两个魔法函数。

比如上面的

with mysql() as cursor

把步骤分析一下:

  1. 当with的后面mysql()函数被执行的时候,对象的enter方法被调用

  2. 函数主动发起数据库连接,获取一个游标

  3. 随后使用yield生成器寄存这个游标

  4. 这个游标被赋值给as后面的cursor

  5. 当with后面的代码全都执行完毕后,调用前面返回对象的exit方法

举个例子:

class magic:def __enter__(self):        print('enter魔法函数执行')return 'enter魔法函数执行完毕'def __exit__(self, exc_type, exc_val, exc_tb):        print('exti魔法函数执行')return 'exit魔法函数执行完毕'

def ma():return magic()

with ma() as a:    print('随便执行一些东西')

运行结果:

enter魔法函数执行随便执行一些东西exti魔法函数执行

是不是想到了装饰器?通过查看contextlib库的源码发现导入了wrapper装饰器,说到装饰器你是不是你又想到了闭包?然后又想到了变量的作用域?

是的,这些知识都是一个完整的体系,相互串联。

还没结束,继续深入分析。

在exit魔法函数的值中的含义

exc_type:异常类(如果抛出异常,这里获取异常的类型 )exc_value:异常实例(如果抛出异常,这里显示异常内容)exc_tb:异常位置(如果抛出异常,这里显示所在位置)traceback:追溯对象()

因为with操作本身就是为了简写try/finally操作的。

比如在熟悉的with操作文本文件一样,打开文件是放在enter函数中,关闭文件放在exit函数中。

with真正强大之处是它不仅可以完善的管理上下文,同时还可以处理异常。

__enter__

__enter__ 用于赋值给 as 后面的变量。不过 with 语句中 as 不是必须的。__enter__ 和 __exit__ 必须并用。

__exit__

用于捕获异常,它的返回值是一个 boolean 对象。除了 self 之外,必须传入另外三个参数,分别表示 exception 的类型,值(如 IndexError: list index out of range 中,冒号后面的部分就是值),以及 traceback。

返回 True 则表示这个异常被忽略。

返回 None, False 等则这个异常会抛出。

如果要忽略所有的异常可以这样写:

def __exit__(self, exc_type, exc_value, traceback):return True

经过测试, SyntaxError 是不能忽略的,其他已知的是可以的。

数值转换

1. __abs__2. __bool__3. __int__4. __float__5. __hash__6. __index__

元类相关

1. __new__2. __init__

说到init你会想到创建类的时候实例的对象,但是new是啥?

依照Python官方文档的说法,__new__方法主要是当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径。还有就是实现自定义的metaclass。

是不是感觉有点迷?没关系,用例子来说明就好了。

class magic(object):def __init__(self,nums):        self.nums = numsprint '666'print 'init:' + self.nums

def __new__(cls, nums):        cls.nums = numsprint 'new:'+cls.nums

a = magic('A')

返回结果:

new:A

这里看到只输出了new的对象,并且注意magic类继承了object,在上一篇文章中有说道,元类(即类的类)都必须要继承自object,因为记住了,继承自object的新类才有__new__

重新修改一下代码,让init也执行下呢?

class magic(object):def __init__(self,nums):        self.nums = numsprint '666'print 'init:' + self.nums

def __new__(cls, nums):        cls.nums = numsprint 'new:'+cls.nums# 执行到这里会打印内容return object.__new__(cls)# 这里会返回一个内容# 返回的内容会传递到init中的self中去a = magic('A')

返回结果:

new:A666init:A

大家注意看,这里返回了object的new方法,然后init就执行了,这证明上面说的,new会先于init执行,并且new方法返回的值就是init方法中的self。

继续举例子:

class magic(object):def __init__(self,nums):        self.nums = self.nums        self.langzi = 'langzi'print '666'print 'init:' + self.nums

def __new__(cls, nums):print nums# 这里打印出传递进来的数值        cls.nums = nums+'BCDEFG'        cls.langzi = '浪子'print 'new:'+cls.numsreturn object.__new__(cls)

a = magic('A')print '-'*10print a.langziprint a.nums

先猜一猜会输出什么呢?

返回结果:

Anew:ABCDEFG666init:ABCDEFG----------langziABCDEFG

来分析一下

  1. 打印传递进来的A

  2. 随后赋值给cls.nums,打印new:ABCDEFG,同时定义cls.langzi=浪子

  3. 继续执行,new方法返回object对象,这个时候会执行到init

  4. 这里的self其实就是new中返回的对象

  5. 然后定义self.nums和self.langzi=langzi(这个时候这个类的langzi对象就变成了langzi,不再是浪子)

  6. 打印666

  7. 打印出init:ABCDEFG

  8. 打印—————-

  9. 验证这个类中的属性值a.langzi和a.nums

通过分析这个实例步骤,来进一步研究new和init的关系:

通过上面代码的执行结果我们可以发现程序首先执行了new,之后执行的init,这说明,在类中,如果new和init同时存在会优先调用new。

new方法会返回所构造的对象,init则不会。init无返回值。

new至少要有一个参数cls,代表要实例化的类(类对象),此参数在实例化时由Python解释器自动提供。

new必须要有返回值,返回实例化出来的实例,这点在自己实现,new时要特别注意,可以return父类new出来的实例或者直接是object的new出来的实例。

init有一个参数self,就是这个new返回的实例,init在new的基础上可以完成一些其它初始化的动作,init不需要返回值

我们可以将类比作制造商,new方法就是前期的原材料购买环节,init方法就是在有原材料的基础上,加工,初始化商品环节

总而言之就是:

  1. new:创建对象时调用,会返回当前对象的一个实例,new是在实例创建之前被调用的,因为它的任务就是创建实例然后返回该实例,是个静态方法,常用于允许继承不可变类型(str,int, tuple)

  2. init:创建完对象后调用,对当前对象的一些实例初始化,无返回值,init是当实例对象创建完成后被调用的,然后设置对象属性的一些初始值。

下面这段代码输出什么?

class B(object):def fn(self):print 'B fn'def __init__(self):print "B INIT"

class A(object):def fn(self):print 'A fn'

def __new__(cls,a):print "NEW", aif a>10:return super(A, cls).__new__(cls)return B()

def __init__(self,a):print "INIT", a

a1 = A(5)a1.fn()a2=A(20)a2.fn()

返回结果:

NEW 5B INITB fnNEW 20INIT 20A fn

使用new方法,可以决定返回那个对象,也就是创建对象之前,这个可以用于设计模式的单例、工厂模式。init是创建对象是调用的。

属性相关

1. __getattr__,__setattr__2. __getattribute__,setattribute__3. __dir__

属性描述符

1. __get__,__set__,__delete__

协程

1. __await__2. __aiter__3. __anext__4. __aenter__5. __aexit__

这个是最重要也是最难的,以后慢慢说。

梳理

  1. 魔法函数是python内置的,具有双下划线开头结尾的特性。

  2. 自定义的类中使用魔法函数,该类就具有了该魔法函数的功能,比如使用iter魔法函数,该类就具有迭代功能。

  3. 使用魔法函数实现高灵活性,实现自己所需要的独特的数据类型。

(完)

看完本文有收获?请转发分享给更多人

关注「Python那些事」,做全栈开发工程师

推荐阅读

  • 大数据时代,该如何进阶?

  • 浅谈 MySQL 中优化 SQL 语句查询常用的 30 种方法

  • 10个Python面试常问的问题

  • 可能是最全的Python面向对象入门教程了

exit函数_全面深入了解 Python 魔法函数相关推荐

  1. python rstrip函数_【C++实现python字符串函数库】strip、lstrip、rstrip方法

    [C++实现python字符串函数库]strip.lstrip.rstrip方法 这三个方法用于删除字符串首尾处指定的字符,默认删除空白符(包括'\n', '\r', '\t', ' '). s.st ...

  2. python 魔法函数 __add___PythonI/O进阶学习笔记_2.魔法函数

    前言: 本文一切观点和测试代码是在python3的基础上. Content: 1.什么是魔法函数,魔法函数__getitem__在python中应用. 2.python的数据模型和数据模型这种设计对p ...

  3. 一文读懂什么是Python魔法函数

    一文读懂什么是Python魔法函数 Python的魔法函数是指Python的类中,一系列函数名由双下划线包裹的函数. 笔者最初接触到魔法函数的使用是在Pytorch中,在Pytorch中的Datase ...

  4. python 魔法函数

    Python魔法函数 前言 魔法函数 定义 常用的魔法方法 类构造与初始化 类的表示 控制属性访问 比较.运算等操作 容器类操作 可调用对象 序列化 总结 前言 本篇博客主要介绍Python的魔法函数 ...

  5. imread函数_不知道这 7 大 OpenCV 函数怎么向计算机视觉专家进阶?

    作者 | Lazar Gugleta译者 | Arvin,责编 | 夕颜头图 | CSDN付费下载自视觉中国出品 | CSDN(ID:CSDNnews)计算机视觉和计算机图形学现在非常流行,因为它们与 ...

  6. 数据库创建函数_达梦数据库创建UUID函数

    数据库创建函数_达梦数据库创建UUID函数 接触达梦数据库有一段时间了,整理了一些资料,今天分享一下达梦数据UUID自定义函数 UUID函数定义 很多数据库都有提供UUID函数,可是接触达梦数据库后, ...

  7. Python魔法函数与两比特量子系统模拟

    技术背景 本文主要涵盖两个领域的知识点:python的魔法函数和量子计算模拟,我们可以通过一个实际的案例来先审视一下这两个需求是如何被结合起来的. 量子计算模拟背景 ProjectQ是一个非常优雅的开 ...

  8. python 魔法函数 运行时_16个python常用魔法函数

    ==,is的使用 ·is是比较两个引用是否指向了同一个对象(引用比较). ·==是比较两个对象是否相等 1.__ init__(): 所有类的超类object,有一个默认包含pass的__ init ...

  9. Python魔法函数

    python中定义的以__开头和结尾的的函数.可以随意定制类的特性.魔法函数定义好之后一般不需要我们自己去调用,而是解释器会自动帮我们调用. __getitem__(self, item) 将类编程一 ...

最新文章

  1. 强势安利5款高质量办公软件,极大提高办公效率
  2. 1001. 温度转换
  3. 指定父对象和信号与槽
  4. python元胞转list_[Python练习向] 简易元胞自动机框架
  5. 怎么查看地址值_西门子S7-200 SMART PID回路控制,配置PID向导,查看项目组件
  6. C# 正则表达式编写及验证方法
  7. 笔记本中美化代码的方法
  8. C语言(CED)编写一个程序,求两个字符之间的加减运算。
  9. LeetCode14最长公共前缀
  10. Android:ViewFlipper、幻灯片
  11. Visio 2013打开自动关闭,闪退的解决办法
  12. [微软苏州校招 Hihocoder] Disk Storage
  13. 测试用例的设计-面试常见问题(基础篇)
  14. 计算机安全群,大开眼界||斯坦福大学信息安全课程群
  15. Python中随机漫步的实现
  16. matlab绘制加速度曲线,加速度积分得到速度和位移的问题小结
  17. 测试连接--ping (IP地址,网址,主机名)
  18. 梦幻西游 手游下载连接
  19. OpenCV-Python人脸识别,车辆识别,自定义物体识别,自定义分类器
  20. Php字符拼出心形,canvas实现九宫格心形拼图的方法(附代码)-

热门文章

  1. Google浏览器(您的连接不是私密连接)解决方案
  2. The database returned no natively generated identity value错误解决方案
  3. Android webview 写入cookie的解决方法以及一些属性设置
  4. Tomcat中出现“RFC 7230 and RFC 3986“错误的解决方法
  5. 宿主机( win 7 系统) ping 虚拟机VMware( cent os 6.6 ) 出现“请求超时”或者“无法访问目标主机”的解决方法
  6. 关于系统弹出错误:429 , ActiveX 部件不能创建对象 的解决方法
  7. 如何使flexbox子代的父母高度为100%?
  8. 如何检查字符串是否包含特定单词?
  9. 如何测试一个空的JavaScript对象?
  10. “ px”,“ dip”,“ dp”和“ sp”有什么区别?