Python编程无师自通:专业程序员的养成

科里·奥尔索夫

资源与支持

  • 本书由异步社区出品,社区(https://www.epubit.com/)为您提供相关资源和后续服务。

第1章 概述

  • 本书不适合那些想要随意了解下编程知识、将写代码作为爱好的人,而是专门写给那些希望以编程为职业的人。
  • 学会一门编程语言还不够,你还需要学会其他技能,才能像计算机科学家一样地工作。
  • 我认为先学习如何编程再学习理论的方法更高效,因为你会拥有了解背后原理的强烈驱动。
  • 你选择读这本书,不是出于老师布置的任务,而是因为你内心学习的渴望,这一点是你所拥有的最大优势。
  • 学会编程,将把你从重复性工作中解放出来。
  • 即使你的目标不是成为软件工程师,科学和金融等领域的岗位也开始倾向那些拥有编程经验的申请者。
  • 为了提高编程技巧,你应该每天练习编程。挡在你面前的唯一障碍就是无法坚持,所以我们要采取一些措施确保自己能够坚持不懈。准备一张检查清单,来确保每天都有做练习,而且也能够帮助你保持专注。
  • 书中代码段前的注释为GitHub的网址,读者可于网站直接复制代码。
  • Git是一个流行的版本控制系统,选择介绍Git是因为笔者认为它已经成为版本控制的业界标准。

第2章 起步

  • “一名优秀的程序员,在穿越单行道时也会确认双向的来车情况。”——道格拉斯·林德(Doug Linder)
  • 编程(programming)指的是编写让计算机执行的指令。
  • Python是一门开源编程语言,由荷兰程序员吉多·范·罗苏姆(Guido van Rossum)发明,并以英国喜剧团体“蒙提·派森(Monty Python)的飞行马戏团”命名。
  • Python自带了一个叫IDLE的程序,全称是交互式开发环境;
  • 在编程界有一个传统,每当教别人一门新编程语言时,第一个程序就是如何打印Hello, World!。
  • 交互式shell对于快速计算、测试小块代码和不会被重复利用的短程序来说很有用。
  • Python文件的名称必须以.py结尾
  • 短小的示例最好在交互式shell中运行,文本编辑器更适合希望保存并编辑的程序。如果在交互式shell中键入的代码有错误,比如拼写错误,导致代码出错,就必须得重新输入所有代码。而使用文本编辑器则可以省下不少工作,如果犯错了,只需要纠正并重新运行即可。

第3章 编程概论

  • “这是我能想到的,唯一可以让我既当工程师又做艺术家的工作。它要求具备极其缜密的技术思维,因为你必须要完成精确的思考,这点我很喜欢。另一方面,它又鼓励你肆意挥洒自己的创意,只有你想不到没有你做不到的。”
  • Courier New是一个固定宽度(不等比)的字体,常用来显示编程文本。每个字符的宽度都一致,因此代码对齐后可以很容易地发现缩进和其他特征。
  • Python用井号(#)来创建注释。
  • 尽量少写注释——不要每行代码都写注释,有特殊情况才需要。
  • 有时一段代码比较长,超过了一行,可以用三引号、圆括号、方括号或者大括号扩展至新一行
  • 缩进可以告诉Python解释器代码块的开始与结束。
  • Python的支持者坚信使用必要的缩进可以让Python比其他语言更易读易写。
  • 数据类型为bool的对象被称为布尔值(boolean),仅有True和False两个值。
  • 数据类型为NoneType的对象,其值永远为None,用来表示数据缺失。
  • 变量(variable)则相反,指的是会改变的值。变量由一个或多个字符组成的名称构成,并使用赋值符(assignment operator)等号赋予了这个名称一个值。
  • 变量不仅仅用于保存整型数的值,还可以表示任何数据类型
  • Python有两种错误:语法错误和异常。不属于语法错误的错误,就是异常(exception)。
  • 如果代码缩进不正确,程序会报“IdentationError”
  • 取模运算被用于检验数字的奇偶性
  • 有两个操作符用于除法运算。第一个是//,返回值为商:
  • 两个操作数和一个操作符共同构成一个表达式(expression)。
  • 可使用PEMDAS方法,帮助记忆数学公式的运算顺序:括号(parentheses)、指数(exponents)、乘法(multiplication)、除法(division)、加法(addition)和减法(subtraction)。
  • 带有比较操作符的表达式最后求值的结果不是True就是False
  • 在含有!=操作符的表达式中,如果左侧的数字不等于右侧的数字,则表达式的值为True,否则即为False:
  • 逻辑操作符(logical operator)也是Python中的一类操作符。与比较操作符类似,逻辑操作符的求值结果也是True或False。
  • 关键字if、elif和else用于条件语句(conditional statement)。条件语句是一种控制结构(control structure):通过分析变量的值从而做出对应决定的代码块。
  • 关键字or可连接两个或多个表达式,如果任意一个表达式的值为True,即返回True:
  • 将关键字not放置在表达式的前面,将改变表达式的求值结果,逆转为原本结果的对立值
  • 每个if语句只有在其表达式求值为True时,才会执行所有的代码。
  • 如果愿意,甚至还可以在if语句中再加入一个if语句(通常称之为嵌套):
  • elif表示另外如果,该语句可无限添加到if-else语句中,使其支持更多的决策。
  • Python中有两类语句:简单语句(simple statement)和复合语句(compoundstatement)。
  • 复合语句由一个或多个从句(clause)组成。从句包括两行或多行代码:代码头(header)及紧随其后的配套代码(suite)。
  • 关于语句还有一点要注意,语句之间是可以有空格的,这不会影响代码的执行。空格有时被用来提高代码的可读性。

第4章 函数

  • 函数(function):可接受输入,执行指令并返回输出的复合语句。
  • 调用(call)一个函数,意味着为函数提供执行指令并返回输出所需的输入。函数的每一个输入就是一个参数(parameter)。当你为函数提供参数时,则被称为“函数传参”。
  • 按惯例,函数名不应使用大写字母,单词用下划线分隔:like_this。
  • 如果后面在程序中有需要使用函数返回值的地方,建议将函数的返回值保存到一个变量中。
  • 函数可以有一个或多个参数,也可以不接受任何参数。如要定义不需要参数的函数,只需要在定义函数时把圆括号内置为空即可
  • 函数必须包含return语句。如果函数没有return语句,则会返回None。
  • Python编程语言中自带了一个被称为内置函数(builtin function)的函数库,它可执行各式各样的计算和任务,而不需任何额外的工作。
  • len也是一个内置函数,表示返回对象的长度
  • 内置函数str接受一个对象作为参数,并返回一个数据类型为str的新对象。
  • float函数可接受一个对象作为参数,并返回一个浮点数对象。
  • int函数可接受一个对象作为参数,并返回一个整型对象。
  • input函数接受一个字符串作为参数,并将其展示给使用该程序的用户。
  • 函数不仅可用于计算并返回值,还可以封装我们希望复用的功能。
  • 这里虽然没有定义函数的返回值,但是该函数还是有用的:
  • 因为可以对函数进行复用,所以利用函数可以减少代码量。
  • Python中还有另一种参数,即可选参数(optional parameter)
  • 如果在函数(或类,本书第二部分将介绍)之外定义了一个变量,则变量拥有全局作用域
  • 你可以定义一个既有必选参数也有可选参数的函数,但是必选参数必须位于可选参数之前。
  • 可以在程序的任何地方对全局变量进行写操作,但是在局部作用域中需稍加注意:必须明确使用global关键字,并在其后写上希望修改的变量。
  • 异常处理使用try和except关键字。
  • 如果用户为b参数提供的输入不是0,则执行try代码块,except代码块不执行。如果用户为b参数提供的输入为0, Python不会报错,而是执行except代码块,并打印“b cannot be zero.”。
  • 在except关键字后添加圆括号,并用逗号分隔两个异常即可将二者捕获
  • 不要在except语句中使用try语句定义的变量,因为异常可能是在变量定义之前发生的,如果在except语句中这样做可能又会导致新的异常出现。
  • 这些注释被称为文档字符串(docstring)。文档字符串用于解释函数的功能,记录所需的参数类型
  • 只有在后面的程序中会用到数据,才有必要将其保存至变量。不要仅仅为了打印数值就将整数保存至变量。

第5章 容器

  • 列表(list)是以固定顺序保存对象的容器
  • 列表用方括号表示。我们可使用两种语法创建列表,一种是使用list函数创建空列表
  • 列表中的元素是有序的。
  • 可使用append方法向列表中添加一个新元素
  • append方法永远是将新元素添加至列表的末尾。
  • 如果可以使用循环访问对象中的每一个元素,那么该对象是可迭代的,被称为可迭代对象。可迭代对象中的每一个元素都有一个索引(index),即表示元素在可迭代对象中位置的数字。
  • pop方法移除列表中的最后一个元素
  • 也可以使用关键字in检查某个元素是否在列表中:
  • 元组(tuple)是存储有序对象的一种容器。与列表不同,元组是不可变的(immutable),这意味着其内容不会变化。创建元组后,无法修改其中任何元组的值,也无法添加或修改元素。用圆括号表示元组,且必须用逗号分隔元组中的元素。
  • 即使元组中只有一个元素,也需要在该元素的后面加上逗号。
  • 创建元组之后,不能再新增元素或修改已有元素。
  • 在in前加上关键字not即可检查元素是否不在元组中
  • 元组不像列表那样可以用作字典的键,
  • 将一个对象链接至另一个对象,也被称为映射(mapping),结果为产生一个键值对(key-value pair)。
  • 与列表和元组不同,字典中存储的对象是无序的。
  • 字典是可变的。创建字典后,可通过语法“[字典名][[键]] = [值]”添加新的键值对,并通过语法“[字典名][[键]]”查找值
  • 可使用关键字del删除字典中的键值对。
  • 但是字典的键必须是不可变的。字符串或元组可以用作字典的键,但是列表或字典不可以。
  • 可以在容器中存储容器。
  • 列表、字典或元组都可以成为字典中的值
  • 也可以在列表中存储元素,在元组中存储列表,还可以在列表或元组中存储字典:

第6章 字符串操作

  • 如果字符串跨越一行以上,可以使用三引号
  • 与列表和元组一样,字符串也是可迭代的。
  • Python还支持使用负索引(negative index)查找列表中的元素:可用来从右向左查找可迭代对象中元素的索引(必须是一个负数)。使用索引-1可以查找可迭代对象中的最后一个元素
  • 字符串和元组一样都是不可变的,无法修改字符串中的字符。如果想要修改,就必须创建一个新的字符串
  • 可使用加法操作符,将两个或多个字符串组合在一起,结果就是由第一个字符串中的字符和其他字符串中的字符共同构成的一个新字符串。
  • 可使用乘法操作符,将字符串与数字相乘。
  • 可使用字符串的upper方法,将字符串中的每个字符改为大写。
  • 可使用字符串的lower方法将字符串中的每个字符改为小写
  • 还可使用字符串的capitalize方法,将字符串的首字母改为大写。
  • 可使用format方法创建新字符串,该方法会把字符串中的“{}”替换为传入的字符串
  • 字符串有一个叫split的方法,可用来将字符串分割为两个或多个字符串。需要传入一个字符串作为split方法的参数,并用其将原字符串分割为多个字符串。
  • join方法可以在字符串的每个字符间添加新字符:
  • 也可以在空字符串上调用join方法,传入一个字符串列表作为参数,从而将这些字符串连接为一个单一字符串
  • 还可以在包含空格符的字符串上,调用join方法,创建一个所有单词均由空格符分隔的字符串
  • 可使用strip方法去除字符串开头和末尾的空白字符
  • 在replace方法中,第一个参数是要被替换的字符串,第二个参数是用来替换的字符串。
  • 可使用index方法,获得字符串中某个字符第一次出现的索引
  • 如果不确定是否有匹配的结果,可使用如下异常处理的方法
  • 关键字in可检查某个字符串是否在另一个字符串中,返回结果为True或False
  • 在双引号前加上反斜杠,即可解决这个错误
  • 字符串转义(escaping),指的是在Python中有特殊意义的字符(上例中为双引号)前加上一个符号,告诉Python在本例中该符号代表的是一个字符,而没有特殊意义。在Python中用反斜杠进行转义。
  • 在字符串中加入\n来表示换行
  • 切片时包含起始索引位置的元素,但不包括结束索引位置的元素
  • 切片(slicing)可将一个可迭代对象中元素的子集,创建为一个新的可迭代对象。切片的语法是[可迭代对象][[起始索引:结束索引]]。起始索引(start index)是开始切片的索引,结束索引(end index)是结束索引的位置。
  • 如果起始索引是0,那么可以将起始索引的位置留空
  • 起始索引和结束索引均留空,则会返回原可迭代对象

第7章 循环

  • 循环(loop)
  • enumerate函数会返回一个对应当前元素索引的值,可保存在变量i中。
  • range函数接受两个参数:序列起始数字和结束数字,返回的整数序列包含从第一个参数到第二个参数之间(不含第二个参数)的所有整数。
  • 程序员通常将用来遍历整数列表的变量命名为i。
  • while 循环:它是一种只要表达式的值为True就一直执行代码的循环。
  • 可使用break语句(带关键字break的语句)来终止循环。
  • 只要Python遇到break语句,循环就会终止。
  • 可使用continue语句(带关键字continue的语句)来终止循环的当前迭代,并进入下一次迭代

第8章 模块

  • 程序员将大型程序分割成多个包含Python代码的文件,也被称为模块(module)
  • 导入模块之后,可通过语法[模块名].[代码]使用其中的代码,将[模块名]替换为已导入模块的名称,[代码]替换为希望使用的函数或变量的名称。
  • random是另一个内置模块。可使用其中一个叫randint的函数生成一个随机整数:传入两个整数,函数返回两者之间的一个随机整数。
  • 可使用内置模块statistics计算由数字组成的可迭代对象的均值、中间值和众数(mode)
  • 可使用内置模块keyword检查字符串是不是Python关键字。
  • 导入模块时,其中的代码都会被执行。
  • 比如你的模块中可能有测试代码,不希望在导入时执行。那么将模块中所有的代码放置在if __name__ == "__main__"语句中,即可解决该问题。

第9章 文件

  • 读取(reading)文件数据指的是访问文件中的数据。向文件中写(writing)数据指的是添加或修改文件中的数据。
  • 为了避免程序在不同操作系统中运行出错,应使用内置的os模块来创建文件路径。模块中的path函数接受文件路径中的文件夹名作为参数,并自动构建完整的文件路径。
  • 使用with语句打开文件的语法是“with open([文件路径], [模式]) as [变量名]: [执行代码]”。[文件路径]代表文件所在的位置,[模式]代表以何种模式打开文件,[变量名]代表文件对象被赋予的变量名,[执行代码]则是需要访问文件对象变量的代码。
  • 在没有关闭又重新打开文件的情况下,你只能调用文件对象的read方法一次。

第12章 编程范式

  • 编程范式(programming paradigm),即编程风格。
  • 不同编程范式之间的根本区别之一,就是对状态(state)的处理。状态,是程序运行时其内部变量的值。全局状态(global state)是程序运行时其内部全局变量的值。
  • 过程式编程(procedural programming):这种编程风格要求你编写一系列步骤来解决问题,每步都会改变程序的状态。
  • 在过程式编程时,我们将数据存储在全局变量中,并通过函数进行处理。
  • 函数式代码有一个特征:没有副作用。它不依赖当前函数之外的数据,也不改变当前函数之外的数据。
  • 作用,因为它依赖函数之外的数据,并改变了当前函数之外的数据——递增了全局变量的值。第二个函数没有副作用,因为它没有依赖或修改自身之外的数据。
  • 根据惯例,Python中的类名都是以大写字母开头,且采用驼峰命名法,即如果类名由多个单词组成,每个单词的第一个字母都应该大写,如LikeThis,而不是用下划线分隔(函数的命令惯例)。
  • 双下划线包围的方法(如__init__),被称为魔法方法(magic method),即Python用于创建对象等特殊用途的方法。
  • 也可使用语法[对象名].[变量名] = [新的值]改变实例变量的值:
  • 面向对象编程有多个优点:鼓励代码复用,从而减少了开发和维护的时间;还鼓励拆解问题,使代码更容易维护。但有一个缺点便是编写程序时要多下些功夫,因为要做很多的事前规划和设计。

第13章 面向对象编程的四大支柱

  • 面向对象编程有四大概念:封装、抽象、多态和继承。
  • 第一个概念是在面向对象编程中,对象将变量(状态)和方法(用来改变状态或执行涉及状态的计算)集中在一个地方——即对象本身
  • 封装包含的第二个概念,指的是隐藏类的内部数据,以避免客户端(client)代码(即类外部的代码)直接进行访问。
  • Python中没有私有变量,所有的变量都是可以公开访问的。Python通过另一种方法解决了私有变量应对的问题:使用命名约定。在Python中,如果有调用者不应该访问的变量或方法,则应在名称前加下划线。Python程序员看见某个方法或变量以下划线开头时,就会知道它们不应该被使用(不过实际仍然是可以使用的)。
  • 抽象(abstraction)指的是“剥离事物的诸多特征,使其只保留最基本的特质”的过程。
  • 多态(polymorphism)指的是“为不同的基础形态(数据类型)提供相关接口的能力”。接口,指的是函数或方法。
  • 在创建子类时,将父类的变量名传入子类,即可继承父类的属性
  • 子类改变从父类中继承方法的实现能力,被称为方法覆盖(method overriding)
  • 通过组合技巧,将一个对象作为变量保存在另一个对象中,可以模拟“拥有”关系。

第14章 深入面向对象编程

  • “视代码如诗词,勿要做无所谓的堆砌。”
  • 在Python中,类即对象
  • 类中有两种类型的变量:类变量(class variable)和实例变量(instance variable)。
  • 类变量可以在不使用全局变量的情况下,在类的所有实例之间共享数据。
  • Python中所有的类,均继承自一个叫Object的父类
  • 表达式中的操作数必须有一个运算符是用来对表达式求值的魔法方法
  • 如果两个对象是相同的对象,关键字is返回True,反之则返回False。
  • 还可以使用关键字is检查变量是否为None

第16章 Bash

  • 命令行、包管理器、正则表达式和版本控制,这些都是程序员工具库中的核心成员。
  • echo命令类似Python中的print函数。
  • 操作系统使用树状结构来表示其目录和目录位置。
  • cd命令后接两个英文句号,可以回到上一层目录
  • 命令支持一个叫旗标(flag)的概念,可以改变命令的执行方式。旗标对于命令来说,是一些值为True或False的执行选项。一个命令的所有旗标默认置为False。如果向命令中添加一个旗标,Bash将把该旗标的值设为True,命令的执行方式也会随之改变。在旗标的名称前面加一个(-)或两个连字符(--),即可将旗标置为True。
  • 使用语法export[变量名]=[变量值],即可在Bash中新建一个环境变量
  • 使用命令whoami可打印操作系统用户的名称

第17章 正则表达式

  • grep命令默认打印匹配文本所在的整行内容。可以添加旗标-o,确保只打印与传入的模式参数相匹配的文本
  • 还可使用美元符号$来匹配结尾指定模式的文本行
  • 将正则表达式的多个字符放在方括号中,即可定义一个匹配多个字符的模式。
  • 可使用[[:digit:]]匹配字符串中的数字:
  • 在Python中使用\d匹配数字
  • 星号符*可让正则表达式支持匹配重复字符。加上星号符之后,星号前面的元素可匹配零或多次。
  • 星号是贪婪匹配(greedy),意味着会尽可能多地匹配文本
  • 如果不想一直贪婪匹配,可以在星号后面加个问号,使得正则表达式变成非贪婪模式(non-greedy)。
  • 在正则表达式中的字符前加上一个反斜杠\即可进行转义

第18章 包管理器

  • 包管理器(package manager)是用来安装和管理其他程序的程序。
  • 可使用命令pip freeze查看已经安装了哪些包
  • 可以使用pip uninstall [包名称]卸载程序。

第19章 版本控制

  • Git和SVN是两个流行的版本控制系统。

第21章 数据结构

  • 栈(stack)是一种数据结构。与列表类似,我们可以向栈中添加或移除元素,但是不同的地方是,只能添加或移除最后一个元素。
  • 这种最后一个放入的元素被第一个取出的数据结构,也被称为先进后出(LIFO)型数据结构。
  • 栈有5个方法:is_empty、push、pop、peek和size。如果栈为空,is_empty返回True,反之则返回False。push向栈的顶部添加一个元素;pop从顶部移除一个元素;peek返回顶部的元素,但不会将其移除;size返回一个表示栈中元素数量的整型数。

第22章 算法

  • 代码word[::-1]可将单词逆序。[::-1]是Python中逆序返回可迭代对象的切片的语法。
  • 1.递归算法必须有终止条件。2.递归算法必须改变自己的状态,不断靠近终止条件。3.递归算法必须递归地不断调用自己。

第23章 最佳编程实践

  • The Pragmatic Programmer
  • 作为一名软件工程师,你在工作时应尽量少写代码。碰到问题时,你首先想到的不应该是“我怎么解决这个问题”,而是“其他人是不是已经解决了这个问题,我能使用他们的方案吗?”如果你自己去解决一个常见的问题,很有可能别人已经有了解决方案。先在网上检索解决办法,只有在确定没人解决过该问题之后,才开始自己动手解决。
  • DRY是不要重复自己(Dont't Repeat Yourself)的简称,指的是不要在程序中编写重复的或是基本相同的代码。正确的做法是将代码封装至函数中,后续可重复使用。
  • PEP8是一系列编写Python代码的指南,强烈建议阅读,可前往https://www.python.org/dev/peps/pep-0008/查看。
  • 最小化攻击面积的几种常见做法包括:避免保存敏感信息,赋予用户最低的访问权限,尽可能少用第三方库(代码量越小、漏洞越少),剔除不再使用的功能代码(代码量越小、漏洞越少)等。

第24章 第一份编程工作

  • 大部分编程面试聚焦两个主题:数据结构和算法。
  • 我强烈建议使用LeetCode网站练习,因为我发现别人在面试中问的问题,在这个网站上都可以找到答案。

第26章 更多学习资料

  • 有一些编程书籍是必读书目。《程序员修炼之道》《设计模式》《代码大全》《编译原理》,以及MIT出版社出版的《算法导论》均为程序员必读书目。另外,我强烈推荐一个免费的互动式算法入门教程,名为《Problem Solving with Data Structures and Algorithms》,这本书比《算法导论》更加容易理解。

第27章 下一步

  • 提升编程能力的一个最好的方式,就是打开碰到的每个黑盒,努力掌握其原理。