前言:
包括:函数def
、数据结构、模块、python输入和输出…
函数 def
函数定义及调用
(1)定义:函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段
(2)优点:函数能提高应用的模块性,和代码的重复利用率。Python提供了许多内建函数,比如print()
。也可以自己创建函数,这被叫做用户自定义函数
(3)函数定义规则:
- 函数代码块以
def
关键词开头,后接函数标识符名称和圆括号()
- 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明
- 函数内容以冒号起始,并且缩进
return [表达式]
结束函数,选择性地返回一个值给调用方。不带表达式的return
相当于返回None
(4)语法:
1 | def 函数名(参数列表): |
例如:
1 | def area(width, height): # 计算面积函数 |
参数传递
(1)在 python 中,类型属于对象,变量是没有类型的
1 | a=[1,2,3] |
以上代码中,[1,2,3]
是List
类型,"Runoob"
是String
类型,而变量a
是没有类型,她仅仅是一个对象的引用(一个指针),可以是指向List
类型对象,也可以是指向String
类型对象
(2)可更改mutable
与不可更改immutable
对象:
不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组)
可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)
(3)函数的参数传递不可变数据(3 个):类似
c++
的值传递,如:整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。比如在fun(a)
内部修改a
的值,只是修改另一个复制的对象,不会影响a
本身1
2
3
4
5
6
7
8def ChangeInt( a ):
a = 10
return a
b = 2
# 实例中有 int 对象 2,指向它的变量是 b,在传递给ChangeInt函数时,按传值的方式复制了变量 b,a 和 b 都指向了同一个 Int 对象,在 a=10 时,则新生成一个 int 值对象 10,并让 a 指向它
print('a is :',ChangeInt(b)) # 结果是 10
print('b is :',b ) # 结果是 2可变数据(3 个):类似
c++
的引用传递,如:列表,字典。如fun(la)
,则是将la
真正的传过去,修改后fun
外部的la
也会受影响1
2
3
4
5
6
7
8
9
10def changeme( mylist2 ):
"修改传入的列表"
mylist2.append([1,2,3,4])
print ("mylist2函数内取值: ", mylist2) # mylist2函数外取值: [10, 20, 30, [1, 2, 3, 4]]
return
# 传入函数的和在末尾添加新内容的对象用的是同一个引用
mylist = [10,20,30]
changeme( mylist )
print ("mylist函数外取值: ", mylist) # mylist函数外取值: [10, 20, 30, [1, 2, 3, 4]]
参数
调用函数时可使用的正式参数类型:必需参数、关键字参数、默认参数、不定长参数
(1)必需参数:必需参数须以正确的顺序传入函数,调用时的数量必须和声明时的一样
1 | def printme( str ): |
(2)关键字参数:函数调用使用关键字参数来确定传入的参数值,使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值
1 | def printme(a, str ): |
(3)默认参数:调用函数时,如果没有传递参数,则会使用默认参数。以下实例中如果没有传入age
参数,则使用默认值
1 | def printinfo( name, age = 35 ): # # 默认参数必须在非默认参数后面,否则会报错 |
默认参数必须在非默认参数后面,否则会报错:SyntaxError: non-default argument follows default argument
(4)不定长参数:一个函数能处理比当初声明时更多的参数,这些参数叫做不定长参数。和上述 2 种参数不同,声明时不会命名
1 | def printme(arg1, *vartuple,a): # 加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数 |
加了星号
*
的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数
单独出现星号*
后的参数必须用关键字传入,如上述示例参数a
1 | def printme(arg1,**vardict): # 加了两个星号 ** 的参数会以字典的形式导入 |
匿名函数
(1)python使用lambda
来创建匿名函数,所谓匿名,意即不再使用def
语句这样标准的形式定义一个函数
- lambda 只是一个表达式,函数体比 def 简单很多
- lambda 的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去
- lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数
- 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率
(2)语法:
1 | # lambda 函数的语法只包含一个语句 |
1 | sum = lambda arg1, arg2: arg1 + arg2 |
关于函数
(1)通过函数名.__doc__
的方式来显示函数的说明文档,感觉这个如果在阅读比较大的程序时应该会有用,同时也在提示自己在写函数时注意添加文档说明
1 | def add(a,b): |
(2)函数返回值的注意事项: ,Python 函数可以返回多个值,多个值以元组的方式返回
1 | def fun(a,b): |
(3)函数内可以访问全局(global)变量,但不能更新(修改)其值
1 | a = 10 |
(4)全局变量global
和外部变量nonlocal
的差别:nonlocal
只能修改外层函数的变量而不能修改外层函数所引用的全局变量,global
关键字会跳过中间层直接将嵌套作用域内的局部变量变为全局变量
1 | x = 0 |
(5)形参和实参:
- 形式参数简称形参,是指在定义函数时,定义的一个变量名,作用是用来接收调用函数时输入的值
- 际参数简称实参,是指在调用函数时传入的实际的数据,这会被绑定到函数的形参上。函数调用时,将值绑定到变量名上,函数调用结束,解除绑定,并且实参将不再存在于程序中
1
2
3
4
5
6def foo(x, y, z): # 此处的x、y、z 就是形参
print("x=", x)
print("y=", y)
print("z=", z)
foo(1,3,5) # 此处的1,3,5是实参
(6)Python 函数装饰器:装饰器Decorators
是Python的一个重要部分。简单地说:他们是修改其他函数的功能的函数。有助于让我们的代码更简短
return语句
return [表达式]
语句用于退出函数,选择性地向调用方返回一个表达式。不带参数值的return
语句返回None
1 | def sum( arg1, arg2 ): # 返回2个参数的和." |
强制位置参数
(1)Python3.8
新增了一个函数形参语法/
用来指明函数形参必须使用指定位置参数,不能使用关键字参数的形式
1 | # 形参 a 和 b 必须使用指定位置参数,c 或 d 可以是位置形参或关键字形参,而 e 或 f 要求为关键字形参 |
参考链接:python3 函数
数据结构
python内置的数据结构包括:列表(list)、集合(set)、字典(dictionary)
- 不可变(3 个):Number(数字)、String(字符串)、Tuple(元组);
- 可变(3 个):List(列表)、Dictionary(字典)、Set(集合)
- 有序:列表list、字典dict(python3.6之后)、Tuple(元组)、Number(数字)、String(字符串)
- 无序:字典dict(python3.6之前)、集合set
列表
Python列表函数&方法
1 | # Python 列表 |
将列表当做堆栈使用
堆栈作为特定的数据结构,最先进入的元素最后一个被释放(后进先出)。列表方法使得列表可以很方便的作为一个堆栈来使用,用append()
方法可以把一个元素添加到堆栈顶,用不指定索引的pop()
方法可以把一个元素从堆栈顶释放出来
1 | stack = [3, 4, 5] |
将列表当做队列使用
把列表当做队列用,只是在队列里第一加入的元素,第一个取出来(先进先出);
但是拿列表用作这样的目的效率不高,在列表的最后添加或者弹出元素速度快,然而在列表里插入或者从头部弹出速度却不快(因为所有其他的元素都得一个一个地移动)
1 | from collections import deque |
列表推导式(列表解析式)
(1)列表推导式提供了从序列创建列表的简单途径。通常应用程序将一些操作应用于某个序列的每个元素,用其获得的结果作为生成新列表的元素,或者根据确定的判定条件创建子序列
(2)每个列表推导式都在for
之后跟一个表达式,然后有零到多个for
或if
子句。返回结果是一个根据表达从其后的for
和if
上下文环境中生成出来的列表
(3)如果希望表达式推导出一个元组,就必须使用括号()
。如果希望表达式推导出一个列表,就必须使用中括号[]
1 | # 记录序列每个元素的平方 |
嵌套列表解析
列表还可以嵌套,如以下3*4
矩阵演示:
1 | matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]] # 一个 3*4 的矩阵 |
del 语句
使用del
语句可以从一个列表中依索引而不是值来删除一个元素。这与使用pop()
返回一个值不同。可以用del
语句从列表中删除一个切割,或清空整个列表
1 | a = [-1, 1, 66.25, 333, 333, 1234.5] |
元组和序列
元组由若干逗号分隔的值组成,元组在输出时总是有括号的,以便于正确表达嵌套结构。在输入时可能有或没有括号,不过括号通常是必须的
1 | t = 12345, 54321, 'hello!' |
集合
(1)集合是一个无序不重复元素的集。基本功能包括关系测试和消除重复元素
(2)可以用大括号{}
创建集合。注意:如果要创建一个空集合,你必须用set()
而不是{}
,后者创建一个空的字典
1 | # 创建集合 |
字典
1 | # 创建字典 |
遍历技巧
(1)字典中遍历时,关键字和对应的值可以使用items()
方法同时解读出来
1 | d={x:x**2 for x in range(5)} |
(2)序列中遍历时,索引位置和对应值可以使用enumerate()
函数同时得到
1 | for i, v in enumerate(['tic', 'tac', 'toe']): |
(3)同时遍历两个或更多的序列,可以使用zip()
组合
1 | questions = ['name', 'quest', 'favorite color'] |
(4)反向遍历一个序列,首先指定这个序列,然后调用reversed()
函数
1 | for s in reversed('abcde'): |
(5)按顺序遍历一个序列,使用sorted()
函数返回一个已排序的序列,并不修改原值
1 | for s in sorted(set('axbzcmkdaahejh')): |
参考资料
模块
基本概念
(1)Python提供了一个办法,把这些定义存放在文件中,为一些脚本或者交互式的解释器实例使用,这个文件被称为模块
(2)模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py
。模块可以被别的程序引入,以使用该模块中的函数等功能。这也是使用python
标准库的方法
- 在python用
import
或者from...import
来导入相应的模块 - 将整个模块(somemodule)导入,格式为:
import somemodule
- 将多个模块导入,格式为:’import random,sys,os,math’
- 从某个模块中导入某个函数,格式为:
from somemodule import somefunction
- 从某个模块中导入多个函数,格式为:
from somemodule import firstfunc, secondfunc, thirdfunc
- 将某个模块中的全部函数(那些由单一下划线
_
开头的名字不在此例)导入,格式为:from somemodule import *
1
2
3
4
5
6import sys
print('================Python import mode==========================')
print ('命令行参数为:')
for i in sys.argv:
print (i)
print ('\n python 路径为',sys.path)
1 | from sys import argv,path # 导入特定的成员 |
import sys
引入python
标准库中的sys.py
模块;这是引入某一模块的方法sys.argv
是一个包含命令行参数的列表sys.path
包含了一个python
解释器自动查找所需模块的路径的列表
搜索路径
(1)当解释器遇到import
语句,如果模块在当前的搜索路径就会被导入
(2)搜索路径是一个解释器会先进行搜索的所有目录的列表,Python解释器就依次从这些目录中去寻找所引入的模块
(3)这看起来很像环境变量,事实上,也可以通过定义环境变量的方式来确定搜索路径
(4)搜索路径是在Python编译或安装的时候确定的,安装新的库应该也会修改,搜索路径被存储在sys模块中的path变量,可以用sys.path.append(filepath)
来修改对应的搜索路径
1 | ================python from import=================================== |
第一项是’E:\python\test’,代表当前脚本所在目录。最后一项’E:\python\study\tool’是指定添加的搜索路径
name属性
(1)一个模块被另一个程序第一次引入时,其主程序将运行。如果我们想在模块被引入时,模块中的某一程序块不执行,我们可以用__name__
属性来使该程序块仅在该模块自身运行时执行
1 | # Filename: using_name.py |
运行输出如下:
1 | $ python using_name.py |
1 | $ python |
- 每个模块都有一个
__name__
属性,当其值是’main‘时,表明该模块自身在运行,否则是被引入 __name__
与__main__
底下是双下划线,_ _
是这样去掉中间的那个空格
dir() 函数
(1)内置的函数dir()
可以找到模块内定义的所有名称。以一个字符串列表的形式返回(如果没有给定参数,那么dir()
函数会罗列出当前定义的所有名称)
1 | import fibo, sys |
包
(1)包是一种管理Python模块命名空间的形式,采用”点模块名称”,比如一个模块的名称是A.B
, 那么他表示一个包A
中的子模块B
,采用点模块名称这种形式不用担心不同库之间的模块重名的情况
(2)设计一套统一处理声音文件和数据的模块(或者称之为一个”包”)
- 现存很多种不同的音频文件格式(基本上都是通过后缀名区分的,例如:
.wav,:file:.aiff,:file:.au,
),所以你需要有一组不断增加的模块,用来在不同的格式之间转换 - 并且针对这些音频数据,还有很多不同的操作(比如混音,添加回声,增加均衡器功能,创建人造立体声效果),所以你还需要一组怎么也写不完的模块来处理这些操作。
这里给出了一种可能的包结构(在分层的文件系统中):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23sound/ 顶层包
__init__.py 初始化 sound 包
formats/ 文件格式转换子包
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ 声音效果子包
__init__.py
echo.py
surround.py
reverse.py
...
filters/ filters 子包
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
(3)在导入一个包的时候,Python会根据sys.path
中的目录来寻找这个包中包含的子目录
(4)目录只有包含一个叫做__init__.py
的文件才会被认作是一个包,主要是为了避免一些滥俗的名字(比如叫做string
)不小心的影响搜索路径中的有效模块
(5)最简单的情况,放一个空的:file:__init__.py
就可以了。当然这个文件中也可以包含一些初始化代码或者为(将在后面介绍的)__all__
变量赋值
1 | import sound.effects.echo # 导入子模块:sound.effects.echo,必须使用全名去访问 |
当使用
from package import item
这种形式的时候,对应的item
既可以是包里面的子模块(子包),或者包里面定义的其他名称,比如函数,类或者变量import
语法会首先把item
当作一个包定义的名称,如果没找到,再试图按照一个模块去导入。如果还没找到,抛出一个:exc:ImportError
异常
如果使用形如import item.subitem.subsubitem
这种导入形式,除了最后一项,都必须是包,而最后一项则可以是模块或者是包,但是不可以是类,函数或者变量的名字
从一个包中导入*
(1)如果我们使用from sound.effects import *
,Python会进入文件系统,找到这个包里面所有的子模块,一个一个的把它们都导入进来,对于一些不区分大小写的系统比较不友好(比如Windows)
(2)导入语句遵循如下规则:如果包定义文件__init__.py
存在一个叫做__all__
的列表变量,那么在使用from package import *
的时候就把这个列表中的所有名字作为包内容导入(在更新包之后需要保证__all__
也更新)
(3)通常我们并不主张使用*
这种方法来导入模块,因为这种方法经常会导致代码的可读性降低,使用from Package import specific_submodule
这种方法永远不会有错(推荐的方法)
1 | import sound.effects.echo |
输入和输出
输出格式美化
(1)Python两种输出值的方式: 表达式语句和print()
函数
(2)第三种方式是使用文件对象的write()
方法,标准输出文件可以用sys.stdout
引用
(3)可以使用str.format()
函数来格式化输出值,使输出的形式更加多样
(4)可以使用repr()
或str()
函数来实现,将输出的值转成字符串:
- str(): 函数返回一个用户易读的表达形式。
- repr(): 产生一个解释器易读的表达形式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18s='Hello World'
# str() 函数可以输出一个字符串
print('s is:',str(s)) # s is: Hello World
# repr() 函数可以转义字符串中的特殊字符
print('s is:',repr(s)) # s is: 'Hello World'
# 输出一个平方与立方的表
for x in range(1, 11):
print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ') # 字符串对象的 rjust() 方法, 它可以将字符串靠右, 并在左边填充空格,还有ljust() 和 center()
# 注意前一行 'end' 的使用
print(repr(x*x*x).rjust(4))
# 输出一个平方与立方的表
for x in range(1, 11):
print('{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x))
# zfill()会在字符串的左边填充 0
print('zfill(5) is:','12'.zfill(5)) # zfill(5) is: 00012
print('zfill(5) is:','a12'.zfill(5)) # zfill(5) is: 00a12
str.format() 的基本使用
(1)括号及其里面的字符 (称作格式化字段) 将会被format()
中的参数替换
(2)位置参数:在括号中的数字可以用于指向传入对象在format()
中的位置
(3)关键字参数:如果在format()
中使用了关键字参数, 那么它们的值会指向使用该名字的参数
1 | print('浏览器:{} 和 {}'.format('Google','Baidu')) # 浏览器:Google 和 Baidu |
(4)!a
(使用ascii()
),!s
(使用str()
)和!r
(使用repr()
) 可以用于在格式化某个值之前对其进行转化
1 | import math |
(5)如果你有一个很长的格式化字符串, 而你不想将它们分开, 那么在格式化时通过变量名而非位置会是很好的事情
a.最简单的就是传入一个字典, 然后使用方括号[]
来访问键值
b.也可以通过在table
变量前使用**
来实现相同的功能
1 | table = {'Google': 1, 'Runoob': 2, 'Taobao': 3} |
旧式字符串格式化
(1)%
操作符也可以实现字符串格式化。它将左边的参数作为类似sprintf()
式的格式化字符串, 而将右边的代入, 然后返回格式化后的字符串
1 | import math |
读取键盘输入
(1)Python提供了input()
内置函数从标准输入读入一行文本,默认的标准输入是键盘,input
可以接收一个Python表达式作为输入,并将运算结果返回
1 | inputStr=input('请输入:') |
读和写文件
(1)open()
将会返回一个file
对象,基本语法格式如下
1 | open(filename, mode) |
模式 | 描述 |
---|---|
r | 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。 |
rb | 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。 |
r+ | 打开一个文件用于读写。文件指针将会放在文件的开头。 |
rb+ | 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。 |
w | 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb | 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
w+ | 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb+ | 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
a | 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
ab | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
a+ | 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。 |
ab+ | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。 |
下图很好的总结了这几种模式:
模式 | r | r+ | w | w+ | a | a+ |
---|---|---|---|---|---|---|
读 | + | + | + | + | ||
写 | + | + | + | + | + | |
覆盖 | + | + | ||||
指针在开始 | + | + | + | + | ||
指针在结尾 | + | + |
1 | # 打开一个文件 |
执行上述脚本后,writefile.txt文件的内容为:
1 | 这是第一行 |
文件对象的方法
(1)f.read():
为了读取一个文件的内容,调用f.read(size)
, 这将读取一定数目的数据, 然后作为字符串或字节对象返回size
是一个可选的数字类型的参数。 当size
被忽略了或者为负, 那么该文件的所有内容都将被读取并且返回
(2)f.readline():f.readline()
会从文件中读取单独的一行。换行符为\n
f.readline()
如果返回一个空字符串, 说明已经已经读取到最后一行
(3)f.readlines():f.readlines()
将返回该文件中包含的所有行
如果设置可选参数sizehint
, 则读取指定长度的字节, 并且将这些字节按行分割
(4)f.write():f.write(string)
将string
写入到文件中, 然后返回写入的字符数;
如果要写入一些不是字符串的东西, 那么将需要先进行转换,str()
方法等
(5)f.tell():f.tell()
返回文件对象当前所处的位置, 它是从文件开头开始算起的字节数
(6)f.seek():
如果要改变文件当前的位置, 可以使用f.seek(offset, from_what)
函数from_what
的值, 如果是0
表示开头, 如果是1
表示当前位置,2
表示文件的结尾(默认值为0)
- seek(x,0) : 从起始位置即文件首行首字符开始移动 x 个字符
- seek(x,1) : 表示从当前位置往后移动x个字符
- seek(-x,2):表示从文件的结尾往前移动x个字符
(7)f.close():
在文本文件中 (那些打开文件的模式下没有b
的), 只会相对于文件起始位置进行定位。
当你处理完一个文件后, 调用f.close()
来关闭文件并释放系统的资源,如果尝试再调用该文件,则会抛出异常
1 | # 打开一个文件 |
pickle 模块
(1)python的pickle
模块实现了基本的数据序列化和反序列化,通过pickle
的序列化操作可以很好的将程序中运行的 对象信息保存到文件中去,永久存储;通过pickle
的反序列化操作可以从文件中创建上一次程序保存的对象
将数据对象转换为二进制流的过程称为对象的序列化(
Serialization
)
(2)基本接口
1 | # 序列化对象:将对象obj保存到文件file中去 |
1 | import pprint,pickle |
(3)示例:通过 pickle 序列化实现一个简单联系人信息管理
1 | import pickle |