Python根据缩进来判断代码行与前一个代码行的关系。Python要求你使用缩进让代码整洁而结构清晰。
1.变量和常用数据类型
变量名只能包含字母、数字和下划线。变量名能以字母或下划线打头,但不能以数字打头。Python中应使用小写的表示变量,全大写的表示常量。
在Python中,注释用井号(#)标识。
1.1 字符串 str
在Python中,用引号括起的都是字符串,其中的引号可以是单引号,也可以是双引号。这种灵活性让你能够在字符串中包含引号和撇号。
str()函数可将数转换为字符串。
修改字符串的方法: title() upper() lower()
msg = "Hello Python World!"
print(msg)
1.1.1 在字符串中使用变量
要在字符串中插入变量的值,可在前引号前加上字母f,再将要插入的变量放在花括号内。
f字符串是Python3.6引入的。如果你使用的是Python 3.5或更早的版本,需要使用 format() 方法,而非这种f语法。使用方法format(),可在圆括号内列出要在字符串中使用的变量。对于每个变量,都通过一对花括号来引用。
msg = "Hello World!"
la = 'python'
words = f"{la} {msg}"
print(words)
#f字符串拼接
print(f"Hello, {la.title()} World.")
old = 'old format'
#format()方法拼接
print("{} by {}".format(msg, old.title()))
1.1.2 删除空白
Python能够找出字符串开头和末尾多余的空白,并删除它:
rstrip() #删除字符串末尾的空白
lstrip() #删除字符串开头的空白
strip() #删除字符串两边的空白
但这种删除只是暂时的,要永久删除需要将删除的结果关联到变量。
message = " Hello World "
message = message.strip()
1.1.3 字节串 bytes
字节串也称作“二进制字符串”。字节串(bytes)和字符串(str)是两种不同的数据类型,即使有时候看上去一样,但作比较时永不相等,也不能混用。
字符串是给人看的,字节串是给机器看的。
要创建一个字节串字面量,可以在字符串前加一个字母b作为前缀。
字符串使用Unicode标准,可以通过encode()编码为字节串;字节串一定包含某种真正的字符串编码格式(默认是UTF-8),可以通过decode()解码为字符串。
1.2 数
1.2.1 整数
在Python中,可对整数执行加(+)减(-)乘(*)除(/)运算。
使用两个乘号 ** 表示乘方运算。
int()方法将整数字符串转为整数。
1.2.2 浮点数
Python将所有带小数点的数称为浮点数。
将任意两个数相除时,结果总是浮点数,即便这两个数都是整数且能整除。
无论是哪种运算,只要有操作数是浮点数,Python默认得到的总是浮点数,即便结果原本为整数也是如此。
float()方法将浮点数字符串转换为浮点数。
1.2.3 数中的下划线
书写很大的数时,可使用下划线将其中的数字分组,使其更清晰易读。这种表示法适用于整数和浮点数,但只有Python 3.6和更高的版本支持。
num = 14_000_100
x, y, z = 23, 37, 73
可在一行代码中给多个变量赋值,Python将按顺序将每个值赋给对应的变量。这有助于缩短程序并提高其可读性。
1.2.4 常量
Python没有内置的常量类型,但可使用全大写来指出应将某个变量视为常量。
1.2.5 无穷大
float("inf") 和 float("-inf") 这两个值分别对应数学世界里的正负无穷大。当它们和任意数值做比较时,满足这样的规律:float("-inf")<任意数值< float("inf")。
1.3 用户输入
Python的 input() 函数将用户输入解释为字符串。
函数input()接受一个参数,要向用户显示的提示(prompt)或说明,让用户知道该如何做。
使用 int() 函数可以将数的字符串转换为数值。
msg = "Tell me something, and I'll repeat it.\n"
msg = input(msg)
print(msg)
age = input('how old are you?')
age = int(age)
print(f'your age {age}')
2 容器
2.1 列表
列表由一系列按特定顺序排列的元素组成。可以将任何东西加入列表中,其中的元素之间可以没有任何关系。列表通常包含多个元素,因此列表变量一般使用复数名称。
在Python中,用方括号 [] 表示列表,并用逗号分隔其中的元素。如果让Python将列表打印出来,Python将打印列表的内部表示,包括方括号。
列表的另一种创建方式是使用内置函数list():list(iterable)可以把任何一个可迭代对象转换为列表。
len() #获取列表的长度
列表的底层使用了数组(array)数据结构。
2.1.1 访问列表
列表是有序集合,因此可以使用下标(索引)访问。在Python中,列表元素的索引也是从0开始的。
Python为访问最后一个列表元素提供了一种特殊语法。通过将索引指定为-1,可让Python返回最后一个列表元素。这种访问方式也适用于其他负数索引。
2.1.2 修改元素
直接使用下标访问,然后指定新值即可。
不要在遍历列表时直接修改列表,可以使用一个新的列表来保存修改后的值。
2.1.3 添加元素
append() #在列表末尾添加元素
insert() #在列表任意位置添加元素,需要指定索引和值
2.1.4 删除元素
如果知道要删除的元素在列表中的位置,可使用del语句删除元素。
pop() #删除末尾的元素,pop会返回被删除的元素,可以继续使用。实际上可以使用pop()来删除列表中任意位置的元素,只需在圆括号中指定要删除元素的索引即可。
remove() #按值删除元素。注意:方法remove()只删除第一个指定的值,如果要删除的值可能在列表中出现多次,就需要使用循环来确保将每个值都删除。
countries = ["China", "America", "Russia", "England", "france"]
num = len(countries)
print(f'There are {num} countries:{countries}')
print(f"I Love {countries[0]}!")
#访问并修改元素
countries[-1] = countries[-1].title()
#新增元素
countries.append('Japan')
countries.insert(-1, "Korean")
#删除元素
del countries[-1]
gl = countries.pop()
print(f'Good Luck, {gl}')
countries.remove('America')
print(countries)
2.1.5 排序列表
sort() #永久的修改列表排列顺序,按字母顺序排序。向sort()方法传递参数reverse=True即可实现字母的反向排序。
sorted() #对列表临时排序,并不影响列表的原始排序。也可向函数sorted()传递参数reverse=True。
reverse() #反转列表元素的排列顺序。
cars = ['BYD', 'TESLA', 'AION', 'ZEEKR', 'Li', 'AITO', 'Geely']
cars.reverse()
print(cars)
cars.sort()
cars.sort(reverse=True)
print(cars)
print(sorted(cars))
cars.sort()
print(sorted(cars, reverse=True))
print(cars)
2.1.6 遍历列表
for循环遍历列表时,默认拿到列表的成员。假如你想在遍历的同时,获取当前循环下标,可以选择用内置函数e numerate() 包裹列表对象。enumerate()接收一个可选的start参数,用于指定循环下标的初始值(默认为0)。
enumerate()适用于任何“可迭代对象”,因此它不光可以用于列表,还可以用于元组、字典、字符串等其他对象。
cars = ['BYD', 'TESLA', 'AION', 'ZEEKR', 'Li', 'AITO', 'Geely']
for car in cars:
print(car)
for index, v in enumerate(cars, start=10):
print(index, car)
2.1.7 数值列表
使用 range() 函数可以轻松生成一系列数值。
调用函数 range() 时,也可只指定一个参数,这样它将从0开始。
使用函数 list() 可将 range() 的结果直接转换为列表。
使用函数range()时,还可指定步长,Python将根据这个步长来生成数。
min() max() sum()
for v in range(1, 6):
print(v) # 1 2 3 4 5
nums = []
for v in range(3):
nums.append(v)
print(nums) # [0, 1, 2]
nums = list(range(6))
print(nums) # [0, 1, 2, 3, 4, 5]
nums = list(range(3, 13, 3))
print(nums) # [3, 6, 9, 12]
print(min(nums)) # 3
print(max(nums)) # 12
print(sum(nums)) # 30
2.1.8 列表解析
列表解析将for循环和创建新元素的代码合并成一行,并自动附加新元素。
squares = [v**2 for v in range(7)]
print(squares) # [0, 1, 4, 9, ,16, 25, 36]
2.1.9 切片
列表的一部分称之为切片。
要创建切片,可指定要使用的第一个元素和最后一个元素的索引。
在表示切片的方括号内可指定第三个值,告诉Python在指定范围内每隔多少元素提取一个。
cars = ['BYD', 'TESLA', 'Geely', 'AION', 'Li', 'AITO']
print(cars[:3]) # ['BYD', 'TESLA', 'Geely']
print(cars[-3:]) # ['AION', 'Li', 'AITO']
print(cars[::2]) # ['BYD', 'Geely', 'Li']
#复制列表
newCars = cars[:]
newCars.sort()
print(newCars) # ['AION', 'AITO', 'BYD', 'Geely', 'Li', 'TESLA']
注意:将一个列表赋值给另一个列表,并不能得到两个列表,它们指向的其实是同一个列表。要复制列表,可创建一个包含整个列表的切片,方法是同时省略起始索引和终止索引([:])。这让Python创建一个始于第一个元素、终止于最后一个元素的切片,即整个列表的副本。
2.2 元组
Python将不可变的列表被称为元组。元组用圆括号 () 表示。
严格地说,元组是由逗号标识的,圆括号只是让元组看起来更整洁、更清晰。
如果要定义只包含一个元素的元组,必须在这个元素后面加上逗号。
2.3 字典
在Python中,字典是一系列键值对。每个键都与一个值相关联,可使用键来访问相关联的值。与键相关联的值可以是数、字符串、列表乃至字典。事实上,可将任何Python对象用作字典中的值。
Python使用花括号 {} 来定义字典。键和值之间用冒号分隔,而键值对之间用逗号分隔。
在Python 3.7中,字典中元素的排列顺序与定义时相同。如果将字典打印出来或遍历其元素,将发现元素的排列顺序与添加顺序相同。
一种不错的做法是,在最后一个键值对后面也加上逗号,为以后在下一行添加键值对做好准备。
字典的底层使用了哈希表数据结构。
2.3.1 访问元素
在python中访问字典的和C++中的map一样,可以使用键作下标访问。
当访问的键不存在时,使用键作下标的访问方式将会出错。这种情况可以使用 get() 方法访问,当键不存在时,返回一个默认值。
方法get()的第一个参数用于指定键,是必不可少的;第二个参数为指定的键不存在时要返回的值,是可选的。如果get()方法没有指定第二个参数,则返回值None。
访问元素时也可以先使用 if语句 判断一下key值是否存在;或者采用try...except语句读取。
2.3.2 删除元素
使用 del 语句删除字典中的元素,但必须指定字典名和要删除的键。
如果要删除的键不存在,该操作就会抛出KeyError异常。但假设你只是单纯地想去掉某个键,并不关心它存在与否、删除有没有成功,那么使用dict.pop(key, default)方法就够了。严格说来,pop方法的主要用途并不是删除某个键,而是取出这个键对应的值。
countries = {
'China': '中国',
'America': '美国',
'Russia': '俄罗斯',
}
print(countries) #{'China': '中国', 'America': '美国', 'Russia': '俄罗斯'}
countries['England'] = '英国'
countries['Japan'] = '日本'
del countries['Japan']
print(countries) #{'China': '中国', 'America': '美国', 'Russia': '俄罗斯', 'England': '英国'}
japan = countries.get('Japan')
print(japan) #None
japan = countries.get('Japan', "There is no japan!")
print(japan) #There is no japan!
2.3.3 遍历字典
Python对字典有多种遍历方式:可遍历字典的所有键值对,也可仅遍历键或值。
items() 返回一个所有键值对的列表。
keys() 返回一个所有键的列表。
values() 返回一个所有值的列表。
遍历字典时,会默认遍历所有的键。
countries = {
'China': '中国',
'America': '美国',
'Russia': '俄罗斯',
}
print(countries)
for k, v in countries.items():
print(f"English: {k}, 中文:{v}")
for k in countries.keys():
print(f"English: {k}")
for v in countries.values():
print(f"中文:{v}")
2.3.4 字典合并
两个字典的合并有多种方法:
d1 = {"name" : "apple"}
d2 = {"price" : 10}
# 会改变d1
d1. update(d2)
# copy()
def merge_dict(d1, d2):
result = d1.copy()
result.update(d2)
return result
merge_dict(d1, d2)
# 解包操作合并字典
d3 = {**d1, **d2}
print(d3)
# |
d4 = d1 | d2
print(d4)
使用双星号**运算符来做解包操作可以更简单地完成字典合并操作。
解包操作默认是浅拷贝。
除了使用**解包字典,你还可以使用单星号*运算符来解包任何可迭代对象。
Python发布的3.9版本中,字典类型新增了对 | 运算符的支持。只要执行d1 | d2,就能快速拿到两个字典合并后的结果。
2.4 集合
Python集合中的每个元素都是独一无二的。
Python集合可以用一对花括号 {} 直接创建。集合不会以特定的顺序存储元素。
通过对包含重复元素的列表调用set(),可让Python找出列表中独一无二的元素,并使用这些元素来创建一个集合。
要初始化一个空集合,只能调用set()方法,因为{}表示的是一个空字典,而不是一个空集合。
集合是一种可变类型,使用.add()方法可以向集合追加新成员。
在使用集合时,可哈希性是个非常重要的概念,假如把不可哈希的对象(比如列表)放入集合,程序就会抛出TypeError异常。
首先,那些不可变的内置类型都是可哈希的;而可变的内置类型都无法正常计算哈希值。其次,由用户定义的所有对象默认都是可哈希的。
集合的底层使用了哈希表数据结构。所以使用集合去重得到的结果会丢失集合内成员原有的顺序。
cars = {'BYD', 'TESLA', 'Geely', 'AION', 'Li', 'AITO'}
print(cars) #{'AION', 'Geely', 'AITO', 'Li', 'BYD', 'TESLA'} 打印顺序随机
可以使用 & | - 运算符对两个集合求 交 并 差集。
2.5 嵌套
Python可以在列表中嵌套字典,在字典中嵌套列表甚至在字典中嵌套字典。
2.6 生成器
生成器(generator)是Python里的一种特殊的数据类型。它是一个不断给调用方“生成”内容的类型。定义一个生成器,需要用到生成器函数与yield关键字。
def generate_even(max_number):
for i in range(0, max_number):
if i % 2 == 0
yield i
for i in generate_even(10):
print(i)
虽然都是返回结果,但 yield 和 return 的最大不同之处在于,return的返回是一次性的,使用它会直接中断整个函数执行,而yield可以逐步给调用方生成结果,调用 next() 可以逐步从生成器对象里拿到结果。
因为生成器是可迭代对象,所以你可以使用list()等函数方便地把它转换为各种其他容器类型。
3. 条件分支控制语句
Python中布尔表达式的值为 True 或 False。
Pyhon使用关键字 and 表示逻辑与,用关键字 or 表示逻辑或。
使用关键字 in 判断特定的值是否已包含在列表中,使用关键字 not in 来判断特定是不是否没有被包含在列表中。
if语句的结构:
if-else if-elif-else 多elif代码块结构
Python并不要求if语句后面必须有else代码块。和for循环一样,在每个分支后面都需要冒号 : 标识后面是该分支的代码块。
if语句可以用来判断列表是否为空:在if语句中将列表名用作条件表达式时,Python将在列表至少包含一个元素时返回True,并在列表为空时返回False。
当某个对象作为主角出现在if分支里时,解释器会主动对它进行“真值测试”,也就是调用bool()函数获取它的布尔值。除整型外,其他内置类型的布尔值规则如下:
布尔值为假:None、0、False、[]、()、{}、set()、frozenset(),等等
布尔值为真:非0的数值、True,非空的序列、元组、字典,用户定义的类和实例,等等
只要给UserCollection类实现__len__魔法方法,users对象就可以直接用于“真值测试。为类定义__len__魔法方法,实际上就是为它实现了Python世界的长度协议。
定义__len__并非影响布尔值结果的唯一办法。除了__len__以外,还有一个魔法方法__bool__和对象的布尔值息息相关。为对象定义__bool__方法后,对它进行布尔值运算会直接返回该方法的调用结果。
假如一个类同时定义了__len__和__bool__两个方法,解释器会优先使用__bool__方法的执行结果。
对于自定义对象来说,它们在进行 == 运算时行为是可操纵的:只要实现类型的__eq__魔法方法就行。
因为 == 的行为可被魔法方法__eq__改变,要严格检查某个对象是否为None时,就需要使用 is 运算符。虽然二者看上去差不多,但有着本质上的区别:
(1)==对比两个对象的值是否相等,行为可被__eq__方法重载;
(2)is 判断两个对象是否是内存里的同一个东西,无法被重载。
仅当你需要判断某个对象是否是None、True、False时,使用is,其他情况下,请使用==。
Python的整型驻留”技术:
Python语言使用了一种名为“整型驻留”(integer interning)的底层优化技术。对于从-5到256的这些常用小整数,Python会将它们缓存在内存里的一个数组中。当你的程序需要用到这些数字时,Python不会创建任何新的整型对象,而是会返回缓存中的对象。这样能为程序节约可观的内存。
注意:and运算符的优先级高于or
4. 循环控制语句
for循环用于针对集合中的每个元素都执行一个代码块,而while循环则不断运行,直到指定的条件不满足为止。
for循环是一种遍历列表的有效方式,但不应在for循环中修改列表,否则将导致Python难以跟踪其中的元素。要在遍历列表的同时对其进行修改,可使用while循环。
for循环和while循环都使用冒号 : 来标识循环体开始。
Python的循环中的 break语句 和 continue语句 的作用与C++等其他语言中的作用是一致的。
修饰可迭代对象:是指用生成器(或普通的迭代器)在循环外部包装原本的循环主体,完成一些原本必须在循环内部执行的工作——比如过滤特定成员、提供额外结果等,以此简化循环代码。
使用takewhile()替代break语句:
takewhile(predicate, iterable)会在迭代第二个参数iterable的过程中,不断使用当前值作为参数调用predicate()函数,并对返回结果进行真值测试,如果为True,则返回当前值并继续迭代,否则立即中断本次迭代。
for循环(和while循环)后的else关键字,代表如果循环正常结束(没有碰到任何break),便执行该分支内的语句。
5. 函数
Python中使用关键字 def 来定义函数。最后,定义以冒号结尾。
文档字符串用三引号括起来,Python使用它们来生成有关程序中函数的文档。
定义函数时,也可以给每个形参指定默认值。规则和其他语言一样,有默认值的参数定义在没有默认值的参数的后面。Python函数的参数默认值只会在函数定义阶段被创建一次,之后不论再调用多少次,函数内拿到的默认值都是同一个对象。一般将默认值设置为None,将其视为占位值。给形参指定默认值时,等号两边不要有空格。
编写函数时,应给函数指定描述性名称,且只在其中使用小写字母和下划线。
Python在进行函数调用传参时,采用的既不是值传递,也不是引用传递,而是传递了“变量所指对象的引用”(pass-by-object-reference)。函数内部的操作是否会影响原始变量,取决于变量本身是否可变。
可变(mutable):列表、字典、集合
不可变(immutable):整数、浮点数、字符串、字节串、元组
可以使用 copy.deepcopy() 函数来进行深拷贝操作。
除了 def 以外,你还可以使用 lambda 关键字来定义一个匿名函数。
5.1 参数传递
向函数传递实参的方式很多:可使用位置实参,这要求实参的顺序与形参的顺序相同;也可使用关键字实参,其中每个实参都由变量名和值组成;还可使用列表和字典。
def add(numa, numb=0):
"""两个数相加"""
return numa + numb
#位置实参
print(add(1, 1))
#关键字实参
print(add(numa = 1, numb = 2))
#使用默认参数
print(add(4))
5.1.1 传列表
将列表传递给函数后,函数就可对其进行修改。在函数中对这个列表所做的任何修改都是永久性的。如果不想修改列表里的元素,可以在调用函数时传入列表的副本,即使用切片方式传参。
def listAdd1(nums):
for i in range(len(nums)):
nums[i] += 1
nums = [v for v in range(1, 10, 2)]
print(nums) #[1, 3, 5, 7, 9]
#传列表
listAdd1(nums)
print(nums) #[2, 4, 6, 8, 10]
#传列表的副本
listAdd1(nums[:])
print(nums) #[2, 4, 6, 8, 10]
5.1.2 传任意数量的实参
定义形参时参数名前加星号 * ,Python将为其创建一个空元组,并将收到的所有值都封装到这个元组中。
如果要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后。Python先匹配位置实参和关键字实参,再将余下的实参都收集到最后一个形参中。
注意:经常会看到通用形参名*args,它也收集任意数量的位置实参。
def printCars(*cars):
print(cars) #('BYD', 'GEELY', 'AITO', 'Li')
for car in cars:
print(car) #BYD GEELY AITO LI
printCars("BYD", "GEELY", "AITO", "Li")
5.1.3 传递任意数量的关键字实参
定义形参时参数名前加两个星号 ** ,Python将为其创建一个空字典,并将收到的所有名称值对都放到到这个字典中。
注意:经常会看到形参名**kwargs,它用于收集任意数量的关键字实参。
5.1.4 仅限关键字和仅限位置参数
仅限关键字参数:在参数列表中插入 * 符号,在 * 后的所有参数都变成了“仅限关键字参数”(keyword-only argument)。如果调用方仍然想用位置参数来提供这些参数值,程序就会抛出错误。
仅限位置参数:在参数列表中插入 / 符号,在 / 之前的参数只能使用位置参数的方式提供。
5.2 返回值
函数可返回任何类型的值,包括列表和字典等较复杂的数据结构。
在Python中,函数可以一次返回多个结果,这其实是通过返回一个元组来实现的。
None是不带任何return语句的函数的默认返回值。
5.3 模块导入
还可以将函数存储在称为模块的独立文件中,再将模块导入到主程序中。import语句允许在当前运行的程序文件中使用模块中的代码。
模块是扩展名为.py的文件,包含要导入到程序中的代码。常见导入方法如下:
#导入整个re模块,函数调用方式:模块名.函数名 re.search
import re
#导入特定函数,函数调用方式:不需要模块名,直接调用 findall
from re import search, match, split, findall
#使用as给函数指定别名
from re import findall as regexpFA
#使用as给模块指定别名
import re as regexp
#导入模块中所有函数 最好不要采用这种导入方法!
from re import *
最佳的做法是,要么只导入需要使用的函数,要么导入整个模块并使用句点表示法。
5.4 常用函数模块 functools
5.4.1 高阶函数 functools.partial()
partial的调用方式为partial(func, *arg, **kwargs),其中:
func是完成具体功能的原函数;
*args / **kwargs 是可选位置与关键字参数,必须是原函数func所接收的合法参数。
5.4.2 加缓存 functools.lru_cache()
5.5 闭包
闭包(closure)是编程语言领域里的一个专有名词。简单来说,闭包是一种允许函数访问已执行完成的其他函数里的私有变量的技术,是为函数增加状态的另一种方式。
正常情况下,当Python完成一次函数执行后,本次使用的局部变量都会在调用结束后被回收,无法继续访问。但是,如果你使用下面这种“函数套函数”的方式,在外层函数执行结束后,返回内嵌函数,后者就可以继续访问前者的局部变量,形成了一个“闭包”结构。
def counter():
value = 0
def _counter():
nonlocal value
value += 1
return value
return _counter
闭包是一种非常有用的工具,非常适合用来实现简单的有状态函数。
5. 类
在Python中,首字母大写的名称指的是类。类也用class定义。
5.1 类的定义及使用
类中的函数称为方法。
__init__()方法是一个特殊的方法,每当你根据类创建新实例时,Python都会自动运行它。在这个方法的名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。定义 __init__()方法时,形参self必不可少,而且必须位于其他形参的前面。
创建实例时,有些属性无须通过形参来定义,可在方法__init__()中为其指定默认值。
每个与实例相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法。
要访问实例的属性,可使用句点表示法。
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def sit(self):
print(f'{self.name} is now sitting.')
def roll_over(self):
print(f'{self.name} rolled over.')
my_dog = Dog("大黄", 2)
print(f'this my dog {my_dog.name}, {my_dog.age} years old.')
my_dog.roll_over()
my_dog.sit()
5.2 继承
在既有类的基础上编写新类时,通常要调用父类的方法__init__()。这将初始化在父类__init__()方法中定义的所有属性,从而让子类包含这些属性。
定义子类时,父类必须包含在当前文件中,且位于子类前面。定义子类时,必须在圆括号内指定父类的名称。方法__init__()接受创建父类实例所需的信息。
super()是一个特殊函数,让你能够调用父类的方法。
class Car:
def __init__(self, brand, model, year):
self.brand = brand
self.model = model
self.year = year
def print_car(self):
print(f'{self.brand} {self.model} {self.year}')
class ElectricCar(Car):
def __init__(self, brand, model, year):
super().__init__(brand, model, year)
self.battery_size = 400
def describe_battery(self):
print(f"The battery of {self.brand} {self.model} is {self.battery_size} kWh.")
car = Car('Audi', 'A6L', 2023)
elecCar = ElectricCar('Tesla', 'Model3', 2023)
car.print_car()
elecCar.print_car()
elecCar.describe_battery()
父类方法也可以被重写。
类名应采用驼峰命名法,即将类名中的每个单词的首字母都大写,而不使用下划线。实例名和模块名都采用小写格式,并在单词之间加上下划线。
6. 文件和异常
6.1 读文件
关键字with在不再需要访问文件后将其关闭。
open()函数可传入多个参数:
第一个参数是要打开的文件名称;
第二个是打开的模式,可指定读取模式('r')、写入模式('w')、附加模式('a')或读写模式('r+')。如果省略了模式实参,Python将以默认的只读模式打开文件。
还可传入编码格式,使用关键字传参:encoding='utf-8'
注意:显示文件路径时,Windows系统使用反斜杠(\)而不是斜杠(/),但在代码中依然可以使用斜杠。如果在文件路径中直接使用反斜杠,将引发错误,因为反斜杠用于对字符串中的字符进行转义。
若想每次一行的方式检查文件,可对文件对象使用for循环。
使用关键字with时,open()返回的文件对象只在with代码块内可用。如果要在with代码块外访问文件的内容,可在with代码块内将文件的各行存储在一个列表中,并在with代码块外使用该列表:可以立即处理文件的各个部分,也可以推迟到程序后面再处理。
方法readlines()从文件中读取每一行,并将其存储在一个列表中。
注意:读取文本文件时,Python将其中的所有文本都解读为字符串。如果读取的是数,并要将其作为数值使用,就必须使用函数int()将其转换为整数或使用函数float()将其转换为浮点数。
with open("test.py") as file_obj:
contens = file_obj.read()
print(contens.rstrip())
with open("helloWorld.py") as hell_file:
for line in hell_file:
print(line)
with open("test.py") as file_obj:
lines = file_obj.readlines()
longstring = ""
for line in lines:
print(line)
longstring += line.rstrip()
print(longstring)
key = input("find what you want\n")
if key in longstring:
print(True)
else:
print(False)
除了直接用循环迭代文件对象读取文件,还可以使用更底层的方法 file.read() 读取文件。每次调用 file.read(chunk_size) 会马上读取从当前游标位置往后 chunk_size 大小的文件内容,不必等待任何换行符出现。
def count_digits(file_nale):
count = 0;
chunk_size = 10.24 * 8
with open(file_name, 'r') as file:
while True:
chunk = file.read(chunk_size)
if not chunk:
break;
for s in chunk:
if s.isdigit():
count += 1
return count
6.2 写文件
Python只能将字符串写入文本文件。要将数值数据存储到文本文件中,必须先使用函数str()将其转换为字符串格式。
如果要写入的文件不存在,函数open()将自动创建它。
函数write()不会在写入的文本末尾添加换行符。要让每个字符串都单独占一行,需要在方法调用write()中包含换行符(\n)。
with open("test.txt", 'w') as file_obj:
file_obj.write('Hello World!')
方法split()以空格为分隔符将字符串分拆成多个部分,并将这些部分都存储到一个列表中。
6.3 异常
Python使用称为异常的特殊对象来管理程序执行期间发生的错误。
异常是使用try-except代码块处理的。try-except代码块让Python执行指定的操作,同时告诉Python发生异常时怎么办。使用try-except代码块时,即便出现异常,程序也将继续运行:显示你编写的友好的错误消息,而不是令用户迷惑的traceback。
还有一种try-except-else代码块,依赖try代码块成功执行的代码都应放到else代码块中。
try:
print(2/0)
except ZeroDivisionError:
print("can't divide by zero")
print("After try-except.")
Python有一个pass语句,可用于让Python在代码块中什么都不要做。pass语句可以充当占位符,在except中执行pass语句,可达到静默失败的目的。
6.4 上下文管理器
with是一个神奇的关键字,它可以在代码中开辟一段由它管理的上下文,并控制程序在进入和退出这段上下文时的行为。并非所有对象都能像open('foo.txt')一样配合with使用,只有满足上下文管理器(context manager)协议的对象才行。
上下文管理器是一种定义了“进入”和“退出”动作的特殊对象。要创建一个上下文管理器,只要实现__enter__和__exit__两个魔法方法即可。
__exit__接收三个参数:exc_type、exc_value和traceback。在代码执行时,假如with管辖的上下文内没有抛出任何异常,那么当解释器触发__exit__方法时,上面的三个参数值都是None;但如果有异常抛出,这三个参数就会变成该异常的具体内容:
(1) exc_type:异常的类型
(2) exc_value:异常对象
(3) traceback:错误的堆栈对象。
上下文管理器功能强大、用处很多,其中最常见的用处之一,就是简化异常处理工作。当__exit__返回True时,会忽略所有异常,当返回False时,异常会继续抛出。
@contextmanager装饰器位于内置模块contextlib下,它可以把任何一个生成器函数直接转换为一个上下文管理器。
6.5 存储数据 -- json模块
json.dump() json.load()
函数json.dump()接受两个实参:要存储的数据,以及可用于存储数据的文件对象。
import json
nums = [v for v in range(1, 10, 2)]
with open("test.json", 'r+', encoding='utf-8') as tf:
json.dump(nums, tf)
with open("test.json") as f:
lists = json.load(f)
print(lists) #[1, 3, 5, 7, 9]
7. 测试代码
单元测试用于核实函数的某个方面没有问题。
测试用例是一组单元测试,它们一道核实函数在各种情形下的行为都符合要求。
7.1 测试函数
要为函数编写测试用例,可先导入模块unittest和要测试的函数,再创建一个继承unittest.TestCase的类,并编写一系列方法对函数行为的不同方面进行测试。
unittest模块中的常用断言方法:
方法 | 用途 |
assertEqual(a, b) | a == b |
assertNotEqual(a, b) | a != b |
assertTrue(a) | a == True |
assertFalse(a) | a == False |
assertIn(ele, list) | ele in list |
assertNotIn(ele, list) | ele not in list |
所有以test_打头的方法都将自动运行。
import unittest
#导入要测试的函数
#from model_name import function_name
def format_name(first, last):
fullName = f'{first} {last}'
return fullName.title()
#创建继承自 unittest.TestCase 的测试类
class NameTestCase(unittest.TestCase):
#所有 test_ 打头的函数都将自动运行
def test_format_name(self):
name = format_name('janis', 'joplin')
#断言方法核实得到的结果是否与期望的结果一致
self.assertEqual(name, 'Janis Joplin')
# __name__ 变量是在程序执行时设置的
if __name__ == '__main__':
unittest.main()
#直接运行该文件的结果:
# .
# ----------------------------------------------------------------------
# Ran 1 test in 0.000s
# OK
运行测试用例时,每完成一个单元测试,Python都打印一个字符:测试通过时打印一个句点,测试引发错误时打印一个E,而测试导致断言失败时则打印一个F。这就是你运行测试用例时,在输出的第一行中看到的句点和字符数量各不相同的原因。如果测试用例包含很多单元测试,需要运行很长时间,就可通过观察这些结果来获悉有多少个测试通过了。
7.2 测试类
类的测试与函数的测试相似,你所做的大部分工作是测试类中方法的行为。
在unittest.TestCase类的 setUp() 方法中创建一系列实例并设置其属性,就可在每个测试方法中直接使用这些实例。如果在TestCase类中包含了方法setUp(),Python将先运行它,再运行各个以test_打头的方法。