定义自己的异常类
定义自己的错误类型有很多好处,比如可以清楚地显示出潜在的错误,让函数和模块更具可维护性。自定义错误类型还可用来提供额外的调试信息。
Python中比较常见的泛型异常类包括像 ValueError,但是在栈调试回溯中,返回的内容可能用处不大。
class NameTooShortError(ValueError):
pass
def validate(name):
if len(name) < 10:
raise NameTooShortError(name)
引入自定义异常类型来表示名字验证失败,现在有了能够“顾名思义”NameTooShortError 异常类型,它扩展自内置的 ValueError类。(其他的内置Error还包括Exception和TypeError类)
还可以使用继承来根据逻辑对异常分组,组成层次结构。
克隆对象
Python 中的赋值语句不会创建对象的副本,而只是将名称绑定到对象上。对于不可变对象也是如此。但为了处理可变对象或可变对象集合,需要一种方法来创建这些对象的“真实副本”或“克隆体”。 有时需要用到对象的副本,以便修改副本时不会改动本体。
浅复制是指构建一个新的容器对象,然后填充原对象中子对象的引用。本质上浅复制只执行 一层,复制过程不会递归,因此不会创建子对象的副本。 深复制是递归复制,首先构造一个新的容器对象,然后递归地填充原始对象中子对象的副本。这种方式会遍历整个对象树,以此来创建原对象及其所有子项的完全独立的副本。
下面的例子中将创建一个新的嵌套列表,然后用 list()工厂函数浅复制:
xs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
xs
ys = list(xs)
ys
这里ys是一个新的独立对象,与xs具有相同的内容:
>>> xs
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> ys
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>>
我们向原对象添加一个新列表,查看ys是否会跟着xs的变化而改变:
xs.append(['new sublist'])
xs
ys
可以看到,修改浅复制的列表完全不会影响副本。
>>> xs
[[1, 2, 3], [4, 5, 6], [7, 8, 9], ['new sublist']]
>>> ys
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>>
但由于前面只创建了原列表的浅副本,所以 ys 仍然含有 xs 子对象的引用!
这些子对象没有复制,只是在 ys 中再次引用。因此在修改 xs 中的子对象时,这些改动也会反映在 ys 中——因为两个列表共享相同的子对象。这个副本是仅含有一层的浅复制:
xs[1][0] = 'X'
xs
ys
这里看上去这是修改了xs,但是ys和xs中索引1处的子列表都被修改了。
>>> xs
[[1, 2, 3], ['X', 5, 6], [7, 8, 9], ['new sublist']]
>>> ys
[[1, 2, 3], ['X', 5, 6], [7, 8, 9]]
>>>
而如果在第一步中创建的是 xs 的深副本,那么这两个对象会互相完全独立。
制作深副本
使用 copy 模块中定义的 deepcopy()函数创建深副本:
import copy
xs = [[1,2,3],[4,5,6],[7,8,9]]
zs = copy.deepcopy(xs)
这里,它们看起来是一样的。
>>> xs
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> zs
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>>
我们看到如果修改原对象(xs)中的某个子对象,则会发现这些修改不会影响深副本(zs)。
xs[1][0] = 'X'
xs
zs
输出结果如下:
>>> xs
[[1, 2, 3], ['X', 5, 6], [7, 8, 9]]
>>> zs
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>>
还可以使用 copy 模块中的一个函数来创建浅副本。copy.copy()函数会创建 对象的浅副本。
在代码中,copy.copy()可以清楚地表明这里创建的是浅副本。但对于内置容器,只需要 使用 list、dict 和 set 这样的工厂函数就能创建浅副本,这种方式更具 Python 特色.