在Python中,有些名称很特别,开头和结尾都是两个下划线。我们可能用过一些,如__future__。这样的拼写表示名称有特殊意义,因此绝不要在程序中创建这样的名称。在这样的名称中,很大一部分都是魔法(特殊)方法的名称。如果你的对象实现了这些方法,它们将在特定情况下(具体是那种情况取决于方法的名称)被Python调用,而几乎不需要直接调用。
今天讨论几个重要的魔法方法,其中最重要的是__init__以及一些处理元素访问的方法(它们让你能够创建序列或映射)。
我们要介绍的第一个魔法方法是构造函数。你可能从未通说过构造函数(constructor),它其实就是所谓的初始化方法,只是命名为__init__。然而,构造函数不同于普通方法的地方在于,将在对象创建后自动调用他们。因此无需采用之前一直使用的做法:
>>> f = FooBar()
>>> f.init()
构造函数只需要让你像下面这样做:
>>> f = FooBar()
在Python中创建构造函数很容易,只需要将方法init的名称从普通的init改为魔法版__init__即可。
class FooBar:
def __init__(self):
self.somevar = 42
>>> f = FooBar()
>>> f.somevar
42
到目前为止一切顺利。但你可能会问,如果给构造函数添加几个参数,结果将如何呢?请看下面的代码:
class FooBar:
def __init__(self, value=42):
self.somevar = value
你认为该如何使用这个构造函数呢?由于参数是可选的,你可以当什么事都没发生,还像原来那样做。但是如果要指定这个参数(或者说如果这个参数不是可选的)呢?你肯定猜到了,不过这里还是演示一下:
>>> f = FooBar('This is a constructor argument')
>>> f.somevar
'This is a constructor argument'
在所有的Python魔法方法中,__init__绝对是你用的最多的。
注意 Python提供了魔法方法__del__,也称为析构函数(destructor)。这个方法在对象被销毁(作为垃圾被收集)前被调用,但鉴于你无法知道准确的调用时间,建议尽可能不要使用__del__。
虽然__init__无疑是你目前遇到最重要的特殊方法,但还有不少其他的特殊方法,让你能够完成很多很酷的任务。接下来将介绍一组很有用的魔法方法,让你能够创建行为类似于序列或映射的对象。
基本的序列和映射协议非常简单,但要实现序列和映射的所有功能,需要实现很多魔法方法。
注意 在Python中,协议通常指的是对范行为的规则,有点类似于接口。协议指定应实现哪些方法以及这些方法该做什么。在Python中,多态仅仅基于对象的行为(而不基于祖先,如属于哪个类或其超类等),因此这个概念很重要:其他语言可能要求对象属于特定的类或实现了特定的接口,而Python通常只要求对象遵循特定的协议。因此,要成为序列,只需遵循序列协议即可。
序列和映射基本上是元素(item)的集合,要实现它们基本的行为(协议),不可变对象要实现2个方法,而可变对象要实现4个。
对于这些方法,还有一些额外的要求。
要了解更复杂的接口和使用的抽象基类(Sequence),请参阅有关模块collections的文档。
下面来试一试,看看能否创建一个无穷序列。
def check_key(key):
"""
指定的键是否是可接受的索引?
键必须是非负整数,才是可以接受的。如果不是整数,
将引发TypeError异常;如果是负数,将引发IndexError
异常(因为这个序列的长度是无穷的)
"""
if not isinstance(key, int):
raise TypeError
if key < 0:
raise IndexError
class ArithmeticSequence:
def __init__(self, start=0, step=1):
"""
初始化这个算数序列
start -序列中的第一个值
step -两个相邻值的差
changed -一个字典,包含用户修改后的值
"""
self.start = start # 存储起始值
self.step = step # 存储步长值
self.changed = {} # 没有任何元素被修改
def __getitem__(self, key):
"""
从算数序列中获取第一个元素
"""
check_key(key)
try:
return self.changed[key]
except KeyError:
return self.start+key*self.step
def __setitem__(self, key, value):
"""
修改算数序列中的元素
"""
check_key(key)
self.changed[key] = value # 存储修改后的值
这些代码实现的是一个算数序列,其中任何两个相邻数字的差都相同。第一个值是由构造函数的参数start(默认为0)指定的,而相邻值之间的差是由参数step(默认为1)指定的。你允许用户修改某些元素。这是通过将不符合规则的值保存在字典changed中实现的。如果元素未被修改,就使用公式self.start+key*self.step来计算它的值。下面的示例演示了如何使用这个类。
>>> s = ArithmeticSequence(1, 2)
>>> s[4]
9
>>> s[4] = 2
>>> s[4]
2
>>> s[5]
11
请注意,我要禁止删除元素,因此没有实现__del__:
>>> del s[4]
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: ArithmeticSequence instance has no attribute '__delitem__'
另外,这个类没有方法__len__,因为其长度是无穷的。
如果所使用的索引类型非法,将引发TypeError异常;如果索引类型正确,但不再允许的范围内(即为负数),将引发IndexError异常。
>>> s["four"]
Traceback (most recent call last):
File "<stdin>", line 1, in ?
...
>>> s[-42]
Traceback (most recent call last):
File "<stdin>", line 1, in ?
...
索引检查是由我为此编写的辅助函数check_index负责的。
本文分享自 Python机器学习算法说书人 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!