Python3:错误和异常、类

Python

本节学习Python的错误和异常,及类等相关知识。

错误和异常

Python中有两种错误: 语法错误和异常。

语法错误,硬伤没办法。 在Python中内置了很多异常

参考内置的异常

异常处理

try语句工作方式:

  • 首先,执行try子句。
  • 如果没有异常,except子句在try执行完成后被忽略。
  • 如果在try子句执行过程中发生了异常,那么该子句其余的部分被忽略。如果异常匹配于except关键字后面指定的类型,就执行对应的except子句。然后继续执行try语句后的代码。
  • 如果发生了一个异常,在except子句中没有与之匹配的分支,它就会传递到上一级try语句中。如果最终仍找不到对应的处理语句,他就成为一个未处理异常,终止程序,显示提示信息。

一个try语句可能包含多个except子句,分别指定处理不同的异常。

一个except子句中可以括号中列出多个异常的名字。

try...except...elsetry语句没有异常需要执行一些代码时,使用else

抛出异常

raise语句允许程序猿强制抛出一个指定的异常。

1
2
3
4
>>> raise NameError("Hello")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: Hello

要抛出的异常由raise的唯一参数标识。它必需是一个异常实例或异常类。

清理行为

try语句还有一个可选的子句,目的在于定义在任何情况下都一定要执行的功能。

实际应用中,使用finally子句释放外部资源(文件或网络链接),无论使用过程。

预定义清理行为,例如:

1
2
3
with open("myfile.txt") as f:
for line in f:
print(line)

语句执行后,文件f总是被关闭,即使在处理文件中的数据时出错也一样。

Python 的类机制通过最小的新语法和语义在语言中实现了类。它是 C++ 或者 Modula-3 语言中类机制的混合

Python作用域和命名空间

命名空间 :从命名到对象的映射。命名空间的例子:内置命名集,模块中的全局命名,函数调用中的局部命名。

不同命名空间中的命名是没有任何联系的。

作用域 :一个Python程序可以直接访问命名空间的正文区域。直接访问的意思是一个对名称的错误引用会尝试在命名空间中查找。尽管作用域是静态定义,在使用时他们都是动态的。

查找顺序:

局部作用域 -> 非局部,非全局 -> 全局作用域

如果一个命名声明为全局的,那么对它的所有引用和赋值会直接搜索包含这个模块全局命名的作用域。如果要重新绑定最里层作用域之外的变量,可以使用 nonlocal 语句;如果不声明为 nonlocal,这些变量将是只读的(对这样的变量赋值会在最里面的作用域创建一个新的局部变量,外部具有相同命名的那个变量不会改变)。

Python特别之处:
如果没有使用 global 语法,其赋值操作总是在最里层的作用域。赋值不会复制数据,只是将命名绑定到对象。删除也是如此:del x 只是从局部作用域的命名空间中删除命名 x 。事实上,所有引入新命名的操作都作用于局部作用域。特别是 import 语句和函数定义将模块名或函数绑定于局部作用域(可以使用 global 语句将变量引入到全局作用域)。

global 语句用以指明某个特定的变量为全局作用域,并重新绑定它。nonlocal 语句用以指明某个特定的变量为封闭作用域,并重新绑定它。

初识类

类对象

类对象支持操作:属性引用和实例化。

属性引用 使用和 Python 中所有的属性引用一样的标准语法:obj.name。类对象创建后,类命名空间中所有的命名都是有效属性名。

类的实例化使用函数符号。只要将类对象看作是一个返回新的类实例的无参数函数即可。

初始化方法

1
2
3

def __init__(self):
self.data = []

类的实例化操作会自动调用这个方法。注意self是必须的。

实例对象

数据属性 : 不需要声明第一次使用时就会生成。

引用属性:方法: 方法是“属于”一个对象的函数。

方法对象

方法的特别之处在于实例对象作为函数的第一个参数传个函数。例如:x.f()相当于MyClass.f(x)

类和实例变量

实例变量用于对每一个实例都是唯一的数据;类变量用于类的所有实例共享的属性和方法。

举个🌰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Dog:
kind = "canine" # 类变量

def __init__(self, name):
self.name = name # 实例属性

>>> d = Dog('Fido')
>>> e = Dog('Buddy')

>>># 共享类属性
>>> d.kind
'canine'
>>> e.kind
'canine'
>>> # 不同的d、e
>>> d.name
'Fido'
>>> e.name
'Buddy'

继承

Python支持多继承。提供了两个用于继承的函数:

  • 函数isinstance()用于检查实例类型
  • 函数issubclass()用于检查类继承。

私有变量

只能从对象内部访问的“私有”实例变量,在Python中不存在。使用下划线开头的命名来表示私有变量(例如_spam)。

迭代器

for循环底层实现: for语句在容器对象中调用iter()。该函数返回一个定义了__next__()方法的迭代器对象,它在容器中逐一访问元素。没有后续元素是抛出StopIteration异常,通知for结束循环。

可以使用内建的next()函数调用__next__()方法。

1
2
3
4
5
6
7
8
9
10
11
12
>>> s = '123'
>>> i = iter(s)
>>> next(i)
'1'
>>> next(i)
'2'
>>> next(i)
'3'
>>> next(i)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

了解迭代器协议的后台机制后,可以为自己的类添加迭代器行为。只需定义一个__iter__()方法,使其返回一个带__next__()方法的对象。例如:

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
class Reverse:
def __init__(self, data):
self.data = data
self.index = len(data)

def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]


rev = Reverse('spam')
print(rev)
for char in rev:
print(char)

## 输出
'''
<__main__.Reverse object at 0x101484da0>
m
a
p
s
'''

生成器

Generator是创建迭代器的简单而强大的工具。返回是需要yield语句,每次next()被代用时,生成器回复它脱离的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def reverse(data):
for index in range(len(data) - 1, -1, -1):
yield data[index]

for char in reverse('golf'):
print(char)

# 输出
'''
f
l
o
g
'''

参考