一、变量变量的命令和使用(2018-04-26)
- 变量名只能包含字母、 数字和下划线。 变量名可以字母或下划线打头, 但不能以数字打头, 例如, 可将变量命名为message_1, 但不能将其命名为1_message。
- 变量名不能包含空格, 但可使用下划线来分隔其中的单词。 例如, 变量名greeting_message可行, 但变量名greeting message会引发错误。
- 不要将Python关键字和函数名用作变量名, 即不要使用Python保留用于特殊用途的单词, 如print (请参见附录A.4) 。
- 变量名应既简短又具有描述性。 例如, name比n好, student_name比s_n好, name_length比length_of_persons_name好。
- 慎用小写字母l和大写字母O, 因为它们可能被人错看成数字1和0。
二、字符串
- 字符串 就是一系列字符。 在Python中, 用引号括起的都是字符串, 其中的引号可以是单引号, 也可以是双引号。
三、数字
- 在Python中, 可对整数执行加(+) 减(-) 乘(*) 除(/ ) 运算。
- Python将带小数点的数字都称为浮点数 。 大多数编程语言都使用了这个术语, 它指出了这样一个事实: 小数点可出现在数字的任何位置。 每种编程语言都须细心设计, 以妥善地处理浮点数, 确保不管小数点出现在什么位置, 数字的行为都是正常的。
- 使用函数str() 避免类型错误
如:
age = 23
message = "Happy " + age + "rd Birthday!"
print(message)
代码报错:
Traceback (most recent call last):
File "birthday.py", line 2, in <module>
message = "Happy " + age + "rd Birthday!"
❶ TypeError: Can't convert 'int' object to str implicitly
这是一个类型错误 , 意味着Python无法识别你使用的信息。 在这个示例中, Python发现你使用了一个值为整数(int ) 的变量, 但它不知道该如何解读这个值(见❶) 。 Python知道, 这个变量表示的可能是数值23, 也可能是字符2和3。 像上面这样在字符串中使用整数时, 需要显式地指出你希望Python将这个整数用作字符串。 为此, 可调用函数str() ,它让Python将非字符串值表示为字符串:
age = 23
message = "Happy " + str(age) + "rd Birthday!"
print(message)
- 3.4 Python2的整数
在Python 2中, 将两个整数相除得到的结果稍有不同
>>> python2.7
>>> 3 / 2
1
Python返回的结果为1, 而不是1.5。 在Python 2中, 整数除法的结果只包含整数部分, 小数部分被删除。 请注意, 计算整数结果时, 采取的方式不是四舍五入, 而是将小数部分直接删除。
四、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!
五、列表
5.1 列表特征
- 在Python中, 用方括号([]) 来表示列表, 并用逗号来分隔其中的元素。
- 列表是有序集合, 因此要访问列表的任何元素, 只需将该元素的位置或索引告诉Python即可。 要访问列表元素, 可指出列表的名称, 再指出元素的索引, 并将其放在方括号内。
- 索引从0而不是1开始。
- Python为访问最后一个列表元素提供了一种特殊语法。 通过将索引指定为-1 , 可让Python返回最后一个列表元素。
- 在列表中添加新元素时, 最简单的方式是将元素附加到列表末尾,使用append()方法。
- 在列表中插入元素,使用insert()方法。
- 如果知道要删除的元素在列表中的位置, 可使用del 语句。
- 使用方法pop() 删除元素。删除末尾元素,并返回该元素。
- 使用pop() 来删除列表中任何位置的元素, 只需在括号中指定要删除的元素的索引即可。
- 如果你不确定该使用del 语句还是pop() 方法, 下面是一个简单的判断标准: 如果你要从列表中删除一个元素, 且不再以任何方式使用它, 就使用del 语句; 如果你要在删除元素后还能继续使用它, 就使用方法pop() 。
- 根据值删除元素,使用remove()方法,传入取值。方法remove() 只删除第一个指定的值。 如果要删除的值可能在列表中出现多次, 就需要使用循环来判断是否删除了所有这样的值。
5.2组织列表
- Python方法sort() 让你能够较为轻松地对列表进行排序。
- 还可以按与字母顺序相反的顺序排列列表元素, 为此, 只需向sort() 方法传递参数reverse=True 。
- 要保留列表元素原来的排列顺序, 同时以特定的顺序呈现它们, 可使用函数sorted() 。
- 要反转列表元素的排列顺序, 可使用方法reverse() 。
- 使用函数len() 可快速获悉列表的长度。
5.3遍历整个列表
- 每个缩进的代码行都是循环的一部分, 且针对列表中的每个值都执行一次。
magicians = ['alice', 'david', 'carolina']
for ma in magicians:
print(ma.title() + ", that was a great trick!");
print("I can't wait to see your next trick, " + ma.title() + ".\n")
print("Thank you, everyone. That was a great magic show!");
- 使用函数list(range(1, 5))创建数字数组,包头不包尾。
5.4列表解析
- 使用一行代码精简数组创建、遍历、赋值等。
print([value ** 2 for value in range(1,11)]);
5.5列表的部分元素-切片
- 切片即截取数组,原则是切头不切尾。
players = ['charles', 'martina', 'michael', 'florence', 'eli'];
print(players[0:3]);
- 复制列表,利用切片复制,可创建一个包含整个列表的切片, 方法是同时省略起始索引和终止索引([:] ) 。 这让Python创建一个始于第一个元素, 终止于最后一个元素的切片, 即复制整个列表。
5.6原组
- Python将不能修改的值称为不可变的 , 而不可变的列表被称为元组 。
- 元组看起来犹如列表, 但使用圆括号而不是方括号来标识。 定义元组后, 就可以使用索引来访问其元素, 就像访问列表元素一样。
六、Py格式设置
- 缩进——PEP 8建议每级缩进都使用四个空格, 这既可提高可读性, 又留下了足够的多级缩进空间。
- 行长——很多Python程序员都建议每行不超过80字符。
- 空行——要将程序的不同部分分开, 可使用空行。
- 本章的每个示例都展示了良好的格式设置习惯。 在条件测试的格式设置方面, PEP 8提供的唯一建议是, 在诸如== 、 >= 和<= 等比较运算符两边各添加一个空格, 例如, if age < 4: 要比if age<4: 好。
七、if语句
-要判断两个值是否不等, 可结合使用惊叹号和等号(!= ) , 其中的惊叹号表示不 , 在很多编程语言中都如此。
- 你可能想同时检查多个条件, 例如, 有时候你需要在两个条件都为True 时才执行相应的操作, 而有时候你只要求一个条件为True 时就执行相应的操作。 在这些情况下, 关键字and 和or 可助你一臂之力。
- 要判断特定的值是否已包含在列表中, 可使用关键字in 。
- 确定特定的值未包含在列表中很重要; 在这种情况下, 可使用关键字not in 。
- 经常需要检查超过两个的情形, 为此可使用Python提供的if-elif-else 结构。
- 确定列表不是空的。
八、字典
- 在Python中, 字典 是一系列键—值对 。 每个键 都与一个值相关联, 你可以使用键来访问与之相关联的值。 与键相关联的值可以是数字、 字符串、 列表乃至字典。 事实上, 可将任何Python对象用作字典中的值。
- 键—值 对是两个相关联的值。 指定键时, Python将返回与之相关联的值。 键和值之间用冒号分隔, 而键—值对之间用逗号分隔。
- 新增、修改用[]。
- 删除使用关键字del彻底删除键值对。
- 字典表遍历,可遍历键-值对,也可分别分别遍历键、值。
- 即便遍历字典时, 键—值对的返回顺序也与存储顺序不同。 Python不关心键、值对的存储顺序, 而只跟踪键和值之间的关联关系。
- 字典总是明确地记录键和值之间的关联关系, 但获取字典的元素时, 获取顺序是不可预测的。
九、用户输入和while循环
9.1 用户输入input()
- 使用函数input() 时, Python将用户输入解读为字符串。
- 如果你使用的是Python 2.7, 应使用函数raw_input() 来提示用户输入。 这个函数与Python 3中的input() 一样, 也将输入解读为字符串。
9.2 while循环 - beark、continue同java。
- for 循环是一种遍历列表的有效方式, 但在for 循环中不应修改列表, 否则将导致Python难以跟踪其中的元素。 要在遍历列表的同时对其进行修改, 可使用while 循环。 将while 循环同列表和字典结合起来使用, 可收集、 存储并组织大量输入, 供以后查看和显示。
十、函数
关键字def 来告诉Python你要定义一个函数。文档字符串用三引号括起, Python使用它们来生成有关程序中函数的文档。下面介绍参数传递,主要有三种种方式,第一、位置实参;第二、关键字实参;第三、默认值形参。
10.1 位置实参
使用位置实参来调用函数时, 如果实参的顺序不正确, 结果可能出乎意料。
10.2 关键字实参
关键字实参 是传递给函数的名称—值对。 你直接在实参中将名称和值关联起来了, 因此向函数传递实参时不会混淆(不会得到名为Hamster的harry这样的结果) 。 关键字实参让你无需考虑函数调用中的实参顺序, 还清楚地指出了函数调用中各个值的用途。
10.2 默认值
编写函数时, 可给每个形参指定默认值 。 在调用函数中给形参提供了实参时, Python将使用指定的实参值; 否则, 将使用形参的默认值。 因此, 给形参指定默认值后, 可在函数调用中省略相应的实参。 使用默认值可简化函数调用, 还可清楚地指出函数的典型用法。
使用默认值时, 在形参列表中必须先列出没有默认值的形参, 再列出有默认值的实参。 这让Python依然能够正确地解读位置实参。
10.3 传递任意数量的实参
有时候, 你预先不知道函数需要接受多少个实参, 好在Python允许函数从调用语句中收集任意数量的实参。例如, 来看一个制作比萨的函数, 它需要接受很多配料, 但你无法预先确定顾客要多少种配料。 下面的函数只有一个形参*toppings , 但不管调用语句提供了多少实参, 这个形参都将它们统统收入囊中。
def make_pizza(*toppings):
print(toppings);
make_pizza('mushrooms', 'green peppers', 'extra cheese')
形参名*toppings 中的星号让Python创建一个名为toppings 的空元组, 并将收到的所有值都封装到这个元组中。 函数体内的print 语句通过生成输出来证明Python能够处理使用一个值调用函数的情形, 也能处理使用三个值来调用函数的情形。
如果要让函数接受不同类型的实参, 必须在函数定义中将接纳任意数量实参的形参放在最后。 Python先匹配位置实参和关键字实参, 再将余下的实参都收集到最后一个形参中。
10.4 导入整个模块
要让函数是可导入的, 得先创建模块。 模块 是扩展名为.py的文件, 包含要导入到程序中的代码。
你还可以导入模块中的特定函数, 这种导入方法的语法如下:
from module_name import function_name
通过用逗号分隔函数名, 可根据需要从模块中导入任意数量的函数:
from module_name import function_name1, function_name2
若使用这种语法, 调用函数时就无需使用句点。 由于我们在import 语句中显式地导入了函数make_pizza() , 因此调用它时只需指定其名称。
10.5 使用as给函数指定别名
如果要导入的函数的名称可能与程序中现有的名称冲突, 或者函数的名称太长, 可指定简短而独一无二的别名 ——函数的另一个名称, 类似于外号。 要给函数指定这种特殊外号, 需要在导入它时这样做。
10.6 导入模块中所有函数
使用星号(* ) 运算符可让Python导入模块中的所有函数:
from module_name import *
10.6 函数编写指南
编写函数时, 需要牢记几个细节。 应给函数指定描述性名称, 且只在其中使用小写字母和下划线。 描述性名称可帮助你和别人明白代码想要做什么。 给模块命名时也应遵循上述约定。
每个函数都应包含简要地阐述其功能的注释, 该注释应紧跟在函数定义后面, 并采用文档字符串格式。 文档良好的函数让其他程序员只需阅读文档字符串中的描述就能够使用它: 他们完全可以相信代码如描述的那样运行; 只要知道函数的名称、 需要的实参以及返回值的类型, 就能在自己的程序中使用它。
给形参指定默认值时, 等号两边不要有空格:
def function_name(parameter_0, parameter_1='default value')
所有的import 语句都应放在文件开头, 唯一例外的情形是, 在文件开头使用了注释来描述整个程序。
十一、类
根据约定, 在Python中, 首字母大写的名称指的是类。 这个类定义中的括号是空的, 因为我们要从空白创建这个类。
class Dog():
def __init__(self, name, age):
self.name = name;
self.age = age;
def sit(self):
print(self.name.title() + ' is now sitting.');
def roll_over(self):
print(self.name.title() + ' rolled over.');
11.1 方法__init__()
方法__init__() 是一个特殊的方法, 每当你根据Dog 类创建新实例时, Python都会自动运行它。 在这个方法的名称中, 开头和末尾各有两个下划线, 这是一种约定, 旨在避免Python默认方法与普通方法发生名称冲突。
在这个方法的定义中, 形参self 必不可少, 还必须位于其他形参的前面。 为何必须在方法定义中包含形参self 呢? 因为Python调用这个__init__() 方法来创建Dog 实例时, 将自动传入实参self 。 每个与类相关联的方法调用都自动传递实参self , 它是一个指向实例本身的引用, 让实例能够访问类中的属性和方法。
11.2 继承
子类继承了其父类的所有属性和方法, 同时还可以定义自己的属性和方法。
创建子类的实例时, Python首先需要完成的任务是给父类的所有属性赋值。 为此, 子类的方法__init__() 需要父类施以援手。
class Car():
def __init__(self, make, model, year):
self.make = make;
self.model = model;
self.year = year;
self.odometer_reading = 0;
def read_odometer(self):
print("This car has " + str(self.odometer_reading) + " miles on it.")
def update_odometer(self, mill):
if mill >= self.odometer_reading:
self.odometer_reading = mill
else:
print('U cannot roll back.');
def get_description_name(self):
long_name = str(self.year) + ' ' + self.make + ' ' + self.model
return long_name.title();
class ElectricCar(Car):
def __init__(self, make, model, year):
super().__init__(make, model, year)
首先是Car 类的代码 。 创建子类时, 父类必须包含在当前文件中, 且位于子类前面。 我们定义了子类ElectricCar 。 定义子类时, 必须在括号内指定父类的名称。 方法init() 接受创建Car 实例所需的信息 。
super() 是一个特殊函数, 帮助Python将父类和子类关联起来。 这行代码让Python调用ElectricCar 的父类的方法init() , 让ElectricCar 实例包含父类的所有属性。 父类也称为超类 (superclass) , 名称super因此而得名。
11.3 重写父类的方法
对于父类的方法, 只要它不符合子类模拟的实物的行为, 都可对其进行重写。 为此, 可在子类中定义一个这样的方法, 即它与要重写的父类方法同名。 这样, Python将不会考虑这个父类方法, 而只关注你在子类中定义的相应方法。
11.4 导入类
随着你不断地给类添加功能, 文件可能变得很长, 即便你妥善地使用了继承亦如此。 为遵循Python的总体理念, 应让文件尽可能整洁。 为在这方面提供帮助, Python允许你将类存储在模块中, 然后在主程序中导入所需的模块。
from car import Car
my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
import 语句让Python打开模块car , 并导入其中的Car 类。 这样我们就可以使用Car 类了, 就像它是在这个文件中定义的一样。
可根据需要在程序文件中导入任意数量的类。 如果我们要在同一个程序中创建普通汽车和电动汽车, 就需要将Car 和ElectricCar 类都导入:
from car import Car, ElectricCar
11.5 导入整个模块
你还可以导入整个模块, 再使用句点表示法访问需要的类。 这种导入方法很简单, 代码也易于阅读。 由于创建类实例的代码都包含模块名, 因此不会与当前文件使用的任何名称发生冲突。下面的代码导入整个car 模块, 并创建一辆普通汽车和一辆电动汽车:
import car
my_beetle = car.Car('volkswagen', 'beetle', 2016)
print(my_beetle.get_descriptive_name())
my_tesla = car.ElectricCar('tesla', 'roadster', 2016)
print(my_tesla.get_descriptive_name())
我们导入了整个car 模块。 接下来, 我们使用语法 module_name.class_name 访问需要的类。
11.6 导入模块中的所有类
要导入模块中的每个类, 可使用下面的语法:
from module_name import *
不推荐使用这种导入方式, 其原因有二。 首先, 如果只要看一下文件开头的import 语句, 就能清楚地知道程序使用了哪些类, 将大有裨益; 但这种导入方式没有明确地指出你使用了模块中的哪些类。 这种导入方式还可能引发名称方面的困惑。 如果你不小心导入了一个与程序文件中其他东西同名的类, 将引发难以诊断的错误。 这里之所以介绍这种导入方式, 是因为虽然不推荐使用这种方式, 但你可能会在别人编写的代码中见到它。
11.7 编码风格
类名应采用驼峰命名法 , 即将类名中的每个单词的首字母都大写, 而不使用下划线。 实例名和模块名都采用小写格式, 并在单词之间加上下划线。
可使用空行来组织代码, 但不要滥用。 在类中, 可使用一个空行来分隔方法; 而在模块中, 可使用两个空行来分隔类。
需要同时导入标准库中的模块和你编写的模块时, 先编写导入标准库模块的import 语句, 再添加一个空行, 然后编写导入你自己编写的模块的import 语句。 在包含多条import 语句的程序中, 这种做法让人更容易明白程序使用的各个模块都来自何方。
十二、文件和异常
下面的程序打开并读取这个文件, 再将其内容显示到屏幕上:
with open('pi_digits.txt') as file_object:
content = file_object.read()
print(content)
在这个程序中, 第1行代码做了大量的工作。 我们先来看看函数open() 。 要以任何方式使用文件——哪怕仅仅是打印其内容, 都得先打开 文件, 这样才能访问它。 函数open()接受一个参数: 要打开的文件的名称。 Python在当前执行的文件所在的目录中查找指定的文件。
12.1 关键字with 在不再需要访问文件后将其关闭。 在这个程序中, 注意到我们调用了open() , 但没有调用close() ; 你也可以调用open() 和close() 来打开和关闭文件, 但这样做时, 如果程序存在bug, 导致close() 语句未执行, 文件将不会关闭。 这看似微不足道, 但未妥善地关闭文件可能会导致数据丢失或受损。 如果在程序中过早地调用close() , 你会发现需要使用文件时它已关闭 (无法访问) , 这会导致更多的错误。 并非在任何情况下都能轻松确定关闭文件的恰当时机, 但通过使用前面所示的结构, 可让Python去确定: 你只管打开文件, 并在需要时使用它, Python自会在合适的时候自动将其关闭。
12.2 创建一个包含文件各行内容的列表
使用关键字with 时, open() 返回的文件对象只在with 代码块内可用。 如果要在with 代码块外访问文件的内容, 可在with 代码块内将文件的各行存储在一个列表中, 并在with 代码块外使用该列表: 你可以立即处理文件的各个部分, 也可推迟到程序后面再处理。
with open('pi_digits.txt') as file_object:
lines = file_object.readlines()
for line in lines:
print(line);
文件对象file_object读取多行文件存储到数组lines中,在with代码块外,依然能够引用该数组,这个例子告诉我们,Python没有块级变量、局部变量。
12.3 写入文件
要将文本写入文件, 你在调用open() 时需要提供另一个实参, 告诉Python你要写入打开的文件。 为明白其中的工作原理, 我们来将一条简单的消息存储到文件中, 而不是将其打印到屏幕上:
filename = 'programming.txt'
with open(filename, 'w') as file_object:
file_object.write('I love programming.');
在这个示例中, 调用open() 时提供了两个实参。 第一个实参也是要打开的文件的名称; 第二个实参('w' ) 告诉Python, 我们要以写入模式 打开这个文件。 打开文件时, 可指定读取模式 ('r' ) 、 写入模式 ('w' ) 、 附加模式 ('a' ) 或让你能够读取和写入文件的模式('r+' ) 。 如果你省略了模式实参, Python将以默认的只读模式打开文件。
12.4 异常
Python使用被称为异常的特殊对象来管理程序执行期间发生的错误。 每当发生让Python不知所措的错误时, 它都会创建一个异常对象。 如果你编写了处理该异常的代码, 程序将继续运行; 如果你未对异常进行处理, 程序将停止, 并显示一个traceback, 其中包含有关异常的报告。
异常是使用try-except 代码块处理的。 try-except 代码块让Python执行指定的操作, 同时告诉Python发生异常时怎么办。 使用了try-except 代码块时, 即便出现异常,程序也将继续运行: 显示你编写的友好的错误消息, 而不是令用户。
- 处理ZeroDivisionError 异常
print(1/0)
Traceback (most recent call last):
File "division.py", line 1, in <module>
print(1/0)
ZeroDivisionError: division by zero
在上述traceback中,ZeroDivisionError 是一个异常对象。 Python无法按你的要求做时, 就会创建这种对象。 在这种情况下, Python将停止运行程序, 并指出引发了哪种异常, 而我们可根据这些信息对程序进行修改。 下面我们将告诉Python, 发生这种错误时怎么办; 这样, 如果再次发生这样的错误, 我们就有备无患了。
- 使用try-except代码块
当你认为可能发生了错误时, 可编写一个try-except 代码块来处理可能引发的异常。 你让Python尝试运行一些代码, 并告诉它如果这些代码引发了指定的异常, 该怎么办。处理ZeroDivisionError 异常的try-except 代码块类似于下面这样:
try:
print(1/0)
except ZeroDivisionError:
print('u can not divide by zero!');
- else代码块
通过将可能引发错误的代码放在try-except 代码块中, 可提高这个程序抵御错误的能力。 错误是执行除法运算的代码行导致的, 因此我们需要将它放到try-except 代码块中。 这个示例还包含一个else 代码块; 依赖于try 代码块成功执行的代码都应放到else 代码块中:
print("Give me two numbers, and I'll divide them.")
print("Enter 'q' to quit.")
while True:
first_num = input('\nFirst Number:')
if first_num=='q':
break;
second_num = input('\nSecond Number:')
if second_num=='q':
break;
try:
answer = int(first_num) / int(second_num)
except ZeroDivisionError:
print('u can not divide by zero!');
else:
print(answer)
- 处理FileNotFoundError 异常
filename = 'alice'
try:
with open(filename) as file_obj:
contents = file_obj.read()
except FileNotFoundError:
print('Sorry, the file "' + filename + '" is not exist.')
- 失败时一声不吭
在前一个示例中, 我们告诉用户有一个文件找不到。 但并非每次捕获到异常时都需要告诉用户, 有时候你希望程序在发生异常时一声不吭, 就像什么都没有发生一样继续运行。要让程序在失败时一声不吭, 可像通常那样编写try 代码块, 但在except 代码块中明确地告诉Python什么都不要做。 Python有一个pass 语句, 可在代码块中使用它来让Python什么都不要做:
filename = 'alice'
try:
with open(filename) as file_obj:
contents = file_obj.read()
except FileNotFoundError:
pass
测试代码
Python在unittest.TestCase 类中提供了很多断言方法。 前面说过, 断言方法检查你认为应该满足的条件是否确实满足。 如果该条件确实满足, 你对程序行为的假设就得到了确认, 你就可以确信其中没有错误。 如果你认为应该满足的条件实际上并不满足, Python将引发异常。