Python 魔术方法

综合编程 2018-05-16 阅读原文

爬虫们请注意

《Python 魔术方法》 最先发表在: http://blog.ihypo.net/15264605833572.html ,如有转载请顺便爬走这段。

魔术方法是 Python 类中那些定义名称类似 __XXX__ 的函数,使用 Python 的魔术方法的优势在于可以使用 Python 提供的内置方法,比如 len()str() ,或者重载运算符,让自定义对象类型可以表现得像内置类型一样。

Python 语言参考手册中一共列出了 83 个特殊方法的名字,其中 47 个用于实现算术运算、位运算和比较操作,概览如下。

跟运算符无关的特殊方法:

跟运算符相关的特殊方法:

有一份文档详细的介绍了所有的魔术方法的使用方式,本文只整理了常用魔术方法以及常见的坑。 文档见: http://pycoders-weekly-chinese.readthedocs.io/en/latest/issue6/a-guide-to-pythons-magic-methods.html

构造与析构

Python 对象通过 __init__ 进行初始化,但是在执行 __init__ 之前,首先被执行的是 __new__ 函数。在初始化一个对象时,会先调用 __new__ 方法,该方法是一个类方法,执行完相关逻辑之后调用 __init__ 并将初始化参数传递给后者。

__del__ 函数是 Python 对象的析构函数,无论是垃圾回收还是手动删除,都是执行这个方法:

class Model(object):
    def __init__(self):
        print("Run: __init__")

    def __del__(self):
        print("Run: __del__")


if __name__ == "__main__":
    m = Model()
    del m

结果:

Run: __init__
Run: __del__

打印

在 Python 魔术方法中,有两个和打印有关, __repr____str__

如果在 Python 直接打印一个类,将会获得一个内存地址:

如果在解释器中想获得一个更友好的显示, __repr__ 是一个非常好的选择。另外,在不把 object 当做字符串处理的场合,也是会使用该方法。

而在把 object 当做字符串处理或者转换为字符串的场合,比如 print(object)"{}".format(object)str(object) 等, __str__ 将会被使用。

class Model(object):
    def __init__(self):
        self.id = uuid.uuid4()
        self.content = "fluent python demo"

    def __str__(self):
        return self.content

    def __repr__(self):
        return "".format(self.id)

一般在 __str__ 中展现更人性化的信息,可能直接返回给用户;而 __repr__ 更多为了在调试中使用方便(解释器中等)。

另外,如果 __str__ 没有定义,那么将会直接使用 __repr__ 方法,因此如果不需要太多差别,只定义后者即可。

数组与字典

如果需要定义一个类似列表的自定义类,为了支持通过下标访问,可以覆写 __getitem__

class Model(object):
    def __init__(self):
        self.content = [r for r in range(0, 3)]

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

该类从此支持了下标访问,并且还支持切片,排序等 Python 内置方法:

if __name__ == "__main__":
    m = Model()
    print(m[1:], sorted(m, reverse=True))

结果:

[1, 2] [2, 1, 0]

对于赋值操作,只需要覆写 __setitem__ 方法:

def __setitem__(self, key, value):
    self.content[key] = value

同理,对于将自定义对象进行类似字典的操作,可以这样实现:

class Model(object):
    def __init__(self):
        self.content = {
            "key": "value"
        }

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

    def __setitem__(self, key, value):
        self.content[key] = value

类成员变量

类成员方法 __getattr____getattribute____setattr__ 是三个方便且危险的方法,可以用来定义类成员属性,前两个是用来获取,最后一个是用来赋值。

前两个的区别是 __getattribute__ 会被首先调用,只有 __getattribute__ 找不到的时候,才会调用 __getattr__

因为本身的类属性操作也是基于这几个方法,因此在覆写时应该避免循环调用最后栈溢出。

总结

简单讲了几种常见的魔术方法,魔术方法可以使得自定义类型支持 Python 原生、内置方法,因而使得代码风格更加简洁和一致。但是,也容易写出难以调试的魔法代码,简洁(或者说 geek)和可维护性可能本身就是两个极端,在使用魔术方法时还需要考虑后继维护者是否能理解自己的行为。

《Python 魔术方法》 最先发表在: http://blog.ihypo.net/15264605833572.html 如有转载请注明。

感觉本站内容不错,读后有收获?

您可以小额赞助,鼓励作者写出更好的文章。

责编内容by:梦旭随想 【阅读原文】。感谢您的支持!

您可能感兴趣的

Image Enhancement in Python Let me start this tutorial by taking some theoretical jargon out of your way. When we talk about image enhancement , th...
Python 之父透露退位隐情,与核心开发团队产生隔阂... Python 创始人 Guido van Rossum 前段时间宣布脱离 Python 决策层,辞去所谓的 BDFL (终生仁慈的独裁者) 身份曾引发热议,当时他以 PEP 572 改进提案的争吵事件为例,表明其退出缘由。近日 Guid...
Python学习笔记—第四天,递归函数(阶乘和汉诺塔游戏)... 今天主要学习了递归函数,已经尝试了一些小例子,这里拿阶乘和汉诺塔来记录下。 1、阶乘函数 阶乘很简单,即n! = 1x2x3x...xn。 先用了常用的迭代函数来写阶乘,代码如下,很简单的函数 1 def factori...
IOError Input / output error when printing I have inherited some code which is periodically (randomly) failing due to an Input/Output error being raised during a c...
HG659 Router | Python Reboot Code Here is some quick code to allow programmatically rebooting your HG659 router. This is quite a common router here in N...