数据分析课程笔记 - 03 - Python基础知识(三):函数

一、函数简介

在程序中,如果实现了某个功能的代码需要多次使用,就把这段代码块组织为一个小模块,这就是函数。函数可以提高编程效率,实现代码复用。之前我们接触到的 print()input(),也是函数,它们是 python 的内建函数。我们也可以自己创建函数,也就是用户自定义函数

例如,圆的面积的计算公式为 s = π × r^2,我们可以创建一个计算圆的面积的函数:s = area_of_circle(r) ,就不用每次计算圆的面积都得写一遍这个公式,而只需要直接调用这个函数就行。

二、函数定义和调用

函数必须先定义,后调用。

1. 定义函数

定义函数的格式:

def 函数名():
    代码(代码前有缩进,tab)

# 定义函数,完成打印信息的功能
def printInfo(): 
    print('************************************') 
    print(' Python 数 据 分 析 ')
    print('************************************')

2. 调用函数

定义函数之后,就相当于完成了某个功能的代码,想要让代码能够执行,需要调用函数,通过 函数名() 即可完成调用。

# 定义函数后,函数是不能够自动执行的,需要调用函数,函数才能执行。
printInfo()

注意:

  • 调用函数时,函数会从头开始执行,当函数中的代码执行完毕后,则函数调用结束。
  • 函数中如果存在 return 语句,执行到 return 语句时,函数调用结束。

函数的文档说明:

  • help(函数名) 能够看到test函数的相关说明例如: help(printInfo)
  • test.__doc__ 直接查看文档说明

三、函数的参数

函数参数的存在使得函数变得非常灵活,不但使得函数能够处理复杂多变的参数,还能简化函数的调用。Python中的函数参数有如下几种:位置参数、默认参数、可变参数、关键字参数。

1. 位置参数

计算 x^2 的函数:

def power(x):
    return x * x

对于 power(x) 函数,参数 x 就是一个位置参数,也叫做必选参数。当我们调用 power 函数时,必须传入有且仅有的一个参数 x

>>> power(5) 
25
>>> power(15) 
225

现在,如果我们要计算 x^3,或 x^4,就可以把 power(x) 修改为 power(x, n) ,用来计算 x^n

def power(x, n): 
    s = 1
    while n > 0:
        n = n - 1 
        s = s * x
    return s

注意:这里的 n 是用来控制 while 循环次数的,几次方就循环几次。

对于这个修改后的 power(x, n) 函数,可以计算任意 n 次方:

>>> power(5, 2)
25
>>> power(5, 3)
125

修改后的 power(x, n) 函数有两个参数:xn,这两个参数都是位置参数,调用函数时,传入的两个值按照位置顺序依次赋给参数xn

2. 默认参数

由于我们使用平方的次数远远大于其他次方,因此我们可以给 n 设置一个默认值,这样我们在传参的时候就只用传一个 x 的值即可:

def power(x, n=2):
    s = 1
    while n > 0:
        n = n - 1 
        s = s * x
    return s

这样,当我们调用 power(5) 时,就相当于调用 power(5, 2) ,而如果要计算其他次方,我们可以再传入第二个参数即可,比如 power(5, 3)

>>> power(5) 
25
>>> power(5, 2)
25
>>> power(5, 3)
125

由此可见,默认参数可以简化函数的调用。尤其是在实际应用中,比如要录入一个班级的学生信息,由于大多数学生都来自同一个地方,因此类似于 city 这样的参数就可以设置为默认参数,只在有特殊情况的时候传入,而把 name、gender 等变化较大的参数设置为必选参数,这样在调用函数的时候大多数情况下就只用传入必选参数即可,大大降低了函数调用的难度。

