- .gitignore
- 01.01 Python 基础.ipynb
- 01.02 Python 进阶.ipynb
- 01.03 机器学习常用的包.ipynb
- 01.04 Keras MNIST Playground.ipynb
- 02.01 基于搜索的问题求解(学生版).ipynb
- 02.01 基于搜索的问题求解.ipynb
- 02.02 决策树(学生版).ipynb
- 02.02 决策树.ipynb
- 02.03 回归分析(学生版).ipynb
- 02.03 回归分析.ipynb
- 02.04 贝叶斯分析(学生版).ipynb
- 02.04 贝叶斯分析.ipynb
- 02.05 神经网络学习(学生版).ipynb
- 02.05 神经网络学习.ipynb
- _overview.md
- data_sample.png
- decision_tree_1.mp4
- essay1_ch.txt
- essay1_en.txt
- essay2_ch.txt
- essay2_en.txt
- essay3_ch.txt
- essay3_en.txt
- foo.csv
- iris.csv
- mnist.npz
- model.h5
- model.png
- nn_media1.mp4
- nn_media2.mp4
- nn_media3.mp4
- nn_media4.mp4
- nn_media5.mp4
- nn_media6.mp4
- search-Copy1.py
- search.py
- Untitled.ipynb
01.02 Python 进阶.ipynb @master — view markup · raw · history · blame
1.2 Python 进阶¶
1.2.1 列表推导式¶
循环可以用来生成列表:
a = [x for x in range(4)]
a
在循环的过程中使用 if
:
a = [x for x in range(3,10) if x % 2 == 0]
a
2 个for
循环:
a = [(x,y) for x in range(1,3) for y in range(1,3)]
a
3 个 for
循环:
a = [(x,y,z) for x in range(1,3) for y in range(1,3) for z in range(1,3)]
a
也可以使用推导式生成集合和字典:
字典推导式:
values = [10, 21, 4, 7, 12]
square_dict = {x: x**2 for x in values if x <= 10}
print(square_dict)
集合推导式:
values = [10, 21, 4, 7, 12]
square_set = {x**2 for x in values if x <= 10}
print(square_set)
def add(x, y):
"""Add two numbers"""
a = x + y
return a
函数通常有一下几个特征:
- 使用
def
关键词来定义一个函数。 def
后面是函数的名称,括号中是函数的参数,不同的参数用,
隔开,def foo():
的形式是必须要有的,参数可以为空;- 使用缩进来划分函数的内容;
docstring
用"""
包含的字符串,用来解释函数的用途,可省略;return
返回特定的值,如果省略,返回None
。
print(add(2, 3))
print(add('foo', 'bar'))
在这个例子中,如果传入的两个参数不可以相加,那么 Python 会将报错:
print(add(2, "foo"))
如果传入的参数数目与实际不符合,也会报错:
print(add(1, 2, 3))
传入参数时,Python 提供了两种选项,第一种是上面使用的按照位置传入参数,另一种则是使用关键词模式,显式地指定参数的值:
print(add(x=2, y=3))
print(add(y="foo", x="bar"))
可以混合这两种模式:
print(add(2, y=3))
设定参数默认值¶
可以在函数定义的时候给参数设定默认值,例如:
def quad(x, a=1, b=0, c=0):
return a*x**2 + b*x + c
可以省略有默认值的参数:
print(quad(2.0))
可以修改参数的默认值:
print(quad(2.0, b=3))
print(quad(2.0, 2, c=4))
这里混合了位置和指定两种参数传入方式,第二个 2 是传给 a
的。
注意,在使用混合语法时,要注意不能给同一个值赋值多次,否则会报错,例如:
print(quad(2.0, 2, a=2))
接收不定长参数¶
使用如下方法,可以使函数接受不定数目的参数:
def add(x, *args):
total = x
for arg in args:
total += arg
return total
这里,*args
表示参数数目不定,可以看成一个元组,把第一个参数后面的参数当作元组中的元素。
print(add(1, 2, 3, 4))
print(add(1, 2))
这样定义的函数不能使用关键词传入参数,要使用关键词,可以这样:
def add(x, **kwargs):
total = x
for arg, value in kwargs.items():
print("adding %s=%s"%(arg,value))
total += value
return total
这里, **kwargs
表示参数数目不定,相当于一个字典,键和值对应于键值对。
print(add(10, y=11, z=12, w=13))
再看这个例子,可以接收任意数目的位置参数和键值对参数:
def foo(*args, **kwargs):
print(args, kwargs)
foo(2, 3, x='bar', z=10)
不过要按顺序传入参数,先传入位置参数 args
,再传入关键词参数 kwargs
。
返回多个值¶
函数可以返回多个值:
def divid(a, b):
"""
除法
:param a: number 被除数
:param b: number 除数
:return: 商和余数
"""
quotient = a // b
remainder = a % b
return quotient, remainder
quotient, remainder = divid(7,4)
print(quotient, remainder)
事实上,Python 将返回的两个值变成了元组:
print(divid(7,4))
因为这个元组中有两个值,所以可以使用
quotient, remainder = divid(7,4)
给两个值赋值。
列表也有相似的功能:
a, b, c = [1, 2, 3]
print(a, b, c)
事实上,不仅仅返回值可以用元组表示,也可以将参数用元组以这种方式传入:
def divid(a, b):
"""
除法
:param a: number 被除数
:param b: number 除数
:return: 商和余数
"""
quotient = a // b
remainder = a % b
return quotient, remainder
z = (7,4)
print(divid(*z))
这里的*
必不可少。
事实上,还可以通过字典传入参数来执行函数:
def divid(a, b):
"""
除法
:param a: number 被除数
:param b: number 除数
:return: 商和余数
"""
quotient = a // b
remainder = a % b
return quotient, remainder
z = {'a':7,'b':4}
print(divid(**z))
map
方法生成序列¶
其用法为:
map(aFun, aSeq)
将函数 aFun
应用到序列 aSeq
上的每一个元素上,返回一个列表,不管这个序列原来是什么类型。
事实上,根据函数参数的多少,map
可以接受多组序列,将其对应的元素作为参数传入函数:
def divid(a, b):
"""
除法
:param a: number 被除数
:param b: number 除数
:return: 商和余数
"""
quotient = a // b
remainder = a % b
return quotient, remainder
a = (10, 6, 7)
b = [2, 5, 3]
print(list(map(divid,a,b)))
1.2.3 模块和包¶
模块¶
Python 会将所有 .py
结尾的文件认定为 Python 代码文件,考虑下面的脚本 ex1.py
:
%%writefile ex1.py
PI = 3.1416
def sum(lst):
"""
计算 lst 序列所有元素的和
:param lst: 序列 e.g. [1,2,3]
:return: lst 序列所有元素的总和
"""
# 获取 lst序列第一个元素
tot = lst[0]
# 循环遍历 lst 序列剩余元素
for value in lst[1:]:
tot = tot + value
return tot
w = [0, 1, 2, 3]
print(sum(w), PI)
可以执行它:
%run ex1.py
这个脚本可以当作一个模块,可以使用import
关键词加载并执行它(这里要求ex1.py
在当前工作目录):
import ex1
ex1
在导入时,Python 会执行一遍模块中的所有内容。
ex1.py
中所有的变量都被载入了当前环境中,不过要使用
ex1.变量名
的方法来查看或者修改这些变量:
print(ex1.PI)
ex1.PI = 3.141592653
print(ex1.PI)
还可以用
ex1.函数名
调用模块里面的函数:
print(ex1.sum([2, 3, 4]))
为了提高效率,Python 只会载入模块一次,已经载入的模块再次载入时,Python 并不会真正执行载入操作,哪怕模块的内容已经改变。
例如,这里重新导入 ex1
时,并不会执行 ex1.py
中的 print
语句:
import ex1
需要重新导入模块时,可以使用 reload
强制重新载入它,例如:
from imp import reload
reload(ex1)
删除之前生成的文件:
import os
os.remove('ex1.py')
__name__
属性¶
有时候我们想将一个 .py
文件既当作脚本,又能当作模块用,这个时候可以使用 __name__
这个属性。
只有当文件被当作脚本执行的时候, __name__
的值才会是 '__main__'
,所以我们可以:
%%writefile ex2.py
PI = 3.1416
def sum(lst):
""" Sum the values in a list
"""
tot = 0
for value in lst:
tot = tot + value
return tot
def add(x, y):
" Add two values."
a = x + y
return a
def test():
w = [0,1,2,3]
assert(sum(w) == 6)
print('test passed.')
if __name__ == '__main__':
test()
运行文件:
%run ex2.py
当作模块导入, test()
不会执行:
import ex2
但是可以使用其中的变量:
ex2.PI
引入模块时可以为它设置一个别名让使用更方便:
import ex2 as e2
e2.PI
其它导入方法¶
可以从模块中导入变量:
from ex2 import add, PI
使用 from
后,可以直接使用 add
, PI
:
add(2, 3)
或者使用 *
导入所有变量:
from ex2 import *
add(3, 4.5)
这种导入方法不是很提倡,因为如果你不确定导入的都有哪些,可能覆盖一些已有的函数。
删除文件:
import os
os.remove('ex2.py')
包¶
假设我们有这样的一个文件夹:
foo/
__init__.py
bar.py
(defines func)baz.py
(defines zap)
这意味着 foo
是一个包,我们可以这样导入其中的内容:
from foo.bar import func
from foo.baz import zap
bar
和 baz
都是 foo
文件夹下的 .py
文件。
导入包要求:
- 文件夹
foo
在 Python 的搜索路径中 __init__.py
表示foo
是一个包,它可以是个空文件。
1.2.4 异常¶
写代码的时候,出现错误不可避免,即使代码语法没有问题,也可能遇到其它问题。
看下面这段代码:
import math
while True:
text = input('> ')
if text[0] == 'q':
break
x = float(text)
y = math.log10(x)
print("log10({0}) = {1}".format(x, y))
这段代码接收命令行的输入,当输入为数字时,计算它的对数并输出,直到输入值为 q
为止。
乍看没什么问题,然而当我们输入 0 或者负数时:
import math
while True:
text = input('> ')
if text[0] == 'q':
break
x = float(text)
y = math.log10(x)
print("log10({0}) = {1}".format(x, y))
log10
函数会报错,因为不能接受非正值。
一旦报错,程序就会停止执行。如果不希望程序停止执行,并且想要捕捉异常,那么我们可以按照 try/except
语句。
import math
while True:
try:
text = input('> ')
if text[0] == 'q':
break
x = float(text)
y = math.log10(x)
print("log10({0}) = {1}".format(x, y))
except ValueError:
print("the value must be greater than 0")
一旦 try
块中的内容出现了异常,那么 try
块后面的内容会被忽略,Python 会寻找 except
里面有没有对应的内容,如果找到,就执行对应的块,没有则抛出这个异常。
在上面的例子中,try
抛出的是 ValueError
,except
中有对应的内容,所以这个异常被 except
捕捉到,程序可以继续执行:
import math
while True:
try:
text = input('> ')
if text[0] == 'q':
break
x = float(text)
y = math.log10(x)
print("log10({0}) = {1}".format(x, y))
except ValueError:
print("the value must be greater than 0")
捕捉不同的错误类型¶
import math
while True:
try:
text = input('> ')
if text[0] == 'q':
break
x = float(text)
y = 1 / math.log10(x)
print("log10({0}) = {1}".format(x, y))
except ValueError:
print("the value must be greater than 0")
假设我们将这里的 y
更改为 1 / math.log10(x)
,此时输入 1
:
import math
while True:
try:
text = input('> ')
if text[0] == 'q':
break
x = float(text)
y = 1 / math.log10(x)
print("log10({0}) = {1}".format(x, y))
except ValueError:
print("the value must be greater than 0")
因为我们的 except
里面并没有 ZeroDivisionError
,所以会抛出这个异常,我们可以通过两种方式解决这个问题。
捕捉所有异常¶
将except
的值改成 Exception
类,来捕获所有的异常。
import math
while True:
try:
text = input('> ')
if text[0] == 'q':
break
x = float(text)
y = 1 / math.log10(x)
print("1 / log10({0}) = {1}".format(x, y))
except Exception:
print("invalid value")
指定特定异常¶
这里,我们把 ZeroDivisionError
加入 except
。
import math
while True:
try:
text = input('> ')
if text[0] == 'q':
break
x = float(text)
y = 1 / math.log10(x)
print("1 / log10({0}) = {1}".format(x, y))
except (ValueError, ZeroDivisionError):
print("invalid value")
或者另加处理:
import math
while True:
try:
text = input('> ')
if text[0] == 'q':
break
x = float(text)
y = 1 / math.log10(x)
print("1 / log10({0}) = {1}".format(x, y))
except ValueError:
print("the value must be greater than 0")
except ZeroDivisionError:
print("the value must not be 1")
事实上,我们还可以将这两种方式结合起来,用 Exception
来捕捉其他的错误:
import math
while True:
try:
text = input('> ')
if text[0] == 'q':
break
x = float(text)
y = 1 / math.log10(x)
print("1 / log10({0}) = {1}".format(x, y))
except ValueError:
print("the value must be greater than 0")
except ZeroDivisionError:
print("the value must not be 1")
except Exception:
print("unexpected error")
得到异常的具体信息¶
在上面的例子中,当我们输入不能转换为浮点数的字符串时,它输出的是 the value must be greater than 0
,这并没有反映出实际情况。
为了得到异常的具体信息,我们将这个 ValueError
具体化:
import math
while True:
try:
text = input('> ')
if text[0] == 'q':
break
x = float(text)
y = 1 / math.log10(x)
print("1 / log10({0}) = {1}".format(x, y))
except ValueError as exc:
if exc.message == "math domain error":
print("the value must be greater than 0")
else:
print("could not convert '%s' to float" % text)
except ZeroDivisionError:
print("the value must not be 1")
except Exception as exc:
print("unexpected error:", exc.message)
同时,我们也将捕获的其他异常的信息显示出来。
这里,exc.message
显示的内容是异常对应的说明,例如
ValueError: could not convert string to float: a
对应的 message
是
could not convert string to float: a
当我们使用 except Exception
时,会捕获所有的 Exception
和它派生出来的子类,但不是所有的异常都是从 Exception
类派生出来的,可能会出现一些不能捕获的情况,因此,更加一般的做法是使用这样的形式:
try:
pass
except:
pass
这样不指定异常的类型会捕获所有的异常,但是这样的形式并不推荐。
try:
print(1)
except:
pass
else:
print('else was called.')
出现异常,else 不会执行。
try:
print(1/0)
except ZeroDivisionError:
print('divide by 0.')
else:
print('else was called.')
finally¶
try/except
块还有一个可选的关键词 finally
。
不管 try
块有没有异常, finally
块的内容总是会被执行,而且会在抛出异常前执行,因此可以用来作为安全保证,比如确保打开的文件被关闭。
try:
print(1)
finally:
print('finally was called.')
在抛出异常前执行:
try:
print(1 / 0)
finally:
print('finally was called.')
如果异常被捕获了,在最后执行:
try:
print(1 / 0)
except ZeroDivisionError:
print('divide by 0.')
finally:
print('finally was called.')
异常的处理流程可参考下图:
import warnings
在需要的地方,我们使用 warnings
中的 warn
函数:
warn(msg, WarningType = UserWarning)
def month_warning(m):
if not 1<= m <= 12:
msg = "month (%d) is not between 1 and 12" % m
warnings.warn(msg, RuntimeWarning)
month_warning(13)
有时候我们想要忽略特定类型的警告,可以使用 warnings
的 filterwarnings
函数:
filterwarnings(action, category)
将 action
设置为 'ignore'
便可以忽略特定类型的警告:
warnings.filterwarnings(action = 'ignore', category = RuntimeWarning)
month_warning(13)
1.2.6 文件读写¶
写入测试文件:
%%writefile test.txt
this is a test file.
hello world!
python is good!
today is a good day.
读文件¶
使用 open
函数来读文件,使用文件名的字符串作为输入参数:
f = open('test.txt')
默认以读的方式打开文件,如果文件不存在会报错:
f = open('test1.txt')
可以使用 read
方法来读入文件中的所有内容:
text = f.read()
print(text)
也可以按照行读入内容,readlines
方法返回一个列表,每个元素代表文件中每一行的内容:
f = open('test.txt')
lines = f.readlines()
print(lines)
使用完文件之后,需要将文件关闭。
f.close()
事实上,我们可以将 f
放在一个循环中,得到它每一行的内容:
f = open('test.txt')
for line in f:
print(line)
f.close()
删除刚才创建的文件:
import os
os.remove('test.txt')
写文件¶
我们使用 open
函数的写入模式来写文件:
f = open('myfile.txt', 'w')
f.write('hello world!')
f.close()
使用 w
模式时,如果文件不存在会被创建,我们可以查看是否真的写入成功:
print(open('myfile.txt').read())
如果文件已经存在, w
模式会覆盖之前写的所有内容:
f = open('myfile.txt', 'w')
f.write('another hello world!')
f.close()
print(open('myfile.txt').read())
除了写入模式,还有追加模式 a
,追加模式不会覆盖之前已经写入的内容,而是在之后继续写入:
f = open('myfile.txt', 'a')
f.write('... and more')
f.close()
print(open('myfile.txt').read())
写入结束之后一定要将文件关闭,否则可能出现内容没有完全写入文件中的情况。
还可以使用读写模式 w+
:
f = open('myfile.txt', 'w+')
f.write('hello world!')
f.seek(6)
print(f.read())
f.close()
这里 f.seek(6)
移动到文件的第6个字符处,然后 f.read()
读出剩下的内容。
删除刚才创建的文件:
import os
os.remove('myfile.txt')
关闭文件¶
在 Python 中,如果一个打开的文件不再被其他变量引用时,它会自动关闭这个文件。
所以正常情况下,如果一个文件正常被关闭了,忘记调用文件的 close
方法不会有什么问题。
关闭文件可以保证内容已经被写入文件,而不关闭可能会出现意想不到的结果:
f = open('newfile.txt','w')
f.write('hello world')
g = open('newfile.txt', 'r')
print(repr(g.read()))
虽然这里写了内容,但是在关闭之前,这个内容并没有被写入磁盘。
使用循环写入的内容也并不完整:
f = open('newfile.txt','w')
for i in range(30):
f.write('hello world: ' + str(i) + '\n')
g = open('newfile.txt', 'r')
print(g.read())
f.close()
g.close()
import os
os.remove('newfile.txt')
出现异常时候的读写:
f = open('newfile.txt','w')
for i in range(30):
x = 1.0 / (i - 10)
f.write('hello world: ' + str(i) + '\n')
查看已有内容:
g = open('newfile.txt', 'r')
print(g.read())
f.close()
g.close()
可以看到,出现异常的时候,磁盘的写入并没有完成,为此我们可以使用 try/except/finally
块来关闭文件,这里 finally
确保关闭文件,所有的写入已经完成。
f = open('newfile.txt','w')
try:
for i in range(30):
x = 1.0 / (i - 10)
f.write('hello world: ' + str(i) + '\n')
except Exception:
print("something bad happened")
finally:
f.close()
g = open('newfile.txt', 'r')
print(g.read())
g.close()
with 方法¶
事实上,Python 提供了更安全的方法,当 with
块的内容结束后,Python 会自动调用它的close
方法,确保读写的安全:
with open('newfile.txt','w') as f:
for i in range(30):
x = 1.0 / (i - 10)
f.write('hello world: ' + str(i) + '\n')
与 try/exception/finally
效果相同,但更简单。
g = open('newfile.txt', 'r')
print(g.read())
g.close()
所以,写文件时候要确保文件被正确关闭。
删除刚才创建的文件:
import os
os.remove('newfile.txt')
1.2.7 CSV 文件和 csv 模块¶
标准库中有自带的 csv
模块处理 csv
格式的文件:
import csv
读 csv 文件¶
假设我们有这样的一个文件:
%%file data.csv
"alpha 1", 100, -1.443
"beat 3", 12, -0.0934
"gamma 3a", 192, -0.6621
"delta 2a", 15, -4.515
打开这个文件,并产生一个文件 reader:
# 打开 data.csv 文件
fp = open("data.csv")
# 读取文件“”
r = csv.reader(fp)
# 可以按行迭代数据
for row in r:
print(row)
# 关闭文件
fp.close()
默认数据内容都被当作字符串处理,不过可以自己进行处理:
data = []
with open('data.csv') as fp:
r = csv.reader(fp)
for row in r:
data.append([row[0], int(row[1]), float(row[2])])
data
清除刚刚创建的文件:
import os
os.remove('data.csv')
写 csv 文件¶
可以使用 csv.writer
写入文件,不过相应地,传入的应该是以写方式打开的文件,不过一般要用 'wb'
即二进制写入方式,防止出现换行不正确的问题:
data = [('one', 1, 1.5), ('two', 2, 8.0)]
with open('out.csv', 'w') as fp:
w = csv.writer(fp)
w.writerows(data)
显示结果:
! cat 'out.csv'