Python基础知识¶
1 Python概述¶
Python 由 Guido van Rossum 于 1989 年底发明,第一个公开发行版发行于 1991 年。
Python 是一种解释型、面向对象、动态数据类型的高级程序设计语言。
- 解释型:在运行时将程序翻译成机器语言的编程语言。这种语言的特点是源代码不是直接翻译成机器语言,而是先翻译成中间代码,再由解释器对中间代码进行解释运行。解释型语言不需要在运行前编译,而是在运行时才翻译成机器语言,每个语句都是执行的时候才翻译。因此,解释型语言的程序每执行一次都要翻译一次,效率相对较低。
- 面向对象:封装、继承和多态。
- 动态数据类型:动态数据类型是指在程序运行时才确定的数据类型,与静态数据类型相对,后者在编译时就已确定数据类型。动态数据类型的概念允许变量在运行时存储任何类型的数据,而无需事先声明。
Python 语法简洁清晰,特色之一是强制用空白符(如空格、制表符和换行符)来定义程序的结构,以增强可读性,同时也使得与其他语言相比显得简洁。
# Python的禅
import this
The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!
2 Python开发推荐IDE¶
- IDLE(默认的 IDE,轻量级,缺点是代码补全和高亮非常简陋)
- Pycharm community(无需配置,缺点是较重,占用资源较多)
- Visual Studio Code(轻量级,插件丰富,缺点是环境需要自行配置)
- neovim/vim(优秀的编辑器,自行配置相关插件,自定义程度非常高,但是配置非常复杂)
3 Python文件和Jupyter源文件¶
Jupyter源文件(.ipynb) | Python文件(.py) | |
---|---|---|
文件格式 | JSON | 纯文本 |
交互式环境 | 支持交互式编程和数据可视化 | 不支持 |
可读性 | 易于理解和阅读 | 需要更多的代码注释 |
可拓展性 | 支持将多个代码块组织在一个文件中 | 通常一个文件包含一个独立的功能或任务 |
代码执行 | 可逐个执行代码块 | 顺序执行整个文件中的所有代码 |
可分享性 | 可以分享包含代码、图像和文本的完整笔记本 | 通常需要将代码和相关文档一起分享 |
代码重用 | 可以轻松复制和粘贴代码块 | 更适合编写可重用的函数、类和模块 |
4 Markdown文件¶
Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档。可以看成高级一点的txt文件,低级一点的Word文件。在Jupyter Notebook中,可以用Markdown单元格来进行大段的解释说明。
常用语法:
- 标题:在行首加上 1 到 6 个 # ,对应标题的级别
- 无序列表:使用 * 号或 - 号开头,后面加上空格
- 有序列表:使用数字和点号开头,后面加上空格
- 链接:使用方括号包裹,后面接上链接地址,然后用括号包裹链接文字
- 图片:使用感叹号! 开头,后面接上图片地址,然后用括号包裹图片标题
- 代码:使用反引号 ` 包裹,后面为代码内容
- 代码块:使用三个反引号
```
开头和结尾,中间为代码块内容 - 表格:使用 | 符号分隔单元格,使用 - 符号分隔标题行和内容行,使用 : 符号对齐
- 引用:在行首加上 > 符号,后面加上空格
- 粗体和斜体:使用两个 * 或 _ 包裹文字,表示粗体,*表示斜体
- 删除线:使用两个 ~~ 包裹文字,~~表示删除线
5 最基础的 Python 程序¶
Python 的语法结构非常简单,通常只需要一行代码就可以完成一个功能。例如,下面的代码就是一个最简单的 Python 程序:
print("Hello World!")
Hello World!
print
函数可以将括号中的内容输出到屏幕上。在 Python 3 中,print 函数是一个内置函数,可以打印任何东西!
Python 使用井号 #
作为单行注释的符号,语法格式为:# 注释内容
从井号 #
开始,直到这行结束为止的所有内容都是注释。Python 解释器遇到 #
时,会忽略它后面的整行内容。
Python 使用三个连续的单引号 '''
或者三个连续的双引号 """
注释多行内容。
print(1) # 打印整数
print(3.9) # 打印浮点数
print('a') # 打印字符
print("string") # 打印字符串
print(3 + 5) # 打印表达式的值
print(print) # 打印函数信息
"""
可以同时打印多个对象,用逗号隔开
可以使用sep和end参数来修改多个对象之间的间隔符和打印完成后的结束符
"""
print(5, '666')
print(1, 2, 3, 4, sep='-', end=';')
print(666)
1 3.9 a string 8 <built-in function print> 5 666 1-2-3-4;666
import keyword
keywords = keyword.kwlist
print(keywords) # 使用keyword模块来查看Python中所有的关键字
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
- 以下划线开头的标识符有特殊含义,除非特定场景需要,应避免使用以下划线开头的标识符。
- 变量名可以是汉字。但我们应尽量避免使用汉字作为标识符,这会避免遇到很多没必要的错误。
- 在Python 命名风格中变量名一般以下划线分割的英文单词组成。如
user_name
和total_amount
。 - 变量名应既要简短又具有描述性,例如:
name
比n
好,name_length
比length_of_persongs_name
好。
在Python 中,万物都是对象(object),每个对象都有类型(type),类型决定了对象有哪些属性(attribute)和方法(method)。可以使用内置函数 type()
来查看对象的数据类型。
a = 5
print(type(a))
a = 6.9
print(type(a))
a = "web"
print(type(a))
<class 'int'> <class 'float'> <class 'str'>
a = 1 # '='符号表示赋值
print(a)
a = -5 # 负整数也是整数
print(a)
b = 2*10**400 + 10 # 一个星号表示乘法运算符,两个星号表示次方运算符,运算次序和算术中的规则相同
print(b)
c = 2**10240
print(c)
1 -5 20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010 35249714121083826571348148398002815464391421343966471060391382605731070276854749365048330296473663862456968155395298373973259049475943113619888338673116133666814706870765271907656205646018608369985558721267670321739031938633833281889192620158426531806923144239269726876399951961191980348023291703472305763782410394589758934585631111078120435303032688818751446435291371357171755632775362932694795076313436687469638004327689390246735321855830610856865924913760826763776003265851716557334210642277343475757799780499021559822412434275087084317293455129570406707590002071704673135527533543217355987568107697577946785796412456048360072965616871024866244650081059068183038134518514222987186837394598019859512993600379236190197576838905080733359989094687008999416247722020061992559931401872357379708488585003666965930609730430774107407494018065365845077094320534700692354400169824131578389153656916754682252425562742895026822086112236185768931940433324078692386463642378029291582384550904012284265277124667452816985659337497580991592510201479766500877427834566619156314388107585743546289067551052434075678195345373363919571323210113622615511765134329627207955793605376892875938357672870881305679305521293359975427801921997534891474090868113467357784359783383091085717100807228425031226776985197364359404683041506613943646666199454899363685801848776729685837803228216113833854742443409221480450232563130417709625320794971672737737385983975520047739978165124906916857931960902407397841536657650378758012409157205939513085324282439290108909069036515430690359963152986587749930516880670326145036987607052961696781556418550966201822821857978020062536824015697620957222738065538832187097409859502669196589025961199448758997373792973191723335549772394878874050854532785922475822836403793986623193174020931432381418437022760412682276382989354839625453241289807108260905134234679130954867570447354549760174691007078528452745027994943853229480544512368831378761119681616719327637308142315105120528704683515182038320225078665313911731749364255621284434304945437214609406008640520972029509955435568094888815701470419410889156523971182172814423274140955428070594328381667048286771972857703435525803544707834567774027206614143419982410109261930698311010857874866840743851472857645330929169548403751084494725893729355450473771059986801058342021902735367627900974872368137838996397379898161454825970910732858202781282973937642847973381838672980693399039429342613001595148968082010016061022316242842367672741265405434553107296623559604413326352140529618171175450657884255099334618722731697920185582437182391397673301168160682516639214706566981465961731374808949131742364752993078326367714117001404210930251538132442219335072672096865184691303027156962439777053707286583949764055151291816402546462452719134797179099210233577596277925646031824172274874084562113440043397395191065473620717104250686040896580928700842593919173283844531470952205600874482302488523867074532907781264990865351844684807012208039108287564534854500486391538876063611476665620230294811468351835374072060530215907909311281816131942219776
6.2.2 浮点数(float)¶
简而言之就是具有小数位的数字。
Python 中的浮点数精度有限。至于为什么……这是一个很复杂的问题,涉及到计算机的硬件、软件、编程语言等等。如果你感兴趣,可以参考 What Every Computer Scientist Should Know About Floating-Point Arithmetic。
a = 3.14
print(a)
a = -3.141592653589793
print(a)
a = round(a, 4) # 使用内置函数round()来获得指定精度的浮点数
print(a)
b = 1.0000000000000000000000000000000000000000000000000000000000000000012
print(b)
x = 0.1
y = 0.2
print(x + y) # 需要注意浮点数运算时可能产生的微小误差
3.14 -3.141592653589793 -3.1416 1.0 0.30000000000000004
6.2.3 复数(complex)¶
Python 中的复数形如 3+4j,其中 3 是实部,4j 是虚部,j 是虚数单位。
复数的计算一般用得不多,简单了解一下就好。
a = 3 + 4j
print(a)
print(abs(a)) # 求复数的模长
(3+4j) 5.0
6.3 布尔类型(bool)¶
Python 中的布尔类型只有两个值:True
和 False
。注意首字母大写。布尔类型的变量一般用于条件判断。
等式和不等式的结果为bool类型。
print(1+1 == 2)
print(1+1 != 3)
print(3.11 > 3.8)
print({1, 3} < {1, 3, 5})
a=1
b=2
print(a==b)
print(2*a==b)
True True False True False True
6.4 字符串类型(str)¶
Python 中的字符串类型用来表示一段文本信息。字符串类型的变量可以用单引号 '
或者双引号 "
括起来,例如 'Hello World!'
或者 "Hello World!"
。在Python中单引号和双引号在单独使用时是没有区别的。但需要嵌套使用时不能连续使用两个单引号或两个双引号。
字符串中默认存在转义,例如 \n 表示换行,\t 表示制表符。如果不希望字符串中的转义字符起作用,可以在字符串前面加上 r。
print("Hello World!")
print('Hello\nWorld!')
print('Hello\tWorld!')
print(r'Hello\nWorld!')
print("I am printing 'Hello World!'")
print('I am printing "Hello World!"')
# 字符串里面可以是任意字符
print("兴隆山技术工程工作站web开发部")
Hello World! Hello World! Hello World! Hello\nWorld! I am printing 'Hello World!' I am printing "Hello World!" 兴隆山技术工程工作站web开发部
Python中有3种格式化字符串的方法,这里只介绍在3.6版本推出的f-string方案,因为它相比于之前的%格式化和字符串format方法写起来更简洁。
它的方法是在字符串前加上f,然后在字符串中用{}来表示被替换字段,其中直接填入替换内容。
pound = 9
penny = 15
option = 'c'
print(f"衬衫的价格是{pound}磅{penny}便士,所以你选择{option.upper()}选项")
衬衫的价格是9磅15便士,所以你选择C选项
补充:Python 不止有一种“用来表示一段文本信息”的数据类型,还有 bytes 类型(字节串),这个数据类型和“编码”有关,可以自行了解一下。(下面的例子仅供展示,暂时不需要理解)
a='你好世界'
print(a)
print(type(a))
b=a.encode('GB2312')
print(b)
print(type(b))
c=b.decode('GB2312')
print(c, end=' ')
print('c == a is', c==a);print('======目前为止还很正常对吧======');print('下面是一些奇怪的东西:')
c=b.decode('utf-8', 'replace')
print(c)
d=c.encode('utf-8').decode('GB2312', 'replace')
print(d)
你好世界 <class 'str'> b'\xc4\xe3\xba\xc3\xca\xc0\xbd\xe7' <class 'bytes'> 你好世界 c == a is True ======目前为止还很正常对吧====== 下面是一些奇怪的东西: ������� 锟斤拷锟斤拷锟斤拷锟�
关于字符串这个类型的对象,内置了很多方法,大家可以自行查阅文档。一个速查网站:菜鸟教程-Python字符串
自己多试试,课上时间有限不可能都讲完,而且未必都能记得住,既然不是考试,我们可以需要的时候现查现用。
6.5 列表类型(list)¶
Python 中的列表类型用来表示一组数据。列表类型的变量用方括号
[]
括起来,其中的每个元素用逗号,
隔开,例如[1, 2, 3]
。列表中的元素可以是任何数据类型,也可以是列表,甚至可以是关键字。同一列表中的不同元素也可以是不同的数据类型。列表类型的变量可以通过下标来访问其中的元素,正向下标从
0
开始,例如a[0]
表示列表a
中的第一个元素,a[1]
表示列表a
中的第二个元素,以此类推。反向下标从-1
开始,例如a[-1]
表示列表a
中的最后一个元素,a[-2]
表示列表a
中的倒数第二个元素,以此类推。不论是正向还是反向,下标索引都不能超过列表的长度,否则会报错。列表类型的变量可以通过下标来修改其中的元素,例如
a[0] = 1
表示将列表a
中的第一个元素修改为1
。这种方法只能修改已经存在的元素,不能创建超越列表长度索引的元素。
a = [] # 定义一个空列表
b = list() # 这也是定义一个空列表
print(a, b)
c = [1, 2, 3]
print(c[0], c[-1])
c[1] = 9
print(c)
l1 = [[1, 2], ['a', 'b'], [3.4, "list"], [int, float, complex]]
print(l1)
[] [] 1 3 [1, 9, 3] [[1, 2], ['a', 'b'], [3.4, 'list'], [<class 'int'>, <class 'float'>, <class 'complex'>]]
列表类型的变量可以通过
append
方法在末尾添加新的元素,例如a.append(4)
表示在列表a
的末尾添加一个元素4
。添加多个元素可以通过
extend
方法,例如a.extend([5, 6, 7])
表示在列表a
的末尾添加三个元素5、6、7
。列表类型的变量可以通过
pop
方法删除指定位置的元素,例如a.pop(2)
表示删除列表a
中的索引为2
的元素,并将这个数返回。如果不填入参数则默认删除列表末尾最后一个元素。可以通过remove
方法删除指定的元素。可以通过
insert
方法在列表任意位置插入元素。
a = [3]
a.append(4)
print(a)
a.extend([5, 6, 7])
print(a)
removed_num = a.pop()
print(a, removed_num)
removed_num = a.pop(-2)
print(a, removed_num)
a.remove(3)
print(a)
a.insert(0, 2)
print(a)
[3, 4] [3, 4, 5, 6, 7] [3, 4, 5, 6] 7 [3, 4, 6] 5 [4, 6] [2, 4, 6]
列表类型的变量可以通过
len
函数获取其中元素的个数,例如len(a)
表示列表a
中元素的个数。列表类型的变量可以通过
in
运算符判断元素是否存在,例如1 in [1, 2, 3]
表示True
,4 in [1, 2, 3]
表示False
。
a = [1.6, 2.4, 3, 0, 7.9]
print(len(a))
print(1 in [1, 2, 3], 4 in [1, 2, 3])
5 True False
- 访问元素可以通过下标,对于访问“子列表”或者称为“切片”、“切片操作”,可以通过
a[start:end:step]
的方式,表示访问 a 中下标从start
开始到end
结束(不包括end
)的元素,例如a[1:3]
表示访问a
中下标为1
和2
的元素;还可以加上步长,例如a[1:5:2]
表示访问a
中下标为1
、3
的元素。
l1 = [i for i in range(100)]
print(l1)
print(l1[20:50:2])
print(l1[50:20:-2])
print(l1[80:])
print(l1[:20])
print(l1[::-1]) # start参数缺失则从第一个元素开始,end参数如果到最后一个元素结束,step参数如果缺失则为1
print(l1[150:200]) # 如果切片范围超出列表索引范围,不会报错,只会得到一个空列表
l1[1:4] = [101, 102, 103] # 切片还可以赋值
print(l1)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] [20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48] [50, 48, 46, 44, 42, 40, 38, 36, 34, 32, 30, 28, 26, 24, 22] [80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] [99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] [] [0, 101, 102, 103, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
实际上字符串也可以通过下标索引来访问相应位置的字符,也可以进行切片操作。
此外,字符串还有一个 split
方法,可以将字符串按照指定的字符分割成若干部分,例如 '1,2,3'.split(',')
表示将字符串 '1,2,3'
按照逗号 ,
分割成若干部分,然后拼接成列表。
相反地,字符串还有一个 join
方法,可以将多个字符按照指定的分隔符拼接成字符串。
a = "Hello World!"
print(a[1:4])
a = '1,2,3'
b = a.split(',')
print(b)
c = '-'.join(b)
print(c)
ell ['1', '2', '3'] 1-2-3
6.6 元组类型(tuple)¶
Python 中的元组类型用来表示一组数据,类似于列表类型,但是元组类型的元素不能修改。元组类型的变量用圆括号 () 括起来,其中的每个元素用逗号 ,
隔开,例如 (1, 2, 3)
。
可以简单理解为一个不可变的列表。
- 由于元组不可变,Python 在内部处理元组时通常更高效,占用的内存也相对较少,尤其是在处理大量数据时。
- 对于列表,由于其可变性,需要更多的内存管理机制,可能在性能上略逊一筹。
- 此外,元组可以用作字典的键,而列表则不能,因为字典的键必须是不可变类型。
a = () # 定义一个空元组
b = tuple() # 这也是定义一个空元组
print(a, b)
"""
需要注意,如果用()来定义一个只含有一个元素的元组,那么在这个元素的后面必须加一个逗号
Python依靠这个逗号将其与普通括号区分
"""
x = (1)
t1 = (1,)
print(type(x), x)
print(type(t1), t1)
() () <class 'int'> 1 <class 'tuple'> (1,)
6.7 集合类型(set)¶
Python 中的集合类型用来表示一组互不相同的数据,类似于数学中的集合。集合类型的变量用花括号 {}
括起来,其中的每个元素用逗号 ,
隔开,例如 {1, 2, 3}
。
回忆一下数学中集合的性质:确定性、无序性、唯一性。 后面两个特性也是Python 集合最重要的特性。
- 无序性:无法通过索引取值。
- 唯一性:添加已存在的元素不会生效。
集合的运算包括并集、交集、差集等等,例如 a | b
表示集合 a
和集合 b
的并集,a & b
表示集合 a
和集合 b
的交集,a - b
表示集合 a
和集合 b
的差集。a ^ b
表示集合 a
和集合 b
的对称差(即 a | b
减去 a & b
)。
集合类型的变量可以通过 add
方法添加新的元素,例如 a.add(4)
表示在集合 a
中添加一个元素 4
。
集合类型的变量可以通过 remove
方法删除元素,例如 a.remove(4)
表示删除集合 a
中的元素 4
。
集合的 update
方法,可以根据输入的另一集合智能地更新集合中的元素。
因为特殊的存储原理,集合的元素必须是可哈希的值(即不可变的对象)。而列表、集合、字典都是可变对象,因此不可哈希,不能作为集合的元素。
a = set() # 需要注意的是空集合只能这样定义
b = {} # 用{}定义的不是空集合,而是空字典
print(a, type(a))
print(b, type(b))
c = {1, 2, 3} # 非空集合可以用{}来定义
c.add(4)
print(c)
d = {1, 3, 5, 7}
d.remove(5)
print(d)
print(c & d)
print(c | d)
print(c - d)
print(c ^ d)
e = {'a', 'b', 'c'}
e.update({'b', 'c', 'd', 'e', 'F'}) # update智能地更新集合的元素
print(e)
s1 = {[1, 2], {1, 3}} # 试图以列表作为集合元素,会产生TypeError错误
set() <class 'set'> {} <class 'dict'> {1, 2, 3, 4} {1, 3, 7} {1, 3} {1, 2, 3, 4, 7} {2, 4} {2, 4, 7} {'e', 'F', 'b', 'c', 'd', 'a'}
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[20], line 22 19 e.update({'b', 'c', 'd', 'e', 'F'}) # update智能地更新集合的元素 20 print(e) ---> 22 s1 = {[1, 2], {1, 3}} # 试图以列表作为集合元素,会产生TypeError错误 TypeError: unhashable type: 'list'
6.8 字典类型(dict)¶
Python 中的字典类型用来表示一组键(key)值(value)对。字典类型的变量用花括号 {}
括起来,其中的每个键值对用逗号 , 隔开,每个键值对的键和值用冒号 :
隔开,例如 {'a': 1, 'b': 2, 'c': 3}
。
字典中的元素是无序的,可以通过键来访问其中的值,例如 a['a']
表示字典 a
中键为 'a'
的值,a['b']
表示字典 a
中键为 'b'
的值,以此类推。
在同一个字典中,键(key)必须是唯一的;如果一个 key 出现两次,后面的赋值会覆盖掉前面的。
字典的键(key)必须使用不可变类型,如数字、字符串、元组。而字典的值(value)可以是任意类型,包括字典类型本身。
和集合一样,字典也可以使用 update
方法来更新元素。
字典还有一个通过键来获取值的方法 get
。与通过中括号 [key]
来获取值相比,get
方法在输入不存在的键时不会报错,并且可以自定义键不存在时的返回值。
a = {} # 定义一个空字典
b = dict() # 这也是定义一个空字典
print(a, type(a))
print(b, type(b))
d1 = {'a':1, 'b':2, 'c':3} # 用大括号按固定格式定义字典
print(d1)
d2 = dict(year=2024, month=11, day=1, weather='sunny') # 用关键字参数定义字典,注意此时键只能为字符串且不加引号
print(d2)
print(d1.keys()) # 返回一个包含字典中的所有键的视图对象
print(d1.values()) # 返回一个包含字典中的所有值的视图对象
print(d1.items()) # 返回一个包含字典中的所有键值对的视图对象,其中每一组键值对以元组的形式呈现
# 由items()方法引申出来的一种字典定义方法:将若干个键值对元组构成的列表转换为dict类型
d3 = dict([('id', 666), ('username', 'Tom')])
print(d3)
d4 = {'Alice':{'age': 23, 'gender': 'female'}} # 值也可以是字典(字典的嵌套)
print(d4)
print(d1['b']) # 通过键来获取对应的值
d1['c'] = 5 # 通过键来修改对应的值
print(d1)
d1['d'] = 9 # 给不存在的键赋值会将该键值对加入字典中
print(d1)
d1.update({'a':2, 'd': 15})
print(d1)
print(d1.get('b', 99))
print(d1.get('z', 99)) # 试图获取不存在键的值,此时会返回给定值99
{} <class 'dict'> {} <class 'dict'> {'a': 1, 'b': 2, 'c': 3} {'year': 2024, 'month': 11, 'day': 1, 'weather': 'sunny'} dict_keys(['a', 'b', 'c']) dict_values([1, 2, 3]) dict_items([('a', 1), ('b', 2), ('c', 3)]) {'id': 666, 'username': 'Tom'} {'Alice': {'age': 23, 'gender': 'female'}} 2 {'a': 1, 'b': 2, 'c': 5} {'a': 1, 'b': 2, 'c': 5, 'd': 9} {'a': 2, 'b': 2, 'c': 5, 'd': 15} 2 99
小技巧:通过
get
方法还可以统计可重复对象中的某元素出现的个数(了解)
l1 = ['A', 'B', 'C', 'B', 'A', 'C', 'A']
d1 = {}
for item in l1:
d1[item] = d1.get(item, 0) + 1
print(d1)
{'A': 3, 'B': 2, 'C': 2}
6.9 空类型(None)¶
Python 中的 None 类型用来表示空值。
None是Python中的一个内置对象,表示空值或空类型。它不等于任何其他对象,包括空字符串、空列表或零。使用None作为变量的初始值,可以明确表达该变量目前没有绑定任何值。
a = None
print(a)
None
6.10 其他一些类型¶
除了上述常用的标准数据类型外,还存在一些的其他类型,后面会细说。
d1 = {'a':6, 'b':7}
print(type(d1.keys()))
print(type(d1.values()))
print(type(d1.items()))
def square(x):
return x * x
print(type(square))
class Student:
def __init__(self, name):
self.name = name
stu1 = Student('Robot')
print(type(Student))
print(type(stu1))
<class 'dict_keys'> <class 'dict_values'> <class 'dict_items'> <class 'function'> <class 'type'> <class '__main__.Student'>
a = [1, 2, 3, 2]
b = tuple(a)
print(b)
c = list(b)
print(c)
d = set(c) # 将其他元素可重复的对象转换成集合时会自动“合并同类项”,利用这个特性可以进行元素去重
print(d)
e = dict(a=6)
print(e)
s1 = '114514'
n1 = int(s1)
print(n1)
n2 = 3.14159265
s2 = str(n2)
print(s2, type(s2))
n3 = int(3.9) # float->int会舍去小数
print(n3)
(1, 2, 3, 2) [1, 2, 3, 2] {1, 2, 3} {'a': 6} 114514 3.14159265 <class 'str'> 3 5.0
6.12 变量(二)¶
说完了Python 中的数据类型,我们来讲讲Python 中变量的实质。
当我们同样创建一个变量a并给他赋值,内存会分配两个空间,一个空间存放这个值,另一个空间才是a真正的归宿,而a中存储的是这个值所在空间的地址,换个说法即a指向这个值所在空间。再换个亲切的说法,这其实就类似于C语言中的指针。
两个帮助你搞清Python 内存分配机制的技巧:
我们可以通过
id
函数来查看变量的“编号”(实际上是内存地址)。我们可以使用
is
运算符来判断两个变量是否引用同一个对象——这个很常用。
通过下面这个例子你会发现Python 中的变量存在一些奇怪的现象。
l1 = [1, 2, 3] # 现在我们定义一个列表
l2 = l1 # 将l1的内容赋给新变量l2
l2[2] = 4 # 改变l2中下标为2的元素
print(f'l1={l1}, l2={l2}') # 发现l1[2]的值也变了,为啥?
l1=[1, 2, 4], l2=[1, 2, 4]
那么就让我们来探究一下Python 中变量的底层逻辑。
a = 5 # 当我们定义一个变量a=5时,Python 会在电脑中寻找一块内存,把5写入这块内存,然后将a指向这块内存。
print(id(a))
a = 6 # 当我们将a的值改成6时,Python 会开辟一块新的内存,然后把6写入这块内存,接着让a转而指向这块内存。
print(id(a))
b = 6 # 定义一个同样为6的变量b,你会发现b指向的内存和a是同一块。
print(id(b))
print(a is b) # 事实上,a和b就是引用同一个对象的不同“别名”
a = 5
print(id(a)) # 把a改回5,发现其指回了之前那一块内存,其中的内容没有变化,仍然是5
140727755916200 140727755916232 140727755916232 True 140727755916200
上面这个例子实际上解释了两个问题:
- 因为给同一个变量赋不同的值时,Python 都会“另起炉灶”——开辟一块新的内存来存储新值,所以我们可以随时改变一个变量的数据类型而不用担心不同数据类型占用空间大小不同引起的危险。
虽然Python 中可以任意改变变量的数据类型,但在实际开发中这是非常不好的操作,应该保证一个确定的变量在整个生命周期中为同一个确定的数据类型。
- 因为给一个int、float、str、tuple类型的变量赋新值并不会改变原来那块内存中存储的值,所以说他们是不可变类型。
不可变类型 的实质:
- 只要变量的值改变,地址也跟着改变。
a = 5
b = a # 将a的值赋给b,他们将指向同一块内存
print(a is b)
b = 6 # 接着改变b的值,b的指向改变,而a不受影响
print(f'a={a}, b={b}')
True a=5, b=6 140727755916200 140727755916232
可变类型 的实质:
- 变量的内容改变时,地址不变,即可以对原地址中的值进行修改。
常见的可变类型 :list、set、dict
定义一个list类型时,系统会申请一段固定长度地址空间存放list,所以当需要添加删除元素时,只需要在申请好的内存空间内操作即可,地址不发生改变。
l1 = [1, 2]
print(id(l1))
l1.append(6)
l1[1] = 3 # 修改列表变量中的元素,其地址不变
print(id(l1))
2375939027392 2375939027392
对可变类型 的变量进行复制时,需要注意区分引用、 浅拷贝 和 深拷贝。
- 引用:使用
=
直接赋值,不仅复制了对象的内容,也复制了其内存地址。两者互相关联,互相影响。 - 浅拷贝:增加一个指向某个对象的地址,而不复制对象本身,新对象和原对象共享一块内存。
- 深拷贝:增加一个指向某个对象的地址并且申请一片新内存,新对象和原对象并不共享内存,二者没有任何关联也不会互相影响。
其中浅拷贝和深拷贝的区别在嵌套的可变类型对象中才会体现。
l1 = ['a', ['b', 'c', 'd'], ['e', 'f']]
l2 = l1 # l2是对l1的引用,实际上相当于给l1起了一个叫l2的别名,因此l1和l2完全是同一个对象。
print(l2 is l1)
l1.append(4)
l2.append(5)
print(l1, l2) # 不管是对l1还是l2操作其实是在操作同一个对象
l1 = ['a', ['b', 'c', 'd'], ['e', 'f']]
l2 = l1.copy() # list内置的copy()方法,仅对第一层为深拷贝,其他层均为浅拷贝
print(l2 is l1) # 可以发现拷贝得到的列表对象的地址和原列表不一样了
print(l1[1] is l2[1]) # 但是其中嵌套的列表依然在同一块内存中,如果修改l1中嵌套的列表,则l2中的对应值也会改变
l2 = l1[:] # 全默认值切片的方法,也是仅对第一层为深拷贝,其他层均为浅拷贝
l2 = []
for i in l1:
l2.append(i) # for循环拷贝法,也是仅对第一层为深拷贝,其他层均为浅拷贝
import copy
l2 = copy.copy(l1) # 使用copy模块中的copy()方法,还是仅对第一层为深拷贝,其他层均为浅拷贝
l2 = copy.deepcopy(l1) # 使用copy模块中的deepcopy()方法,对所有层均为深拷贝
l2[0] = 'g'
l2[1][0] = 'f' # 不管修改新列表中哪一层的元素,都不会对旧列表造成影响
print(f'l1={l1}, l2={l2}')
True ['a', ['b', 'c', 'd'], ['e', 'f'], 4, 5] ['a', ['b', 'c', 'd'], ['e', 'f'], 4, 5] False True l1=['a', ['b', 'c', 'd'], ['e', 'f']], l2=['g', ['f', 'c', 'd'], ['e', 'f']]
7 运算符¶
按运算优先级来排列:
()
括号可以强制改变运算优先级,括号内的表达式具有最高优先级。~
按位取反,涉及二进制运算,基本上用不到**
指数-
+
正负号,一般只用负号*
/
//
%
乘法、除法、取模(即求余数)+
-
加减法>>
<<
位移运算符,涉及二进制运算,基本上用不到&
^
|
按位与、按位或、按位异或,涉及二进制运算,基本上用不到>
>=
<
<=
==
!=
比较运算符,得到的结果为False/Trueis
is not
身份运算符,检查两个标识符是否引用同一个对象in
not in
成员运算符,检查元素是否存在于列表、元组、集合、字典、字符串中not
and
or
逻辑非,逻辑与,逻辑或=
+=
-=
*=
/=
//=
%=
**=
赋值运算符
# 加号
a = 3 + 4 # 数字的算术和
b = [1, 2] + [3, 4, 5] # 列表的拼接
c = (1, 2) + (3, 4, 5) # 元组的拼接
d = 'py' + 'thon' # 字符串的拼接
# 集合和字典不支持加法运算符
print(a, b, c, d)
# 减号
a = 3 - 4 # 数字的算术差
b = {1, 2} - {1, 3} # 集合求差集
print(a, b)
7 [1, 2, 3, 4, 5] (1, 2, 3, 4, 5) python -1 {2}
# 乘号
a = 3 * 4 # 数字的算术积
b = [1, 2] * 3 # 将列表中的元素的复制成三份(元组和字符串也可以)
c = [[1]] * 3 # 需要注意,如果列表中的元素是列表,那么这样复制的话其中的所有子列表都是同一个对象
c[0].append(2)
print(a, b, c)
print(id(c[0]), id(c[1]), id(c[2]))
# 对容器进行解包
s1 = 'python' # 对字符串解包,得到其中的所有元素
l1 = [1, 2, 3] # 对列表解包,得到其中的所有元素
t1 = (1, 2, 3) # 对元组解包,得到其中的所有元素
set1 = {1, 2, 3} # 对集合解包,得到其中的所有元素
d1 = {'a':1, 'b':2} # 对字典直接解包,得到的是所有的键
print(*s1)
print(*l1)
print(*t1)
print(*d1)
12 [1, 2, 1, 2, 1, 2] [[1, 2], [1, 2], [1, 2]] 1189223684608 1189223684608 1189223684608 p y t h o n 1 2 3 1 2 3 a b
# 除号
a = 3/4 # 真除法,得到的结果为商的精确值且为浮点型
b = 3//4 # 整除法,得到的结果是商的整数部分
print(a, b)
# 取余号
c = 15%4 # 19/4=4···3
print(c)
d = -15%4 # -19/4=-5···1 说明余数只取非负数
print(d)
0.75 0 3 1
# 比较运算符,得到的结果为Bool类型True/False
a = 5
print(4<a<6) # 在Python 中支持连续比较,等价于 a>4 and a<6
b = 5
c = 5
print(a==b==c) # 等价于a==b and b==c
# 使用不同的比较运算符连续比较也是被允许的,但是这样写可读性很差,不建议使用
print(6>a<7) # 等价于a<6 and a<7
True True True
# 成员运算符,得到的结果为Bool类型True/False
print(3 in [1, 2, 3])
print('d' in 'Hello World!')
print('a' in {'a':1, 'b':2}) # 如果in后面是字典,则只会判断前面的元素是否为字典的键
print(2 in {'a':1, 'b':2})
print(3 not in {4, 5})
# 注意in后面的对象必须是可迭代的,或者说能包含多个元素的类型,比如列表,元组,集合,字典等
True True True False True
# 逻辑运算符,具有惰性求值特点
# 首先需要知道的是,对于逻辑运算来说,参与的元素只有True和False两种,并且只有0相当于False,其他所有值都相当于True
print(not 5) # not表示逻辑非,会将True的东西转换为False,将False的东西转换为True
print(not 0)
# and表示逻辑与,就是我们通常所说的“且”,连接的两个表达式均为True时整个表达式才为True,否则为False
print('a' and 5) # 当and之前的表达式值为True时,整个表达式的值为and之后的表达式的值
print(0 and 6) # 当and之前的表达式值为False时,整个表达式的值为and之前的表达式的值
# or表示逻辑或,连接的两个表达式均为False时整个表达式才为False,否则为True
print(0 or [1, 2, 3]) # 当or之前的表达式值为False时,整个表达式的值为or之后的表达式的值
print(0 or False)
print(7 or 5) # 当or之前的表达式值为True时,整个表达式的值为or之前的表达式的值
False True 5 0 [1, 2, 3] False 7
# 赋值运算符
a = 5 # 给变量赋值
b = a
a += 3 # 变量自身加3
b = b + 3 # 和上式结果一样,但实现过程不同。不同在哪呢?在不可变类型上看不出来,我们用可变类型的对象试试
l1 = [1, 2]
print(id(l1))
l1 += [3]
print(l1,id(l1)) # 用+=符号赋值,是改变对象自身的值。
l2 = [1, 2]
print(id(l2))
l2 = l2 + [3] # 用=符号赋值,是创建了一个新的对象
print(l2,id(l2))
# 其余算术运算也可有赋值符号,如-=,*=,/=,%=
1307121602176 [1, 2, 3] 1307121602176 1307121597120 [1, 2, 3] 1307120987712
实际上,赋值运算符 =
的玩法远不止于此。下面的这些可能会让你怀疑人生。
a, b = 10, 20 # 同时分别给a和b赋值
print(a, b)
a, b = b, a # 无需中间变量直接交换a和b的值
print(a, b)
a, b = (6, 7) # 将序列中的元素依此赋给a和b,需要保证序列中元素的数量和变量的数量一致
print(a, b)
a, b, *c = [1, 2, 3, 4, 5] # 将序列中的前两个元素赋值给a和b,将剩余的元素以列表形式赋值给c,需要使用*解包运算符
print(a, b, c)
a, b, *c = (1, 2, 3, 4, 5) # 虽然原序列为元组,但是剩余的元素依然以列表形式赋值给c
print(a, b, c)
# 将序列中的前三个元素赋值给a、b和c,将最后一个元素赋值给d,剩余元素以列表形式赋给c
a, b, *c, d = (1, 2, 3, 4, 5, 6)
print(a, b, c, d)
10 20 20 10 6 7 1 2 [3, 4, 5] 1 2 [3, 4, 5] 1 2 [3, 4, 5] 6
a=input('请输入:') # 读取输入的内容,转换为字符串
if a=='1':
print('a is 1')
elif a.isnumeric(): # isnumeric是str内置函数,判断字符串里的内容是否是数字
print('a is a number, but not 1')
else:
print('a is not a number')
a is a number, but not 1
到这里我们接触到了Python 中一个极为重要的语法:缩进。
Python 中的缩进通常是用于标识代码块的开始和结束,类似其他编程语言中的花括号{}。连续的具有同样缩进量的代码会被认为是一个代码块。
缩进可以用空格 Space
或制表符 Tab
来实现,其中制表符在表现形式上等价于若干个空格。在Python 规范中一般建议使用4个空格来表示一个缩进,因为这样可以提高代码的跨平台兼容性。但是,从偷懒的角度出发(bushi),大家可能会更喜欢用制表符来表示缩进。
制表符显示大小可以改动,一般建议设置为4个空格(代码缩进层次较少时)或2个空格(代码缩进层次较多时)。
在同一Python 文件中,务必保证所有层次的缩进量相同,否则会严重影响程序可读性,甚至导致语法错误。
a = -5
if a > 0:
print("a is positive")
else:
print("a is not positive")
print("error!")
print("Please give a positive number")
print("End of program")
a is not positive error! Please give a positive number End of program
if、else语句还有一种三目运算符的用法:
a = 5
b = 7
max_num = a if a > b else b # 相当于C语言中的a>b?a:b
print(max_num)
7
8.3 循环结构¶
循环结构是编程语言中最基本的控制结构之一。循环结构的作用是重复执行某段代码,直到满足某种条件为止。
Python中常用的循环结构有for循环和while循环(while循环其实不咋用)。这两种循环结构都可以用来遍历序列(如列表、元组、字符串)、执行固定次数的循环、或根据条件判断执行循环。
a = 20
while a > 0: # while后面跟一个表达式,当表达式为True时,循环继续执行,当表达式为False时,循环结束。
print("上学哪有不疯的,硬撑罢了!")
a -= 1
上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了! 上学哪有不疯的,硬撑罢了!
a = [1,2,3,4,5,6,7,8,9,10]
while a: # while后面跟一个可迭代序列,可以进行遍历
print(a.pop(),end=' ')
print(a)
10 9 8 7 6 5 4 3 2 1 []
是不是有点扯,好像还是下面这种方法更合理一点。
a=[1,2,3,4,5,6,7,8,9,10]
for i in a: # 对于在列表a中的每一个元素i,执行以下语句
print(i, end=' ')
print(a)
1 2 3 4 5 6 7 8 9 10 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a={'a':1, 'b':2, 'c':3, 'd':4, 'e':0}
# 遍历字典的时候可以使用我们之前提到过的几种方法来处理不同的需求。
for i in a:
print(i, end=' ')
print()
for i in a.keys():
print(i, end=' ')
print()
for i in a.values():
print(i, end=' ')
print()
for key, value in a.items(): # 如果每次遍历得到的对象是序列,可以分别获取其中的所有元素
print(key, value, end=' ')
print()
for i in a.items():
print(i , end=' ')
print()
a, b = (1, 2)
print(a, b)
a b c d e a b c d e 1 2 3 4 0 a 1 b 2 c 3 d 4 e 0 ('a', 1) ('b', 2) ('c', 3) ('d', 4) ('e', 0) 1 2
for循环在遍历序列时,比while循环有更高的自由度,但有个问题,如果要指定循环次数怎么办呢?
Python提供了 range()
函数来生成一个整数序列,可以用for循环来遍历这个序列。
range()
函数可以接受三个参数,第一个参数是起始值,第二个参数是终止值(不包含),第三个参数是步长(默认为1)。
例如,range(1, 10, 2)
生成的序列是 1, 3, 5, 7, 9
。
for i in range(10): # 只给一个参数,则认为给的值为终止值,起始值为0,步长为1
print(i, end=' ')
print()
for i in range(10, 20): # 给两个参数,则认为给的第一个值为起始值,第二个值为终止值,步长为1
print(i, end=' ')
print()
for i in range(-20, 100, 15): # 给三个参数,则认为给的第一个值为起始值,第二个值为终止值,第三个值为步长4
print(i, end=' ')
print()
for i in range(15, 5, -2): # 步长可以为负
print(i, end=' ')
print()
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 -20 -5 10 25 40 55 70 85 15 13 11 9 7
有了条件判断语句和循环语句,我们就可以创建任意初值的列表、字典、集合等数据结构,并对其进行操作了。
举个例子,如果想要创建一个含有0~20中所有奇数的列表,可以这样实现:
l1 = [] # 先定义一个空列表
for i in range(20):
if i % 2 == 1:
l1.append(i) # 奇数添加到列表中
print(l1)
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
但是这样感觉有点麻烦唉,好在Python 还提供了一种列表推导式的写法,可以快速生成列表。下面这种写法和上面的代码完全等效,但代码更为简介,在条件判断不过于复杂的情况下性能也更为优秀。
l1 = [i for i in range(20) if i%2==1]
print(l1)
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
注意这种方法既然叫列表推导式,就说明是只有列表才有的魔法,其余容器类型都没有。
如果你不信的话,可以把列表推导式中的中括号 []
换成 ()
试试,看看会有什么不同。
改完之后,你会发现,我们得到了一个叫做“生成器”的对象。生成器是一种特殊的迭代器,它可以生成一系列值,但并不存储所有值。
- 生成器的优点是它节省了内存,因为它只生成需要的值,而不是一次性生成所有值。
- 生成器的缺点是它只能用于迭代,不能用于索引。
g1 = (i for i in range(20) if i%2==1)
print(g1)
print(next(g1)) # 使用next()函数获取生成器的第一个值
print(next(g1)) # 使用next()函数获取生成器的下一个值
print(list(g1)) # 将生成器转换为列表,可以生成过的值都已经被删除了
print(list(g1)) # 上面的行为获取了生成器中所有的剩余值,因此生成器已经空了
<generator object <genexpr> at 0x000002C60E4D7440> 1 3 [5, 7, 9, 11, 13, 15, 17, 19] []
循环有一个break语句,用于提前结束循环。
for i in range(100, 1000):
num_str = str(i)
d1, d2, d3 = int(num_str[0]), int(num_str[1]), int(num_str[2])
if d1**3 + d2**3 + d3**3 == i:
print(f"最小的水仙花数是:{i}")
break
最小的水仙花数是:153
还有一个continue语句,可以跳过本次循环的剩余语句,直接进入下一次循环。
for i in range(100, 1000):
num_str = str(i)
d1, d2, d3 = int(num_str[0]), int(num_str[1]), int(num_str[2])
if d1**3 + d2**3 + d3**3 != i: # 判断不是水仙花数,就跳出本次循环,不打印
continue
print(i) # 是水仙花数就打印
153 370 371 407
def func():
print("这是一个函数")
print(type(func))
print(func)
func()
<class 'function'> <function func at 0x00000215C7187F60> 这是一个函数
函数定义的时候不会执行函数体中的代码,只有在调用函数的时候才会执行。
函数可以有参数,也可以有返回值(用return语句),例如:
def add(a, b):
return a + b
print(add(2, 3))
print(add('Hello ', 'World'))
5 Hello World <function add at 0x00000215C7186340>
如果一个函数在定义时没有给定返回值,则默认返回None。
def func1():
pass # 表示一个空的代码段
print(func1())
None
在Python 中,函数的参数在定义和使用时有以下几种类型:
位置参数:是指在函数调用时,按照顺序传入函数的参数。例如,在函数
add(a, b)
中,a
和b
是位置参数。默认参数:是指在函数定义时,给函数指定默认值的参数。例如,在函数
add(a, b=1)
中,b
是默认参数,默认值为1。当用户没有传入b
的值时,默认值1会被使用。关键字参数:是指调用函数时,可以通过关键字指定参数的值,这样即使参数的位置改变,只要关键字不变,仍然可以正确传递参数值。例如,
add(b=2, a=1)
和add(a=1, b=2)
是等价的。接收任意数量的位置参数:是指在函数定义时,使用
*args
作为参数名,表示接收任意数量的位置参数。例如,在函数add(*args)
中,*args
表示接收任意数量的位置参数。接收任意数量的关键字参数:是指在函数定义时,使用
**kwargs
作为参数名,表示接收任意数量的关键字参数。例如,在函数add(**kwargs)
中,**kwargs
表示接收任意数量的关键字参数。
def add(a, b):
return a + b
print(add(2, 3))
def subtract(a, b=1):
return a - b
print(subtract(5))
def multiply(a, b):
return a * b
print(multiply(b=4, a=5))
def summation(*args): # 接收任意数量的位置参数一般习惯用args这个名字,当然其他名字也可以
result = 0
for i in args:
result += i
return result
print(summation(1, 2, 3, 4, 5))
def product(**kwargs): # 接收任意数量的关键字参数一般习惯用kwargs这个名字,当然其他名字也可以
result = 1
for i in kwargs.values():
result *= i
return result
print(product(a=2, b=3, c=4, d=5))
def all_kinds_of_args(a, b=1, *args, **kwargs):
print(a, b, args, kwargs)
all_kinds_of_args(1, 2, 3, 4, 5, x=6, y=7) # args打印出来是一个元组,kwargs打印出来是一个字典
5 4 20 15 120 1 2 (3, 4, 5) {'x': 6, 'y': 7}
如果要定义一个具有多种上述类型参数的函数,需要主要排列参数的顺序,必须按照上面介绍时的顺序来定义。
函数的参数可以是任何类型,但是如果以列表等可变类型作为默认参数,函数内对参数的修改将影响到下一次调用。
def func1(l = []):
l.append(1)
return l
print(func1())
print(func1())
print(func1())
[1] [1, 1] [1, 1, 1]
这大概率不是我们要的效果,要使得可变类型默认参数在每次调用函数时都维持默认值,需要在函数开始前重置一下默认值。
def func1(l = []):
l = []
l.append(1)
return l
print(func1())
print(func1())
print(func1())
[1] [1] [1]
函数的返回值也可以是任何类型,甚至是多个值。
def func1(a, b, c):
return a, b, c
print(func1(1, 2, 3)) # 返回多个值,则会用一个元组包裹起来
(1, 2, 3)
还有一种lambda匿名函数。只需要使用一次的函数,可以用lambda表达式来定义。:
前面表示参数,后面表示返回值。
l1 = [1, 2, 3, 4, 5]
print(list(filter(lambda x: x % 2 == 0, l1)))
# lambda x: x % 2 == 0 等价于下面这个函数
def is_even(x):
return x % 2 == 0
print(list(filter(is_even, l1)))
[2, 4] [2, 4]
10 模块和包¶
模块 就是一个Python文件,其中具有一定功能的变量、函数和类,可以被其他程序导入使用。
包 就是一个文件夹(其中必须包含一个__init__.py文件),其中包含多个模块 ,也可以被其他程序导入使用。
Python 中有一些内置的模块,例如 math
模块,可以用来进行数学运算。而更多的模块可以通过 pip
工具来安装。
导入模块和包使用 import
语句,有以下几种用法:
# 引入模块中指定的东西
from math import sin, cos, pi
print(sin(pi/6), cos(pi/6))
# 引入模块中所有的东西
from math import *
print(exp(1), log(10), sqrt(2))
# 引入模块中所有的东西,调用时必须使用指定模块名
import random
print(random.randint(1, 10))
# 导入模块时,可以给模块指定别名
import random as rd
print(rd.randrange(1, 10))
0.49999999999999994 0.8660254037844387 2.718281828459045 2.302585092994046 1.4142135623730951 4 2
pip
是 Python 的包管理工具,可以用来安装、卸载、管理 Python 包。
常用的相关命令如下:
pip list # 列出已安装的包
pip install package_name # 安装包
pip uninstall package_name # 卸载包
pip show package_name # 查看包的详细信息
11 虚拟环境¶
在Python中,虚拟环境(Virtual Environment)是一个独立的、隔离的Python运行环境,它拥有自己的Python解释器、第三方库和应用程序。通过创建虚拟环境,可以确保项目之间的依赖关系不会相互干扰,每个项目都可以使用自己独立的Python解释器和第三方库版本。
11.1 虚拟环境的特点¶
- 隔离性:每个虚拟环境都是独立的,互不影响。这意味着在一个虚拟环境中安装的Python包不会影响其他虚拟环境或全局Python环境。
- 可定制性:可以根据项目的需求,为每个虚拟环境选择特定的Python版本和安装所需的第三方包。
- 可复制性:虚拟环境可以轻松地复制和迁移到其他机器上,确保在不同环境中的一致性。
- 易于管理:通过激活和停用虚拟环境,可以方便地切换到不同的Python项目环境。
11.2 虚拟环境的创建¶
这里介绍一种比较简便的方案。
11.2.1 通过Python标准库venv来创建¶
venv 模块支持创建轻量的“虚拟环境”,每个虚拟环境将拥有它们自己独立的安装在其 site 目录中的 Python 软件包集合。 虚拟环境是在现有的 Python 安装版基础之上创建的,这被称为虚拟环境的“基础”Python,并且还可选择与基础环境中的软件包隔离开来,这样只有在虚拟环境中显式安装的软件包才是可用的。 当在虚拟环境中使用时,常见安装工具如 pip 将把 Python 软件包安装到虚拟环境而无需显式地指明这一点。
在命令行中执行 venv 指令来创建一个 虚拟环境 :
python -m venv [virtual environment]
切换到虚拟环境(Command Prompt/PowerShell):
[virtual environment]/Scripts/activate [virtual environment]
切换到虚拟环境(Git Bash/Bash):
source [virtual environment]/bin/activate
退出虚拟环境:
deactivate
在命令行中输入长文件名时,只需要给出前几个字母,然后按 Tab 键即可自动补全。在一些高级终端中,很多命令也支持 Tab 键自动补全。
常见终端:
- Command Prompt (Windows自带,比较原始)
- PowerShell (Windows自带,可以作为Command Prompt的上位替代)
- Git Bash (安装Git后自带,比PowerShell更好用,和Bash差不多)
- Bash (Linux/Mac自带,功能非常强大)
Vs Code中按 Ctrl
+ `
打开终端后右上角的 + 号处的下拉菜单中可以选择不同终端,同时可以更改默认终端。
12 文件操作¶
Python提供了对文本文件、二进制文件的操作,包括读写、创建、删除等。
Python中使用open()
函数来打开文件,并返回一个文件对象,可以对文件进行读写操作。
读写文件的模式有好几种:
表格:
模式 | 描述 |
---|---|
r | 读取模式,文件必须存在,否则报错 |
w | 写入模式,文件不存在则创建,存在则覆盖 |
a | 追加模式,文件不存在则创建,存在则在末尾追加 |
r+ | 读写模式,文件必须存在,否则报错 |
w+ | 读写模式,文件不存在则创建,存在则覆盖 |
a+ | 读写模式,文件不存在则创建,存在则在末尾追加 |
b | 二进制模式,用于读写二进制文件 |
需要注意的是,打开文本操作完成后,必须关闭文件,否则会出现资源泄露。
f = open("test.txt", "w")
f.write("Hello World!")
f.close()
f = open("test.txt", "r")
print(f.read())
f.close()
Hello World!
实际使用中对文件进行一大串操作后很容易忘记关闭,好在Python提供了 with
语句来自动处理文件的开关。
下面的代码和上面的是完全等效的,with
语句会自动关闭文件。因此,推荐使用 with
语句来处理文件。
with open('test.txt', 'w') as f:
f.write('Hello, world!')
with open('test.txt', 'r') as f:
print(f.read())
除此以外,Python还可以处理一些其他类型的文件,比如我们后面很可能会用到的Excel文件。
有很多第三方库可以处理Excel文件,比如openpyxl、xlrd、xlwt... 强大的数据分析库Pandas也可以用来处理Excel文件。这里我们用Pandas来试试。
执行下面的代码需要先安装Pandas和openpyxl。
import pandas as pd
df = pd.read_excel('data.xlsx')
print(df)
# 将整个DataFrame转换为一个字典,列名为键,数据列为值
df_dict = df.to_dict()
print(df_dict)
# 将每一行转换为一个字典,列名为键,行数据为值
df_dict = df.to_dict('records')
print(df_dict)
# 将列转换为字典,列名为键,单个值为值
df_dict = df.to_dict('list')
print(df_dict)
姓名 学号 班级 绩点 名次 0 张三 202500998877 自动化25.1 99.7 3 1 李四 202500996655 自动化25.2 99.8 2 {'姓名': {0: '张三', 1: '李四'}, '学号': {0: 202500998877, 1: 202500996655}, '班级': {0: '自动化25.1', 1: '自动化25.2'}, '绩点': {0: 99.7, 1: 99.8}, '名次': {0: 3, 1: 2}} [{'姓名': '张三', '学号': 202500998877, '班级': '自动化25.1', '绩点': 99.7, '名次': 3}, {'姓名': '李四', '学号': 202500996655, '班级': '自动化25.2', '绩点': 99.8, '名次': 2}] {'姓名': ['张三', '李四'], '学号': [202500998877, 202500996655], '班级': ['自动化25.1', '自动化25.2'], '绩点': [99.7, 99.8], '名次': [3, 2]}
这里面的操作太多了,大家要学会自己查阅资料。