不过,设置默认参数时,有几点要注意:

  • 一是必选参数在前,默认参数在后,否则 Python 的解释器会报错;
  • 二是当函数有多个参数时,把经常变化的参数放前面,不经常变化的参数放后面。不经常变化的参数就可以作为默认参数。
  • 三是当有多个默认参数时,调用时既可以按顺序提供默认参数,也可以不按顺序加上参数名来提供,比如一个 enroll(name, gender, age=18, city="Beijing") 的函数,在调用时可以输入enroll('Bob', 'M', 17, "Tianjin")按顺序传入 age 和 city 两个默认参数的值,也可以输入enroll('Adam', 'M', city='Tianjin', age=17) 不按顺序按关键词传入。

扩展

默认参数有个最大的坑,演示如下:

先定义一个函数,传入一个 list,添加一个 END 再返回:

def add_end(L=[]): 
    L.append('END') 
    return L

当你正常调用时,结果似乎不错:

>>> add_end([1, 2, 3])
[1, 2, 3, 'END']
>>> add_end(['x', 'y', 'z'])
['x', 'y', 'z', 'END']

当你使用默认参数调用时,一开始结果也是对的:

>>> add_end() 
['END']

但是,再次调用 add_end() 时,结果就不对了:

>>> add_end() 
['END', 'END']
>>> add_end() 
['END', 'END', 'END']

这就奇了怪了,默认参数是 [] ,但函数好像每次都“记住了”上次添加了 END 后的 list。

原因是这样的:

Python函数在定义的时候,默认参数 L 的值就被计算出来了,即 [],因为默认参数 L 也是一个变量,它指向对象 [] ,每次调用该函数,如果改变了 L 的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的 [] 了。

因此,定义默认参数要牢记一点:默认参数必须指向不变对象!

要修改上面的例子,我们可以用 None 这个不变对象来实现:

def add_end(L=None): 
    if L is None:
        L = []
    L.append('END') 
    return L

现在,无论调用多少次,都不会有问题:

为什么要设计 strNone 这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。

3. 可变参数(不定长参数)

在Python函数中,还可以定义可变参数。顾名思义,可变参数就是传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个,因此可变参数还被叫做不定长参数。

以一个数学题为例,给定一组数字 a,b,c…… ,计算 a^2 + b^2 + c^2 +....

要定义出这个函数,我们必须确定输入的参数。由于参数个数不确定,我们首先想到可以把 a,b,c…… 作为一个 list 或 tuple 传进来,这样,函数可以定义如下:

def calc(numbers): 
    sum = 0
    for n in numbers: 
        sum = sum + n * n
    return sum

但是调用的时候,需要先组装出一个 list 或 tuple :

>>> calc([1, 2, 3])
14
>>> calc((1, 3, 5, 7))
84

如果利用可变参数,调用函数的方式可以简化成这样:

>>> calc(1, 2, 3)
14
>>> calc(1, 3, 5, 7)
84

所以,我们把函数的参数改为可变参数

def calc(*numbers): 
    sum = 0
    for n in numbers: 
        sum = sum + n * n
    return sum

定义可变参数和定义一个 list 或 tuple 参数相比,仅仅在参数前面加了一个 * 号。在函数内部,参数 numbers 接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数:

>>> calc(1, 2)
5
>>> calc() 
0

如果已经有一个 list 或者 tuple,要调用一个可变参数怎么办?可以这样做:

>>> nums = [1, 2, 3]
>>> calc(nums[0], nums[1], nums[2]) 
14

这种写法当然是可行的,问题是太繁琐,所以Python允许你在list或tuple前面加一个 * 号,把 list 或 tuple 的元素变成可变参数传进去:

>>> nums = [1, 2, 3]
>>> calc(*nums) 
14

*nums 表示把 nums 这个 list 的所有元素作为可变参数传进去。

4. 命名关键字参数

对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数。至于到底传入了哪些,就需要在函数内部通过 kw 检查。

仍以 person() 函数为例,我们希望检查是否有 cityjob 参数:

def person(name, age, **kw): 
    if 'city' in kw:
        # 有city参数
        pass
    if 'job' in kw: 
        # 有job参数
        pass
    print('name:', name, 'age:', age, 'other:', kw)

