python语言程序设计入门笔记

python语言程序设计

关舒文

第一章 程序设计方法

程序设计语言

程序设计语言包含 3 大类:

  • 机器语言(一种二进制语言)
  • 汇编语言
  • 高级语言(包含C,C++,C#,Go等)

其中机器语言和汇编语言统称为低级语言

编译和解释

高级语言按照计算机执行方式不同可分为:

  • 静态语言(C,C++,Java)
  • 脚本语言(JavaScript,PHP,python)

静态语言采用编译运行(编译器执行编译),脚本语言采用解释(解释器直接解释)执行.

编译的好处

  1. 对于相同源代码,目标代码执行速度更快;
  2. 目标代码不需要解释器即可运行,在同类型操作系统上可灵活使用.

解释的好处

  1. 只需要保留原代码,程序纠错和维护十分方便;
  2. 只需要存在解释器,源代码可以在任何平台运行,可移植性较好.

第二章 python程序基本语法

程序的框架

python使用缩进来表明程序的格式框架

注释

使用#来表示单行注释,'''表示多行注释:

1
2
3
4
5
6
7
8
print('hello world') #单行注释

'''
这是一个多行注释
'''

def hello():
'''函数注释常用多行注释'''

命名与保留字

python语言允许使用大写字母,小写字母,数字,下划线,汉字等字符或字符组合给变量命名.不允许将数字用作名称首字符.变量命名区分大小写.特别的,变量命名不能与保留字相同(注意大小写):

and as assert break class continue
def del elif else except finally
for from False global if import
in is lambda nonlocal not None
or pass raise return try True
while with yield `

字符串索引与切片

索引操作

双引号"单引号'表示字符串,例如字符串"Hello"'world'.对字符串

1
>>> Tempstr = "Hello world"

的第一个字符'H'有两种索引方式:
1
2
Temstr[0] = "H" #正向索引,第一个字符为0
Temstr[-11] = "H" #反向索引最后一个字符为-1

切片操作

我们可以通过切片操作来实现字符串的区间访问或列表的区间访问:

1
object[start_index : end_index : step] #object可为字符串或列表

start_index: 表示起始索引(包含该索引本身);该参数省略时,表示从对象”端点”开始取值,至于是从“起点”还是从”终点”开始,则由step参数的正负决定,step为正从”起点”开始,为负从”终点”开始.

end_index: 表示终止索引(不包含该索引本身);该参数省略时, 表示一直取到数据”端点”, 至于是到”起点”还是到”终点”, 同样由step参数的正负决定, step为正时直到”终点”, 为负时直到”起点”.

step: 正负数均可,其绝对值大小决定了切取数据时的”步长”,而正负号决定了”切取方向”,正表示”从左往右”取值,负表示”从右往左”取值.当step省略时,默认为1,即从左往右以增量1取值.

切片操作例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> Tempstr = "hello"
>>> Templist = [1, 2, 3, 4, 5, 6]
# 正向切片
>>> Tempstr[1::3] = "eo"
>>> Templist[-5:5:2] = [2,4]
# 反向切片
>>> Tempstr[::-1] = "olleh"
# 矛盾切片
>>> a[-1:6]= []
# start_index=-1在end_index=6的右边, 因此从右往左取值, 但step=1则决定了从左往右取值, 两者矛盾.
# 正负索引混合切片
>>> a[-1:6:-1] = [9, 8, 7]
# start_index=-1在end_index=6的右边, 因此从右往左取值, 而step=-1同样决定了从右往左取值.

赋值语句

python语言中,=用于赋值,即将等号右侧的计算结果赋值给左侧变量.例如:

1
i = 0 #将0赋给变量 i

同步赋值语句:
1
<变量 1>,...,<变量 N> = <表达式 1>,...,<表达式 N>

我们可以通过同步赋值语句来实现两个变量的交换:
1
x, y = y, x

第三章 基本数据类型

数字类型

整数

默认情况下使用的是十进制,四种进制的表示如下表:

进制类型 引导符号 描述 例子
十进制 Decimal 数字0-9组成 12345, 7829
二进制 Binary 0b 或 0B 数字0-1组成 0b101, 0B110
八进制 Octal 0o 或 0O 数字0-7组成 0o711, 0O127
十六进制 Hexadecimal 0x 或 0X 数字0-9,字母a-f(A-F)组成 0xABC

浮点数

浮点数表示带有小数的数值,python要求浮点数必须带有小数部分以便区分浮点数和整数,有两种表示方法:

  • 十进制表示法: 1999.0, 0.0, -77.;
  • 科学计数法: 4.3e-3, 9.6e5.
    浮点数的特点
    对于科学计数法 $a\textrm{E}\pm b = a\times10^b$,在python中系数$a$最长可输出16个数字;十进制表示的浮点数最长可输出17个数字.

    根据sys.float_info的结果计算机能够提供15个数字的准确性,最后一位存在误差.

复数

python语言中,复数的虚数部分通过后缀”J”或”j”表示,复数表示采用以下形式:

1
z = (a+bj) # a为实部,b为虚部

需要特别注意,复数类型中的实数部分和虚数部分都是浮点类型.
可以使用real操作获取复数实部,imag操作获取虚部:
1
2
3
4
>>> (1.23e-4+5j).real
0.000123
>>> (1.23e-4+5j).imag
5.0

数字类型操作

内置数值运算操作符

操作符 描述 对应增强赋值操作符
x - y x与y之差 -=
x * y x与y之积 *=
x + y x与y之和 +=
x / y x与y之商 /=
x // y x整除y //=
x % y x除以y的余数 %=
x ** y x的y次幂,即$x^y$ **=

增强赋值操作符可简化代码表达:

x op = y 等价于x = x op y,其中op为增强复制操作符

函数 描述 输出
abs(x) x的绝对值,若x为复数则返回模 数值
divmod(x, y) 返回包含整除,求余结果的二元组形式(也称为元组类型) (x//y,x%y)
pow(x, y[, z]) (X*y)%z,即 pow(x,y),它与x\*y相同 数值
round(x[, ndigits]) 对x四舍五入,保留 ndigits 位小数.round(x)返间四舍五入的整数 数值
max(x1,x2,…,xn) 值x1,x2,…,xn的最大值,n没有限定co 数值
min(x1,x2,…,xn) 值x1,x2,…,xn的最大值,n没有限定 数值

特别的,pow()函数的第三个参数在加解密算法中十分重要,见如下对比:

1
2
3
4
>>> pow(3, pow(3, 999), 10000) # 幂运算和模运算同时进行,速度较快
4587
>>> pow(3, pow(3, 999))%10000 # 计算机较难运行,两种表述算术意义上等价
4587

内置数字类型转换

函数 合法输入 输出
int(x) 浮点数,整数,字符串 整数
float(x) 浮点数,整数,字符串 浮点数
complex(实部[,虚部]) 实部:整数,浮点数,字符串;虚部:整数,浮点数(不允许字符串) 复数

int()会对浮点数进行截尾,不进行四舍五入操作;

int(),float()不允许输入复数.

math

常用数学常数:

常数 数学表示 描述
math.pi $\pi$ 圆周率
math.e $\textrm{e}$ 自然对数
math.inf $\infty$ 正无穷大
math.nan 非浮点数标记,NaN

数值函数:

函数 数学表示 描述
math.fabs(x) $\mid x \mid$ x的绝对值
math.fmod(x,y) $x$ % $y$ x求余y
math.fsum([x,y,…]) $x+y+\cdots$ 浮点数精确求和
math.ceil(x) $\lceil x \rceil$ 向上取整
math.floor(x) $\lfloor x \rfloor$ 高斯取整
math.factorial(x) $x!$ x的阶乘
math.gcd(a,b) a,b的公约数
math.isfinite(x) 当x不是无穷大或NaN时为真
math.isnan(x) 当x是NaN时为真

幂对数函数:

函数 数学表示 描述
math.pow(x,y) $x^y$ x的y次幂
math.exp() $\mathrm{e}^x$ e的x次幂
math.sqrt() $\sqrt{x}$ x的平方根
math.log(a[,b]) $\log_ba$ a以b为底的对数,b省略时为$\ln$x

三角运算:

函数 数学表示 描述
math.degrees(x) 弧度转角度
math.radians(x) 角度转弧度
math.hypot(x,y) $\sqrt{x^2+y^2}$ 坐标(x,y)离原点距离

字符串类型和基本操作

字符串的表示

  • 单引号表示 'hello'
  • 双引号表示 "hello"
  • 三引号表示 '''hello'''
  1. 单引号或双引号都可以作为字符串的一部分,例如
    1
    'you can print "hello" by "print("hello")"' # 反正就是可以套娃啦
  2. 三引号可用于表示多行字符串,也可以用于表示多行注释(必须以'''开头)
    1
    2
    3
    4
    5
    6
    7
    >>> print('''
    hello
    world
    ''')
    # 注意:此处为空行, '''后的第一个字符为换行符"\n"
    hello
    world

    字符串的输入

    输入语法:
    1
    <变量1> = input(<提示字符串>)
    例如:
    1
    2
    >>> name = input("请输入您的名字 ")
    请输入您的名字 | # 将会打印提示并产生一个光标接受输入

    字符串操作

    基本字符串操作符:
操作符 描述
x + y 连接两个字符串x与y
x * n 或 n * x 复制n次字符串x
x in s 如果x是s的子串, 返回True
str[i] 否则返回False 索引, 返回第t个字符
str[N:M] 切片, 返回索引第N到第M的子串, 其中不包含M

特殊格式化控制字符:

操作符符 作用
\a 蜂呜, 响铃.
\b 回退, 向后退一格.
\f 换页.
\n 换行, 光标移动到下行首行.
\r 回车, 光标移动到本行首行.
\t 水平制表.
\v 垂直制表.
\0 NULL,什么都不做.

内置字符串处理函数

函数 描述
len(x) 返回字符串 x 的长度, 也可返回其他组合数据类型元素个数
str(x) 返回任意类型 x 所对应的字符串形式
chr(x) 返回Ascii编码x 对应的单字符
ord(x) 返回单字符表示的Asciii编码
hex(x) 返回整数 x 对应十六进制数的小写形式字符串
oct(x) 返回整数 x 对应八进制数的小写形式字符串

ASCII码表中: 大写字母A~Z对应65~90,小写字母a~z对应97~122

内置字符串处理方法

↓—-编者水平有限,该段内容存在问题,有待研究,请跳过—-
python中对数据的处理可分为函数与方法.函数常以<库>.<函数>(待处理对象)的形式实现

1
2
>>> math.floor(23.4)
23

方法常为面向对象的,其通常以<对象>.<方法>(方法选项)
1
2
>>> "Hello world".center(20,'😀')
'😀😀😀😀Hello world😀😀😀😀😀'

当然,方法与函数都可看作为函数,只不过他们的定义方式有所不同.
↑—-编者水平有限,该段内容存在问题,有待研究,请跳过—-

常用的内置字符串处理方法(str表示字符串变量)

方法 描述
str.lower() 返回字符串str的副本, 全部字符小写
str.upper() 返回字符串str的副本, 全部字符大写
str.islower() 当str所有字符都是小写时为真
str.isprintable() 当str所有字符都是可打印的时候为真
str.isnumeric() 当str所有字符都是数字的时候为真
str.isspace() 当str所有字符都是空格,返回True, 否则返回False
str.endswith(suffix [,start[,end]]) str[start: end]以suffix结尾返回True, 否则返回False
str.startswith(prefix[,start[,end]]) str[start: end]以prefix开始返回True, 否则返回False
str.split(sep=None, maxsplit=-1) 返回一个列表,由str根据sep被分隔的部分构成
str.count(sub[,start[,end]]) 返回str[start: end]中sub子串出现的次数
str.replace(old, new[, count]) 返回字符串str的副本, 所有old子串被替换为new, 如果count给出, 则前count次old出现被替换
str.center(width[,fillchar]) 字符串居中函数,返回长度为width得字符串,str位于中间位置,以fillchar填充两边
str.strip([chars]) 返回字符串str的副木, 在其左侧和右侧去掉chars中列出的字符
str.zfill(widlh) 返回字符串str的副本, 长度为width,不足部分在左侧补0,如果左侧为”+”,”-“则从第二个字符开始补0
str.format() 返回字符串str的一种排版格式
str.join(iterable) 返回一个新字符串, 由组合数据类型terable变址的每个元素组成, 元素间用str分隔

例子:

1
2
3
4
5
6
7
8
9
10
11
>>> "Python is an excellent language.".split()
['Python','is','an','excellent','language'.]

>>> "PYTHON".center(40,'=')
'=================PYTHON================='

>>> "123".zfill(40)
'0000000000000000000000000000000000000123'

>>> "-123".zfill(40)
'-000000000000000000000000000000000000123'

字符串的format()格式化

format()方法的使用

基本格式:

1
<模板字符串>.format(<以逗号分割的参数(字符串或数值表达式)>)

该方法的基本思想是将format()方法中用逗号分隔的参数按照序号关系替换到模板字符串的槽中,槽用大括号{}表示.大括号中可以指定使用参数的序号,按照序号替换.注意format()中的序号从0开始.
实例:
1
2
>>> "{1} {0}".format("hello", "world")
"world hello"

槽的格式控制

我们可以在槽中输入各种控制字符以进行字符格式化:

1
{ <参数序号> : <填充><对齐><宽度><千分位分隔符><浮点精度或字符串最大长度><数值类型>}

示例:
1
2
3
4
5
>>> "{:😀^20}".format("hello world")
'😀😀😀😀hello world😀😀😀😀😀'

>>> "{0:😀^20,.3f}".format(12345.67890)
' 😀😀😀😀😀12,345.679😀😀😀😀😀'

  • 引导符号 ::来引导后续格式控制标记
  • 填充 指定宽度(必须指定对齐方式)内除了参数外的字符采用什么方式表示, 默认采用空格,可以通过填充替换,
  • 对齐 <左对齐,>右对齐,^居中对齐
  • 宽度 指当前槽的设定输出字符宽度, 如果该槽对应的format()参数长度比<宽度>设定值大, 则使用参数实际长度;如果该值的实际位数小于指定宽度, 则位数将以填充字符串进行补充.
  • 千分位分隔符 以逗号作为千分位分割符(输入,)
  • 精度.开头后加数字.对于浮点数,表示输出的有效位数,对于字符串表示最大输出长度(超出部分被截尾)
  • 类型
    • 对于整数类型:
      • b: 输出整数的二进制方式.
      • C: 输出整数对应的Unicode字符.
      • d: 输出整数的十进制方式.
      • 0: 输出整数的八进制方式.
      • X: 输出整数的小写十六进制方式.
      • X: 输出整数的大写十六进制方式.
    • 对于浮点数类型:
      • e: 输出浮点数对应的小写字母e的指数形式.
      • E: 输出浮点数对应的大写字母E的指数形式.
      • f: 输出浮点数的标准浮点形式.
      • %: 输出浮点数的百分形式.

        第四章 程序的控制结构

        if条件语句

        1
        2
        3
        4
        5
        6
        if <condition_1>: # 注意添加":"
        <statement_block_1>
        elif <condition_2>: # 注意添加":"
        <statement_block_2>
        else: # 注意添加":"
        <statement_block_3>
  1. 如果 <condition_1> 为 True 将执行 <statement_block_1> 块语句
  2. 如果 <condition_1> 为False, 将判断 <condition_2>
  3. 如果<condition_2> 为 True 将执行 <statement_block_2>块语句
  4. 如果 <condition_2> 为False, 将执行<statement_block_3>块语句
    if常用的操作符:
操作符 描述
< 小于
<= 小于或等于
> 大于
>= 大于或等于
== 等于, 比较两个值是否相等
!= 不等于

循环语句

for循环
1
2
3
4
for <variable> in <sequence>:
<statements>
else:
<additional_statements>

当for循环正常执行之后, 程序会继续执行else语句中的内容,若循环非正常退出则不执行.

while循环
1
2
3
4
while <expr>:
<statement>
else:
<additional_statement>

当while循环正常退出(此时<expr>的值为False)后,将会执行else语句中的内容.

特别的,我们可以通过创造永真的条件<expr>来实现无限循环,示例:

1
2
3
4
5
6
7
8
9
10
11
# 制作一个不断更新的时钟
import time

while True: # 条件永远为真,循环一直进行,直至人为中断
time.sleep(1) # 时间等待一秒(更新间隔为1s)
print('\r', # 刷新输入(原理类似于进度条)
time.strftime('%Y-%m-%d %H:%M:%S',
time.localtime(time.time())
), # 获取时间并格式化
end=''
)

循环保留字: break, continue

循环结构有两个保留字: breakcontinue, 它们用来辅助控制循环执行. 具体作用如下:

  • break 语句可以跳出 for 和 while 的循环体. 如果你从 for 或 while 循环中终止, 任何对应的循环 else 块将不执行.
  • continue 语句被用来告诉 Python 跳过当前循环块中的剩余语句, 然后继续进行下一轮循环.
    两个保留字在循环中的区别
    函数: range()
    在for循环中可用range()指定循环遍历的范围.用法:
    1
    range([起始数值, ]结束数值[, 步长])
    以下 n,a,b,x 均为整数
  • range(n)(仅指定结束数值)表示取整数${1,2,3,\cdots,n-1}$
  • range(a, b)(指定起始数值)表示取整数${a,a+1,\cdots,b-1}$
  • range(a, b, x)表示取整数${a,a+x,\cdots,a+kx}$(其中$a+kx\leq b$)

random库的使用

函数 描述
seed(a=None) 初始化随机数种子, 默认值为当前系统时间
random() 生成一个[0.0,1.0)之间的随机小数
randint(a, b) 生成一个[a,b]之间的整数
getrandbits(k) 生成一个K比特长度的随机整数
randrange(start, stop[, step]) 生成一个[start, stop)之间以step为步数的随机整数
uniform(a, b) 生成一个[a, b]之间的随机小数
choice(seq) 从序列类型, 例如列表中随机返回-个元素
shuffle(seq) 将序列类型中的元素随机排列, 返回打乱后的序列
sample(pop, k) 从pop类型中随机选取k个元素,以列表类型返回

生成随机数之前可以通过seed()函数指定随机数种子,随机数种子一般是一个整数, 只要种子相同, 每次生成的随机序列也相同. 这种情况便于测试和同步数据

异常处理

1
2
3
4
5
6
7
8
9
10
11
12
try:
<执行代码>
except <异常1> as <异常变量1>:
<发生错误1时执行的代码>
except <异常2> as <异常变量2>:
<发生异常2时执行的代码>
except:
<发生其他异常时执行的代码>
else:
<没有异常时执行的代码>
finally:
<无论是否发生异常均执行的代码>

可以使用except...as..:语句实现将错误信息返回至一个变量的效果

1
2
3
4
5
6
7
>>> try:
a = []
print(a[1])
except Exception as err:
print(err)

'list index out of range' # 索引超出范围

可以实现将所有错误信息返回至err并打印.
以下是常见的错误代码:

异常代码 异常原因
AttributeError 属性错误, 特性引用和赋值失败时会引发属性错误
NameError 试图访问的变量名不存在
SyntaxError 语法错误, 代码形式错误
Exception 所有异常的基类, 因为所有python异常类都是基类Exception的其中一员, 异常都是从基类Exception继承的, 并且都在exceptions模块中定义.
IOError 一般常见于打开不存在文件时会引发IOError错误, 也可以解理为输出输入错误
KeyError 使用了映射中不存在的关键字(键)时引发的关键字错误
IndexError 索引错误, 使用的索引不存在, 常索引超出序列范围, 什么是索引
TypeError 类型错误, 内建操作或是函数应于在了错误类型的对象时会引发类型错误
ZeroDivisonError 除数为0, 在用除法操作时, 第二个参数为0时引发了该错误
ValueError 值错误, 传给对象的参数类型不正确, 像是给int()函数传入了字符串数据类型的参数.
异常抛出

使用raise保留字来抛出异常,常用于try语句中自定义返回错误:

1
raise [Exception [, args [, traceback]]]

示例:
1
2
3
4
5
6
7
8
9
>>> raise Exception('出错了啦つ﹏⊂,快去debug')
# 错误提示↓
----------------------------------------
Exception
Traceback (most recent call last)
<ipython-input-76-c07bf7fa8af7> in <module>
----> 1 raise Exception('出错了啦つ﹏⊂,快去debug')

Exception: 出错了啦つ﹏⊂,快去debug

也可以结合try语句使用
1
2
3
4
5
6
>>> try:
raise Exception('咦?`(*>﹏<*)′')
except Exception as err:
print(err)

咦?`(*>﹏<*)′

第五章 函数与代码复用

定义函数

Python 定义函数使用 def 保留字, 一般格式如下:

1
2
3
4
def 函数名(参数列表):
'''函数帮助信息'''
函数体
return <变量>

注意:

  1. 函数代码块以 def 关键词开头, 后接函数标识符名称和圆括号 ().
    1. 任何传入参数和自变量必须放在圆括号中间, 圆括号之间可以用于定义参数.
    2. 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明.
    3. 函数内容以冒号起始, 并且缩进.
    4. return [表达式]结束函数, 选择性地返回一个值给调用方. 不带表达式的return相当于返回 None.

调用函数

函数的调用过程

  1. 调用程序在调用处暂停执行.
  2. 在调用时将实参复制给函数的形参.
  3. 执行函数体语旬.
  4. 函数调用结束给出返回值, 程序回到调用前的暂停处继续执行.

函数的调用方式

1
<函数名>(参数)

例如:

1
2
3
4
5
>>> def pra(age):
print(age)

pra(13)
13

参数传递

在 python 中, 类型属于对象, 变量是没有类型的:

1
2
a = [1,2,3]
a = "hello"

以上代码中, [1,2,3] 是 List 类型, “hello” 是 String 类型, 而变量 a 是没有类型, 她仅仅是一个对象的引用(一个指针), 可以是指向 List 类型对象, 也可以是指向 String 类型对象.

可更改(mutable)与不可更改(immutable)对象

在 python 中, strings, tuples, 和 numbers 是不可更改的对象, 而 list,dict 等则是可以修改的对象.

  • 不可变类型: 变量赋值 a=5 后再赋值 a=10, 这里实际是新生成一个 int 值对象 10, 再让 a 指向它, 而 5 被丢弃, 不是改变 a 的值, 相当于新生成了 a.

  • 可变类型: 变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改, 本身la没有动, 只是其内部的一部分值被修改了.

python 函数的参数传递:

  • 不可变类型: 类似 C++ 的值传递, 如 整数、字符串、元组. 如 fun(a), 传递的只是 a 的值, 没有影响 a 对象本身. 如果在 fun(a))内部修改 a 的值, 则是新生成来一个 a. 示例(id()函数可显示内存地址):
    1
    2
    3
    4
    5
    6
    7
    8
    def change(a):
    print(id(a)) # 指向的是同一个对象
    a=10
    print(id(a)) # 一个新对象

    a=1
    print(id(a))
    change(a)
    返回:
    1
    2
    3
    94127348335360
    94127348335360
    94127348335648
    可以看见在调用函数前后, 形参和实参指向的是同一个对象(对象 id 相同), 在函数内部修改形参后, 形参指向的是不同的 id.
  • 可变类型: 类似 C++ 的引用传递, 如 列表, 字典. 如 fun(la), 则是将 la 真正的传过去, 修改后 fun 外部的 la 也会受影响.示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def changeme( mylist ):
    "修改传入的列表"
    mylist.append([1,2,3,4])
    print ("函数内取值: ", mylist)
    return

    # 调用changeme函数
    mylist = [10,20,30]
    changeme( mylist )
    print ("函数外取值: ", mylist)
    返回:
    1
    2
    函数内取值:  [10, 20, 30, [1, 2, 3, 4]]
    函数外取值: [10, 20, 30, [1, 2, 3, 4]]
    传入函数的和在末尾添加新内容的对象用的是同一个引用. 故输出结果相同.

鉴于传递的特征,我们可以使用global <变量>声明来将局部变量的改变应用到全局变量中.

参数

以下是调用函数时可使用的正式参数类型:

  • 必需参数
  • 关键字参数
  • 默认参数
  • 不定长参数
    必需参数
    1
    2
    3
    4
    def func(a):
    print(a)

    func('hello')
    该函数中参数a已被调用,故使用该函数时必须输入参数a,这种参数称为必须参数.必需参数须以正确的顺序传入函数. 调用时的数量必须和声明时的一样.
    关键字参数
    关键字参数和函数调用关系紧密, 函数调用使用关键字参数来确定传入的参数值. 使用关键字参数允许函数调用时参数的顺序与声明时不一致, 因为 Python 解释器能够用参数名匹配参数值. 使用方法:
    1
    2
    3
    4
    def func(a):
    print(a)

    func(a='hello')
    默认参数(可选参数)
    调用函数时, 如果没有传递参数, 则会使用默认参数. 可以通过指定参数的默认值,来避免没有该参数输入时发生错误.
    1
    2
    3
    4
    5
    6
    def func(a, b=10):
    print(a+b)
    # 传入b的值
    func(1,2)
    # 不传入b,b使用默认值
    func(1)
    返回:
    1
    2
    3
    11
    不定长参数(可变数量参数)
    当我们需要一个函数能处理比当初声明时更多的参数. 这些参数叫做不定长参数.
  • 带一个*的不定长参数: 通过在最后一个参数前增加*实现.
    特点:

    1. 不定长参数一定为最后一个参数
    2. 该参数将会以元组的形式传入函数
    3. 如果在函数调用时没有指定参数, 它就是一个空元组. 我们也可以不向函数传递未命名的变量

      示例:

      1
      2
      3
      4
      5
      6
      7
      def mean(a,*b):
      '求平均值'
      for i in b:
      a+=i
      print(a/(len(b)+1))
      mean(1)
      mean(1,2,3,4))

      返回:

      1
      2
      1.0
      2.5
  • 带两个*的不定长参数: 通过在最后一个参数前增加**实现.
    区别:
    1. 该参数将会以字典的形式传入函数
      1
      2
      3
      4
      5
      6
      7
      8
      def printinfo( arg1, **vardict ):
      "打印任何传入的参数"
      print ("输出: ")
      print (arg1)
      print (vardict)

      # 调用printinfo 函数
      printinfo(1, a=2,b=3)
      返回:
      1
      2
      1
      {'a': 2, 'b': 3}
  • *单独出现的不定长参数 声明函数时, 参数中星号*可以单独出现,如果单独出现星号 ,` `后的参数必须用关键字参数方式传入*. 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> def f(a,b,*,c):
    return a+b+c

    >>> f(1,2,3) # 报错
    ------------------------------------------
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: f() takes 2 positional arguments but 3 were given

    >>> f(1,2,c=3) # 正常
    6

    匿名函数

    定义方法:
    1
    <函数名> = lambda <参数列表>: <表达式>

特点:

  1. lambda 只是一个表达式, 函数体比 def 简单很多.
  2. lambda的主体是一个表达式, 而不是一个代码块. 仅仅能在lambda表达式中封装有限的逻辑进去.
  3. lambda 函数拥有自己的命名空间, 且不能访问自己参数列表之外或全局命名空间里的参数.

代码复用与模块化设计

模块化设计指通过函数或对象的封装功能将程序划分成主程序、子程序和子程序间关系的表达. 模块化设计是使用函数和对象设计程序的思考方法, 以功能块为基本单位, 一般有以下两个基本要求.

  1. 紧耦合: 尽可能合理划分功能块, 功能块内部耦合紧密.
  2. 松耦合: 模块间关系尽可能简单, 功能块之间耦合度低.

    使用函数只是模块化设计的必要不充分条件, 根据计算需求合理划分函数十分重要. 一般来说, 完成特定功能或被经常复用的一组语句应该采用函数来封装, 并尽可能减少函数间参数和返回值的数量.

函数的递归

我们看看数学中的递归:

$x=0$称为递归的基例,我们可以用python函数实现以上的思想:

例1: 计算阶乘
数学表述:

python实现:

1
2
3
4
5
6
7
def fact(n):
if n == 0:
return 1 # 基例
else:
return n*fact(n-1)

print(fact(10))

输出:
1
3628800

例2: 汉诺塔问题
有三个立柱A、B、C. A柱上穿有大小不等的圆盘N个, 较大的圆盘在下, 较小的圆盘在上. 要求把A柱上的圆盘全部移到C柱上, 保持大盘在下、小盘在上的规律(可借助B柱). 每次移动只能把一个柱子最上面的圆盘移到另一个柱子的最上面. 请输出移动过程.
递归逻辑:
A, B, C三个圆柱, 分别为初始位, 过渡位, 目标位, 设A柱为初始位, C位为最终目标位

  1. 将最上面的n-1个圆盘从初始位移动到过渡位
  2. 将初始位的最底下的一个圆盘移动到目标位
  3. 将过渡位的n-1个圆盘移动到目标位
    python实现:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def move(n,a,b,c):   #n为圆盘数, a代表初始位圆柱, b代表过渡位圆柱, c代表目标位圆柱
    if n==1:
    print(a,'-->',c)
    else:
    move(n-1,a,c,b)
    '''将初始位的n-1个圆盘移动到过渡位, 此时初始位为a, 上一级函数的过渡位b即为
    本级的目标位, 上级的目标位c为本级的过渡位'''
    print(a,'-->',c)

    move(n-1,b,a,c)
    '''将过渡位的n-1个圆盘移动到目标位, 此时初始位为b, 上一级函数的目标位c即为
    本级的目标位, 上级的初始位a为本级的过渡位'''

递归特点

  1. 递归必须设计基例
  2. 递归深度不应超出1000层,若有需要可通过以下代码更改最大层数.
    1
    2
    import sys
    sys.setrecursionlimit(<新的层数限制>)

第六章 组合数据类型

序列类型

组合数据类型
序列类型特点:

  1. 所有序列类型都可以使用相同的索引体系
  2. 所有序列类型均支持成员关系操作符in,长度计算函数len(),切片[M:N:step]
  3. 元素自身亦可以为序列类型

序列类型的通用操作符或函数:

操作符 描述
x in s 如果x是s的元素,返回True,否则返回False
x not in s 如果x不足s的元素, 返回True, 否则返同False
s+t 连接s和t
s n 或 n s 将序列s复制n次
s[i] 索引,返回序列的第i个元素
s[i: j] 分片,返回包含序列s第i到j个元素的子序列(不包含第j个元素)
s[i: j: k] 步骤切片, 返回包含序列s第i到j个元素以k为步数的子序列
len(s) 序列s的元素个数(长度)
min(s) 序列s中的最小元素
max(s) 序列s中的最大元素
s.index(x[, i[, j]]) 序列s中从1开始到j位置中第一次出现元素x的位置
s.count(x) 序列s中出现x的总次数

元组

元组采用逗号,和圆括号(非必须)()来表示:

1
2
3
>>> emotion = ('😀','😟','😔')
print(type(emotion))
'tuple'

注意: 元组一旦被创建就不能修改

列表

列表具有可修改的特性故除了序列操作,还有以下操作:

方法 描述
list.append(x) 把一个元素添加到列表的结尾, 相当于 a[len(a):] = [x].
list.extend(L) 或 list += L 通过添加指定列表的所有元素来扩充列表, 相当于 a[len(a):] = L.
list.insert(i, x) 在指定位置插入一个元素. 第一个参数是准备插入到其前面的那个元素的索引, 例如 a.insert(0, x) 会插入到整个列表之前, 而 a.insert(len(a), x) 相当于 a.append(x) .
list.remove(x) 删除列表中值为 x 的第一个元素. 如果没有这样的元素, 就会返回一个错误.
list.pop([i]) 从列表的指定位置移除元素, 并将其返回. 如果没有指定索引, a.pop()返回最后一个元素. 元素随即从列表中被移除.
list.clear() 移除列表中的所有项, 等于del a[:].
list.index(x) 返回列表中第一个值为 x 的元素的索引. 如果没有匹配的元素就会返回一个错误.
list.count(x) 返回 x 在列表中出现的次数.
list.sort() 对列表中的元素进行排序.
list.reverse() 倒排列表中的元素.
list.copy() 返回列表的浅复制, 等于a[:].
列表的复制

从上表可以知道,我们可以使用list.copy()来复制列表,不妨考虑如下两种情况:
情况 1

1
2
3
4
5
list1 = [1, 2, 3]
list2 = list1
print(id(list1), id(list2))
list1.append(4)
print(list1, list2)

输出
1
2
139674627313168 139674627313168
[1, 2, 3, 4] [1, 2, 3, 4]

我们发现这种赋值方式将会导致两个列表变量指向同一个内存指针,对其中一个修改,将会导致两个变量均发生变化.
情况 2
1
2
3
4
5
list1 = [1, 2, 3]
list2 = list1.copy()
print(id(list1), id(list2))
list2.append(4)
print(list1, list2)

输出:
1
2
139674626873936 139674626888560
[1, 2, 3] [1, 2, 3, 4]

list.copy()方法将会产生一个新的列表变量,其指向不同的内存指针,两者互相独立不相互改变

关于这两种情况,我们可以稍微引申一下:

1
2
3
4
5
a = 1
b = a
print(id(a), id(b))
b = a + 1
print(id(a), id(b))

输出:
1
2
93874524533504 93874524533504
93874524533504 93874524533536

当我们未对变量 b 进行修改时, b与a指向相同的地址,但当我们对b作出修改,此时a,b不再关联.

列表的递推公式

每个列表递推公式都在 for 之后跟一个表达式, 然后有零到多个 for 或 if 子句. 返回结果是一个根据表达从其后的 for 和 if 上下文环境中生成出来的列表. 如果希望表达式推导出一个元组, 就必须使用括号. 语法如下:

1
2
3
4
5
6
7
#单变量递推
[f(x) for x in <遍历范围> if <服从条件>]
#多变量递推
[f(x,y) for x in <x遍历范围>
for y in <y遍历范围>
if <x服从条件>
if <y服从条件>]

例 1
小明和小红丢骰子,问小明的点数比小红大的概率是多少?

小明和小红丢骰子丢出的点数相互独立,故他们可取以下值:

1
2
3
4
# 小明
A = [x for x in range(1,7)] # = [1, 2, 3, 4, 5, 6]
# 小红
B = [x for x in range(1,7)] # = [1, 2, 3, 4, 5, 6]

设小明点数比小红大的事件为$H$,则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> H = [(x,y) for x in A for y in B if x>y]
[(2, 1),
(3, 1),
(3, 2),
(4, 1),
(4, 2),
(4, 3),
(5, 1),
(5, 2),
(5, 3),
(5, 4),
(6, 1),
(6, 2),
(6, 3),
(6, 4),
(6, 5)]

可以求出事件$H$发生的概率为:
1
2
>>> print('概率 = ',len(H)/len([(x,y) for x in A for y in B]))
概率 = 0.4166666666666667

列表的排序 list.sort()方法与sorted()函数

列表的排序有两种处理方式,分别是list.sort()方法与sorted()函数.他们有如下区别

.sort()sorted() 区别:

sort 是应用在 list 上的方法, sorted 可以对所有可迭代的对象进行排序操作. list 的 sort 方法返回的是对已经存在的列表进行操作, 无返回值, 而内建函数 sorted 方法返回的是一个新的 list, 而不是在原来的基础上进行的操作.

关于该区别,有以下辨识:

1
2
ls = [1,2,4,3]
print(ls.sort())

该程序的输出应为None,猜错的小伙伴要再去看看上面的区别啦😜.sort()方法是没有返回值的,结果咧,你print了个寂寞.

list.sort()方法语法
1
list.sort(key=None, reverse=False)
  • key 通过函数来指定可迭代对象中的一个元素来进行排序;
  • reverse 决定排序是否反向,默认为False

示例:

1
2
3
list = [(2, 2), (3, 4), (4, 1), (1, 3)]
list.sort(key=lambda x:x[1]) # sort将会改变原列表(详见上述区别)
print(list)

返回:
1
[(4, 1), (2, 2), (1, 3), (3, 4)]

sorted()函数语法
1
sorted(iterable, key=None, reverse=False)

使用方法同上.

del 语句

使用 del 语句可以从一个列表中依索引而不是值来删除一个元素. 这与使用 pop() 返回一个值不同. 可以用 del 语句从列表中删除一个切片, 或清空整个列表(del a[:])而不删除该列表变量. 例如:

1
2
3
4
>>> a = [1, 66.25, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.25, 1234.5]

集合

集合的定义类似于数学上的定义,有以下特点:

  1. 集合是无序组合,没有索引与位置的概念,不支持切片;
  2. 集合中的元素是不能重复的(可利用该特征过滤重复元素);
  3. 打印出的集合会按照一定规律排列,不一定与定义顺序一致.
    集合的表示与生成
    使用大括号{}表示的数组称为集合,例如:
    1
    {'😊', '😀', (1, 2), 1234, 'hello world'}
    为一个集合.

使用set()来生成集合,示例如下:

1
2
>>> w = set('apple')
{'e', 'p', 'a', 'l'} # 注意单独输入字符串将被看作一个序列并拆分字符

集合类型的操作符

操作符 描述
S - T 或 S.difference(T) $S-T$
S -= T 或 S.difference_update(T) $S=S-T$
S & T 或 S.intersection(T) $S\cap T$
S&=T 或 S.intersectionupdate(T) $S=S\cap T$
S^T 或 s.symmetric_ difference(T) $S\cup T-S\cap T$
S^=T 或 s.symmetric_difference_update(T) $S=S\cup T-S\cap T$
S|T 或 S.union(T) $S\cup T$
S|=T 或 S.update{T) $S=S\cup T$
S<=T 或 S.issubset(T) 当$S\subseteqq T$ 时为真
S<T 当$S\subsetneqq T$ 时为真
S>=T 或 S.issuperset(T) 当$S\supseteqq T$ 时为真
S>T 当$S\supsetneqq T$ 时为真


集合类型的操作函数或方法

操作函数或方法 描述
S.add(x) 如果数据项x不在集合S中, 将x增加到s
S.clear() 移除S中的所有数据项
S.copy() 返回集合S的一个副本
S.pop() 随机返回集合S中的一个元素, 如果S为空, 产生KeyError异常
S.discard(x) 如果x在集合S中, 移除该元素
S.remove(x) 如果x在集合S中, 移除该元素
S.isdisjoint(T) 如果集合S与T没有相同元素, 返回True
len(S) 返同集合S的元素个数
X in S 如果x是S的元素, 返回True, 否则返回False
x not in S 如果x不是S的元素, 返回True, 否则返回False

字典

在python中我们可以把字典看作键值对的集合(他们是不同的数据结构,字典不属于集合),
特点:

  1. 序列是以连续的整数为索引, 与此不同的是, 字典以关键字为索引, 关键字可以是任意不可变类型, 通常用字符串或数值.
  2. 字典键值对之间没有顺序也不能重复
  3. 字典的键必须是元组,字符串,数字;不允许使用列表,字典,集合作为键.
    字典的创建
  4. 使用{}创建空字典
    1
    dictionary = {}
  5. 使用函数 dict() 直接从键值对元组列表中构建字典

    1
    2
    >>> dict([('萝卜', '🥕'), ('香蕉', '🍌'), ('苹果', '🍎')])
    {'萝卜': '🥕', '香蕉': '🍌', '苹果': '🍎'}
  6. 使用递推公式生成键和值

    1
    2
    3
    4
    >>> parrot =  {str(x)+'个萝卜🥕': str(x)+'个坑' for x in ['一', '两', '三']}
    >>> print(parrot)

    {'一个萝卜🥕': '一个坑', '两个萝卜🥕': '两个坑', '三个萝卜🥕': '三个坑'}
  7. 当键只是简单的字符串, 使用键参数指定键值
    1
    2
    >>> dict(sape=4139, guido=4127, jack=4098)
    {'sape': 4139, 'jack': 4098, 'guido': 4127}
    字典的访问
    通过索引符号实现:
    1
    <字典变量>[<键>]
    当找不到该键时,将会返回KeyError.该访问方式也可以修改或添加键值对
    1
    <字典变量>[<键>] = <键值>
    字典的操作
函数和方法 描述
<d>.keys() 返回所有的键信息
<d>.values() 返回所有的值信息
<d>.items() 返回所有的键值对
<d>.get(<key>,<default>) 键存在则返回相应值, 否则返回默认值
<d>.pop(<key>, <default>) 键存在则返回相应值, 同时删除键们对, 否则返回默认值
<d>.popitem() 随机从字典中取出一个键值对, 以元组(key, value)形式返回
<d>.clear() 删除所有的键值对
del <d>[<key>] 删除字典中某一个键值对
<key> in <d> 如果键在字典中则返回True, 否则返回False

需要注意的是.keys(),.values(),.items()他们的输出类型不是列表,我们可以使用list()函数来返回列表:

1
2
3
4
5
6
7
8
dictionary = dict([('萝卜', '🥕'), ('香蕉', '🍌'), ('苹果', '🍎')])
print(dictionary.keys())
print(dictionary.values())
print(dictionary.items())

print(type(dictionary.keys()))
print(type(dictionary.values()))
print(type(dictionary.items()))

输出:
1
2
3
4
5
6
7
dict_keys(['萝卜', '香蕉', '苹果'])
dict_values(['🥕', '🍌', '🍎'])
dict_items([('萝卜', '🥕'), ('香蕉', '🍌'), ('苹果', '🍎')])

<class 'dict_keys'>
<class 'dict_values'>
<class 'dict_items'>

注意观察他们的输出类型.

例子 : 词频统计
可以利用字典键不重复的特性进行词频统计,建立{单词:词频}的映射,思路如下:

1
2
for word in <string>:
counts[word] = count.get(word, 0) + 1

遍历

在字典中遍历时, 键值对可以使用items()方法同时解读出来:

1
2
3
4
5
6
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.items():
... print(k, v)
...
gallahad the pure
robin the brave

在序列中遍历时, 索引位置和对应值可以使用enumerate()函数同时得到:
1
2
3
4
5
6
>>> for i, v in enumerate(['🥕', '🍌', '🍎']):
... print(i, v)
...
0 🥕
1 🍌
2 🍎

同时遍历两个或更多的序列, 可以使用zip()组合:
1
2
3
4
5
6
7
8
>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
... print('What is your {0}? It is {1}.'.format(q, a))
...
What is your name? It is lancelot.
What is your quest? It is the holy grail.
What is your favorite color? It is blue.

要反向遍历一个序列, 首先指定这个序列, 然后调用reversed() 函数:
1
2
3
4
5
6
7
8
>>> for i in reversed(range(1, 10, 2)):
... print(i)
...
9
7
5
3
1

第七章 文件和数据格式化

文件的打开关闭

python使用open()方法实现文件的打开,打开文件之前需保证文件不被占用(否则返回OSError),若文件被python占用,需使用close()解除占用.语法格式:

1
<变量名> = open(<filename>, <mode>)

<mode>处用于指定文件的打开方式,有如下打开方式:

文什的打开模式 含义
‘r’ 只读校式, 如果文件不存在, 返回异常FileNotFoundError,默认值
‘w’ 覆盖写模式, 指针会从第一行行首开始覆写,文件不存在则创建, 存在则完全疫盖
‘x’ 创建马模式, 文件不存在则创建, 存在则返同异常FileExistsError
‘a’ 追加写模式, 文件不存在则创建, 存在则在文件最后追加内容
‘b’ 二进制文件模式
‘t’ 文本文件模式, 默认值
‘+’ 与r/w/x/a 一同使用, 在原功能基础上增加同时读写功能

以上打开方式可以组合使用,打开后记得使用close()方法释放.
示例:

1
2
3
file = open('とある科学の超電磁砲.txt', 'r')
<程序体>
file.close()

文件的读写

注意文件读写过程中,每一行都可能需要换行符.读取时记得删除换行符,写入时记得按照需求添加换行符.换行符是真实存在的字符,只不过看不见而已啦.

读取

操作方法 含义
<file>.readall() 读入整个文件内容, 返回-个字符串或字节流
<file>.read(size=-1) 从文件中读入整个文件内容, 如果给出参数, 读入前size长度的字符串或字节流
<file>.readline(size=-1) 从文件中读入一行内容, 如果给出参数, 读入该行前size长度的字符串或字节流
<file>.readlines(hint=-1) 从文件中读入所有行, 以每行为元素形成一个列表, 如果给出参数, 读入hint行

我们常用以下方式(python此时将文件看作一个行序列,通过遍历进行读取)来逐行读入并修改,这样可以避免一次性读入过多内容影响内存性能:

1
2
3
4
file = input('とある科学の超電磁砲.txt', 'r')
for line in file:
print(line)
file.close()

这样就可以把一本超炮打印出来啦…

写入

写入方法 含义
<file>.write(s) 向文件写入一个字符串或字节流
<file>. writelines(Lines) 将一个元素全为字符串的列表写入文件
<file>.seek(offset) 改变当前文件操作指针的位置, offset的值: 0-文件开头: l一当前位置: 2一文件结尾

python语言程序设计入门笔记
https://shevon.is-a.dev/python/
作者
ShevonKuan
发布于
2020年8月16日
许可协议