python
python入门
0.表达式
表达式 是值、变量和操作符的组合。单独一个值也被看作一个表达式,单独的变量也是如此。所以下面都是合法的表达式:
在任何可以使用值得地方,都可以使用任意表达式,但是赋值表达式得左边必须是变量名称,在左边放置任何其他的表达式都是语法错误
❌错误实例
str * 10 = “error”
1 | n = n + 12 |
n = 12是一个赋值语句,n+12就是一个表达式,求出对应的值,解释器会执行它,并输出
操作顺序
Python遵守数学的传统规则
- 括号拥有最高的优先级,并可以用来强制表达式按照你需要的顺
序进行求值 - 乘方操作拥有次高的优先级,所以1+2**3 的结果是9,而不
是27, - 乘法和除法优先级相同,并且高于亦有相同优先级的加法和减法,所以2*3-1 是5,而不是
4,并且6+4/2 是8,而不是5。
其它的运算符当用到时可以查表,这里只是说明Python遵守数学的传统规则
字符串操作
通常来说,字符串不能进行数学操作。即使看起来像数字也不行。下面的操作是非法
的:
1 |
|
⚠But有两个例外:+和*。
操作符+进行字符串拼接 (string concatenation)操作,意即将前后两个字符首尾连接
起来。
1
2
3
4 song = "god's plan"
singer = 'drake:'
print(singer+song)
操作符*也适用于字符串;它进行重复操作。
1
2 # 这条语句会输出十个wf
print("wf"*10)
赋值
赋值语句 可以建立新的变量,并给它们赋值:
1 | name = 'stan' |
上面的代码有三个赋值语句,每个变量现在都存储着一个值
变量名称
常常选择有意义的名称作为变量名——以此标记变量的用途。
下划线“_”可以出现在变量名称中。它经常出现在由多个词组成的变量名中,如
your_name 或airspeed_of_unladen_swallow 。
命名
如何起一个好的文件名,是一件纠结而且烦恼的事情
1.💬规则:
(1)变量名只能包含字母、数字、下划线,并且不能以数字开头
❌ 1_msg, @asd,
✔ msg_1,
(2)变量不能包含空格,但可以用下划线来分割空格。
❌ study py
✔ study_py
(3)⚠不要将Python关键字和函数名用作变量名 ,也不要将要使用的文件夹命名为已经安装的第三方库(导入包时可能会踩坑)。
比如requests, common等安装了的库
(4)变量名应既简短又具有描述性。例如,
name比n好,student_name比s_n好,name_length
比length_of_persons_name好。
(5)慎用小写字母l和大写字母O,因为它们可能被人错看成数字1和0。
💭总结:实践实践还是™的实践,过程当中应秉持着大胆假设,小心求证的思想。
1.函数
1 | a = int('31') |
代码的第一行调用了int函数,将’31’字符串转化为int
代码第二行,int 可以将浮点数转换为整数,但不会做四舍五入操作,而是直接舍弃小数部分
代码第三行,float 函数将整数和字符串转换为浮点数:
代码第四行,str 函数将参数转换为字符串:
(1)函数命名规范
和变量名称一样,如果忘了,回去看看~~
(2)定义函数
函数定义的第一行称为函数头 (header),其他部分称为函数体 (body)。函数头
应该以冒号结束,函数体则应当整体缩进一级。依照惯例,缩进总是使用4个空格,函数
体的代码语句行数不限。
函数名后的空括号表示它不接收任何参数。
1 | def greet_user(): |
🚩本例中print 语句里的字符串使用双引号括起来。
单引号和双引号的作用相同。大部分情况下,人们都使用单引号,
只在本例中这样的特殊情况下才使用双引号:
也就是字符串当中有单引号时, 外面只能用双引号括起来
(3)执行
函数定义的执行方式和其他语句一样,不同的是执行后会创建函数对象。
函数体里面的语句并不会立即运 行,而是等到函数被调用时才执行。函数定义不会产生任何输出。
🚩聪明的你已经猜到要运行一个函数肯定要先创建,换言之,函数定义必须在函数
被调用之前先运行。
❌错误实例:
1 | # 调用函数 |
代码将报错 NameError: name 'greet_user' is not defined
(4)执行流程
执行总是从程序的第一行开始。语句按照从上到下的顺序逐一运行。
函数定义并不会改变程序的执行流程,但应注意函数体中的语句并不立即运行,而是
等到函数被调用时运行。
函数调用可以看作程序运行流程中的一个迂回路径。遇到函数调用时,并不会直接继
续运行下一条语句,而是跳到函数体的第一行,继续运行完函数体的所有语句,再跳回到
原来离开的地方。
🚩举个栗子
求斐波那契数
1 | def fibonacci (n): |
在上面得代码中,当n=0或n=1时,返回函数。(先不考虑求斐波那契数其中原理,如果没学过的话)
辗转相除法求:最大公约数
1 | def gcd(a, b): |
(5)变量和形参局部性
1 | def cat_twice(part1, part2): |
🚩当cat_twice在被调用结束后,cat变量会被销毁,如果再打印cat
就会报错了:NameError: name 'cat' is not defined
我们可以画个栈图,来表示每个变量所属的函数__main__
是用于表示整个栈图的图框的特别名称。在所有函数之外新建变量时,它就是属于__main__ 的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0TKHN4VU-1657165152270)(https://rcc4qlp1o.hd-bkt.clouddn.com/mymd202205232113719.png)]
2.实参和形参
定义函数greet_user()时,要求给变量username指定一个值。调用这个函数并提供这种
信息(人名)时,它将打印相应的问候语。
在函数greet_user()的定义中,变量username是一个形参——函数完成其工作所需的一项信
息。在代码greet_user(‘stan’)中,值’stan’是一个实参。实参是调用函数时传递给函数的信
息。我们调用函数时,将要让函数使用的信息放在括号内。在greet_user(‘stan’)中,将实参
'stan’传递给了函数greet_user(),这个值被存储在形参username中。
1 | def greet_user(username): |
(1)传递实参
鉴于函数定义中可能包含多个形参,因此函数调用中也可能包含多个实参。向函数传递实参
的方式很多,可使用位置实参,这要求实参的顺序与形参的顺序相同;也可使用关键字实参,其
中每个实参都由变量名和值组成;还可使用列表和字典。下面来依次介绍这些方式。
位置实参
你调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参。也就是说,实参传递的位置和形参是一一对应的
1 | def describe_pet(animal_type, pet_name): |
⚠函数调用中实参的顺序与函数定义中形参的顺序一致。
关键字实参
关键字实参是,在你传递实参的时候将名称和值关联起来。
1 | def describe_pet(animal_type, pet_name): |
⚠这样就不会考虑顺序了
默认值
1 | def describe_pet(pet_name, animal_type='dog'): |
pet_name要放在前面,不然会报错
non-default parameter follows default parameter
由于给animal_type指定了默认值,
无需通过实参来指定动物类型,因此在函数调用中只包含一个实参——宠物的名字。然而,Python
依然将这个实参视为位置实参,因此如果函数调用中只包含宠物的名字,这个实参将关联到函数
定义中的第一个形参。这就是需要将pet_name放在形参列表开头的原因所在
避免实参错误
等你开始使用函数后,如果遇到实参不匹配错误,不要大惊小怪。你提供的实参多于或少于
函数完成其工作所需的信息时,将出现实参不匹配错误。例如,如果调用函数describe_pet()时
没有指定任何实参,结果将如何呢?
(2)有返回值
调用函数会产生一个返回值,我们一般会将它赋值给一个变量或者用作表达式
的组成部分:
1 | def get_formatted_name(first_name, last_name,middle_name=''): |
计算一个圆的周长
1 | import math |
在上面的栗子中,我们也可以将a的值直接返回,就不需要变量a了
在函数中同样会遇到多个分支,这些分支同样可以返回不用的值
1 | def absolute_value(x): |
🚩一旦return 语句运行,当前的函数就会终结,后面的语句不会执行。return 语句
之后的代码,或者在其他程序流程永远不可能达到的地方的代码,称为无效代码 (dead
code)。
1 | def absolute_value(x): |
如果值为0,则函数没有遇到return就结束了,
如果执行流程到了函数的结尾,返回值是None ,
(3)无返回值
1 | def greet_user(username): |
❌无返回值函数:如果把该结果赋值给某个变量,或打印时,回得到一个结果 None
,
也即是说并不是没有返回值,而是返回的值是None
🚩None是一种特殊的类型,print(type(greet_user(‘stan’)))
会得到下面的结果
STAN
None
<class ‘NoneType’>
(4)传递任意数量的实参
1 | def make_pizza(*toppings): |
形参名*toppings中的星号让Python创建一个名为toppings的空元组,并将收到的所有值都封
装到这个元组中。
目标:主要使用if表达式,下面先介绍两个操作符
3.条件和递归
(1)向下取整//和求模%
1 | # 向下取整,hour=2 |
取模的用途
🚩根据取模的性质,我们可以用来做很多事情:
- 检测一个数是不是另一个数的倍数,
如果是倍数关系则: x % y == 0- 可以得到一个数的后n位,举个栗子
x % 10 可以得到x 的个位数, % 100就可以得到个位和十位,以此类推
(2)布尔表达式
布尔表达式 是值为真或假的表达式。下面的例子中使用了== 操作符,来比较两个操
作对象是否相等。如果相等,则得True ,否则是False :
1 | print(5 == 5) |
⚠需要注意的是 = 是一个赋值操作符, 而== 是一个关系操作符
(3)逻辑操作符
逻辑操作符 有3个:and 、or 和not 。这些操作符的语义(意义)和它们在英语中的
意思差不多。
🚩举个栗子
and
:x > 0 and x < 10 只有当x 比0大且 比10小时才为真。or
:n%2 == 0 or n%3 ==0 ,当其中任意 一个条件为真时为真,也就是说,数n 可以被2或3整除都可以。not
:操作符可以否定一个布尔表达式,所以not (x > y) 在x > y 为假时为
真,即当x 小于等于y 时真。
⚠ 严格地说,逻辑操作符的操作对象应该都是布尔表达式,但是Python并不那么严格。 任何非0的数都被解释为True 。
(4)条件执行
先看if表达式:
1 | x = 3 |
🚩if 之后的布尔表达式被称为条件 (condition)。如果它为真,则之后缩进的语句会
运行。否则,什么都不发生。
if-else 语句
执行if,就不执行else,可以理解为if和else组成了两个范围,在判断时
寻找满足条件的答案,找到后跳出判断
if-elif-else 结构
1.相当于if和elif和else不含有交集,一般来说,if和elif和else都是不同范围的代码块,
3.if-elif后面不强行要求一定要有else
1 | age =12 |
列表本身也可以作为判断
不为空就是True
1 | d = [] |
4.递归
1 | def countdown(n): |
🚩解读一下代码:
如果n 是0或负数,它会输出单词“Blastoff!”,其他情况下,它会输出n ,并调用一个
名为countdown 的函数——它自己——并传入实参n-1 。
countdown 的执行从n=3 开始,因为n 比0大,所以会输出3,并接着调用自己…
countdown 的执行从n=2 开始,因为n 比0大,所以会输出2,并接着调用自己…
countdown 的执行从n=1 开始,因为n 比0大,所以会输出1,并接着调用自己…
countdown 的执行从n=0 开始,因为n 不比0大,所以会输出单词“Blastoff!”,并返回。
接收n=1 的函数countdown 返回。
接收n=2 的函数countdown 返回。
接收n=3 的函数countdown 返回。
然后就会到了__main__ 函数。
文字太过赘述,咋们用图来解释
一个函数每次被调用时,Python会创建一个帧(function frame),来包含函数的局部
变量和参数。对于递归函数,栈上可能同时存在多个函数帧。
和往常一样,栈的顶端是__main__ 的函数帧。因为我们没有在__main__ 函数里新
建任何变量或传入任何参数,所以它是空的。
⚠下面是一个无限递归函数, 程序会一直执行下去,但是无限递归的函数并不会真的永远执行。Python会在递归深度到
达上限时报告一个出错消息:
RuntimeError: Maximum recursion depth exceeded
1 | def recurse(): |
5.迭代
迭代即重复运行一段代码语句块的能力。比如for循环和递归,这章主要使用while
变量重新赋值
1 | x= 5 |
在上面的过程中,对一个变量进行多次赋值,这合法吗,当然合法了
1 | # 得到一个元组 |
更新变量
重新赋值的最常见形式是更新 ,此时变量的新值依赖于旧值。
1 | x = 0 |
如果尝试更新一个并不存在的变量,则会得到错误,因为Python在赋值给x 之前会先
计算等号右边的部分
:
1 | x = x + 1 |
所以:在更新变量之前,必须先对它进行初始化 。通常通过一个简单赋值操作来进行初始
化:
1 | x = 0 |
while
计算机常被用来自动化重复处理某些任务。重复执行相同或相似的任务,而不犯错
误,这是电脑所擅长于人之处。在计算机程序中,重复也被称为迭代 。
下面是使用while 语句实现的countdown 函数:
1 | def countdown(n): |
我们来看看运行流程:
- 确定条件是真还是假。
- 如果条件为假,退出while 语句,并继续执行后面的语句。
- 如果条件为真,则运行while 语句的语句体,并返回第1步。
这种类型的流程称为循环 (loop),因为第3步又循环返回到最顶端的第1步了。
再看个例子:
1 | a = 1 |
while删除列表的值
1 | pets = ['dog', 'cat', 'dog', 'goldfish', 'cat', 'rabbit', 'cat'] |
break
有时候只有在循环语句体的执行途中才能知道是不是到了退出循环的时机。这时候可
以使用break 语句来跳出循环。
1 | while True: |
6.字符串定义
字符串和整数、浮点数以及布尔类型都不同。字符串是一个序列 (sequence),即它
是一个由其他值组成的有序集合。本章中你将见到如何访问构成字符串的各个字符,并学
到字符串类提供的一些方法。
下标表示的是离字符串开头的偏移量,而第一个字母的偏移量是0。
1 | a = 'banana' |
操作符 [n:m]
返回字符串从第n 个字符到第m 个字符的部分,包含第n 个字符,但不
包含第m 个字符。
len函数
len 是一个内置函数,返回字符串中字符的个数:
1 | a = 'banana' |
for循环遍历字符串
有很多计算都涉及对字符串每次处理一个字符的操作。它们常常从开头起,每次选择
一个字符,对它做一些处理,再继续,直到结束。这种处理的模式,我们称为遍历
(traversal)。
编写遍历逻辑的方法之一是使用while 循环:
1 | fruit = 'banana' |
for循环遍历:
1 | fruit = 'banana' |
由此可见在有的时候,选择合适的方式会使代码简短很多
字符串是不可变的
想要修改字符串的某个字符,你可能会想直接在赋值左侧使用[] 操作符。例如:
1 | greeting = 'Hello, world!' |
TypeError: ‘str’ object does not support item assignment
查找字符串某个值下标
有多个,则返回的是第一个下标
1 | # letter是要查找的字符 |
字符出现次数
1 | word = 'banana' |
字符串方法
字符串提供了很多完成各种操作的有用的方法。方法 和函数很相似——它接收形参
并返回值——但语法有所不同。例如,方法upper 接收一个字符串,并返回一个全部字母
都是大写的字符串。
1 | word = "stanstanstan" |
1 | # find 方法 |
操作符in
in 是一个布尔操作符,操作于两个字符串上,如果第一个是第二个的子串,则返回
True ,否则返回False :
1 | if 'a' in 'banana': |
字符串比较
关系操作符也可以用在字符串上。检查两个字符串是否相等
注意的是:Python处理大小写字母时和人处理时不一样。所有的大写字母都在小写字母之前。所
所以在比较时最好都转为同一种形式
1 | word = "banana" |
调试
当写程序时,遇到bug我们也不要怕,看报错,然后分析代码,
举个栗子:
下面是一个函数,能够比较两个单词,如果它们互为倒序,则返回True
1 | def is_reverse(word1, word2): |
可以看到代码包错了,接下里教大家如何debug
为了调试这类错误,第一步可以在发生错误的那行代码之前打印出索引的值。
1 | def is_reverse(word1, word2): |
可以看出第一次迭代时,j 的值是4,超出了’stan’ 的范围。最后一个字符的下标是3,
所以j的初始值应该减一:j = len(word2)-1
完整代码:
1 | def is_reverse(word1, word2): |
最后返回结果True
7.列表
列表是一个有序集合,在Python中,列表是一个动态的指针数组
访问元素
要访问列表的元素,只需要该元素的位置或者索引
并且列表的索引从0开始
1 | singer = ['drake', 'eminem', 'wyt', 'msw'] |
🚩range遵从左闭右开,也就是左区间第一个开始取值,右区间最后一个索引不取
倒序取值
python支持负的索引下标
1 | singer = ['drake', 'eminem', 'wyt', 'msw'] |
添加元素,修改元素
1 | singer = ['drake', 'eminem', 'wyt', 'msw'] |
删除元素
1 | singer = ['drake', 'eminem', 'wyt', 'msw'] |
sort方法
按升序对列表进行排序并返回 None。排序是就地的(即列表本身被修改)
且稳定 (即保持两个相等元素的顺序)。 如果给定了一个键函数,
则将其应用于每个列表项并根据它们的函数值对它们进行升序或降序排序。
可以设置反向标志以降序排序。
函数sorted()对列表进行临时排序
返回一个新列表,其中包含从可迭代中升序排列的所有项目。
可以提供自定义键函数来自定义排序顺序,传递参数reverse=True。
反向排序
并且可以设置反向标志以按降序请求结果。
处理数字列表的函数
min
max
sum
字符串方法
1 | t = ['a', 'c', 'd'] |
切片
1 | #得到某个区间的值 |
复制列表
1 | lis = [1, 4, 5, 7, 3, 11] |
列表操作
+操作符可以拼接列表:
1 | a = [1, 2, 3] |
8.列表和字符串
字符串是字符的序列,而列表是值的序列,但字符的列表和字符串并不相同。若要将
一个字符串转换为一个字符的列表,可以使用函数list :
1 | s = 'qbasd' |
遍历列表
1 | # 遍历的次数为一维数组的长度,参数的个数就是 |
字符串组合
join 是split 的逆操作。它接收字符串列表,并拼接每个元素。join 是字符串的方
法,所以必须在分隔符上调用它,并传入列表作为实参:
1 | t = ['pining', 'for', 'the', 'fjords'] |
9.对象和值
下面的a和b有两种可能的状态
一种可能是,a 和b 引用着不同的对象,它们的值相同。另一种情况下,它们指向同
一个对象。
1 | a = 'stan' |
对一个列表进行修改
1 | t4 = [1,2 ,4] |
🚩在bad的开头,t 和t4 指向同一个列表。在函数最后,t 指向了一个
新的列表,但t4 仍然指向原先的那个没有改变的列表。
这个函数不会修改原始列表。
1 | t4 = [1,2 ,4] |
range的使用
数range()让你能够轻松地生成一系列的数字
举个栗子
1 | li = list(range(1, 10)) |
步长
range(起始位置:结束位置:步长)
默认步长是1
起始位置 < 结束位置,步长是正数
起始位置 > 结束位置,步长是负数
步长为负数实现倒序排列
1 | li = list(range(1, 10, 2)) |
1 | li = list(range(10, 0, -1)) |
11.元组
目标
- 展示列表、字典和元组三者如何一起工作。
- 很有用的可变长参数列表功能:收集操作符和分散操作符。
(1)不可变
元组是值的一个序列。其中的值可以是任何类型,并且按照整数下标索引,所以从这 方面看,元组和列表很像。元组和列表之间的重要区别是,元组是不可变的。
语法:
1 | # 元组就是用(逗号)分隔的一列值(有的话需要仔细的琢磨哦~) |
如果你想输出一个元素的元组
1 | t1 = ('1') |
运行上面的代码,找不同。
🚩小结:
元组就是用(逗号)分隔的一列值,上面的代码可以看出,使用tuple方法后,t1
输出的值是 ('1',)
如果参数是一个序列(字符串、列表或者元组),结果就是一个包含序列的元素的元 组:
1 | st = tuple('lupins') |
❌因为tuple 是内置函数的名称,所以应当避免用它作为变量名称。
运算符使用
关系运算符适用于元组和其他序列。
1 | print( (0, 1, 2) < (0, 3, 4)) |
Python从比较每个序列的第一个元素开始。如果 它们相等,它就继续比较下一个元素,依次类推,直到它找到不同元素为止。
(2)元组赋值
在做算法题时,时常会用到交换两个变量的值,传统的赋值方式,需要使用一个临时变量。
1 | a = 2 |
这种解决方案很笨拙,而元组赋值 则更优雅:
1 | a = 2 |
🚩 第三行,左边是一个变量的元组,右边是表达式的元组,。每个值会被赋值给相应的变量。右边 所有的表达式,都会在任何赋值操作进行之前完成求值。类似得用法很多,需要自己在实践中摸索,大胆实践。
⚠但是左右两边的值的个数必须得相同
❌a, b = 1,2,3
(3)作为返回值的元组
严格的讲,函数只能返回一个值,但如果返回值是元组的话,效果和返回多个值差不 多。例如,如果将两个整数相除,得到商和余数,那么先计算x/y 再计算x%y 并不高效。 更好的方法是同时计算它们。
python内置函数:
divmod
接收两个参数,并返回两个值的元组,即商和余数。可以将结果存 为一个元组:
builtins.py
内置函数:
1 | def divmod(x, y): # known case of builtins.divmod |
使用:
1 | t = divmod(7,3) |
结果:
或者可以使用元组赋值来分别存储结果中的元素
1 | val, mod = divmod(7,3) |
来演示一个冗余的操作,主要是利于理解
1 | def mod_val(a,b): |
(4)可变长参数元组
函数可以接收不定个数的参数。以* 开头的参数名会收集 (gather)所有的参数到一 个元组上。
来写一个接收任意个数的参数并打印它们:
1 | def print_all(*args): |
还是以divmod
这个函数为例:
收集的反面是分散 (scatter)。如果有一个序列的值而想将它们作为可变长参数传入 到函数中,可以使用* 操作符。
因为divmod
只允许传入两个值,所以我们不能传入元组,不信可以试试。但是我们可以将元组分散:
1 | t = (7 , 3) |
结果:
🚩python很多内置函数都是提供可变长参数元组,比如max,查看方法:在pycharm
里面按住CTRL
再鼠标左点。
(5)列表和元组
zip 是一个内置函数,接收两个或多个序列,并返回一个元组列表。每个元组包含来 自每个序列中的一个元素。这个函数的名字取自拉链(zipper),它可以将两行链牙交替 连接起来。
(6)序列的序列
列表比元组更加通用,主要因为它是可变的。但也有一些情况下你可能会优先选择元 组。
- 在有些环境中,如返回语句中,创建元组比创建列表从语法上说更容易。
- 如果需要用序列作为字典的键,则必须使用不可变类型,如元组或字符串。
- 如果你要向函数传入一个序列作为参数,使用元组可能会减少潜在的由假名导致 的不可预知行为。
🚩因为元组是不可变的,它们不提供类似sort 和reverse 之类的方法,这些方法修改 现有的序列。但Python也提供了内置函数sorted ,可以接收任何序列作为参数,并按排 好的顺序返回带有同样元素的新列表。
1.字典
主要介绍三种存储方式
存储字典的列表、存储列表的字典和存储字典的字典。
字典是没有索引的
(1)去重
字典自动去掉相同键的键值对
1 | alien_0 = {'color': 'green', 'points': 5, 'points': 5} |
(2)访问字典的值和修改
没什么好说的这些
1 | alien_0 = {'color': 'green', 'points': 5, 'points': 5} |
(3)删除键值对
对于字典中不再需要的信息,可使用del语句将相应的键—值对彻底删除。使用del语句时,
必须指定字典名和要删除的键。
(4)字典做计数器
统计字符串中字母的个数
1 | def hub(x): |
(5)反向查找
给定一个字典d 和键k ,找到对应的值v = d[k] 非常容易。这个操作称为查找
(lookup)。
但是如果有v ,而想找到k 时怎么办?这里有两个问题:首先,可能存在多个键映射
到同一个值v 上。随不同的应用场景,也许可以挑其中一个,或者也许需要建立一个列表
来保存所有的键。其次,并没有可以进行反向查找 的简单语法,你需要使用搜索(如果你学过二分查找,
那么就可以找到你需要的值了)
1 | alien_0 = {'color': 'green', 'points': 5, 'singer':'drake','age':5} |
🚩raise 语句 会生成一个异常;在这个例子里它生成一个LookupError
,这是一个内置异常,通常用来表示查找操作失败。(不懂先不用管,或者百度)
(6)字典与列表
来做一个小栗子:
例如,如果你遇到一个将字母映射到频率的字
典,可能会想要反转它;也就是说,建立一个字典,将频率映射到字母上。因为可能出现
多个字母频率相同的情况,在反转的字典中,每项的值应当是字母的列表。
1 | # 统计每个字母出现的次数 |
❌列表可以用作字典的值,但它们不能用作键。
🚩小结:
字典是通过散列表的方式实现的,这意味着键必须是可散列
(hashable)的。散列 是一个函数,接收(任意类型)的值并返回一个整数。字典使用这些被称为散列值的整数来保存和查找键值对。
因此键必须是可散列的,而类似列表这样的可变类型是不可散列的。绕过这种限制的 最简单办法是使用元组。
因为字典是可变的,它也不能用作键,但它可以 用作字典的值。
(7)备忘
在3_函数-(4)执行流程中,我们写了求斐波那契数的代码
从图中可以看出fib(1)和fib(0)被计算了很多次,而且当参数变大时,事情会变得更糟,一个解决办法是记录已经计算过的值,并将它们保存在一个字典中
。将之前计算的值 保存起来以便后面使用的方法称为备忘 (memo)
1 | known = {0:0, 1:1} |
先解释下代码:
每当fibonacci 被调用时,它会先检查known 。如果结果已经存在,则可以立即返 回。如果不存在,它需要计算这个新值,将其添加进字典,并返回。
我们将0和1保存下来, 提高了计算的效率,(当然这个不是最优的,别杠,这里只是为了用来展示字典的用法)
2.遍历字典⚠
1 | alien_0 = {'color': 'green', 'points': 5} |
(1)排序
按照键的大小排序
1 | alien_2 = {'color': 'blue', 'points': 11,'a':1} |
列表嵌套
1 | alien_0 = {'color': 'green', 'points': 5} |
3.全局变量
main 之中的变量有时被称为全局 变量,因为它们可以在任意函数中访问。和局 部变量在函数结束时就消失不同,全局变量可以在不同函数的调用之间持久存在
全局变量常常用作标志 (flag);它是一种布尔变量,可以标志一个条件是否为真。
比如我们想要通过函数改变一个全局变量时:
1 | been_called = False |
❌举个错误例子,更新全局变量
1 | count = 0 |
就会报错:
1 | UnboundLocalError: local variable 'count' referenced before assignment |
解决方法:
在函数中声明全局变量
1 | count = 0 |
🚩小结:如果想要给全局变量 重新赋值,则需要声明它