但是调用者仍可以传入不受限制的关键字参数:

>>> person('Jack', 24, city='Beijing', addr='Chaoyang', zipcode=123456)

如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收 cityjob 作为关键字参数。这种方式定义的函数如下:

def person(name, age, *, city, job): 
    print(name, age, city, job)

和关键字参数 **kw 不同,命名关键字参数需要一个特殊分隔符 ** 后面的参数被视为命名关键字参数。

调用方式如下:

>>> person('Jack', 24, city='Beijing', job='Engineer') 
Jack 24 Beijing Engineer

如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符 * 了:

def person(name, age, *args, city, job): 
    print(name, age, args, city, job)

命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错:

>>> person('Jack', 24, 'Beijing', 'Engineer') 
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
TypeError: person() takes 2 positional arguments but 4 were given

由于调用时缺少参数名 cityjob ,Python 解释器把这 4 个参数均视为位置参数,但 person() 函数仅接受 2 个位置参数。

命名关键字参数可以有缺省值,从而简化调用:

def person(name, age, *, city='Beijing', job): 
    print(name, age, city, job)

由于命名关键字参数 city 具有默认值,调用时,可不传入 city 参数:

>>> person('Jack', 24, job='Engineer') 
Jack 24 Beijing Engineer

使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个 * 作为特殊分隔符。如果缺少 *,Python解释器将无法识别位置参数和命名关键字参数:

def person(name, age, city, job): 
# 缺少 *,city和job被视为位置参数
    pass

5. 参数组合

在 Python 中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这 5 种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数默认参数可变参数命名关键字参数关键字参数

比如定义一个函数,包含上述若干种参数:

def f1(a, b, c=0, *args, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)

def f2(a, b, c=0, *, d, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)

在函数调用的时候,Python 解释器自动按照参数位置和参数名把对应的参数传进去。

>>> f1(1, 2)
a = 1 b = 2 c = 0 args = () kw = {}
>>> f1(1, 2, c=3)
a = 1 b = 2 c = 3 args = () kw = {}
>>> f1(1, 2, 3, 'a', 'b')
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
>>> f1(1, 2, 3, 'a', 'b', x=99)
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
>>> f2(1, 2, d=99, ext=None)
a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}

最神奇的是通过一个 tuple 和 dict ,你也可以调用上述函数:

>>> args = (1, 2, 3, 4)
>>> kw = {'d': 99, 'x': '#'}
>>> f1(*args, **kw)
a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
>>> args = (1, 2, 3)
>>> kw = {'d': 88, 'x': '#'}
>>> f2(*args, **kw)
a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}

所以,对于任意函数,都可以通过类似 func(*args, **kw) 的形式调用它,无论它的参数是如何定义的。

虽然可以组合多达 5 种参数,但不要同时使用太多的组合,否则函数接口的可理解性很差。

四、函数返回值

1. “返回值”介绍

所谓“返回值”,就是程序中函数完成一件事情后,最后给调用者的结果

2. 带有返回值的函数

想要在函数中把结果返回给调用者,需要在函数中使用 return,如下示例:

def cal(a, b): 
    c = a+b 
    return c

或者

def cal(a, b): 
    return a+b

3. 保存函数的返回值

保存函数的返回值示例如下:

#定义函数
def cal(a, b): 
    return a+b

#调用函数,顺便保存函数的返回值
result = cal(100,98)

#result已经保存了cal的返回值,所以接下来就可以使用了print(result)

结果:
198

4. 多个返回值

(1)多个return?

def cal_nums(): 
    print("---1---") 
    return 1
# 函数中下面的代码不会被执行,因为return除了能够将数据返回之外,还有一个隐藏的功能:结束函数
    print("---2---") 
    return 2
    print("---3---")

总结1: 一个函数中可以有多个 return 语句,但是只要有一个 return 语句被执行到,那么这个函数就会结束了,因此后面的return 没有什么用处。

