Appearance
全局配置
Appearance
Life is short, you need Python.
人生苦短,我用Python。

简洁胜于复杂
做肯定要比不做强,但还没准备好就去做也并非是一件好事。
Python是一门编程语言,它只是众多编程语言中的一种
语法简洁、优雅、编写的程序容易阅读。
跨平台,可以运行在Windows、Linux 以及 MacOS 等操作系统上。
易于学习。站在非计算机专业的角度来讲,如果把编程语言当做解决问题的工具,Python确实相较于C++、Java、 JavaScript等语言要易于学习和掌握。
极为强大而丰富的标准库与第三方库,比如电子邮件,比如图形GUI界面。
Python是面向对象的语言。
简洁,灵活,优雅,哲学(Python之禅)
易于上手难于精通
Python即有动态脚本的特性,又有面向对象的特性,非常具有自己的特点
运行速度慢,适合处理大数据量的计算任务。
内存管理困难,容易出现内存泄漏。
动态类型,不支持多重继承。
缺乏静态类型,不支持类型检查。
解释型语言,运行速度慢。(运行效率与开发效率,鱼与熊掌不可兼得)
编译型语言 (C、C++)、解释型语言 (Javascript、Python)
世界不是只有网站,还有很多问题需要使用编程来解决。
Web是基础
本质在于这是一个互联网的时代,有网络的地方就需要Web,Web编程确实是最好的语言学习实践。
当你遇到问题时,随手拿起Python,编写一个工具,这才是 Python正确的打开方式。
爬虫
大数据与数据分析(Spark)
自动化运维与自动化测试
Web开发:Flask、Django
机器学习:TensorFlow、Scikit-learn
胶水语言:混合其他如C++、Java等来编程。能够把用其他语言制作的各种模块(尤其是C/C++)很轻松地联结在一起。
脚本处理:Python可以用来写各种脚本,比如自动化运维脚本、自动化测试脚本、数据处理脚本等。

IDLE是“Integrated Development and Learning Environment”的缩写,中文意思是“集成开发与学习环境”。IDLE是Python编程语言的官方集成开发环境(IDE),提供了用于编写、运行和调试Python代码的功能。
可能默认MacOS是python2版本,打开终端,输入IDLE,自动弹出IDLE。
如果是python3版本,在终端输入IDLE3
执行 print ('Hello, World')

什么是代码?
代码是现实世界事物在计算机世界中的映射
什么是写代码?
写代码是将现实世界中的事物用计算机语言来描述
Python的整型有四种:
布尔型只有True和False (首字母必须大写)两个值,可以用and、or、not运算符进行逻辑运算。
在python中,0、None、空字符串''、空列表[]、空元组()、空字典{}都被视为False,其他值都视为True。

二进制表示:0b开头
八进制表示:0o开头
十六进制表示:0x开头
bin(num)函数:将num转换为二进制字符串,如bin(10)返回'0b1010'
oct(num)函数:将num转换为八进制字符串,如oct(10)返回'0o12'
hex(num)函数:将num转换为十六进制字符串,如hex(10)返回'0xa'
int(num)函数,将num转换为十进制整数,如int(10.5)返回10,int(0b10)返回2,int(0x10)返回16。
int(str, base)函数,将字符串str转换为整数,base为进制数,如int('1010', 2)返回10。
base默认为10,如int('10')返回10。
如果字符串以0b开头、0o开头或0x开头,则base默认为2、8或16。如int('0b1010')返回10,int('0o12')返回10,int('0xa')返回10。

浮点型的精度问题:
注意python3中,整数的除法结果默认是浮点型,需要使用//来进行整数除法。(// 结果仅保留整数部分)


在Python中,复数(Complex Numbers)是一种数值类型,用于表示具有实部和虚部的数值。复数由一个实部和一个虚部组成,形式为 a + bj,其中 a 表示实部,b 表示虚部,而 j 表示虚数单位,满足 j^2 = -1。
Python中的复数可以通过内置的complex()函数创建,也可以直接使用虚数单位j来创建。
创建复数:
使用complex()函数创建复数,或者直接使用虚数单位j来创建:
num1 = complex(2, 3) # 2 + 3j
num2 = 1 + 2j获取实部和虚部:
使用.real属性获取复数的实部,使用.imag属性获取虚部:
real_part = num1.real # 2.0
imag_part = num1.imag # 3.0复数运算:
复数可以进行基本的算术运算,包括加法、减法、乘法和除法:
sum_result = num1 + num2
diff_result = num1 - num2
prod_result = num1 * num2
div_result = num1 / num2复数操作:
Python提供了许多操作来处理复数,例如共轭复数(.conjugate()方法)、复数的模(abs()函数)、复数的相位角(使用cmath.phase()函数)等。
复数的相位角,也称作辐角,是指复数在复平面上的位置角度。对于复数 z = a + bi (其中 a, b 属于实数集 R ),其相位角 W 的计算方法是 tan(W) = b / a 。相位角有多个可能的值,因为相位是可以周期性变化的,通常可以加上 2πk ( k 是整数)来表示所有可能的相位值。
复数的模是指复数在复平面上的距离,对于复数 z = a + bi (其中 a 和 b 是实数),其模可以通过公式 sqrt(a^2 + b^2) 直接计算得出。
conjugate_num = num1.conjugate() # 共轭复数
abs_num = abs(num1) # 复数的模
phase_angle = cmath.phase(num1) # 复数的相位角和JavaScript一样,字符串可以用单引号或双引号括起来,但不能同时使用。某些情况下,单引号和双引号的使用可以互换。仅使用单引号或者双引号情况下,可以使用转义字符\来转义单引号或双引号。

Python 3版本中,字符串是以Unicode编码的,也就是说,Python的字符串支持多语言。
Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符。
如果知道字符的整数编码,还可以用十六进制这么写str。
由于Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。
Python对bytes类型的数据用带b前缀的单引号或双引号表示。
注意区分'ABC'和b'ABC',前者是str,后者虽然内容显示得和前者一样,但bytes的每个字符都只占用一个字节。
以Unicode表示的str通过encode()方法可以编码为指定的bytes
纯英文的str可以用ASCII编码为bytes,内容是一样的,含有中文的str可以用UTF-8编码为bytes。含有中文的str无法用ASCII编码,因为中文编码的范围超过了ASCII编码的范围,Python会报错。
在bytes中,无法显示为ASCII字符的字节,用\x##显示。
反过来,如果我们从网络或磁盘上读取了字节流,那么读到的数据就是bytes。要把bytes变为str,就需要用decode()方法。
要计算str包含多少个字符,可以用len()函数。
len()函数计算的是str的字符数,如果换成bytes,len()函数就计算字节数。
1个中文字符经过UTF-8编码后通常会占用3个字节,而1个英文字符只占用1个字节。

