全面学习Python魔法方法(magic methods) I

全面学习Python魔法方法(magic methods) II

全面学习Python魔法方法(magic methods) III

Copying

有时,特别是在处理可变对象时,您希望能够复制对象并进行更改,而不会影响您从中复制的内容。 这是Python的copy发挥作用的地方,但我们必须告诉Python如何有效地复制。

  • __copy__(self)

    为类的实例定义copy.copy()的行为。 copy.copy()返回对象的浅拷贝{这意味着,虽然实例本身是一个新实例,但它的所有数据都被引用,即对象本身被复制,但其数据仍被引用(因此浅拷贝中的数据更改可能会导致原始更改)。

  • __deepcopy__(self, memodict=)

    为类的实例定义copy.deepcopy()的行为。copy.deepcopy()返回对象的深拷贝,对象及其数据都是copied.memodict是一个以前复制的对象的缓存,这可以优化复制并防止在复制递归数据结构时无限递归。如果要深拷贝单个属性,请在该属性上调用copy.deepcopy(),并将memodict作为第一个参数。

与往常一样,在任何情况下,我们都需要比默认行为提供的更细粒度的控制。例如,如果试图复制一个将缓存存储为字典(可能很大)的对象,那么复制缓存可能也没有意义——如果缓存可以在实例之间在内存中共享。

Pickling

pickle是Python数据结构的序列化过程,当需要存储一个对象并稍后检索它时,它可能非常有用。pickle非常重要,因此它不仅有自己的模块(pickle),还具有自己的协议和相应的魔法方法。但是首先,简单介绍一下如何pickle现有类型。

Pickling: A Quick Soak in the Brine

让我们深入pickling。 假设有一个要稍后存储和检索的字典,可以将其内容写入文件,仔细确保编写正确的语法,然后使用exec()或处理文件输入来检索它。

但这最是不危险的:如果以明文形式存储重要数据,则可能会以任何方式损坏或更改程序导致崩溃或更糟糕地在计算机上运行恶意代码。因此,我们要pickle数据:

1
2
3
4
5
6
7
8
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 it:

1
2
3
4
5
6
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可能比使用纯文本文件更安全,但它仍然可以用于运行恶意代码。它在不同版本的Python中也是不兼容的,所以不要期望发布pickle对象,也不要期望人们能够打开它们。但是,它也可以是缓存和其他常见序列化任务的强大工具。

Pickling your own Objects

Pickle不仅适用于内置类型。它适用于遵循pickle协议的任何类。pickle协议有四个可选方法供Python对象自定义它们的行为方式(对于C扩展,它有点不同,但这不在我们的范围内):

  • __getinitargs__(self)

    如果想在类被打开时调用__init__,可以定义__getinitargs__,它应该返回想要传递给__init__的参数的元组。 请注意,此方法仅适用于旧式类

  • __getnewargs__(self)对于新式类,可以影响在unpickling时传递给__new__的参数。 此方法还应返回一个参数元组,然后传递给__new__

  • __getstate__(self)

    不存储对象的__dict__属性,而是可以返回在对象被pickle时存储的自定义状态。当对象被打开时,__setstate__将使用该状态。

  • __setstate__(self,state)当对象被unpickled时,如果定义了__setstate__,则对象的状态将被传递给它,而不是直接应用于对象的__dict__。这与__getstate__相同:当两者都被定义时,你可以用你想要的任何东西来表示对象的pickle状态。

  • __reduce__(self)

    当定义扩展类型(即使用Python的C API实现的类型)时,如果想要pickle这些类型,则必须告诉Python如何pickle这些类型。__reduce__()在定义它的对象被pickle时被调用,它可以返回一个表示Python将查找和pickle的全局名称的字符串,或者一个元组。

    元组包含2到5个元素:一个可调用对象,用于重新创建对象,一个可调用对象的参数元组,一个要传递给__setstate__(可选)的状态,一个迭代器,产生要被pickle的列表项(可选)和一个迭代器,产生要被pickle的字典项(可选)

  • __reduce_ex__(self)

    如果它已定义,则__reduce_ex__将在pickle时调用__reduce____reduce__也可以定义为不支持__reduce_ex__的旧版本的pickling API。

An Example

我们的例子是一个Slate,它记住它的值是什么以及何时写入它们的值。但是,每次进行pickle时,这个Slate都会变为空白:不会保存当前值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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('%s\t %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

How to Call Magic Methods

Python中的一些魔法方法直接映射到内置函数; 在这种情况下,如何调用它们是相当明显的。但是,在其他情况下,调用就不那么明显了,让我们看一看一些不明显的语法,这些语法会导致调用魔法的方法。

魔法方法 被调用时 解释
__new__(cls [,...]) instance = MyClass(arg1, arg2) __new__is called on instance creation
__init__(self [,...]) instance = MyClass(arg1, arg2) __init__ is called on instance creation
__cmp__(self, other) self == other, self > other, etc. Called for any comparison
__pos__(self) +self Unary plus sign
__neg__(self) -self Unary minus sign
__invert__(self) self Bitwise inversion
__index__(self) x[self] Conversion when object is used as index
__nonzero__(self) bool(self) Boolean value of the object
__getattr__(self, name) self.name # name doesn’t exist Accessing nonexistent at tribute
__setattr__(self, name, val) self.name = val Assigning to an attribute
__delattr__(self, name) del self.name Deleting an attribute
__getattribute__(self, name) self.name Accessing any attribute
__getitem__(self, key) self[key] Accessing an item using an in dex
__setitem__(self, key, val) self[key] = val Assigning to an item using an index
__delitem__(self, key) del self[key] Deleting an item using an in dex
__iter__(self) for x in self Iteration
__contains__(self, value) value in self, value not in self Membership tests using in
__call__(self [,...]) self(args) “Calling” an instance
__enter__(self) with self as x: with statement context man agers
__exit__(self, exc, val, trace) with self as x: with statement context man agers
__getstate__(self) pickle.dump(pkl_file, self) Pickling
__setstate__(self) data = pickle.load(pkl_- file) Pickling