不过,如果程序设计为如下,是可以的。因为不同的场景下执行不同的 return:

def cal_nums(num): 
    print("---1---") 
    if num == 100:
        print("---2---")
        return num+1    
# 函数中下面的代码不会被执行,因为return除了能够将数据返回之外,还有一个隐藏的功能:结束函数
    else:
        print("---3---") 
        return num+2
    print("---4---")

result1 = cal_nums(100) 
print(result1)    # 打印101 
result2 = cal_nums(200) 
print(result2)    # 打印202

(2)一个函数返回多个数据的方式

def calculate(a, b): 
    shang = a//b 
    yushu = a%b
    return shang, yushu    #默认是元组

result = calculate(5, 2) 
print(result)   # 输出(2, 1)

总结2: return 后面可以是元组,列表、字典等,只要是能够存储多个数据的类型,就可以一次性返回多个数据:

def my_function():
    # return [1, 2, 3]
    # return (1, 2, 3)
    return {"num1": 1, "num2": 2, "num3": 3}

如果 return 后面有多个数据,那么默认是元组,也就是无论是 return 1, 2, 3,还是 return (1, 2, 3),返回结果都是元组 (1, 2, 3)

>>> def my_func():
...     return 1,2,3
...
>>> result = my_func()
>>> print(result, type(result))
(1, 2, 3) <class 'tuple'>
>>> def my_func():
...     return (1,2,3)
...
>>> result = my_func()
>>> print(result, type(result))
(1, 2, 3) <class 'tuple'>
>>> exit()

五、递归函数

在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数

举个例子,我们来计算阶乘 n! = 1 x 2 x 3 x ... x n ,用函数 fact(n) 表示,可以看出:

fact(n) = n! = 1 x 2 x 3 x ... x (n-1) x n = (n-1)! x n = fact(n-1) x n

所以,fact(n) 可以表示为 n x fact(n-1) ,只有 n=1 时需要特殊处理。于是,fact(n) 用递归的方式写出来就是:

def fact(n):
    if n==1:
        return 1
    return n * fact(n - 1)

上面就是一个递归函数。可以试试:

>>> fact(1) 
1
>>> fact(5) 
120
>>> fact(100) 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。

使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。可以试试 fact(1000) :

>>> fact(1000)
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module>       
    File "<stdin>", line 4, in fact
    ...
    File "<stdin>", line 4, in fact
RuntimeError: maximum recursion depth exceeded in comparison

解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。

尾递归是指,在函数返回的时候,调用自身本身,并且, return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。

上面的 fact(n) 函数由于 return n * fact(n - 1) 引入了乘法表达式,所以就不是尾递归了。要改成尾递归方式,需要多一点代码,主要是要把每一步的乘积传入到递归函数中:

def fact(n):
    return fact_iter(n, 1)

def fact_iter(num, product): 
    if num == 1:
        return product
    return fact_iter(num - 1, num * product)

可以看到, return fact_iter(num - 1, num * product) 仅返回递归函数本身,num - 1num * product 在函数调用前就会被计算,不影响函数调用。

fact(5) 对应的 fact_iter(5, 1) 的调用如下:

===>    fact_iter(5, 1)
===>    fact_iter(4, 5)
===>    fact_iter(3, 20)
===>    fact_iter(2, 60)
===>    fact_iter(1, 120)
===>    120

尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。

遗憾的是,大多数编程语言没有针对尾递归做优化,Python 解释器也没有做优化,所以,即使把上面的 fact(n) 函数改成尾递归方式,也会导致栈溢出。

六、局部变量与全局变量

1. 局部变量

局部变量,就是在函数内部定义的变量,其作用范围是这个函数内部,即只能在这个函数中使用,在函数的外部是不能使用的。因为其作用范围只是在自己的函数内部,所以不同的函数可以定义相同名字的局部变量(打个比方,把你、我是当做成函数,把局部变量理解为每个人手里的手机,你可有个iPhone11,我当然也可以有个iPhone11了,互不相关)