由于Python源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为UTF-8编码。当Python解释器读取源代码时,为了让它按UTF-8编码读取,我们通常在文件开头写上这两行。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释;
第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。
申明了UTF-8编码并不意味着你的.py文件就是UTF-8编码的,必须并且要确保文本编辑器正在使用UTF-8编码。
如果.py文件本身使用UTF-8编码,并且也申明了# -- coding: utf-8 --,打开命令提示符测试就可以正常显示中文。
在Python中,可以使用三引号('''或""")来表示多行字符串。

Python中的转义字符主要用于插入一些特殊字符或者行为,不能直接通过普通的键入输入。以下是一些常见的Python转义字符及其用途:
\n:换行符。\\:反斜杠符号。\':单引号。\":双引号。\\a:响铃。\\b:退格。\\t:水平制表符。\\r:回车符。\\f:换页符。\\v:垂直制表符。这些转义字符在编写字符串时非常有用,尤其是在引用字符串。
在Python中,\a 是一个转义字符,代表 响铃字符(Bell character)。这个字符的功能是通过计算机的扬声器发出一个短促的“嘟”的声音,通常用于提醒用户某个事件发生。
print("Hello, World!\a")运行上述代码时,如果你的系统支持响铃字符,你可能会听到一个嘟的声响。
并不是所有的操作系统和终端都支持响铃字符,所以实际效果可能因环境而异。
在一些现代计算机和环境中,响铃功能可能被禁用或未实现,因此有可能听不到声音。
在Python中,有一种字符串前面加上r或R,表示原始字符串,即字符串中的字符串表示方式不发生转义。
print(r'\n') # 输出 \nPython支持字符串的拼接、重复、切片、比较、成员关系测试等运算。
字符串的拼接可以使用加号(+)运算符,也可以使用join()方法。
str1 = "Hello"
str2 = "World"
str3 = str1 + " " + str2
print(str3) # 输出 "Hello World"
str4 = "Python"
str5 = "is" + " " + "awesome"
str6 = str4 + str5
print(str6) # 输出 "Python is awesome"
str7 = "Hello"
str8 = "World"
str9 = " ".join([str7, str8])
print(str9) # 输出 "Hello World"字符串的重复可以使用乘号(*)运算符。
str1 = "Hello"
str2 = str1 * 3
print(str2) # 输出 "HelloHelloHello"字符串的切片可以使用方括号([])运算符。 [a🅱️c]表示从索引a开始,到索引b-1结束,每隔c取一个元素。 a、b、c可以是整数、负数、字符串、None。 如果a省略,表示从头开始;如果b省略,表示到结尾;如果c省略,表示取一个元素。 c为负数时,表示逆序切片。
str1 = "Hello, World!"
str2 = str1[0:5] # 输出 "Hello"
str3 = str1[7:12] # 输出 "World"
# 表示从头到尾取字符串str1的元素,步长为-1,即从右向左每次取一个元素,最终得到的结果为字符串str1的反向字符串。
str4 = str1[::-1] # 输出 "!dlroW ,olleH"
# 每隔一个字符取一个子串
str5 = "abcdefg"
str6 = str5[::2]
print(str6) # 输出 "aceg"
# 从倒数第二位开始每隔两个字符取一个子串
str7 = "123456789"
str8 = str7[-2::-2]
print(str8) # 输出 "8642"
# 从第二位开始到倒数第二位,每隔三个字符取一个子串:
str9 = "abcdefghij"
str10 = str9[1:-1:3]
print(str10) # 输出 "beh"
# 取字符串的奇数索引位置字符
str11 = "abcdefg"
str12 = str11[1::2]
print(str12) # 输出 "bdf"字符串的成员关系测试可以使用 in 和 not in 运算符。
str1 = "Hello, World!"
if "H" in str1:
print("H is in str1") # 输出 "H is in str1"
if "M" not in str1:
print("M is not in str1") # 输出 "M is not in str1"Python的字符串类型支持比较运算符,可以比较两个字符串的大小。字符串的比较基于字符的 ASCII 值。
:比较两个字符串的大小,如果第一个字符串大于第二个字符串,则返回True。
=:比较两个字符串的大小,如果第一个字符串大于等于第二个字符串,则返回True。
str1 = "Hello, World!"
str2 = "Hello, World!"
if str1 == str2:
print("str1 is equal to str2") # 输出 "str1 is equal to str2"
# 字符 'P' 的 ASCII 值为 80
# 字符 'J' 的 ASCII 值为 74
# 在字符串比较中,确实是从第一个字符开始逐个比较它们的 ASCII 值。根据 ASCII 值,'J' (74) < 'P' (80)。因此,"Python" 大于 "Java"。
str3 = "Python"
str4 = "Java"
if str3 > str4:
print("str3 is more than str4") # 输出 "str3 is more than str4"字符串的格式化可以使用%运算符。
在字符串内部,%s表示用字符串替换,它会把任何数据类型转换为字符串,%d表示用整数替换,有几个%?占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个%?,括号可以省略。
name = "John"
age = 30
print("My name is %s and I am %d years old." % (name, age)) # 输出 "My name is John and I am 30 years old."
'Age: %s. Gender: %s' % (25, True) # 输出 'Age: 25. Gender: True'
# 格式化整数和浮点数还可以指定是否补0和整数与小数的位数
print('%.2f' % 3.1415926) # 输出 3.14
print('%06d' % 12) # 输出 000012
print('%2d-%02d' % (3, 1)) # 输出 3-01| 占位符 | 替换内容 |
|---|---|
| %c | 单个字符 |
| %d | 整数 |
| %f | 浮点数 |
| %s | 字符串 |
| %x | 十六进制整数 |
| %o | 八进制整数 |
另一种格式化字符串的方法是使用字符串的format()方法,它会用传入的参数依次替换字符串内的占位符{0}、{1}……,不过这种方式写起来比%要麻烦得多。
name = "John"
age = 30
print("My name is {} and I am {} years old.".format(name, age)) # 输出 "My name is John and I am 30 years old."
'Age: {}. Gender: {}'.format(25, True) # 输出 'Age: 25. Gender: True'
# 格式化整数和浮点数还可以指定是否补0和整数与小数的位数
print('{:.2f}'.format(3.1415926)) # 输出 3.14
print('{0:06d}'.format(12)) # 输出 000012
print('{0:2d}-{1:02d}'.format(3, 1)) # 输出 3-01Python的字符串类型提供了很多方法,可以对字符串进行各种操作。
str1 = "Hello, World!"
print(str1.upper()) # 输出 "HELLO, WORLD!"
print(str1.lower()) # 输出 "hello, world!"
print(str1.replace("H", "J")) # 输出 "Jello, World!"
str2 = "Python is awesome"
print(str2.split()) # 输出 ['Python', 'is', 'awesome']
print(str2.split("o")) # 输出 ['Pythn', ' is awe', 'm']有三种基本序列类型:list, tuple 和 range 对象。
list是一种有序的集合,是用方括号标注,逗号分隔的一组值。列表 可以包含不同类型的元素,list元素也可以是另一个list,但一般情况下,各个元素的类型相同。列表可以随时添加和删除其中的元素。
和字符串(及其他内置 sequence 类型)一样,列表也支持索引和切片
squares = [1, 4, 9, 16, 25]
squares[0] # 索引操作将返回条目
# 1
squares[-1]
# 25
squares[-3:] # 切片操作将返回一个新列表
# [9, 16, 25]列表还支持合并操作,用加号(+)运算符将两个列表合并成一个新的列表。
squares + [36, 49, 64, 81, 100]
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]cubes = [1, 8, 27, 65, 125]
cubes[3] = 64
print(cubes) # [1, 8, 27, 64, 125]可以在通过使用 list.append() 方法,在列表末尾添加新条目
cubes.append(216) # 添加 6 的立方
cubes.append(7 ** 3) # 和 7 的立方
print(cubes) # [1, 8, 27, 64, 125, 216, 343]要删除list末尾的元素,用pop()方法。
要删除指定位置的元素,用pop(i)方法,其中i是索引位置。
cubes.pop() # 删除末尾元素
print(cubes) # [1, 8, 27, 64, 125, 216]
cubes.pop(2) # 删除索引为 2 的元素
print(cubes) # [1, 8, 64, 125, 216]要在指定位置插入元素,用insert(i, x)方法,其中i是索引位置,x是要插入的元素。
cubes.insert(2, 3 ** 3) # 在索引为 2 的位置插入 3 的立方
print(cubes) # [1, 8, 27, 64, 125, 216]要移除指定元素,用remove()方法。 注意,remove()方法只删除第一个匹配的元素。
cubes.remove(27) # 移除索引为 2 的元素
print(cubes) # [1, 8, 64, 125, 216]列表还支持排序操作,用sort()方法。
numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
numbers.sort()
print(numbers) # 输出 [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]
fruits = ['banana', 'apple', 'cherry', 'orange']
fruits.sort()
print(fruits) # ['apple', 'banana', 'cherry', 'orange']列表还支持反转操作,用reverse()方法。
fruits.reverse()
print(fruits) # ['orange', 'cherry', 'banana', 'apple']列表支持遍历操作,用for循环或其他迭代器。
fruits = ['banana', 'apple', 'cherry', 'orange']
for fruit in fruits:
print(fruit)列表推导式是一种创建新列表的简洁方式。 它由一个表达式、一个或多个循环和可选的过滤条件组成。 列表推导式与普通循环相比,代码更简洁,更易于阅读。
# 计算 1 到 10 的平方
squares = [x ** 2 for x in range(1, 11)]
print(squares) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
fruits = ['banana', 'apple', 'cherry', 'orange']
new_fruits = [fruit.upper() for fruit in fruits]
print(new_fruits) # ['BANANA', 'APPLE', 'CHERRY', 'ORANGE']元组是另一种有序的集合,是用圆括号标注,逗号分隔的一组值。元组与列表类似,但元组是不可变的,不能添加或删除元素,一旦初始化就不能修改。
列出同学的名字的元组:
# 当你定义一个tuple时,在定义的时候,tuple的元素就必须被确定下来
students = ('Alice', 'Bob', 'Charlie', 'David')
# 可以正常地使用classmates[0],classmates[-1],但不能赋值成另外的元素。
# 只有1个元素的tuple定义时必须加一个逗号,来消除歧义,以免你误解成数学计算意义上的括号。
single_tuple = ('a',)# tuple定义的时候有3个元素,分别是'a','b'和一个list
t = ('a', 'b', ['A', 'B'])
t[2][0] = 'X'
t[2][1] = 'Y'
print(t)
# ('a', 'b', ['X', 'Y'])
# tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向'a',就不能改成指向'b',指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的。
# 要创建一个内容也不变的tuple怎么做?那就必须保证tuple的每一个元素本身也不能变。range 类型表示不可变的数字序列,通常用于在 for 循环中循环指定的次数。
class range(start, stop[, step]) 创建一个 range 对象,包含从 start 到 stop-1 的整数,步长为 step。
range 类型相比常规 list 或 tuple 的优势在于一个 range 对象总是占用固定数量的(较小)内存,不论其所表示的范围有多大(因为它只保存了 start, stop 和 step 值,并会根据需要计算具体单项或子范围的值)。
# 创建一个从 0 到 9 的 range 对象
r = range(10)
print(list(r))
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 创建一个从 1 到 10 的 range 对象,步长为 2
r = range(1, 11, 2)
print(list(r))
# [1, 3, 5, 7, 9]
# 创建一个从 0 到 10 且步长为 3 的 range 对象
r = range(0, 11, 3)
print(list(r))
# [0, 3, 6, 9]
# 创建一个从 0 到 -10 且步长为 -1 的 range 对象
list(range(0, -10, -1))
# [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]range 对象支持迭代,可以用 for 循环来遍历其中的元素。
for i in range(5):
print(i)
# 0
# 1
# 2
# 3
# 4序列是有序的,集合是无序的,集合中的元素不能重复。
目前有两种内置集合类型,set 和 frozenset。
set 类型是可变的,其内容可以使用 add() 和 remove() 这样的方法来改变。 由于是可变类型,它没有哈希值,且不能被用作字典的键或其他集合的元素。
frozenset 类型是不可变并且为 hashable, 其内容在被创建后不能再改变;因此它可以被用作字典的键或其他集合的元素。
set 是一种无序的集合,元素是唯一的,且不可变。
# 定义一个空集合
empty_set = set()
print(empty_set)
# set()
# 显式定义一个有元素的集合
set1 = {1, 2, 3}
print(set1)
# {1, 2, 3}
# 提供一个字符串作为输入集合
set2 = set('hello')
print(set2)
# {'e', 'h', 'o', 'l'}
# 提供一个list作为输入集合
set3 = set([1, 2, 3])
# {1, 2, 3}
# 提供一个元组作为输入集合
set4 = set((1, 2, 3))
# {1, 2, 3}# 计算集合的长度
len(set1)
# 3
# 计算集合的并集(两个集合的并集)
set_sum = set1.union(set2) # 等同于 set1 | set2
print(set_sum)
# {1, 2, 3, 'e', 'h', 'o', 'l'}
# 计算集合的交集(两个集合的共同元素)
set_sum.intersection(set2) # 等同于 set_sum & set2
# {'e', 'h', 'o', 'l'}
# 计算集合的差集(集合1中有而集合2中没有的元素)
set_sum.difference(set2) # 等同于 set1 - set2
# {1, 2, 3}
# 计算集合的对称差集:对称差集是指两个集合中不同时存在的元素组成的集合。
# A = {1, 2, 3, 4},B = {3, 4, 5, 6},A - B = {1, 2},B - A = {5, 6} ,它们的对称差集为{1, 2, 5, 6}。
set1.symmetric_difference(set2) # 等同于 set1 ^ set2
# {1, 2, 3, 'h', 'e', 'l', 'o'}
# 判断元素是否在集合中
'h' in set2
# True
# 添加元素到集合
set1.add(4)
# {1, 2, 3, 4}
# 删除元素从集合
set1.remove(2)
# {1, 3, 4}
# 清空集合
set1.clear()
# set()目前仅有一种标准映射类型:字典。
dict 是一种无序的键值对集合,键必须是不可变且唯一的,值可以是任意的。
键可以是数字、字符串、元组,即key必须是不可变的,但不能是列表、字典、集合。
值可以是任意类型。
花括号 {} 用于创建空字典。
另一种初始化字典的方式是,在花括号里输入逗号分隔的键值对,这也是字典的输出方式。
字典的主要用途是通过关键字存储、提取值。
用 del 可以删除键值对。用已存在的关键字存储值,与该关键字关联的旧值会被取代。
通过不存在的键提取值,则会报错。
对字典执行 list(d) 操作,返回该字典中所有键的列表,按插入次序排列(如需排序,请使用 sorted(d))。
len(d) 返回字典 d 中的项数。
检查字典里是否存在某个键,使用关键字 in。
# 定义一个空字典
empty_dict = {}
print(empty_dict)
# {}
# 显式定义一个有元素的字典
dict1 = {'name': 'Alice', 'age': 20, 'city': 'Beijing'}
print(dict1)
# {'name': 'Alice', 'age': 20, 'city': 'Beijing'}
# 定义一个字典,使用关键字初始化
dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
# {'sape': 4139, 'jack': 4098, 'guido': 4127}
dict2 = dict(name='Bob', age=25, city='Shanghai')
print(dict2)
# {'name': 'Bob', 'age': 25, 'city': 'Shanghai'}
# 访问字典中的值
print(dict1['name'])
# Alice
# 修改字典中的值
dict1['age'] = 21
print(dict1)
# {'name': 'Alice', 'age': 21, 'city': 'Beijing'}
# 添加键值对
dict1['gender'] = 'female'
print(dict1)
# {'name': 'Alice', 'age': 21, 'city': 'Beijing', 'gender': 'female'}
# 删除键值对
del dict1['age']
print(dict1)
# {'name': 'Alice', 'city': 'Beijing', 'gender': 'female'}
# 遍历字典
for key in dict1:
print(key, dict1[key])
# name Alice
# city Beijing
# gender female
# 字典推导式
dict3 = {x: x ** 2 for x in range(1, 6)}
print(dict3)
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# 排序
sorted_dict = dict(sorted(dict1.items()))
print(sorted_dict)
# {'city': 'Beijing', 'gender': 'female', 'name': 'Alice'}
# dict1.items()返回一个包含字典 dict1 中所有键值对的视图对象,即 (('city', 'Beijing'), ('gender', 'female'), ('name', 'Alice'))。
# 默认情况下,sorted 函数会根据键(即每个元组的第一个元素)进行排序。
# list()方法可以将字典的键转换为列表
list(dict1)
# ['city', 'gender', 'name']
# in 运算符可以检查字典是否包含某个键
'name' in dict1
# True变量是程序中用来存储数据的内存单元,用来保存数据。
变量在程序中就是用一个变量名表示了,变量名必须是大小写英文、数字和_的组合,且不能用数字开头。
在Python中,等号=是赋值语句,可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量。
# 在内存中创建了一个'ABC'的字符串;在内存中创建了一个名为a的变量,并把它指向'ABC'。
a = 'ABC'
# 解释器创建了变量b,并把b指向a指向的字符串'ABC'
b = a
# 解释器创建了字符串'XYZ',并把a的指向改为'XYZ',但b并没有更改。
a = 'XYZ'
print(b)
# 输出'ABC'所谓常量就是不能变的变量,比如常用的数学常数π就是一个常量。
在Python中,通常用全部大写的变量名表示常量。
PI = 3.141592653589793但事实上PI仍然是一个变量,Python根本没有任何机制保证PI不会被改变,所以,用全部大写的变量名表示常量只是一个习惯上的用法,如果你一定要改变变量PI的值,也没人能拦住你。
Python中,值类型和引用类型是两种主要的变量类型。
值类型:
引用类型:
# 值类型
a = 10
id(a) # id()函数可以查看变量的内存地址
# 输出140727206727488
b = a
b = a
a = 20
id(a)
# 输出xxxxxxxxxxxx,但不是140727206727488
print(b)
# 输出10
# 引用类型
c = [1, 2, 3]
d = c
c[0] = 4
print(d)
# 输出[4, 2, 3]运算符是一种特殊的符号,它代表着对两个或多个操作数的一种操作。
Python支持多种运算符,包括算术运算符、赋值运算符、比较运算符、逻辑运算符、成员运算符、身份运算符、位运算符等。
运算符的优先级决定了运算顺序,优先级高的运算符先进行计算。
Python的运算符优先级从高到低依次为:
# 加法运算符
2 + 3
# 5# 减法运算符
5 - 3
# 2# 乘法运算符
2 * 3
# 6# 除法运算符
7 / 3
# 2.3333333333333335# 取模运算符
7 % 3
# 1# 幂运算符
2 ** 3
# 8# 整除运算符
7 // 3
# 2# 自然对数运算符
import math
math.log(10)
# 2.302585092994046# 取整运算符
import math
# 向下取整运算符
math.floor(3.14)
# 3
# 向上取整运算符
math.ceil(3.14)
# 4# 简单赋值运算符
a = 10
print(a)
# 输出10没有a++或a--的情况下,Python提供了自增运算符和自减运算符。
# 自增运算符
a = 1
a += 1
print(a)
# 输出2# 自减运算符
a = 1
a -= 1
print(a)
# 输出0# 乘方赋值运算符
a = 2
a **= 3
print(a)
# 输出8# 除法赋值运算符
a = 10
a /= 2
print(a)
# 输出5.0# 取模赋值运算符
a = 10
a %= 3
print(a)
# 输出1# 幂赋值运算符
a = 2
a **= 3
print(a)
# 输出8# 整除赋值运算符
a = 10
a //= 3
print(a)
# 输出3# 等于运算符
1 == 1
# True# 不等于运算符
1 != 1
# False# 大于运算符
2 > 1
# True# 小于运算符
1 < 2
# True# 大于等于运算符
2 >= 2
# True# 小于等于运算符
1 <= 2
# True# 字符串也可以做比较运算
# 原因是字符串是由字符组成的序列,每个字符都有自己的ASCII码,因此可以比较两个字符串的ASCII码值。
'abc' > 'def'
# True
# 列表也可以做比较运算
# 会逐个比较两个列表中的元素。
[1, 2, 3] > [1, 2, 4]
# True
# 元组也可以做比较运算
# 会逐个比较两个元组中的元素。
(1, 2, 3) > (1, 2, 4)
# True逻辑运算符操作和返回的是布尔值(True或False)。
int 0、空字符串''、空列表[]、空元组()、None、False都被视为False,其他值都视为True。
# 逻辑非运算符
not True
# False
not not True
# True# 逻辑与运算符
True and True
# True# 逻辑或运算符
True or False
# True成员运算符是用来判断某个值或变量是否在序列(如字符串、列表、元组、集合等)中。
# in运算符
'a' in 'abc'
# True
# 列表也可以使用in运算符
1 in [1, 2, 3]
# True
# 元组也可以使用in运算符
1 in (1, 2, 3)
# True
# 集合也可以使用in运算符
1 in {1, 2, 3}
# True
# 字典也可以使用in运算符
'name' in {'name': 'Alice', 'age': 20}
# True# not in运算符
'a' not in 'abc'
# False在Python中,is 运算符用于检查两个对象是否是同一个对象(即它们是否引用相同的内存地址)。
注意,对于小整数(通常在范围 -5 到 256 之间),Python 会进行整数缓存(integer interning),这意味着这些范围内的整数对象在第一次创建后会被缓存并重用。
所以不要依赖 is 来比较值:虽然在这个例子中 a is b 返回 True,但这只是因为整数缓存机制。如果你需要比较两个变量的值是否相等,应该使用 == 操作符,而不是 is。
is 的正确使用场景:is 主要用于检查对象的身份,最常见的是用于检查 None:
x = None
print(x is None)
# True# is运算符
# 由于a和b都是小整数,因此它们的内存地址相同。所以 a 和 b 引用了同一个整数对象,所以它们的内存地址是相同的,因此 a is b 返回 True。
a = 10
b = 10
a is b
# True
# 超出小整数范围的比较
c = 257
d = 257
c is d
# False
# 变量的内存地址相同
a = 10
b = a
a is b
# True
# 列表
a = [1,2,3]
b = [2,1,3]
a == b # False,因为列表是有序且可变的序列类型。当你创建两个列表 a 和 b 时,它们的元素顺序必须完全相同,才能被认为是相等的。
a is b # False,因为两个变量的内存地址不同。
# 元组
a = (1,2,3)
b = (2,1,3)
a == b # False,因为元组是有序且不可变的序列类型。当你创建两个元组 a 和 b 时,它们的元素顺序必须完全相同,才能被认为是相等的。
a is b # False,因为两个变量的内存地址不同。
# 集合
a = {1,2,3}
b = {2,1,3}
a == b # True,因为集合是无序的,即使它们的元素顺序不同,只要它们包含相同的元素,这两个集合就是相等的。
a is b # False,因为两个变量的内存地址不同。# is not运算符
a = 10
b = 20
a is not b
# True# 按位与运算符
5 & 3
# 1# 按位或运算符
5 | 3
# 7# 按位异或运算符
5 ^ 3
# 6# 按位取反运算符
~5
# -6# 左移运算符
5 << 1
# 10# 右移运算符
5 >> 1
# 2# 无符号右移运算符
-5 >> 1
# 1073741822表达式(Expression) 是运算符(operator)和操作数(operand) 所构成的序列。
and 优先级高于or,但是 not 优先级高于and。

# 这是一个单行注释'''
这是一个多行注释
可以跨越多行
'''python 使用缩进来表示代码块。
if语句执行有个特点,它是从上往下判断,如果在某个判断上是True,把该判断对应的语句执行后,就忽略掉剩下的elif和else。
# if-else语句
a = 10
if a > 5:
print('a大于5')
else:
print('a不大于5')# if-elif-else语句
a = 10
if a > 5:
print('a大于5')
elif a < 5: # elif是 else if的缩写,完全可以有多个 elif
print('a小于5')
else:
print('a等于5')input()函数用于从控制台获取用户输入,并将其转换为字符串。
# input函数
a = input('请输入一个数字:')
print(a)让你保持在更抽象的层次进行思考。pass 会被默默地忽略掉,不会对程序的运行产生任何影响。
# pass语句
if a > 5:
passPython 3.10 引入了新的语法 match-case,它可以取代 switch 语句。
match 语句接受一个表达式并把它的值与一个或多个 case 块给出的一系列模式进行比较。如果匹配成功,则执行对应的语句。 match 语句从上到下依次匹配,直到找到一个匹配项。 match 语句的每个 case 块可以包含一个或多个变量,这些变量将接收匹配的值。
最后一个代码块:“变量名” _ 被作为 通配符(相当于其他语言的default),并必定会匹配成功。如果没有 case 匹配成功,则不会执行任何分支。
match和其他语言中的switch语句的不同的是,大多数语言的 switch 语句支持 fallthrough 行为,即如果没有 break 语句,控制流会继续执行下一个 case。 Python 的 match 语句没有 fallthrough 行为,每个 case 都是独立的。
但是依旧为模式添加 if 作为守卫子句。如果守卫子句的值为假,那么 match 会继续尝试匹配下一个 case 块。
# match-case语句
a = 10
match a:
case 10:
print('a等于10')
case 5:
print('a等于5')
case _:
print('a不等于10或5')你可以用 | (“或”)将多个字面值组合到一个模式中。
# match-case语句
a = 10
match a:
case 10 | 5:
print('a等于10或5')
case _:
print('a不等于10或5')# match-case守卫子句
a = 7
match a:
case 5 if a > 5:
print('a大于5')
case 10 if a < 10:
print('a小于5')
case _:
print('a大于5且a小于10')# match 语句可以把匹配后的值绑定到变量
age = 15
match age:
# 第一个case x if x < 10表示当age < 10成立时匹配,且赋值给变量x
case x if x < 10:
print(f'< 10 years old: {x}')
case 10:
print('10 years old.')
case 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18:
print('11~18 years old.')
case 19:
print('19 years old.')
case _:
print('not sure.')# 用match匹配来解析列表
args = ['gcc', 'hello.c', 'world.c']
# args = ['clean']
# args = ['gcc']
match args:
# 列表仅有'gcc'一个字符串,没有指定文件名,报错
case ['gcc']:
print('gcc: missing source file(s).')
# 列表第一个字符串是'gcc',第二个字符串绑定到变量file1,后面的任意个字符串绑定到*files(实际上表示至少指定一个文件)
case ['gcc', file1, *files]:
print('gcc compile: ' + file1 + ', ' + ', '.join(files))
# 表示列表仅有'clean'一个字符串
case ['clean']:
print('clean')
# 表示其他所有情况
case _:
print('invalid command.')Python的循环有两种,一种是for...in循环,一种是while循环。
for...in循环主要是用来遍历序列或者集合、字典
# 把每个元素代入变量x,然后执行缩进块的语句
sum = 0
for x in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:
sum = sum + x
print(sum)
# 双重循环
for x in range(1, 4):
for y in range(1, 4):
print(x, y)# Python提供一个range()函数,可以生成一个整数序列,再通过list()函数可以转换为list
sum = 0
for x in list(range(1, 11)):
sum = sum + x
print(sum)
# range()函数可以指定步长
for x in range(1, 11, 2):
print(x)
# 输出:1 3 5 7 9
# 递减循环
for x in range(10, 0, -2):
print(x)
# 输出:10 8 6 4 2
# 遍历列表
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in range(0, len(a), 2):
print(a[i])
# 输出:1 3 5 7 9
# 遍历字典
d = {'name': 'Alice', 'age': 20, 'city': 'Beijing'}
for key in d:
print(key, ':', d[key])
# 输出:name : Alice age : 20 city : Beijing# while循环,只要条件满足,就不断循环,条件不满足时退出循环。
sum = 0
n = 99
while n > 0:
sum = sum + n
n = n - 2
print(sum)break语句可以提前退出循环。(跳出后不会进入该for循环匹配的else语句块)
# break语句
sum = 0
n = 99
while n > 0:
if n == 33:
break
sum = sum + n
n = n - 2
print(sum)continue语句可以跳过当前循环的剩余语句,直接开始下一轮循环。(跳出后会进入到else语句块)
# continue语句
sum = 0
n = 99
while n > 0:
if n == 33:
continue
sum = sum + n
n = n - 2
print(sum)循环的else语句在循环正常结束时执行,循环被break终止时不执行。
# for...in循环中的else语句
for x in range(1, 11):
print(x)
else:
print('循环结束')
# while循环中的else语句
n = 1
while n <= 10:
print(n)
n += 1
else:
print('循环结束')包 > 模块 > 类 > 函数、变量
包(Package) 是 Python 中组织代码的一种方式。(类比文件夹)
包可以包含模块、子包(类比子文件夹)、子模块等。
包和文件夹的区别:
__init__.py,该文件可以用来控制包的导入行为。(从 Python 3.3 开始,引入了 隐式命名空间包(Implicit Namespace Packages),这意味着 __init__.py 文件不再是必需的。)
__init__.py文件可以为空,这个模块的名称就是包的名称(目录名)。
命名空间(Namespace) 是 Python 中用来管理变量名和其他对象的一种方式。
命名空间可以包含模块、函数、类、变量等。
例如,通过包名.模块名.函数名 这样的语法可以访问模块中的函数。
模块(Module) 是 Python 中用来封装代码的一种方式。(类比文件)
模块可以被导入到其他模块中使用。
模块的导入(import)有两种方式:
import 模块名from 模块名 import 函数名 或 from 模块名 import 变量名# 导入整个模块
import math
print(math.pi)
# 导入整个模块并给其别名
import math as m
print(m.pi)
# 导入模块中的某个函数
from math import pi
print(pi)
# 导入模块中的多个函数
from math import pi, sin, cos
# 也可以写作 from math import (pi, sin, cos)
print(pi, sin(pi), cos(pi))
# 导入模块中的某个变量
from math import e
print(e)
# 导入模块中的所有函数和变量
from math import *
print(pi, sin(pi), cos(pi), e)
# 注意,math模块中是需要将pi, sin, cos, e等函数和变量导入到当前命名空间才能使用。
# 导出方式为:__all__ = ['pi','sin', 'cos', 'e']
# 这样就可以直接使用pi, sin, cos, e等函数和变量。
# __all__规定了模块中哪些函数和变量可以被直接导入。
# 类似__all__的变量被称为模块的内置属性。# 绝对导入
import 包名.包名.模块名 #执行入口文件所在的目录下的某个包
from 包名.包名.模块名 import 函数名 #执行入口文件所在的目录下的某个包.表示当前目录,..表示上级目录,...表示更上级目录,....表示更上上级目录,以此类推。
但是往上级是有限制的,不能超过顶级包的位置。否则会报错valueError: attempted relative import beyond top-level package。
事实上,相对导入是根据模块内置的 __name__ 变量来判断的。
如果 __name__ 变量的值是 __main__,则表示当前模块是被直接运行的,所以__main__模块是不可以相对导入的,只能使用绝对导入。(除非运行时使用-m参数,把入口文件当作一个模块来运行)
否则表示当前模块是被导入的,其值是导入模块的名称。
# 相对导入
from .包名.模块名 import 函数名
from ..包名.模块名 import 函数名
from ...包名.模块名 import 函数名模块的搜索路径(Module Search Path) 是 Python 用来查找模块的路径。
模块的搜索路径存储在 sys.path 变量中。
sys.path 变量是一个列表,列表中的每个元素是一个字符串,表示一个搜索路径。
搜索路径的顺序:
.)PYTHONPATH 中的路径__init__.py 文件 __init__.py 文件是 Python 包的初始化文件,它可以用来控制包的导入行为。
当一个包被导入时,__init__.py文件会自动执行。
Python 会在 __init__.py 文件中查找 __all__ 变量,如果该变量存在,则只导入 __all__ 变量中指定的函数和变量。
如果 __all__ 变量不存在,则导入模块中所有的函数和变量。
__init__.py中适合处理批量导入模块。
# __init__.py
from .module1 import *
from .module2 import *
from .module3 import *dir() 函数可以查看模块内置的变量、函数和类。
如果dir的变量没传,则默认打印当前模块的变量、函数和类。
__name__变量:
__name__变量等于模块名。__name__变量等于模块名。__package__变量:
__package__变量等于None。__package__变量等于父模块的名称。python -m命令运行,则__package__变量等于模块的导入路径。__file__变量:
__file__变量等于模块的文件路径。__file__变量等于模块的文件路径。python -m命令运行,则__file__变量等于模块的路径,而不是模块的导入路径。__doc__变量:
__doc__变量等于None。__all__变量:
__all__变量,则dir()函数默认打印模块的变量、函数和类。__all__变量,则dir()函数只打印__all__变量中指定的函数和变量。# t/test.py
print('package:' + (__package__ or '当前模块不属于任何包'))
print('name:' + __name__)
print('doc:' + (__doc__ or '当前模块没有文档字符串'))
print('file:' + __file__)
# 输出:
# package:t
# name:t.test
# doc:当前模块没有文档字符串
# file: xxx\t\test.py# index.py (当成应用程序入口使用python命令执行)
import t.test
print('package:' + (__package__ or '当前模块不属于任何包'))
print('name:' + __name__)
print('doc:' + (__doc__ or '当前模块没有文档字符串'))
print('file:' + __file__)
# 输出:
# package: 当前模块不属于任何包 (因为当前模块不属于任何包)
# name: __main__
# doc: 当前模块没有文档字符串
# file: index.py (当前模块是被直接运行的,作为入口文件,是不会显示完整路径的,显示的相对路径和执行python命令的目录有关)# 模块内置变量
import math
print(dir(math))
```python
import math
print(dir(math))
print(dir())Make a script both importable and executable.(使脚本既可被其他模块导入又可作为普通模块自己执行)
// 判断当前模块是否是入口文件
if __name__ == 'main':
print('This is a application entry point.')
else:
print('This is a regular module')另外,通过 "-m" 参数,python可以把一个文件当作模块来运行。
// 运行当前模块
python -m t.test
# 这里 -m 参数告诉python解释器,要运行t目录下的test模块。Python 标准库(Standard Library) 是 Python 自带的库,包含了常用的模块和函数。
标准库的安装路径:
C:\PythonXX\Lib/usr/lib/pythonXX/site-packages/Library/Frameworks/Python.framework/Versions/XX/lib/pythonXX/site-packagesos 模块:操作系统相关的功能,如文件和目录的操作、获取环境变量、获取系统信息等。sys 模块:系统相关的功能,如退出程序、获取命令行参数、设置退出状态等。math 模块:数学相关的功能,如对数、三角函数、随机数生成等。json 模块:JSON(JavaScript Object Notation)数据格式的编解码。re 模块:正则表达式。argparse 模块:命令行参数解析。datetime 模块:日期和时间相关的功能。collections 模块:高级容器数据类型,如 OrderedDict、defaultdict、Counter 等。itertools 模块:迭代器相关的功能,如循环、生成器、迭代器等。functools 模块:函数相关的工具,如缓存、偏函数等。operator 模块:操作符相关的函数,如获取操作符对应的函数等。heapq 模块:堆排序算法。bisect 模块:二分查找算法。struct 模块:打包和解包二进制数据。hashlib 模块:哈希算法。hmac 模块:加密哈希算法。base64 模块:Base64 编码。xml 模块:处理 XML 数据。csv 模块:处理 CSV(Comma-Separated Values)数据。configparser 模块:处理配置文件。contextlib 模块:上下文管理器。logging 模块:日志记录。unittest 模块:单元测试。multiprocessing 模块:多进程。concurrent.futures 模块:并发执行。asyncio 模块:异步编程。socket 模块:网络编程。ssl 模块:安全套接字层。select 模块:网络 I/O 多路复用。selectors 模块:事件循环。asyncore 模块:异步网络。smtplib 模块:发送邮件。imaplib 模块:处理邮件。poplib 模块:处理邮件。nntplib 模块:处理新闻组。smtpd 模块:邮件服务器。telnetlib 模块:Telnet 客户端。uuid 模块:UUID 生成器。ctypes 模块:调用 C 语言库。sqlite3 模块:SQLite 数据库。dbm 模块:数据库管理。zlib 模块:压缩和解压数据。gzip 模块:压缩和解压数据。bz2 模块:压缩和解压数据。lzma 模块:压缩和解压数据。zipfile 模块:压缩和解压 ZIP 文件。tarfile 模块:压缩和解压 TAR 文件。csv 模块:处理 CSV 文件。json 模块:处理 JSON 文件。html 模块:处理 HTML 文件。HTMLParser 模块:HTML 解析器。xml 模块:处理 XML 文件。email 模块:处理邮件。cgi 模块:处理 CGI(Common Gateway Interface)请求。webbrowser 模块:打开浏览器。tkinter 模块:Tk 图形界面。turtle 模块:海龟绘图。urllib 模块:URL 处理。venv 模块:创建虚拟环境。asyncio 模块:异步编程。asyncio.tasks 模块:异步任务。asyncio.locks 模块:异步锁。asyncio.queues 模块:异步队列。asyncio.events 模块:异步事件。asyncio.subprocess 模块:异步子进程。asyncio.streams 模块:异步流。asyncio.protocols 模块:异步协议。asyncio.transports 模块:异步传输。asyncio.runners 模块:异步运行器。asyncio.exceptions 模块:异步异常。asyncio.coroutines 模块:异步协程。命令行终端输入
help(函数名)可以查看函数的文档。
在Python中,定义一个函数要使用def语句,依次写出函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回。
如果没有 return 语句时,函数返回None。
# 定义一个简单的函数
def my_abs(x):
# 对参数类型做检查,只允许整数和浮点数类型的参数。数据类型检查可以用内置函数isinstance()实现
# 添加了参数检查后,如果传入错误的参数类型,函数就可以抛出一个错误
if not isinstance(x, (int, float)):
# raise语句抛出一个异常,并打印异常信息
raise TypeError('bad operand type')
if x >= 0:
return x
else:
return -x
print(my_abs(-99))如果想定义一个什么事也不做的空函数,可以用pass语句:pass可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass,让代码能运行起来。
def my_func():
passPython的函数返回多值其实就是返回一个tuple,但写起来更方便。 在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值
# 返回一个tuple
def my_func(x, y):
return x + y, x - y
a, b = my_func(10, 5)
print(a, b) # 15 5
# 接收一个tuple
def my_func(t):
a, b = t
return a + b
t = (10, 5)
print(my_func(t)) # 15def my_func(a, b, c):
print(a, b, c)
return a + b + c
# 序列解包
t = (1, 2, 3)
my_func(*t)Python的函数正常定义的必选参数外,还可以使用默认参数、可变参数和关键字参数,使得函数定义出来的接口,不但能处理复杂的参数,还可以简化调用者的代码。
# 函数有两个参数:x和n,这两个参数都是位置参数,调用函数时,传入的两个值按照位置顺序依次赋给参数x和n。
def power(x, n):
s = 1
while n > 0:
n = n - 1
s = s * x
return s当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。
必选参数在前,默认参数在后。
默认参数最大的好处是能降低调用函数的难度。
# 调用power(5)时,相当于调用power(5, 2)。对于n > 2的其他情况,就必须明确地传入n。
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时,需要把参数名写上。
def enroll(name, gender, age=6, city='Beijing'):
print('name:', name)
print('gender:', gender)
print('age:', age)
print('city:', city)
# city参数用传进去的值,其他默认参数继续使用默认值。
enroll('Adam', 'M', city='Tianjin')默认参数的坑:
Python函数在定义的时候,默认参数L的值就被计算出来了,即[]。 因为默认参数L也是一个变量,它指向对象[]。 每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。
def add_end(L=[]):
L.append('END')
return L
add_end() # ['END']
# 第二次调用add_end()时,L已经不是空列表了,而是['END']。
add_end() # ['END', 'END']# 要修改上面的例子,我们可以用None这个不变对象来实现,现在,无论调用多少次,都不会有问题。
def add_end(L=None):
if L is None:
L = []
L.append('END')
return L
# 不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。在参数前面加了一个*号来定义可变参数。可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。
# 定义可变参数函数
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
print(calc(1, 2, 3)) # 14
# Python允许你在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去。
nums = [1, 2, 3]
print(calc(*nums)) # 14在参数前面加了一个**号来定义关键字参数,关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。
# 定义关键字参数函数
# person函数里,我们保证能接收到name和age这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。
def person(name, age, **kw):
print('name:', name)
print('age:', age)
print('other:', kw)
# 例如做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。
person('Adam', 45, city='Beijing', job='Engineer') # 输出:name: Adam, age: 45, other: {'city': 'Beijing', 'job': 'Engineer'}
# 以先组装出一个dict,然后,把该dict转换为关键字参数传进去
extra = {'city': 'Beijing', 'job': 'Engineer'}
person('Jack', 24, **extra) # 输出:name: Jack, age: 24, other: {'city': 'Beijing', 'job': 'Engineer'}命名关键字参数是为了限制关键字参数的名字,同时可以提供默认值。
命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数。
命名关键字参数必须传入参数名,和位置参数不同,如果没有传入参数名,调用将报错。
# 定义命名关键字参数函数
# 例如,只接收city和job作为关键字参数。
def person(name, age, *, city='Beijing', job):
print(name, age, city, job)
# 由于命名关键字参数city具有默认值,调用时,可不传入city参数。
person('Jack', 24, job='Engineer') # 输出:Jack 24 Beijing Engineer如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了。
如果没有可变参数,就必须加一个*作为特殊分隔符。
# 如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了
def person(name, age, *args, city, job):
print(name, age, args, city, job)
person('Jack', 24, 'teacher', 'Engineer', city='Beijing') # 输出:Jack 24 ('teacher', 'Engineer') Beijing Engineer函数的参数可以组合使用,但请注意顺序:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的。
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
args = (1, 2, 3, 4)
kw = {'d': 99, 'x': 'foo'}
f1(*args, **kw) # a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': 'foo'}递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
解决递归调用栈溢出的方法是通过尾递归优化。尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
尾递归的函数调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化,所以,即使把上面的fact(n)函数改成尾递归方式,也会导致栈溢出。
def fact(n):
return fact_iter(n, 1)
def fact_iter(num, product):
if num == 1:
return product
# 返回递归函数本身
return fact_iter(num - 1, num * product)import sys
print(sys.getrecursionlimit()) // 获取递归深度
sys.setrecursionlimit(100000) // 设置递归深度c = 50
def add(x,y):
c = x + y
print(c)
add(10,20) // 输出:30
print(c) // 输出:50# c = 10 # 全局变量
def demo():
c = 50 #局部变量
for i in range (0,9):
a ='a'
c += 1
print(c) # 输出:59
print(a) # 输出:a。 python中不存在块级作域,a 虽然在for循环中定义,但a仍然是局部变量,在函数中可以访问,在函数外无法访问。
demo()函数中的变量,可以访问到函数外的变量,反之不可以。
c = 1
def func1():
c = 2
print(c) # 输出:2
def func2():
c = 3
print(c) # 输出:3
func2()
func1() # 输出:3global 关键字,可以修改函数的变量为全局变量。
def func1():
global c
c = 2
func1()
print(c) # 输出:2面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
在实例方法中,可以通过self访问实例变量,可以通过self.__class__ 或者 类名.类变量名 访问类变量。
class Person:
name = 'John'
def __init__(self, name):
self.name = name
def get_name(self):
print(self.name)
print(self.__class__.name)
print(Person.name)
return self.age
p = Person('Jack')
p.get_name()在Python中,我们可以使用装饰器 @classmethod 来定义类方法。
class Person:
name = 'John'
def __init__(self, name):
self.name = name
@classmethod
def get_name(cls):
print(cls.name)
return cls.name
p = Person('Jack')
Person.get_name()python中的静态方法不需要传递 self 或 cls 参数。
在Python中,我们可以使用装饰器 @staticmethod 来定义静态方法。
python中的静态方法一般被用作工具方法,比如和类本身无关的方法。
class Person:
name = 'John'
def __init__(self, name):
self.name = name
@staticmethod
def get_name():
print(Person.name)
return Person.name
p = Person('Jack')
Person.get_name()在Python中,我们可以使用 super() 关键字来调用父类的方法。
class Person:
def __init__(self, name):
self.name = name
# 继承Person类
class Student(Person):
def __init__(self, school, name):
super().__init__(name)
self.school = school
s = Student('MIT', 'Jack')
print(s.name) # 输出:Jack
print(s.school) # 输出:MIT正则表达式是一种用来匹配字符串的强有力的武器。它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的。
不使用正则表达式和JSON的情况下,我们可以使用字符串的方法来处理字符串。
a = 'C|C++|Java|C#|Python|Javascript'
# 判断字符串中是否包含Python
print(a.index('Python') > -1) # 输出:True
# 判断字符串中是否包含Python
print('Python' in a) # 输出:True# 导入re模块(正则表达式模块)
import re
a = 'C|C++|Java|C#|Python|Javascript'
# 判断字符串中是否包含Python
print(re.search('Python', a)) # 输出:<re.Match object; span=(15, 21), match='Python'>
# 查询字符串中所有的Python
r = re.findall('Python', a)
if len(r) > 0:
print('字符串中包含Python')
else:
print('字符串中不包含Python')
print(r) # 输出:['Python']import re
a = 'C1C++2Java4C#8Python7Javascript'
# 从字符串中提取数字
r = re.findall('\d', a)
print(r) # 输出:['1', '2', '4', '8', '7']| 元字符 | 描述 |
|---|---|
| . | 匹配除换行符之外的任意字符 |
| \w | 匹配字母或数字或下划线 |
| \s | 匹配任意的空白符 |
| \d | 匹配数字 |
| \b | 匹配单词的开始或结束 |
| ^ | 匹配字符串的开始 |
| $ | 匹配字符串的结束 |
| \W | 匹配非字母、数字或下划线 |
| \S | 匹配非空白符 |
| \D | 匹配非数字 |
| \B | 匹配非单词的开始或结束 |
| * | 重复零次或更多次 |
| \ | 匹配转义字符 |
| + | 重复一次或更多次 |
| ? | 重复零次或一次 |
| [...] | 匹配字符组中的字符 |
| [^...] | 匹配除了字符组中字符的所有字符 |
| [a-z] | 匹配a到z之间的任意字符 |
| [^a-z] | 匹配除a到z之间的任意字符 |
import re
s = 'abc, acc, adc, aec, afc, agc, ahc'
# 匹配acc和afc
r = re.findall('a[cf]c', s)
print(r) # 输出:['acc', 'afc']
k = re.findall('a[^cf]c', s)
print(k) # 输出:['abc', 'aec', 'agc', 'ahc']
# 匹配 c-f 之间的字符
m = re.findall('a[c-f]c', s)
print(m) # 输出:['acc', 'adc', 'aec', 'afc']\w、\s、\d、\b、\W、\S、\D、\B 等都是概括字符集。
* 重复零次或更多次+ 重复一次或更多次? 重复零次或一次python中,默认是贪婪匹配,即尽可能多的匹配。
如果要使用非贪婪匹配,即在匹配到第一个符合条件的字符后,就停止匹配,可以在数量词后面加上一个 ? 号。
import re
a = 'python 11111java678php'
# 贪婪匹配
r = re.findall('[a-z]{3,6}', a)
print(r) # 输出:['python', 'java', 'php']
# 非贪婪匹配
r = re.findall('[a-z]{3,6}?', a)
print(r) # 输出:['pyt', 'hon', 'jav', 'php']import re
a = 'PythonPythonPythonPythonPython'
# 匹配Python,用括号括起来,表示一个组
r = re.findall('(Python){3}', a)
print(r) # 输出:['PythonPythonPython']findall() 方法的的参数有三个:
匹配模式有:
import re
language = 'PythonC#\nJavaPHP'
# 忽略大小写
r = re.findall('c#', language, re.I)
print(r) # 输出:['C#']
# 使. 匹配包括换行符在内的所有字符
r = re.findall('C#.{1}', language, re.S)
print(r) # 输出:['C#\n']
# 多个标识符用 | 连接
r = re.findall('c#.{1}', language, re.S | re.I)
print(r) # 输出:['C#\n']re.sub() 方法用于替换字符串中的匹配项。
import re
language = 'PythonC#\nJavaPHP'
# 替换C#为Go
r = re.sub('C#', 'Go', language)
print(r) # 输出:PythonGo\nJavaPHP常规字符串替换可以使用str.replace()方法。
language = 'PythonC#\nJavaPHP'
# 替换C#为Go
r = language.replace('C#', 'Go')
print(r) # 输出:PythonGo\nJavaPHPimport re
language = 'PythonC#\nJavaPHP'
def covert(value):
matched = value.group()
return '!!' + matched + '!!'
r = re.sub('C#', covert, language)
print(r) # 输出:Python!!C#!!\nJavaPHPimport re
def covert(value):
matched = value.group()
if int(matched) >= 6:
return '9'
else:
return '0'
r = re.sub('\d', covert, '123456789')
print(r) # 输出:123450000re.search() 函数用于在字符串中搜索正则表达式模式的第一个匹配项。返回的是对象。没有匹配时返回None。 re.match() 函数用于在字符串的开头搜索正则表达式模式的第一个匹配项。返回的是对象。没有匹配时返回None。
search和match一旦搜索到第一个匹配项,就会停止搜索。
import re
language = 'PythonC#\nJavaPHP'
# 搜索第一个匹配项
r = re.search('C#', language)
print(r) # 输出:<re.Match object; span=(6, 8), match='C#'>
print(r.group()) # 输出:C#
print(r.span()) # 输出:(6, 8)
# 搜索第一个匹配项
r = re.match('Python', language)
print(r) # 输出:<re.Match object; span=(0, 6), match='Python'>
print(r.group()) # 输出:Python
print(r.span()) # 输出:(0, 6)group() 方法用于返回匹配项。 group(0) 表示匹配项的全匹配,group(1) 表示匹配项的第一个分组,以此类推。
import re
s = 'life is short, i use python'
r = re.search('life(.*)python', s)
print(r.group()) # 等同于r.group(0)。输出:life is short, i use python
print(r.group(1)) # 输出: is short, i use
# 使用单个组
t = re.findall('life(.*)python', s)
print(t) # 输出:[' is short, i use']
# 使用多个组
text = "name:Alice age:20, name:Bob age:25"
r3 = re.findall('name:(.*?)age:(.*?)[,]?', text)
print(r3) # 输出:[('Alice ', '20'), ('Bob ', '25')]JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。
JSON对象:JSON对象是由键值对组成的对象,键是字符串,值可以是字符串、数字、布尔值、数组、对象、null。
json.loads() 方法用于将字符串反序列化为对象。
import json
json_str = '{"name": "Jack", "age": 24}'
# 反序列化
json_obj = json.loads(json_str)
print(json_obj) # 输出:{'name': 'Jack', 'age': 24}
print(json_obj['name']) # 输出:Jackjson.dumps() 方法用于将对象序列化为字符串。
import json
json_obj = {'name': 'Jack', 'age': 24}
# 序列化
json_str = json.dumps(json_obj)
print(json_str) # 输出:{"name": "Jack", "age": 24}
print(type(json_str)) # 输出:<class 'str'>from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
print(Color.RED) # 输出:Color.RED
print(Color.RED.name) # 输出:RED
print(Color.RED.value) # 输出:1通过name属性可以获取枚举名称,通过value属性可以获取枚举值。
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
print(Color.RED) # 输出:Color.RED
print(Color.RED.name) # 输出:RED
print(Color.RED.value) # 输出:1from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
for color in Color:
print(color) # 输出:Color.RED Color.GREEN Color.BLUEfrom enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
GREEN_ALIAS = 2
class Fruit(Enum):
APPLE = 1
BANANA = 2
ORANGE = 3
print(Color.RED == Color.RED) # 输出:True
print(Color.RED is Color.RED) # 输出:True
print(Color.RED == Color.GREEN) # 输出:False
print(Color.RED == Color.GREEN_ALIAS) # 输出:False
print(Color.RED is Color.GREEN) # 输出:False
print(Color.RED is Color.GREEN_ALIAS) # 输出:False
print(Color.RED == Fruit.APPLE) # 输出:False
print(Color.RED is Fruit.APPLE) # 输出:False枚举注意事项: 如果枚举名称相同,但是枚举值不同,那么枚举名称相同的枚举值是不相等的。后定义的枚举值可视为先定义同名枚举值的别名。
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
GREEN_ALIAS = 2
# 遍历出现重复值的枚举类型,只输出先定义的枚举名称。
for color in Color:
print(color) # 输出:Color.RED Color.GREEN Color.BLUE
# 如果想遍历出现重复值的枚举类型,输出所有枚举名称。
for color in Color.__members__.items():
print(color) # 输出:('RED', <Color.RED: 1>) ('GREEN', <Color.GREEN: 2>) ('BLUE', <Color.BLUE: 3>) ('GREEN_ALIAS', <Color.GREEN: 2>)
for color in Color.__members__.values():
print(color) # 输出:<Color.RED: 1> <Color.GREEN: 2> <Color.BLUE: 3> <Color.GREEN: 2>
for color in Color.__members__.keys():
print(color) # 输出:RED GREEN BLUE GREEN_ALIAS
for color in Color.__members__:
print(color) # 输出:RED GREEN BLUE GREEN_ALIASfrom enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
print(Color(1)) # 输出:Color.RED
print(Color(2)) # 输出:Color.GREEN
print(Color(3)) # 输出:Color.BLUEIntEnum是Enum的子类,它的枚举值必须是整数。
from enum import IntEnum
class Color(IntEnum):
RED = 1
GREEN = 2
BLUE = 3
print(Color.RED) # 输出:Color.RED
print(Color.RED.name) # 输出:RED
print(Color.RED.value) # 输出:1
print(Color(1)) # 输出:Color.RED
print(Color(2)) # 输出:Color.GREEN
print(Color(3)) # 输出:Color.BLUEunique是一个装饰器,它可以限制枚举类型的值不能相同。
from enum import Enum, unique
@unique
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
RED_ALIAS = 1
# 报错:
# ValueError: duplicate values found in <enum 'Color'>: RED_ALIAS -> REDdef func():
pass
print(type(func)) # 输出:<class 'function'>闭包是指一个函数对象,它可以访问定义它的函数的局部变量。
def outer():
x = 1
def inner():
print(x)
return inner
f = outer()
x = 2
f() # 输出:1,不是2,因为inner函数访问的是定义它的函数的局部变量。
# __closure__ 属性是一个元组,元组中的元素是一个cell对象。cell对象是一个封装了局部变量的值的对象。
# 如果外层函数没有定义任何变量作为内层函数引用的局部变量(闭包),那么__closure__属性为None。
print(f.__closure__) # 输出:(<cell at 0x000001F42D877588: int object at 0x000000005D2582E0>,)
print(f.__closure__[0].cell_contents) # 输出:1def f1():
a = 10
def f2():
a = 20
print(a)
print(a)
f2()
print(a)
f1() # 输出:10 20 10origin = 0
def go(step):
new_pos = origin + step
origin = new_pos
return new_pos
print(go(2)) # 输出:UnboundLocalError: local variable 'origin' referenced before assignment
# 报错原因是在函数go中,函数内origin出现在赋值运算符左边的情况,所以origin被认为是一个局部变量,但是在函数go中又对origin进行了赋值,所以会报错。正确的写法是:
origin = 0
def go(step):
# 使用global声明origin为全局变量
global origin
new_pos = origin + step
origin = new_pos
return new_pos
print(go(2)) # 输出2采用闭包的方式:
# 定义一个全局变量origin
origin = 0
# pos为工厂函数的参数
def factory(pos):
def go(step):
# 因为下面pos出现在赋值运算符左边的情况,不使用nonlocal声明pos为非局部变量的话,pos被认为是一个非局部变量。
nonlocal pos
new_pos = pos + step
pos = new_pos
return new_pos
return go
# 传递origin作为参数
tourist = factory(origin)
print(tourist(2)) # 输出:2
print(tourist(3)) # 输出:5
print(tourist(4)) # 输出:9匿名函数:函数名 = lambda 参数列表 : 函数体
# 定义一个匿名函数
f = lambda x, y: x + y
print(f(1, 2)) # 输出:3
# 相当于:
def add(x, y):
return x + y高阶函数:函数的参数是函数或者函数的返回值是函数。
# 定义一个函数,参数是函数
def func(f):
return f()
# 定义一个函数,返回值是函数
def func2():
return lambda: 1
print(func2())
print(func2()())python中的三元表达式语法为:
x if condition else yx = 1
y = 2
# 三元表达式: 如果x > y,返回x,否则返回y
r = x if x > y else y
print(r) # 输出:2map函数用于将一个函数应用到一个序列的每个元素上,并返回一个迭代器。
# 定义一个函数,将列表中的每个元素乘以2
def func(x):
return x * 2
# 定义一个列表
lst = [1, 2, 3, 4, 5]
# 使用map函数,将func函数应用到lst列表的每个元素上,返回一个迭代器map对象
r = map(func, lst)
# r是一个迭代器map对象
print(r) # 输出:<map object at 0x000001F42D877588>
# 将r转换为列表
print(list(r)) # 输出:[2, 4, 6, 8, 10]map函数和lambda函数一起使用,可以简化代码,提高代码可读性。
# 定义一个列表
lst = [1, 2, 3, 4, 5]
# 使用map函数和lambda函数,将lst列表的每个元素乘以2,返回一个迭代器map对象
r = map(lambda x: x * 2, lst)
# r是一个迭代器map对象
print(r) # 输出:<map object at 0x000001F42D877588>
# 将r转换为列表
print(list(r)) # 输出:[2, 4, 6, 8, 10]# 定义两个列表
list_x = [1, 2, 3]
list_y = [4, 5, 6]
# 计算两个列表对应位置的元素之和
r = map(lambda x, y: x + y, list_x, list_y)
print(list(r)) # 输出:[5, 7, 9]# 两个列表元素个数不相等时,以短的列表为准!
list_x = [1, 2, 3]
list_y = [4, 5, 6, 7, 8]
# 计算两个列表对应位置的元素之和
r = map(lambda x, y: x + y, list_x, list_y)
print(list(r)) # 输出:[5, 7, 9]reduce函数用于将一个函数应用到一个序列的每个元素上,并返回一个值。
reduce函数参数有3个参数:
python3中,reduce函数已经移除了,可以使用functools.reduce代替。
from functools import reduce
# 定义一个函数,将两个参数相加
def func(x, y):
return x + y
# 定义一个列表
lst = [1, 2, 3, 4, 5]
# 使用reduce函数,将func函数应用到lst列表的每个元素上,返回一个值
r = reduce(func, lst)
print(r) # 输出:15from functools import reduce
# 带初始值的reduce函数
r = reduce(lambda x, y: x + y, [1, 2, 3, 4, 5], 10)
print(r) # 输出:25filter函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
# 定义一个函数,判断元素是否大于3
def func(x):
return x > 3
# 定义一个列表
lst = [1, 2, 3, 4, 5]
# 使用filter函数,过滤掉lst列表中小于等于3的元素,返回一个迭代器filter对象
r = filter(func, lst)
# r是一个迭代器filter对象
print(r) # 输出:<filter object at 0x000001F42D877588>
# 将r转换为列表
print(list(r)) # 输出:[4, 5]# 定义一个列表
lst = [1, 2, 3, 4, 5]
# 使用filter函数,过滤掉lst列表中小于等于3的元素,返回一个迭代器filter对象
r = filter(lambda x: x > 3, lst)
# r是一个迭代器filter对象
print(r) # 输出:<filter object at 0x000001F42D877588>
# 将r转换为列表
print(list(r)) # 输出:[4, 5]装饰器:装饰器是一种特殊的函数,它的参数是函数,返回值也是函数。
装饰器的使用语法为:
@decorator
def decorated_function():
pass装饰器的作用是在不改变原函数的情况下,为原函数添加新的功能。 装饰器的使用场景:
# 自定义一个装饰器
def decorator(func):
def wrapper():
print('before')
func()
print('after')
return wrapper
# 定义一个函数
def func():
print('func')
# 装饰器的使用
func = decorator(func)
func()
# 输出:
# before
# func
# afterimport time
# 定义一个装饰器
def decorator(func):
def wrapper():
print('before')
start_time = time.time()
func()
end_time = time.time()
print('after')
# 计算函数执行时间
print('time: ', end_time - start_time)
return wrapper
# 定义函数并使用装饰器
@decorator
def func():
print('func')
time.sleep(1)
# 调用函数
func()带参数的装饰器:
# 定义一个装饰器
def decorator(func):
# 定义一个包装函数, 接收任意参数, 并将其传递给原函数。可变参数和关键字(key word)参数来确保传递给原函数的参数数量和类型与原函数相同。
def wrapper(*args, **kwargs):
print('before')
# 调用原函数, 并将参数传递给原函数
func(*args, **kwargs)
print('after')
return wrapper
# 装饰器的使用
@decorator
def func(name, **kwargs):
print('func', name, kwargs)
# 调用函数
func('Jack', age=18)
# before
# func Jack {'age': 18}
# afterflask框架中装饰器的使用:
@app.route('/') # 添加路由
@auth.login_required # 添加登录验证
def hello():
return 'Hello World!'# 引入正则表达式模块
import re
import ssl
# request 方法可以直接请求url
from urllib import request
ssl._create_default_https_context = ssl._create_unverified_context
class Spider():
url = 'https://movie.douban.com/top250'
# 容器的正则:([\s\S]*?)表示匹配任意字符,0或多次,非贪婪模式,如果不加?则为贪婪模式,会匹配到最后一个</li>
# 在字符串前加r表示不转义,否则会报错
list_pattern = r'<div class="info">[\s\S]*?<div class="bd">[\s\S]*?</div>[\s\S]*?</div>'
# 名称的正则
name_pattern = r'<span class="title">([^&]*?)</span>'
# 评分的正则
score_pattern = r'<span class="rating_num" property="v:average">([\s\S]*?)</span>'
# 获取网页内容
def __fetch_content(self):
# 创建请求对象并添加headers
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'
}
# 创建请求对象,并添加headers,防止触发反爬
req = request.Request(url=Spider.url,headers=headers,method="GET")
# request.urlopen() 用来请求url
r = request.urlopen(req)
# read() 读取url的内容
htmls = r.read()
# 将bytes转换为str
htmls = str(htmls, encoding='utf-8')
# 如果有必要可以将htmls写入文件进行排查
# with open('output.html', 'w', encoding='utf-8') as f:
# f.write(htmls)
return htmls
def __analysis(self, htmls):
# 通过信息容器正则表达式获取容器中的内容
list_html = re.findall(Spider.list_pattern, htmls)
infoArr = []
# 遍历list_html, 并通过名称和评分的正则表达式获取名称和评分
# 将获取的信息封装到字典中,并添加到infoArr中
for html in list_html:
name = re.findall(Spider.name_pattern, html)
score = re.findall(Spider.score_pattern, html)
info = {'name':name, 'score':score}
infoArr.append(info)
return infoArr
# 细化解析数据
def __refine(self, arr):
l = lambda item: {
# strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。
'name':item['name'][0].strip(),
'score':item['score'][0]
}
return map(l, arr)
# 排序
def __sort(self, arr):
#sorted() 函数对所有可迭代的对象进行排序操作。
# key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
# reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
arr = sorted(arr, key=self.__sort_seed,reverse=True)
return arr
# 排序种子: 这里指定按照score排序
def __sort_seed(self, item):
r = re.findall(r'[1-9]\d*\.?\d*', item['score'])
number = float(r[0])
return number
def __show(self, arr):
for rank in range(0, len(arr)):
print('rank ' + str(rank + 1)
+ ' : '+ arr[rank]['name']
+ ' ' + arr[rank]['score'])
# 入口函数
def go(self):
# 获取网页内容
htmls = self.__fetch_content()
# 分析网页内容
infos = self.__analysis(htmls)
# 整理数据
infos = list(self.__refine(infos))
# 排序
infos = self.__sort(infos)
# 展示
self.__show(infos)
# 实例化
spider = Spider()
# 调用spider的go方法
spider.go()
# 输出:
# rank 1 : 肖申克的救赎 9.7
# rank 2 : 霸王别姬 9.6
# rank 3 : 控方证人 9.6
# rank 4 : 泰坦尼克号 9.5
# rank 5 : 阿甘正传 9.5
# rank 6 : 美丽人生 9.5
# rank 7 : 辛德勒的名单 9.5
# rank 8 : 千与千寻 9.4
# rank 9 : 这个杀手不太冷 9.4
# rank 10 : 星际穿越 9.4
# rank 11 : 盗梦空间 9.4
# rank 12 : 楚门的世界 9.4
# rank 13 : 忠犬八公的故事 9.4
# rank 14 : 海上钢琴师 9.3
# rank 15 : 放牛班的春天 9.3
# rank 16 : 机器人总动员 9.3
# rank 17 : 无间道 9.3
# rank 18 : 熔炉 9.3
# rank 19 : 触不可及 9.3
# rank 20 : 教父 9.3
# rank 21 : 三傻大闹宝莱坞 9.2
# rank 22 : 疯狂动物城 9.2
# rank 23 : 大话西游之大圣娶亲 9.2
# rank 24 : 当幸福来敲门 9.2
# rank 25 : 寻梦环游记 9.1switch (day) {
case 0:
dayName = "Sunday";
break;
case 1:
dayName = "Monday";
break;
case 2:
dayName = "Tuesday";
break;
case 3:
dayName = "Wednesday";
break;
case 4:
dayName = "Thursday";
break;
case 5:
dayName = "Friday";
break;
case 6:
dayName = "Saturday";
break;
default:
dayName = "Unknown";
break;
}day = 0
def get_sunday():
return "Sunday"
def get_monday():
return "Monday"
def get_tuesday():
return "Tuesday"
def get_wednesday():
return "Wednesday"
def get_thursday():
return "Thursday"
def get_friday():
return "Friday"
def get_saturday():
return "Saturday"
def get_default():
return "Unknown"
# 使用字典映射代替switch case语句
switcher = {
0: get_sunday,
1: get_monday,
2: get_tuesday,
3: get_wednesday,
4: get_thursday,
5: get_friday,
6: get_saturday
}
# 使用get方法获取字典中的值,如果不存在则返回默认值
dayName = switcher.get(day, get_default)()列表推导式是一种简洁的语法,用于创建列表、集合、字典等数据结构。
语法为:
[expression for item in iterable if condition]expression:推导表达式,用于生成列表中的元素。
item:可迭代对象中的元素。 iterable:可迭代对象,例如列表、元组、集合、字典等。 condition:条件,用于过滤可迭代对象中的元素。
# 列表推导式
# 创建一个列表,列表中包含1到10的数字
lst = [i*i for i in range(1, 11)]
print(lst) # 输出: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 对偶数进行立方
lst2 [i**3 for i in range(1, 11) if i%2==0]
print(lst2) # 输出: [4, 16, 36, 64, 100]
# set 集合推导式
s = {i for i in range(1, 11)}
print(s) # 输出: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
# dict 字典推导式
d = {i: i**2 for i in range(1, 11)}
print(d) # 输出: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}
# 元组推导式
t = tuple(i for i in range(1, 11))
print(t) # 输出: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
# 字典推导式-交互key和value
d = {'a': 1, 'b': 2, 'c': 3}
d2 = {v: k for k, v in d.items()}
print(d2) # 输出: {1: 'a', 2: 'b', 3: 'c'}iterator:迭代器是一个对象,它实现了迭代器协议,即实现了__iter__()和__next__()方法。可被for循环迭代的对象。
可迭代对象(Iterable)与迭代器(Iterator)的区别 :
__iter__() 方法的对象__iter__() 和 __next__() 方法的对象。列表、元组、字典它们是可迭代的对象,但不是迭代器,因为它们没有实现__next__()方法。
# 自定义一个可迭代的对象
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
# 返回迭代器对象(即自身)
return self
def __next__(self):
# 如果索引大于等于列表长度,抛出异常
if self.index >= len(self.data):
# 抛出异常
raise StopIteration
# 获取当前索引对应的值
value = self.data[self.index]
# 索引加1
self.index += 1
# 返回值
return value
# 创建一个可迭代的对象
my_iterator = MyIterator([1, 2, 3, 4, 5])
# 使用for循环迭代
for item in my_iterator:
print(item)
# 结果:
# 1
# 2
# 3
# 4
# 5
# 迭代器只能迭代一次
for item in my_iterator:
print(item)
# 结果:
# 无输出
# 如果需要重新迭代,需要重新创建一个可迭代的对象。
my_iterator2 = MyIterator([1, 2, 3, 4, 5])
# 或者使用copy模块的deepcopy方法
import copy
# 深拷贝
my_iterator3 = copy.deepcopy(my_iterator)
# 浅拷贝
my_iterator4 = copy.copy(my_iterator)generator:生成器是一个函数,它使用yield关键字来返回一个值,每次调用生成器函数时,它会从上次返回的位置继续执行。
语法为:
def my_generator():
yield 1
yield 2
yield 3yield关键字:
生成器的优点:
# 自定义一个生成器
def gen(max):
n = 0
while n <= max :
n += 1
yield n
# 创建一个生成器
g = gen(3)
# 使用for循环迭代
for item in g:
print(item)
# 结果:
# 1
# 2
# 3
# 列表推导式得到生成器:使用()代替[]
g2 = (i for i in range(1, 4))
# 使用for循环迭代
for item in g2:
print(item)None不等于0、False、空字符串、空列表、空字典、空元组、空集合、空函数、空类、空对象等。 None是一个特殊的常量,表示没有值。 None是一个对象,它的类型是NoneType。 None是一个单例对象,即只有一个实例。 None是一个不可变对象,即不能修改。
print(None) # None
print(type(None)) # <class 'NoneType'>
print(id(None)) # 140709434444160
print(id(None)) # 140709434444160
print(None == 0) # False影响自定义对象的bool值的因素:
默认情况下,__len__方法返回0,__nonzero__方法返回True。
python3中,__len__方法和__nonzero__方法被__bool__方法取代。
如果要自定义对象的bool值,需要定义__bool__方法。
class A:
pass
a = A()
print(a) # <__main__.A object at 0x104e00070>
print(bool(a)) # True
print(a == 0) # False
print(a == None) # False
print(a == []) # False
print(a == ()) # False
print(a == {}) # False
print(a == '') # False
print(a == {}) # False
print(a == {}) # Falseclass A:
def __bool__(self):
print('__bool__')
return False
def __len__(self):
print('__len__')
return 0
a = A()
print(a) # <__main__.A object at 0x104e00070>
print(bool(a)) # Falseimport time
# 定义一个装饰器
def decorator(func):
def wrapper():
start_time = time.time()
print('start time:', start_time)
func()
return wrapper
# 定义函数并使用装饰器
@decorator
def func():
'''
这是func函数
'''
print(func.__name__)
# 调用函数
func()
# 结果:
# start time: 1682319803.034595
# wrapper
print(help(func))
# Help on function wrapper in module __main__:
# wrapper()
# None
# 如果不使用decorator,func.__name__结果为:func。
# 如果不使用decorator,help(func)结果为:help on function func in module __main__:
# func()
# 这是func函数
# None
# 可以看到,使用了decorator后func的name已经变成了wrapper,这是因为装饰器的副作用。
# help(func)的结果也变成了wrapper的帮助文档。
# 装饰器的副作用:装饰器会改变被装饰函数的一些属性,例如__name__、__doc__等。要解决装饰器的副作用,我们可以使用functools.wraps装饰器,它会将被装饰函数的属性复制到wrapper函数中。
import time
# 也可以使用 from functools import wraps
import functools
# 定义一个装饰器
def decorator(func):
@functools.wraps(func)
def wrapper():
start_time = time.time()
print('start time:', start_time)
func()
return wrapper
# 定义函数并使用装饰器
@decorator
def func():
'''
这是func函数
'''
print(func.__name__)
# 调用函数
func()
# 结果:
# start time: 1682319803.034595
# func
print(help(func))
# Help on function func in module __main__:
# func()
# 这是func函数
# Nonewalrus operator:海象运算符,也叫海象表达式。 语法为:
if (a := expression) is not None:
print(a)海象运算符的作用是在表达式中同时进行赋值和判断。 例如:if (a := expression) is not None: 等价于:a = expression; if a is not None:
a = 'python'
if (b := len(a)) > 5:
print('长度大于5,' + '真实长度为' + str(b))
# 结果:
# 长度大于5,真实长度为6f关键字做字符串拼接,语法为:
f'字符串{表达式}'f关键字做字符串拼接,会将表达式的值替换到字符串中。
name = 'python'
age = 18
print(f'name: {name}, age: {age}')
# 结果:
# name: python, age: 18dataclass装饰器:数据类装饰器,也叫数据类。 语法为:
@dataclass
class Person:
name: str
age: intdataclass装饰器会自动生成
__init__、__repr__、__eq__等方法。 例如:@dataclass 等价于:
class Person:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
# __repr__方法的作用是返回一个字符串,用于表示对象。
def __repr__(self):
return f'Person(name={self.name}, age={self.age})'
# __eq__方法的作用是判断两个对象是否相等。
def __eq__(self, other):
if isinstance(other, Person):
return self.name == other.name and self.age == other.age
return Falsefrom dataclasses import dataclass
# 定义一个数据类
@dataclass
class Person:
# 定义两个属性
name: str
age: int
p1 = Person('python', 18)
p2 = Person('python', 18)
print(p1)
# 结果:
# Person(name='python', age=18)
print(p1 == p2)
# 结果:
# True