基础知识重点摘录
字符串
在Python中,用引号括起的都是字符串,其中的引号可以是单引号,也可以是双引号。这种灵活性让你能够在字符串中包含引号和撇号:
- 'I told my friend, "Python is my favorite language!"'
- "The language 'Python' is named after Monty Python, not the snake."
- "One of Python's strengths is its diverse and supportive community."
在用单引号括起的字符串中,如果包含撇号,就将导致错误。这是因为这会导致Python将第一个单引号和撇号之间的内容视为一个字符串,进而将余下的文本视为Python代码,从而引发错误。
数字
Python使用两个乘号表示乘方运算。
空格不影响Python计算表达式的方式,它们的存在旨在让你阅读代码时,能迅速确定先执行哪些运算。如: 2 + 3*4
或 (2 + 3) * 4
。
列表
- 在Python中,用方括号(
[]
)来表示列表,并用逗号来分隔其中的元素。 - Python为访问最后一个列表元素提供了一种特殊语法。通过将索引指定为-1,这种约定也适用于其他负数索引,例如,索引-2返回倒数第二个列表元素。
- 修改列表元素:
motorcycles[0] = 'ducati'
- 在列表中添加元素:
- 在列表末尾添加元素
motorcycles.append('ducati')
- 在列表中插入元素:
motorcycles.insert(0, 'ducati')
- 从列表中删除元素:
- 使用
del
语句删除元素:del motorcycles[1]
- 使用方法
pop()
删除元素(将元素从列表中删除,并接着使用它的值):motorcycles.pop(5)
- 根据值删除元素:
motorcycles.remove('ducati')
,方法remove()
只删除第一个指定的值。如果要删除的值可能在列表中出现多次,就需要使用循环来判断是否删除了所有这样的值。 - 排序
- 使用方法 sort() 对列表进行永久性排序
- 使用函数 sorted() 对列表进行临时排序,如果你要按与字母顺序相反的顺序显示列表,也可向函数
sorted()
传递参数reverse=True
。
- 确定列表的长度:
len(cars)
- 使用 range()创建数字列表,还可指定步长:
list(range(2,11,2))
- 列表解析:
squares = [value**2 for value in range(1,11)]
- 复制列表:使用
friend_foods = my_foods[:]
;而不是friend_foods = my_foods
,使用赋值不能得到两个列表。
元组
- 列表是可以修改的,Python将不能修改的值称为不可变的,而不可变的列表被称为元组。
- 元组看起来犹如列表,但使用圆括号而不是方括号来标识。定义元组后,就可以使用索引来访问其元素,就像访问列表元素一样。
- 虽然不能修改元组的元素,但可以给存储元组的变量赋值。
字典
在Python中, 字典是一系列键—值对。每个键都与一个值相关联,你可以使用键来访问与之相关联的值。与键相关联的值可以是数字、字符串、列表乃至字典。事实上,可将任何Python对象用作字典中的值。
使用字典来存储用户提供的数据或在编写能自动生成大量键—值对的代码时,通常都需要先定义一个空字典。
- 访问字典中的值:
print(alien_0['color'])
- 添加键—值对:
alien_0['x_position'] = 0
- 修改字典中的值:
alien_0['color'] = 'yellow'
- 删除键—值对:
del alien_0['points']
- 遍历所有的键—值对:
for key, value in user_0.items():
- 遍历字典中的所有键:
for name in favorite_languages.keys():
- 按顺序遍历字典中的所有键:
for name in sorted(favorite_languages.keys()):
- 遍历字典中的所有值:
for language in favorite_languages.values():
,这种做法提取字典中所有的值,而没有考虑是否重复。如果需要踢出重复项,使用for language in set(favorite_languages.values()):
嵌套
- 在列表中嵌套字典
- 在字典中嵌套列表
- 在字典中嵌套字典
注意:列表和字典的嵌套层级不应太多。
用户输入
- 函数input()让程序暂停运行,等待用户输入一些文本。例如:
message = input("Tell me something, and I will repeat it back to you: ")
- 使用函数input()时, Python将用户输入解读为字符串。而函数int()将数字的字符串表示转换为数值表示,如:
age = int(age)
for 与 while 循环
- for循环是一种遍历列表的有效方式,但在for循环中不应修改列表,否则将导致Python难以跟踪其中的元素。
- 要在遍历列表的同时对其进行修改,可使用while循环。通过将while循环同列表和字典结合起来使用,可收集、存储并组织大量输入,供以后查看和显示。
函数
- 传递实参:
- 位置实参,这要求实参的顺序与形参的顺序相同,如:
describe_pet('harry', 'hamster')
- 关键字实参,其中每个实参都由变量名和值组成;还可使用列表和字典,如:
describe_pet(animal_type='hamster', pet_name='harry')
,注意:使用关键字实参时,务必准确地指定函数定义中的形参名。
- 默认值:编写函数时,可给每个形参指定默认值。给形参指定默认值后,可在函数调用中省略相应的实参。使用默认值可简化函数调用,还可清楚地指出函数的典型用法。注意 使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的实参。这让Python依然能够正确地解读位置实参。
- 可混合使用位置实参、关键字实参和默认值,通常有多种等效的函数调用方式。使用哪种调用方式无关紧要,只要函数调用能生成你希望的输出就行。使用对你来说最容易理解的调用方式即可。
- 传递列表
- 在函数中修改列表:在函数中对这个列表所做的任何修改都是永久性的,这让你能够高效地处理大量的数据。
- 禁止函数修改列表:切片表示法
[:]
创建列表的副本。 - 虽然向函数传递列表的副本可保留原始列表的内容,但除非有充分的理由需要传递副本,否则还是应该将原始列表传递给函数,因为让函数使用现成列表可避免花时间和内存创建副本,从而提高效率,在处理大型列表时尤其如此。
- 传递任意数量的实参:形参名
*toppings
中的星号让Python创建一个名为toppings的空元组,并将收到的所有值都封装到这个元组中。如:def make_pizza(*toppings):
- 结合使用位置实参和任意数量实参:如果要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后。 如:
def make_pizza(size, *toppings):
- 使用任意数量的关键字实参:预先不知道传递给函数的会是什么样的信息。在这种情况下,可将函数编写成能够接受任意数量的键—值对。如:
def build_profile(first, last, **user_info):
- 编写函数时,你可以以各种方式混合使用位置实参、关键字实参和任意数量的实参。知道这些实参类型大有裨益,因为阅读别人编写的代码时经常会见到它们。
导入函数(将函数存储在模块)
函数的优点之一是,使用它们可将代码块与主程序分离。通过给函数指定描述性名称,可让主程序容易理解得多。你还可以更进一步,将函数存储在被称为模块的独立文件中,再将模块导入到主程序中。
- 导入整个模块:
import module_name
- 导入特定的函数:
from module_name import function_name
- 使用 as 给函数指定别名:
from module_name import function_name as fn
- 使用 as 给模块指定别名:
import module_name as mn
- 导入模块中的所有函数:
from module_name import *
,使用并非自己编写的大型模块时,最好不要采用这种导入方法(如果模块中有函数的名称与你的项目中使用的名称相同,可能导致意想不到的结果),最佳的做法是,要么只导入你需要使用的函数,要么导入整个模块并使用句点表示法。这能让代码更清晰,更容易阅读和理解。
类
class Car(): """一次模拟汽车的简单尝试""" def __init__(self, make, model, year): """初始化描述汽车的属性""" self.make = make self.model = model self.year = year def get_descriptive_name(self): """返回整洁的描述性信息""" long_name = str(self.year) + ' ' + self.make + ' ' + self.model return long_name.title() 复制代码
- 每个与类相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法。
- 根据约定,在Python中,我们通常可以认为首字母大写的名称(如:Dog)指的是类,而小写的名称(如my_dog)指的是根据类创建的实例。如:
my_dog = Dog('willie', 6)
- 句点表示法:
- 访问实例的属性:
my_dog.name
- 调用Dog类中定义的任何方法:
my_dog.sit()
类继承
一个类继承另一个类时,它将自动获得另一个类的所有属性和方法;原有的类称为父类,而新类称为子类。子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法。
class ElectricCar(Car): """电动汽车的独特之处""" def __init__(self, make, model, year): """初始化父类的属性""" super().__init__(make, model, year) 复制代码
导入类(将类存储在模块)
为遵循Python的总体理念,应让文件尽可能整洁。为在这方面提供帮助, Python允许你将类存储在模块中,然后在主程序中导入所需的模块。
- 导入单个类:
from car import ElectricCar
- 从一个模块中导入多个类:
from car import Car, ElectricCar
- 导入整个模块:
import car
,然后使用语法module_name.class_name
访问需要的类。 - 导入模块中的所有类。如:
from module_name import *
,不推荐使用这种导入方式。 - 需要从一个模块中导入很多类时,最好导入整个模块,并使用
module_name.class_name
语法来访问类。这样做时,虽然文件开头并没有列出用到的所有类,但你清楚地知道在程序的哪些地方使用了导入的模块;你还避免了导入模块中的每个类可能引发的名称冲突。
文件
读取文件
with open('pi_digits.txt') as file_object: contents = file_object.read() print(contents) 复制代码
- 要以任何方式使用文件——哪怕仅仅是打印其内容,都得先打开文件,这样才能访问它。
- 关键字with在不再需要访问文件后将其关闭。
- 逐行读取:
for line in file_object:
- 使用关键字with时, open()返回的文件对象只在with代码块内可用。如果要在with代码块外访问文件的内容,可在with代码块内将文件的各行存储在一个列表中,并在with代码块外使用该列表,创建一个包含文件各行内容的列表:
lines = file_object.readlines()
- 读取文本文件时, Python将其中的所有文本都解读为字符串。如果你读取的是数字,并要将其作为数值使用,就必须使用函数int()将其转换为整数,或使用函数float()将其转换为浮点数。
写入文件
filename = 'programming.txt' with open(filename, 'w') as file_object: file_object.write("I love programming.") 复制代码
- 打开文件时,可指定读取模式( 'r')、 写入模式( 'w')、 附加模式( 'a')或让你能够读取和写入文件的模式( 'r+')。如果你省略了模式实参, Python将以默认的只读模式打开文件。
- 如果你要写入的文件不存在,函数open()将自动创建它。
- 以写入( 'w')模式打开文件时千万要小心,因为如果指定的文件已经存在,Python将在返回文件对象前清空该文件。
- Python只能将字符串写入文本文件。要将数值数据存储到文本文件中,必须先使用函数
str()
将其转换为字符串格式。
使用Json格式存取数据
- 存储数据:
json.dump(numbers, f_obj)
,其中,f_obj为文件对象。 - 读取数据:
numbers = json.load(f_obj)
,其中,f_obj为文件对象。
异常
Python使用被称为异常的特殊对象来管理程序执行期间发生的错误。每当发生让Python不知所措的错误时,它都会创建一个异常对象。
- 如果你编写了处理该异常的代码,程序将继续运行;
- 如果你未对异常进行处理,程序将停止,并显示一个traceback,其中包含有关异常的报告。
异常是使用try-except代码块处理的。使用了try-except代码块时,即便出现异常,程序也将继续运行:显示你编写的友好的错误消息,而不是令用户迷惑的traceback。
示例:
print("Give me two numbers, and I'll divide them.") print("Enter 'q' to quit.") while True: first_number = input("\nFirst number: ") if first_number == 'q': break second_number = input("Second number: ") try: answer = int(first_number) / int(second_number) except ZeroDivisionError: print("You can't divide by 0!") else: print(answer) 复制代码
有一些仅在try代码块成功执行时才需要运行的代码;这些代码应放在else代码块中。 except代码块告诉Python,如果它尝试运行try代码块中的代码时引发了指定的异常,该怎么办。
通过预测可能发生错误的代码,可编写健壮的程序,它们即便面临无效数据或缺少资源,也能继续运行,从而能够抵御无意的用户错误和恶意的攻击。
测试代码
单元测试和测试用例
Python标准库中的模块unittest提供了代码测试工具。
- 单元测试用于核实函数的某个方面没有问题;
- 测试用例是一组单元测试,这些单元测试一起核实函数在各种情形下的行为都符合要求。
- 全覆盖式测试用例包含一整套单元测试,涵盖了各种可能的函数使用方式。对于大型项目,要实现全覆盖可能很难。通常,最初只要针对代码的重要行为编写测试即可。
测试未通过时怎么办?
测试未通过时,不要修改测试,而应修复导致测试不能通过的代码:检查刚对函数所做的修改,找出导致函数行为不符合预期的修改。
各种断言方法
unittest
Module中的断言方法如下:
assertEqual(a, b)
: 核实a == bassertNotEqual(a, b)
: 核实a != bassertTrue(x)
: 核实x为TrueassertFalse(x)
: 核实x为FalseassertIn(item, list)
: 核实item在list中assertNotIn(item, list)
: 核实item不在list中
针对方法进行测试
针对方法进行测试得示例如下:
import unittest from name_function import get_formatted_name class NamesTestCase(unittest.TestCase): """测试name_function.py """ def test_first_last_name(self): """能够正确地处理像Janis Joplin这样的姓名吗? """ formatted_name = get_formatted_name('janis', 'joplin') self.assertEqual(formatted_name, 'Janis Joplin') def test_first_last_middle_name(self): """能够正确地处理像Wolfgang Amadeus Mozart这样的姓名吗? """ formatted_name = get_formatted_name( 'wolfgang', 'mozart', 'amadeus') self.assertEqual(formatted_name, 'Wolfgang Amadeus Mozart') unittest.main() 复制代码
在TestCase类中使用很长的方法名是可以的;这些方法的名称必须是描述性的,这才 能让你明白测试未通过时的输出。
针对类进行测试
针对类进行测试得示例如下:
import unittest from survey import AnonymousSurvey class TestAnonymousSurvey(unittest.TestCase): """针对AnonymousSurvey类的测试""" def setUp(self): """ 创建一个调查对象和一组答案,供使用的测试方法使用 """ question = "What language did you first learn to speak?" self.my_survey = AnonymousSurvey(question) self.responses = ['English', 'Spanish', 'Mandarin'] def test_store_single_response(self): """测试单个答案会被妥善地存储""" self.my_survey.store_response(self.responses[0]) self.assertIn(self.responses[0], self.my_survey.responses) def test_store_three_responses(self): """测试三个答案会被妥善地存储""" for response in self.responses: self.my_survey.store_response(response) for response in self.responses: self.assertIn(response, self.my_survey.responses) unittest.main() 复制代码
测试自己编写的类时,方法setUp()让测试方法编写起来更容易:可在setUp()方法中创建一系列实例并设置它们的属性,再在测试方法中直接使用这些实例。
注意:运行测试用例时,每完成一个单元测试, Python都打印一个字符:测试通过时打印一个句点;测试引发错误时打印一个E;测试导致断言失败时打印一个F。这就是你运行测试用例时,在输出的第一行中看到的句点和字符数量各不相同的原因。如果测试用例包含很多单元测试,需要运行很长时间,就可通过观察这些结果来获悉有多少个测试通过了。
设置代码格式
代码被阅读的次数比编写的次数多。如果一定要在让代码易于编写和易于阅读之间做出选择, Python程序员几乎总是会选择后者。
- 缩进:PEP 8建议每级缩进都使用四个空格,这既可提高可读性,又留下了足够的多级缩进空间。混合使用制表符和空格会让Python解释器感到迷惑。在程序中混合使用制表符和空格可能导致极难解决的问题。
- 行长:很多Python程序员都建议每行不超过80字符。PEP 8还建议注释的行长都不超过72字符,因为有些工具为大型项目自动生成文档时,会在每行释开头添加格式化字符。
- 空行:你应该使用空行来组织程序文件,但也不能滥用。空行不会影响代码的运行,但会影响代码的可读性。 Python解释器根据水平缩进情况来解读代码,但不关心垂直间距。
- 在条件测试的格式设置方面, PEP 8提供的唯一建议是,在诸如==、 >=和<=等比较运算符两边各添加一个空格,例如,
if age < 4:
要比if age<4:
好。
变量的规则
- 变量名只能包含字母、数字和下划线。变量名可以字母或下划线打头,但不能以数字打头,例如,可将变量命名为message_1,但不能将其命名为1_message。
- 变量名不能包含空格,但可使用下划线来分隔其中的单词。例如,变量名greeting_message可行,但变量名greeting message会引发错误。
- 不要将Python关键字和函数名用作变量名,即不要使用Python保留用于特殊用途的单词,如print
- 变量名应既简短又具有描述性。例如, name比n好, student_name比s_n好, name_length比length_of_persons_name好。
- 慎用小写字母l和大写字母O,因为它们可能被人错看成数字1和0。
要创建良好的变量名,需要经过一定的实践,在程序复杂而有趣时尤其如此。随着你编写的程序越来越多,并开始阅读别人编写的代码,将越来越善于创建有意义的变量名。
注意:就目前而言,应使用小写的Python变量名,避免使用大写字母。