局部变量的作用,是为了临时保存数据需要在函数中定义变量来进行存储。当函数调用时,局部变量被创建,当函数调用完成后这个变量就不能够使用了。

def show():
    # 定义局部变量
    sal = 15000 
    print("薪资:", sal)
show()
print(sal)
# 这里就会报错,因为在全局作用域下,不存在 `sal` 这个变量。

2. 全局变量

如果一个变量,既能在一个函数中使用,也能在其他的函数中使用,这样的变量就是全局变量

比如全家每个人各有一部手机只能自己使用,但是家里还装了一个固定电话,全家人都可以使用,那么每个人自己的手机就是局部变量,固定电话就是全局变量。

# 定义全局变量
money = 1200

def test1():
    print(money)    
    # 虽然没有定义变量money但是可是使用全局变量money

def test2():
    print(money)    
    # 虽然没有定义变量money但是可是使用全局变量money

# 调用函数
test1() 
test2()

运行结果: 
1200
1200

总结:在函数外边定义的变量叫做全局变量。全局变量能够在所有的函数中进行访问。

3. 全局变量和局部变量的冲突问题

# 定义全局变量
x = 100

def test1():
    # 定义局部变量,与全局变量名字相同
    x = 300
    print('---test1---%d'%x) 
    #修改
    x = 200
    print('修改后的%d'%x)

def test2():
    print('x = %d'%x)

test1() 
test2()

结果:
---test1---300
修改后的200 
x = 100

总结:当函数内出现局部变量和全局变量相同名字时,函数内部中的此时理解为定义了一个局部变量,而不是修改全局变量的值。

4. 修改全局变量

函数中使用全局变量时可否进行修改呢?

# 定义全局变量
x = 100

def test1():
    # 定义全局变量,使用 global 函数声明变量 x 为全局变量
    global x
    print('修改之前:%d'%x)

    #修改
    x = 200
    print('修改后的%d'%x)

def test2():
    print('x = %d'%x)

test1() 
test2()

结果:
修改之前:100 
修改后的200 
x = 200

七、捕获异常

程序一旦出错,还要一级一级上报,直到某个函数可以处理该错误(比如,给用户输出一个错误信息)。所以高级语言通常都内置了一套 try...except...finally...错误处理机制,Python也不例外。

让我们用一个例子来看看 try 的机制:

try:
    print('try...') 
    r = 10 / 0
    print('result:', r)
except ZeroDivisionError as e: 
    print('except:', e)
finally:
    print('finally...') 
    print('END')

当我们认为某些代码可能会出错时,就可以用 try 来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即 except 语句块,执行完 except 后,如果有 finally 语句块,则执行 finally 语句块,至此, 执行完毕。

上面的代码在计算 10 / 0 时会产生一个除法运算错误:

try...
except: division by zero 
finally...
END

从输出可以看到,当错误发生时,后续语句 print('result:', r) 不会被执行,except 由于捕获到 ZeroDivisionError ,因此被执行。最后,finally 语句被执行。然后,程序继续按照流程往下走。

如果把除数 0 改成 2 ,则执行结果如下:

try... 
result: 5 
finally... 
END

由于没有错误发生,所以 except 语句块不会被执行,但是 finally如果有,则一定会被执行(可以没有 finally 语句)。

当然,错误应该有很多种类,如果发生了不同类型的错误,应该由不同的 except 语句块处理:

try:
    print('try...') 
    r = 10 / int('a')
    print('result:', r) 
except ValueError as e:
    print('ValueError:', e) 
except ZeroDivisionError as e:
    print('ZeroDivisionError:', e) 
finally:
    print('finally...') 
    print('END')

int() 函数可能会抛出 ValueError ,所以我们用一个 except 捕获 ValueError,用另一个 except 捕获 ZeroDivisionError

