源码网商城,靠谱的源码在线交易网站 我的订单 购物车 帮助

源码网商城

Python魔术方法详解

  • 时间:2022-05-31 01:19 编辑: 来源: 阅读:
  • 扫一扫,手机访问
摘要:Python魔术方法详解
[b]介绍[/b] 此教程为我的数篇文章中的一个重点。主题是魔术方法。 什么是魔术方法?他们是面向对象的Python的一切。他们是可以给你的类增加"magic"的特殊方法。他们总是被双下划线所包围(e.g. __init__ 或者 __lt__)。然而他们的文档却远没有提供应该有的内容。Python中所有的魔术方法均在Python官方文档中有相应描述,但是对于他们的描述比较混乱而且组织比较松散。很难找到有一个例子(也许他们原本打算的很好,在开始语言参考中有描述很详细,然而随之而来的确是枯燥的语法描述等等)。 所以,为了修补我认为Python文档应该修补的瑕疵,我决定给Python中的魔术方法提供一些用平淡的语言和实例驱使的文档。我在开始已经写了数篇博文,现在在这篇文章中对他们进行总结。 我希望你能够喜欢这篇文章。你可以将之当做一个教程,一个补习资料,或者一个参考。本文章的目的仅仅是为Python中的魔术方法提供一个友好的教程。 [b]构造和初始化[/b] 每个人都知道一个最基本的魔术方法, __init__ 。通过此方法我们可以定义一个对象的初始操作。然而,当我调用 x = SomeClass() 的时候, __init__ 并不是第一个被调用的方法。实际上,还有一个叫做 __new__ 的方法,来构造这个实例。然后给在开始创建时候的初始化函数来传递参数。在对象生命周期的另一端,也有一个 __del__ 方法。我们现在来近距离的看一看这三个方法: __new__(cls, [...) __new__ 是在一个对象实例化的时候所调用的第一个方法。它的第一个参数是这个类,其他的参数是用来直接传递给 __init__ 方法。 __new__ 方法相当不常用,但是它有自己的特性,特别是当继承一个不可变的类型比如一个tuple或者string。我不希望在 __new__ 上有太多细节,因为并不是很有用处,但是在 Python文档 [url=http://www.python.org/download/releases/2.2/descrintro/#__new]http://www.python.org/download/releases/2.2/descrintro/#__new[/url]__ 中有详细的阐述。 __init__(self, […) 此方法为类的初始化方法。当构造函数被调用的时候的任何参数都将会传给它。(比如如果我们调用 x = SomeClass(10, 'foo')),那么 __init__ 将会得到两个参数10和foo。 __init__ 在Python的类定义中被广泛用到。 __del__(self) 如果 __new__ 和 __init__ 是对象的构造器的话,那么 __del__ 就是析构器。它不实现语句 del x (所以代码将不会翻译为 x.__del__() )。它定义的是当一个对象进行垃圾回收时候的行为。当一个对象在删除的时候需要更多的清洁工作的时候此方法会很有用,比如套接字对象或者是文件对象。注意,因为当解释器退出的时候如果对象还存在,不能保证 __del__ 能够被执行,所以 __del__ can't serve as a replacement for good coding practices ()~~~~~~~ 放在一起的话,这里是一个 __init__ 和 __del__ 实际使用的例子。
[url=http://en.wikipedia.org/wiki/Bitwise_operation#NOT]http://en.wikipedia.org/wiki/Bitwise_operation#NOT[/url]>_ [b]普通算数操作符[/b] 现在我们仅仅覆盖了普通的二进制操作符:+,-,*和类似符号。这些符号大部分来说都浅显易懂。 __add__(self, other) 实现加法。 __sub__(self, other) 实现减法。 __mul__(self, other) 实现乘法。 __floordiv__(self, other) 实现 // 符号实现的整数除法。 __div__(self, other) 实现 / 符号实现的除法。 __truediv__(self, other) 实现真除法。注意只有只用了 from __future__ import division 的时候才会起作用。 __mod__(self, other) 实现取模算法 % __divmod___(self, other) 实现内置 divmod() 算法 __pow__ 实现使用 ** 的指数运算 __lshift__(self, other) 实现使用 << 的按位左移动 __rshift__(self, other) 实现使用 >> 的按位左移动 __and__(self, other) 实现使用 & 的按位与 __or__(self, other) 实现使用 | 的按位或 __xor__(self, other) 实现使用 ^ 的按位异或 [b]反运算[/b] 下面我将会讲解一些反运算的知识。有些概念你可能会认为恐慌或者是陌生。但是实际上非常简单。以下是一个例子:
[url=http://www.python.org/dev/peps/pep-0343/]http://www.python.org/dev/peps/pep-0343/[/url]>_ 被添加后。它被成为一级语言结构。你也许之前看到这样的语句:
[u]复制代码[/u] 代码如下:
with open('foo.txt') as bar: # perform some action with bar
回话控制器通过包装一个 with 语句来设置和清理行为。回话控制器的行为通过两个魔术方法来定义: __enter__(self) 定义当使用 with 语句的时候会话管理器应该初始块被创建的时候的行为。注意 __enter__ 的返回值被 with 语句的目标或者 as 后的名字绑定。 __exit__(self, exception_type, exception_value, traceback) 定义当一个代码块被执行或者终止后会话管理器应该做什么。它可以被用来处理异常,清楚工作或者做一些代码块执行完毕之后的日常工作。如果代码块执行成功, exception_type , exception_value , 和 traceback 将会是 None 。否则的话你可以选择处理这个异常或者是直接交给用户处理。如果你想处理这个异常的话,确认 __exit__ 在所有结束之后会返回 True 。如果你想让异常被会话管理器处理的话,那么就这样处理。 __enter 和 __exit__ 对于明确有定义好的和日常行为的设置和清洁工作的类很有帮助。你也可以使用这些方法来创建一般的可以包装其他对象的会话管理器。以下是一个例子。
[u]复制代码[/u] 代码如下:
class Closer: '''通过with语句和一个close方法来关闭一个对象的会话管理器''' def __init__(self, obj):     self.obj = obj def __enter__(self):     return self.obj # bound to target def __exit__(self, exception_type, exception_val, trace):     try:         self.obj.close()     except AttributeError: # obj isn't closable         print 'Not closable.'         return True # exception handled successfully
以下是一个使用 Closer 的例子,使用一个FTP链接来证明(一个可关闭的套接字):
[u]复制代码[/u] 代码如下:
>>> from magicmethods import Closer >>> from ftplib import FTP >>> with Closer(FTP('ftp.somesite.com')) as conn: ...     conn.dir() ... >>> conn.dir() >>> with Closer(int(5)) as i: ...     i += 1 ... Not closable. >>> i 6
你已经看到了我们的包装器如何静默的处理适当和不适当的使用行为。这是会话管理器和魔术方法的强大功能。 [b]创建对象的描述器[/b] 描述器是通过得到,设置,删除的时候被访问的类。当然也可以修改其他的对象。描述器并不是鼓励的,他们注定被一个所有者类所持有。当创建面向对象的数据库或者类,里面含有相互依赖的属性时,描述器将会非常有用。一种典型的使用方法是用不同的单位表示同一个数值,或者表示某个数据的附加属性(比如坐标系上某个点包含了这个点到远点的距离信息)。 为了构建一个描述器,一个类必须有至少 __get__ 或者 __set__ 其中一个,并且 __delete__ 被实现。让我们看看这些魔术方法。 __get__(self, instance, owner) 定义当描述器的值被取得的时候的行为, instance 是拥有者对象的一个实例。 owner 是拥有者类本身。 __set__(self, instance, value) 定义当描述器值被改变时候的行为。 instance 是拥有者类的一个实例 value 是要设置的值。 __delete__(self, instance) 定义当描述器的值被删除的行为。instance 是拥有者对象的实例。 以下是一个描述器的实例:单位转换。
[u]复制代码[/u] 代码如下:
class Meter(object): '''Descriptor for a meter.'''     def __init__(self, value=0.0):     self.value = float(value)     def __get__(self, instance, owner):     return self.value     def __set__(self, instance, value):     self.value = float(value) class Foot(object):     '''Descriptor for a foot.'''     def __get__(self, instance, owner):     return instance.meter * 3.2808     def __set__(self, instance, value):     instance.meter = float(value) / 3.2808 class Distance(object):     '''Class to represent distance holding two descriptors for feet and     meters.'''     meter = Meter()     foot = Foot()
[b]储存你的对象[/b] 如果你接触过其他的 Pythoner,你可能已经听说过 Pickle 了, Pickle 是用来序列化 Python 数据结构的模块,在你需要暂时存储一个对象的时候(比如缓存),这个模块非常的有用,不过这同时也是隐患的诞生地。 序列化数据是一个非常重要的功能,所以他不仅仅拥有相关的模块( Pickle , cPickle ),还有自己的协议以及魔术方法,不过首先,我们先讨论下关于序列化内建数据结构的方法。 Pickling: 简单例子 让我们深入研究 Pickle,比如说你现在需要临时储存一个字典,你可以把它写入到一个文件里,并且要小心翼翼的确保格式正确,之后再用 exec() 或者处理文件输入来恢复数据,实际上这是很不安全的,如果你使用文本存储了一些重要的数据,任何方式的改变都可能会影响到你的程序,轻则程序崩溃,重则被恶意程序利用,所以,让我们用 Pickle 代替这种方式:
[u]复制代码[/u] 代码如下:
import pickle data = {'foo': [1, 2, 3],         'bar': ('Hello', 'world!'),         'baz': True} jar = open('data.pkl', 'wb') pickle.dump(data, jar) # write the pickled data to the file jar jar.close()
嗯,过了几个小时之后,我们需要用到它了,只需把它 unpickle 了就行了:
[u]复制代码[/u] 代码如下:
import pickle pkl_file = open('data.pkl', 'rb') # connect to the pickled data data = pickle.load(pkl_file) # load it into a variable print data pkl_file.close()
正如你期望的,数据原封不动的回来了! 同时要给你一句忠告: pickle 并不是很完美, Pickle 文件很容易被不小心或者故意损坏, Pickle 文件比纯文本文件要稍微安全一点,但是还是可以被利用运行恶意程序。 Pickle 不是跨版本兼容的(译注:最近刚好在 《Python Cookbook》上看到相关讨论,书中描述的 Pickle 是跨版本兼容的,此点待验证),所以尽量不要去分发 Pickle 过的文本,因为别人并不一定能够打开。不过在做缓存或者其他需要序列化数据的时候, Pickle 还是很有用处的。 [b]序列化你自己的对象[/b] Pickle 并不是只支持内建数据结果,任何遵循 Pickle 协议的类都可以,Pickle 协议为 Python 对象规定了4个可选方法来自定义 Pickle 行为(对于 C 扩展的 cPickle 模块会有一些不同,但是这并不在我们的讨论范围内):
[u]复制代码[/u] 代码如下:
__getinitargs__(self)
如果你希望在逆序列化的同时调用 __init__ ,你可以定义 __getinitargs__ 方法,这个方法应该返回一系列你想被 __init__ 调用的参数,注意这个方法只对老样式的类起作用。
[u]复制代码[/u] 代码如下:
__getnewargs__(self)
对于新式的类,你可以定义任何在重建对象时候传递到 __new__ 方法中的参数。这个方法也应该返回一系列的被 __new__ 调用的参数。
[u]复制代码[/u] 代码如下:
__getstate__(self)
你可以自定义当对象被序列化时返回的状态,而不是使用 __dict 方法,当逆序列化对象的时候,返回的状态将会被 __setstate__ 方法调用。
[u]复制代码[/u] 代码如下:
__setstate__(self, state)
在对象逆序列化的时候,如果 __setstate__ 定义过的话,对象的状态将被传给它而不是传给 __dict__ 。这个方法是和 __getstate__ 配对的,当这两个方法都被定义的时候,你就可以完全控制整个序列化与逆序列化的过程了。 [b]例子[/b] 我们以 Slate 为例,这是一段记录一个值以及这个值是何时被写入的程序,但是,这个 Slate 有一点特殊的地方,当前值不会被保存。
[u]复制代码[/u] 代码如下:
import time class Slate:     '''Class to store a string and a changelog, and forget its value when     pickled.'''     def __init__(self, value):         self.value = value         self.last_change = time.asctime()         self.history = {}     def change(self, new_value):         # Change the value. Commit last value to history         self.history[self.last_change] = self.value         self.value = new_value         self.last_change = time.asctime()     def print_changes(self):         print 'Changelog for Slate object:'         for k, v in self.history.items():             print '%st %s' % (k, v)     def __getstate__(self):         # Deliberately do not return self.value or self.last_change.         # We want to have a "blank slate" when we unpickle.         return self.history     def __setstate__(self, state):         # Make self.history = state and last_change and value undefined         self.history = state         self.value, self.last_change = None, None
[b]结论[/b] 这份指南的希望为所有人都能带来一些知识,即使你是 Python 大牛或者对于精通于面向对象开发。如果你是一个 Python 初学者,阅读这篇文章之后你已经获得了编写丰富,优雅,灵活的类的知识基础了。如果你是一个有一些经验的 Python 程序员,你可能会发现一些能让你写的代码更简洁的方法。如果你是一个 Python 大牛,可能会帮助你想起来一些你已经遗忘的知识,或者一些你还没听说过的新功能。不管你现在有多少经验,我希望这次对于 Python 特殊方法的旅程能够带给你一些帮助(用双关语真的很不错 XD)(译注: 这里的双关在于标题为 Magic Methods 这里是 神奇的旅程 ,不过由于中英语序的问题,直译略显头重脚轻,所以稍微变化了下意思,丢掉了双关的含义)。 [b]附录:如何调用魔术方法[/b] 一些魔术方法直接和内建函数相对,在这种情况下,调用他们的方法很简单,但是,如果是另外一种不是特别明显的调用方法,这个附录介绍了很多并不是很明显的魔术方法的调用形式。 [img]http://img.1sucai.cn/uploads/article/2018010709/20180107090117_0_71866.jpg[/img] 希望这个表格对你对于什么时候应该使用什么方法这个问题有所帮助。
  • 全部评论(0)
联系客服
客服电话:
400-000-3129
微信版

扫一扫进微信版
返回顶部