慕课网Python3入门课程笔记
Python变量和数据类型
数据类型
- 整数和浮点数:在计算机内部存储的方式是不同的,整数运算永远是精确的(除法也是精确的),而浮点数运算则可能会有四舍五入的误差;
- 布尔值:用
True
、False
表示(注意大小写); - 空值:用
None
表示,None
不能理解为0,因为0是有意义的,而None
是一个特殊的空值。
Print语句
print语句也可以跟上多个字符串,用逗号“,”隔开,print会依次打印每个字符串。每个逗号“,”会输出一个空格。
注释
Python的注释以#
开头,后面的文字直到行尾都算注释。
变量
- 变量名必须是大小写英文、数字和_的组合,且不能用数字开头;
- 动态语言:变量本身类型不固定的语言,例如Python;
- 常量:在Python中,通常用全部大写的变量名表示常量,但事实上仍然是一个变量;
定义字符串
字符串可以用'...'
或者"..."
括起来表示。
- 如果字符串本身包含
'
,可以用"..."
括起来表示; - 如果字符串本身包含
"
,可以用'...'
括起来表示; - 如果字符串既包含
'
又包含"
,使用转义字符\
。
注意:Python区分大小写!
raw字符串和多行字符串
raw字符串
在字符串前面加个前缀r
, 表示这是一个raw字符串,里面的字符就不需要转义。例如:
|
|
多行字符串
多行字符串,可以用'''...'''
表示:
|
|
上面这个字符串的表示方法和下面的是完全一样的:
|
|
注:还可以在多行字符串前面添加r
,把这个多行字符串也变成一个raw字符串:
|
|
字符串编码
几种常见的编码
- GB2312:国标简体中文编码;
- GBK:GBK是包括中日韩字符的大字符集合,GB2312是GBK的子集;
- Unicode:Unicode把所有语言都统一到一套编码里;
- UTF-8:UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,是可变长编码;
默认情况下,Python3源码文件中使用UTF-8编码,在内存中使用Unicode编码,即Python3中的字符串是Unicode编码的。
Unicode编码与其他编码的转换
Unicode编码转换成其他编码时,使用encode
方法,参数值为要转换成的编码类型:
|
|
其他编码转换成Unicode编码时,使用decode
方法,参数值为原编码类型:
|
|
数据类型
python中数有四种类型:整数、长整数、浮点数和复数。
- 整数:如
1
, 二进制0b1001
, 十六进制0x2E
; - 长整数:是比较大的整数;
- 浮点数:如
1.23
、3E-2
; - 复数:如
1+2j
、1.1+2.2j
;
布尔类型
- Python把0、None、空字符串、空list、空tuple、空dict和空set看成
False
,其他数值和非空数据类型都看成True
。 - and 和 or 运算的重要法则——短路计算
- 在计算
a and b
时,如果 a 是 False,则根据与运算法则,整个结果必定为 False,因此返回 a;如果 a 是 True,则整个计算结果必定取决与 b,因此返回 b; - 在计算
a or b
时,如果 a 是 True,则根据或运算法则,整个计算结果必定为 True,因此返回 a;如果 a 是 False,则整个计算结果必定取决于 b,因此返回 b。
注:利用这一点,可以通过 or 运算,把空字符串“变成”默认字符串,而非空字符串保持不变。
List和Tuple类型
List
创建list
list是一种有序的列表,可以随时添加和删除其中的元素。用 [ ]
把list的所有元素括起来就构造了一个list对象。list中包含的元素可以是不同种数据类型。一个元素也没有的list是空list。list中元素可重复。
按照索引访问list
- 索引从 0 开始,格式为
L[0]
; - 使用索引时,千万注意不要越界,否则报错
IndexError
。
倒序访问list
- 用 -1 这个索引来表示最后一个元素,即
L[-1]
;类似的,倒数第二用 -2 表示,倒数第三用 -3 表示。 - 使用倒序索引时,也要注意不要越界,否则同样报错
IndexError
。
添加新元素
- 用 list 的
append()
方法把新元素添加到 list 的尾部;
|
|
- 用list的
insert()
方法把新元素添加到除尾部的其他位置,它有两个参数,第一个参数是索引位置,第二个参数是待添加的新元素;
|
|
注意:使用insert()
方法,,可以认为是插入到了索引位置的前面。使用倒序索引时注意:
|
|
从list删除元素
- 用list的
pop()
方法删掉list的末尾元素,返回值是被删掉的元素;
|
|
- 用list的
pop(x)
方法删掉list的任意位置元素,参数x为索引(可倒序索引),返回值是被删掉的元素;
|
|
替换元素
对list中的某一个索引(可倒序索引)赋值,就可以直接用新的元素替换掉原来的元素,list包含的元素个数保持不变。
|
|
Tuple
创建tuple
tuple是另一种有序的列表,中文翻译为“元组”。创建tuple使用()
。tuple一旦创建完毕就不能修改。tuple没有append()方法,也没有insert()和pop()方法。可以使用索引方式访问元素,但是不能对其中的元素赋值。
创建单元素tuple
- 包含 0 个元素的 tuple,也就是空tuple,直接用 ()表示,即
t = ()
; - 因为()既可以表示tuple,又可以作为括号表示运算时的优先级,用()定义单元素的tuple有歧义。所以 Python 规定,单元素 tuple 要多加一个逗号“,”避免歧义:
|
|
“可变”的tuple
tuple所谓的“不变”是说,tuple的每个元素,指向永远不变,即指向’a’,就不能改成指向’b’。指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的!
|
|
条件判断和循环
Python代码的缩进规则:
- 具有相同缩进的代码被视为代码块。
- 缩进为4个空格,不要使用Tab,更不要混合Tab和空格,否则很容易造成因为缩进引起的语法错误。
条件判断语句
if语句
if 语句后接条件表达式,然后用:
表示代码块开始。
if-else
如果条件判断是**“非此即彼”**的,即要么符合条件1,要么符合条件2,可以用一个if ... else ...
语句把它们统一起来。根据条件表达式的值为 True 或者 False ,分别执行 if 代码块或者 else 代码块。注意else后面有个“:”。
if-elif-else
|
|
注意:这一系列条件判断会从上到下依次判断,如果某个判断为 True,执行完对应的代码块,后面的条件判断就直接忽略,不再执行了。
循环
for…in循环 & range()函数
|
|
name 这个变量是在 for 循环中定义的,意思是,依次取出list中的每一个元素,并把元素赋值给 name,然后执行for循环体(就是缩进的代码块)。
range(x)
函数生成一个整数序列list,元素是从0开始到x-1的整数。例如求1-100的整数和:
|
|
while循环
while 循环不会迭代 list 或 tuple 的元素,而是根据表达式判断循环是否结束。while循环每次先判断条件表达式,如果为True,则执行循环体的代码块,否则,退出循环。
|
|
注意:要特别留意while循环的退出条件。
break退出循环
用 for 循环或者 while 循环时,如果要在循环体内直接退出循环,可以使用 break 语句。break语句生效时,其后面的循环体语句将不会执行。
continue继续循环
在循环过程中,可以用continue跳过后续循环代码,结束本次循环,继续下一次循环。continue语句生效时,其后面的循环体语句将不会执行。
多重循环
在循环内部,还可以嵌套循环。
Dict和Set类型
Dict
什么是dict
dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。dict用{ }
表示,元素按照key: value
写出来即可。最后一个key: value
的逗号可以省略。
|
|
访问dict
- 使用
d[key]
的形式来查找对应的 value,如果key 存在,dict就返回对应的value。这和 list 的不同之处是,list 使用索引返回对应的元素,而dict使用key。 注意:使用时要先用in操作符判断一下 key 是否存在,否则会报错KeyError
!
|
|
- 使用dict提供的一个get方法,在Key不存在的时候,返回
None
或者自己指定的value。
|
|
dict的特点
查找速度快。无论dict有10个元素还是10万个元素,查找速度都一样。但代价是dict 占用内存大。(由于dict是按 key 查找,所以key不能重复。)
存储的key-value序对是没有顺序的。print的顺序不一定是我们创建时的顺序,而且,不同的机器打印的顺序都可能不同,这说明dict内部是无序的,不能用dict存储有序的集合。
key只能是不可变对象。Python的基本类型如字符串、整数、浮点数都是不可变的,都可以作为key。但是list是可变的,就不能作为key。
更新dict
- 添加元素:可以用
d[NewKey] = NewValue
往dict中添加新的 key-value。如果 key 已经存在,则赋值会用新的 value 替换掉原来的 value。
|
|
- 删除元素:要删除一个key,用
pop(key)
方法(返回值是value),对应的value也会从dict中删除:
|
|
遍历dict
直接使用for循环可以遍历 dict 的 key。又由于通过 key 可以获取对应的 value,因此在循环体内可以获取到value的值。
|
|
Set
什么是set
set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中key不重复、无序且是确定的。set相当于数学上定义的集合。
创建 set 的方式是使用set()
函数并传入一个可迭代对象,可迭代对象的元素将作为set的元素。当包含重复元素时,set会自动去掉重复的元素。
|
|
注意:上述打印的形式类似 list,但它不是 list;打印的顺序和原始 list 的顺序有可能是不同的,因为set内部存储的元素是无序的。
遍历set
由于 set 也是一个集合,所以,遍历 set 和遍历 list 类似,通过 for 循环实现。
注意: for循环在遍历set时,元素的顺序和list的顺序很可能是不同的,而且不同的机器上运行的结果也可能不同。
更新set
更新set主要做两件事:
- 添加元素:用set的
add()
方法。可以直接添加,元素已经存在则不会添加。 - 删除元素:用set的
remove()
方法。如果删除的元素不存在会报错KeyError
,因此remove()
前需要判断。
函数
调用函数
调用一个函数,需要知道函数的名称和参数。调用函数的时候,如果传入的参数数量不对,或者参数类型不能被函数所接受,会报TypeError
的错误。
编写函数
定义函数要使用def
语句,依次写出函数名、括号、括号中的参数和冒号,然后,在缩进中编写函数体,函数的返回值用return
语句返回。
注意:
- 函数体内部的语句在执行时,一旦执行到
return
时,函数就执行完毕,并将结果返回; - 如果没有
return
语句,函数执行完毕后也会返回结果,只是结果为None
; - return None可以简写为return;
返回多值
在函数体中,使用return x, y
的形式来返回多个值,实际返回值是一个tuple。
|
|
这样我们就可以同时获得返回值:
|
|
但其实这只是一种假象,Python函数返回的仍然是单一值——一个tuple:
|
|
在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple,但写起来更方便。
递归函数
如果一个函数在内部调用自身本身,这个函数就是递归函数。
递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。尾递归可以有效防止栈溢出,但是Python并不支持:)
汉诺塔的移动可以看做是递归函数。我们对柱子编号为a, b, c,将所有圆盘从a移到c可以描述为:如果a只有一个圆盘,可以直接移动到c;如果a有N个圆盘,可以看成a有1个圆盘(底盘) + (N-1)个圆盘,首先需要把 (N-1) 个圆盘移动到 b,然后,将 a的最后一个圆盘移动到c,再将b的(N-1)个圆盘移动到c。请编写一个函数move(n, a, b, c),给定输入 n, a, b, c,打印出移动的步骤。 例如,输入 move(2, ‘A’, ‘B’, ‘C’),打印出: A –> B A –> C B –> C
|
|
函数的参数
定义默认参数
默认参数的作用是简化调用,你只要把必须的参数传进去。但在必要的时候,又可以传入额外的参数来覆盖默认参数值。默认参数必须指向不变对象。
由于函数的参数按从左到右的顺序匹配,所以默认参数只能定义在必需参数的后面——def function(a, b=1, c=2)
。b和c即是默认参数。
设置原则:当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。
定义可变参数
可变参数的目的也是为了简化调用。一个可变参数能让一个函数接受任意个参数。
可变参数的名字前面有个*
号,我们可以传入0个、1个或多个参数给可变参数。
|
|
在函数内部,参数numbers接收到的是一个tuple。
函数调用时,在list或者tuple前加一个*
,可以把list或tuple的元素变成可变参数传进去。
|
|
关键字参数
关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。
|
|
|
|
和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去:
|
|
**extra
表示把extra
这个dict的所有key-value用关键字参数传入到函数的**kw
参数,kw
将获得一个dict,注意kw
获得的dict是extra
的一份拷贝,对kw
的改动不会影响到函数外的extra
。
命名关键字参数
命名关键字参数用来限制传入函数的关键字参数的名字,只接收指定名字的参数。
命名关键字参数需要一个特殊分隔符*
,*
后面的参数被视为命名关键字参数。命名关键字参数必须传入参数名。
|
|
|
|
如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*
了:
|
|
命名关键字参数可以有默认值,此时调用时可以不传入该参数:
|
|
参数组合
参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
对于任意函数,都可以通过类似func(*args, **kw)
的形式调用它,无论它的参数是如何定义的。
切片
对list/tuple进行切片
Python提供切片(Slice)操作符用于取list/tuple指定索引范围。
|
|
L[0:3]
表示,从索引0开始取,直到索引3为止,但不包括索引3。即索引0,1,2。
|
|
- 如果第一个索引是0,还可以省略:
|
|
- 只用一个
:
,表示从头到尾取全部元素(实际上复制出了一个新list。):
|
|
- 切片操作还可以指定第三个参数。第三个参数表示每N个取一个,上面的 L[::2] 会每两个元素取出一个来,也就是隔一个取一个。
|
|
对list切片的结果是list,对tuple切片的结果是tuple。
倒序切片
倒序切片也是包含起始索引,不包含结束索引。
|
|
对字符串切片
字符串'xxx'
可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,操作结果仍是字符串。
|
|
迭代
什么是迭代
在Python中,迭代操作就是对于一个集合,无论该集合是有序还是无序,我们用 for 循环遍历这个集合,依次取出集合的每一个元素,这种遍历我们称为迭代(Iteration)。
注意:集合是指包含一组元素的数据结构,我们已经介绍的包括:
- 有序集合:list,tuple,str;
- 无序集合:set
- 无序集合并且具有 key-value 对:dict
Python的for循环抽象程度要高于Java的for
循环。Python的for
循环不仅可以用在list
或tuple
上,还可以作用在其他可迭代对象上。
判断可迭代对象
通过collections
模块的Iterable
类型可以判断。
|
|
迭代list的下标和元素
Python内置的enumerate
函数可以把一个list变成“(索引, 元素)”形式的tuple组成的可迭代对象,这样就可以在for循环中同时迭代索引和元素本身:
|
|
迭代dict的value
dict 对象有一个values()
方法,可以用来迭代dict的value,注意dict是无序的。
|
|
迭代dict的key
默认情况下,dict迭代的是key。
|
|
迭代dict的key和value
dict对象的items()
方法可以用来同时迭代key和value。
|
|
列表生成式
生成列表
如果要生成[1x1, 2x2, 3x3, …, 10x10],方法一是循环,但是循环太繁琐;方法二是写列表生成式。
写列表生成式时,把要生成的元素 x * x 放到前面,后面跟 for
循环,就可以把list创建出来。
|
|
进行条件过滤
列表生成式的 for
循环后面还可以加上 if
判断,只有 if
判断为 True
的时候,才把循环的当前元素添加到列表中。
|
|
使用多层表达式
for
循环可以嵌套,因此,在列表生成式中,也可以用多层 for
循环来生成列表。
|
|
生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。
使用()
创建生成器
把一个列表生成式的[]
改成()
,就创建了一个generator。如果要一个一个打印出generator的元素,可以通过next()
函数。generator保存的是算法,每次调用next()
,就计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration
的错误。
当然,上面这种不断调用next()
函数实在是太变态了,正确的方法是使用for
循环,因为generator也是可迭代对象。
|
|
使用yield
创建生成器
- 如果一个函数定义中包含
yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator。**变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。**同样的,把函数改成generator后,我们基本上从来不会用next()
来调用它,而是直接使用for
循环来迭代。
|
|
迭代器
可以被next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator
。
通过collections
模块的Iterator
类型可以判断是否是迭代器。
|
|
迭代器和可迭代对象的区别
迭代器一定是可迭代对象,可迭代对象不一定是迭代器。两者是包含与被包含的关系。
生成器都是Iterator
对象,但list、dict、str虽然是Iterable
,却不是Iterator
。
Iterator
的计算是惰性的,只有在需要返回下一个数据时它才会计算。Iterator
甚至可以表示一个无限大的数据流,例如全体自然数
把可迭代对象变为迭代器
把list、dict、str等Iterable
变成Iterator
可以使用iter()
函数:
|
|
Python的for循环本质上就是通过不断调用next()
函数实现的。
写于2015年5月,2017年2月整理为Python3版本。