此外,如果没有错误发生,可以在 except 语句块后面加一个 else,当没有错误发生时,会自动执行 else 语句:

try:
    print('try...') 
    r = 10 / int('2')
    print('result:', r) 
except ValueError as e:
    print('ValueError:', e) 
except ZeroDivisionError as e:
    print('ZeroDivisionError:', e) 
else:
    print('no error!') 
finally:
    print('finally...') 
    print('END')

Python的错误其实也是 class ,所有的错误类型都继承BaseException,所以在使用 except 时需要注意的是,它不但能捕获该类型的错误,还把其子类也“一网打尽”。比如:

try:
    foo()
except Exception as e: 
    print('Exception') 
except TypeError as e: 
    print('TypeError')
finally:
    print('finally...')

第二个 except 永远也捕获不到 TypeError,因为 TypeErrorException 的子类,如果有,也被第一个 except 给捕获了。

Python所有的错误都是从 BaseException 类派生的,常见的错误类型和继承关系可以看下面这个链接:

https://docs.python.org/3/library/exceptions.html#exception-hierarchy

使用 try...except 捕获错误还有一个巨大的好处,就是可以跨越多层调用,比如函数 main() 调用 foo()foo() 调用 bar() ,结果 bar() 出错了,这时,只要 main() 捕获到了,就可以处理:

def foo(s):
    return 10 / int(s)

def bar(s):
    return foo(s) * 2

def main(): 
    try:
        bar('0')
    except Exception as e: 
        print('Error:', e)
    finally:
        print('finally...')

main()

输出结果:

Error: division by zero
finally...

也就是说,不需要在每个可能出错的地方去捕获错误,只要在合适的层次去捕获错误就可以了。这样一来,就大大减少了写try...except...finally 的麻烦。

python所有的标准异常类:

异常名称 描述
BaseException 所有异常的基类
SystemExit 解释器请求退出
KeyboardInterrupt 用户中断执行(通常是输入^C)
Exception 常规错误的基类
StopIteration 迭代器没有更多的值
GeneratorExit 生成器(generator)发生异常来通知退出
SystemExit Python 解释器请求退出
StandardError 所有的内建标准异常的基类
ArithmeticError 所有数值计算错误的基类
FloatingPointError 浮点计算错误
OverflowError 数值运算超出最大限制
ZeroDivisionError 除(或取模)零 (所有数据类型)
AssertionError 断言语句失败
AttributeError 对象没有这个属性
EOFError 没有内建输入,到达EOF 标记
EnvironmentError 操作系统错误的基类
IOError 输入/输出操作失败
OSError 操作系统错误
WindowsError 系统调用失败
ImportError 导入模块/对象失败
KeyboardInterrupt 用户中断执行(通常是输入^C)
LookupError 无效数据查询的基类
IndexError 序列中没有没有此索引(index)
KeyError 映射中没有这个键
MemoryError 内存溢出错误(对于Python 解释器不是致命的)
NameError 未声明/初始化对象 (没有属性)
UnboundLocalError 访问未初始化的本地变量
ReferenceError 弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError 一般的运行时错误
NotImplementedError 尚未实现的方法
SyntaxError Python 语法错误
IndentationError 缩进错误
TabError Tab 和空格混用
SystemError 一般的解释器系统错误
TypeError 对类型无效的操作
ValueError 传入无效的参数
UnicodeError Unicode 相关的错误
UnicodeDecodeError Unicode 解码时的错误
UnicodeEncodeError Unicode 编码时错误
UnicodeTranslateError Unicode 转换时错误
Warning 警告的基类
DeprecationWarning 关于被弃用的特征的警告
FutureWarning 关于构造将来语义会有改变的警告
OverflowWarning 旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning 关于特性将会被废弃的警告
RuntimeWarning 可疑的运行时行为(runtime behavior)的警告
SyntaxWarning 可疑的语法的警告
UserWarning 用户代码生成的警告
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容