Python 核心编程(第2版)

第1章 欢迎来到 Python 世界

  • Python 提倡简洁的代码设计、高级的数据结构和模块化的组件。
    Python 的可插入化和模块化架构能使你的项目生机盎然和易于管理。

第2章 快速入门

  • 有些语言通过函数输出数据到屏幕,Python 和大多数解释执行的脚本语言使用语句输出。
    print 语句调用str()函数显示对象,而交互式解释器则调用repr()函数显示对象。

  • _在解释器中表示最后一个表达式的值。

  • 建议函数应该保持其清晰性,也就是它只应该接受参数,返回结果。
    将函数分为两大类,一类只做事,不需要返回值,另一类则执行一些运算,最后返回结果。
    除非输出就是目的。

  • //是地板除法,/是浮点除法。

  • 括号在 Python 语言中不是必须存在的,不过为了可读性,使用括号总是值得的。

  • Python 不支持自增和自减操作符。

  • print ***,抑制自动生成的换行符。

第3章 Python 基础

  • 反斜线换行和括号元素换行,推荐使用括号,可读性更好。

  • 赋值并不是直接将一个值赋给一个变量。在 Python 语言中,对象是通过引用传递的。
    赋值语句不会返回值,但是链式赋值没问题。

  • 多元赋值时,建议总是加上圆括号以提供更高的可读性。
    多元赋值可以实现无需中间变量交换两个变量的值:(x, y) = (y, x)

  • Python 不支持重载操作符。

  • _xxx,不用from module import *导入。
    _xxx_,系统定义名字。
    _xxx,类中的私有变量名。

  • 在模块、类声明或函数声明中第一个没有赋值的字符串,可以用属性obj.__doc__访问,其中obj是模块、类或函数的名字。

  • 查看 Python 之禅,在 Python 解释器输入import this然后回车。

  • 合理的布局:
    1、起始行(类 Unix 环境下使用,以直接执行文件)
    2、模块文档
    3、模块导入
    4、全局变量定义
    5、类定义
    6、函数定义
    7、主程序

  • 大部分的 Python 模块都是用于导入调用的,直接运行模块应该调用该模块的回归测试代码。

  • 所有的模块都有能力执行代码。
    通常只有主程序模块中有大量的顶级可执行代码,所有其它被导入的模块只应该有很少的顶级执行代码,所有的功能代码都应该封装在函数或类当中。

  • __name__的值指示模块应如何被加载:
    如果模块是被导入,该值为模块名字;
    如果模块是被直接执行,该值为__main__
    用于直接执行的测试代码应该随着测试条件及测试结果的变更及时修改,每次代码更新都应该运行测试代码,以确认没有引发新问题。

  • 追踪或调试程序会给对象增加一个额外引用。
    解释器负责跟踪对象的引用,垃圾收集器负责释放内存。

  • 将经常用到的模块属性替换为一个本地引用,代码速度更快,也不用老是敲那么长的变量名了。

第4章 Python 对象

  • 所有的 Python 对象都拥有三个特性:身份(可用内建函数id()得到),类型(可用内建函数type()查看,类型也是对象,所以返回的是对象),值。

  • 所有类型对象的类型都是type,它也是所有 Python 类型的根和所有 Python 标准类的默认元类。

  • 类就是类型,实例是对应类型的对象。

  • 比较操作是针对对象的值进行的。
    Python 也支持对象本身的比较,即a is ba is not b,二者等价于id(a) == id(b)id(a) != id(b)

  • 对象就像一个装着内容的盒子。当一个对象被赋值到一个变量,就像在这个盒子上贴了一个标签,表示创建了一个引用。当这个对象有了一个新的引用,就会在盒子上新贴一张标签。当一个引用被销毁时,这个标签就会被撕掉。当所有的标签都被撕掉时,这个盒子就会被回收。每个对象都天生具有一个计数器,记录它自己的引用次数。

  • 整型对象和字符串对象是不可变对象,所以 Python 会很高效的缓存它们。

  • repr()返回的是一个对象的“官方”字符串表示,绝大多数情况下可以通过eval()重新得到该对象。
    str()致力于生成一个对象的可读性好的字符串表示,很适合于print语句输出。
    也就是说repr()输出对 Python 比较友好,而str()输出对用户比较友好。

  • 每次调用函数都会付出性能代价,如果能减少调用次数,就会提高程序的性能。

  • 运行效率:if type(num) == type(0)<import types; if type(num) == types.IntType<if type(num) is types.IntType<from types import IntType; if type(num) is IntType

  • 工厂函数,看上去像函数,实质上是类。当调用它们时,实际上是生成了该类型的一个实例,就像工厂生产货物一样。

  • Python 的标准数据类型可以按照存储模式、更新模式、访问模式分类。

第5章 数字

  • Python 标准整型类型等价于 C 的(有符号)长整型。
    Python 的长整型类型能表达的数值仅仅与机器支持的(虚拟)内存大小有关。
    整型和长整型正在缓慢的统一,只有对长整型调用repr()才有机会看到L,调用str()就看不到L

  • int()直接截去小数部分(返回值为整型),floor()得到最接近原数但小于原数的整数(返回值为浮点型),round()得到最接近原数的整数(返回值为浮点型)。

  • coerce(num1, num2)num1num2转换为统一类型,以一个元组的形式返回。
    divmod(num1, num2)返回一个元组(num1 / num2, num1 % num2)
    pow(num1, num2, mod=1)如果提供mod参数,则取num1num2次方后再对mod取余。

第6章 序列

  • 循环显示一个字符串,每次砍掉一个末位字符:
s = 'abcde'
for i in [None] + range(-1, -len(s), -1):
    print s[:i]
  • 一般来说,从性能的角度来考虑,把重复操作作为参数放到循环里面进行是非常低效的。

  • extend()比连接操作符+的一个优点是它实际上是把新列表添加到了原有的列表里面,而不是像连接操作那样新建一个列表。

  • 可以用dir()得到一个对象的所有方法和属性。

  • 可变对象的方法会原地执行操作,对象的值会被改变,但是没有返回值。
    不可变对象的方法必须返回一个新的对象。

  • 只有一个元素的元组赋值需要在元素后加一个逗号,以防止跟普通的分组操作符混淆。

  • 虽然元组对象本身是不可变的,但这并不意味着元组包含的可变对象也不可变了。

  • 所有的多对象的、逗号分隔的、没有明确用符号封装的集合,默认的类型都是元组。
    所有函数返回的多对象(不包括有符号封装的)都是元组类型。

  • 序列类型对象的浅拷贝是默认类型拷贝,包括几种实施方式:(1)完全切片操作[:];(2)利用工厂函数;(3)使用copy模块的copy()函数。
    深拷贝需要copy.deepcopy()函数。

  • 非容器类型没有深拷贝一说,浅拷贝是用完全切片操作来完成的。
    如果容器只包含原子类型对象,对它的深拷贝将不会进行。

第7章 映射和集合类型

  • 字典是 Python 语言中唯一的映射类型。

  • 序列类型只用数字类型的键。
    映射类型可以用其它对象类型做键,一般最常见的是用字符串做键。

  • 映射类型通常被称作哈希表。
    哈希表的算法是获取键,对键执行一个叫做哈希函数的操作,并根据计算的结果,选择在数据结构的某个地址中来存储对应的值。

  • 可以用内建方法fromkeys()创建一个“默认”字典,字典中的元素具有相同的值(如果没有给出,默认为None)。

  • hash()函数会返回对象的哈希值。只有可哈希的对象,才可以作为字典的键。
    值相等的数字表示相同的键,如整型数字1和浮点型数字1.0。
    元组中只包括不可变元素,才可以作为字典中的键。

  • 集合操作符的结果类型与左操作数的类型相同。
    加号不是集合类型的操作符。

  • 集合的很多内建方法几乎和操作符等价。“几乎”的意思是它们间有一个重要的区别:当用操作符时,两边的操作数必须是集合;使用内建方法时,对象也可以是迭代类型的。

第8章 条件和循环

  • 使用映射对象的一个最大好处就是它的搜索操作比类似 if-elif-else语句或是for循环这样的序列查询要快得多:
if user.cmd == 'create':
    action = 'create item'
elif user.cmd == 'delete':
    action = 'delete item'
elif user.cmd == 'update':
    action = 'update item'
else:
    action = 'invalid choice... try again!'
if user.cmd in ('create', 'delete', 'update'):
    action = '%s item' % user.cmd
else:
    action = 'invalid choice... try again!'
msgs = {'create': 'create item', 
        'delete': 'delete item',
        'update': 'update item'}
default = 'invalid choice... try again!'
action = msgs.get(user.cmd, default)
  • Python 的 for更像是 shell 或是脚本语言中的foreach循环。

  • 通过print语句调试for循环中的序列时,如果在应该看到字符串的地方发现的却是单个字符,那么很有可能接受的是一个字符串,而不是对象的序列。

  • 直接迭代序列要比通过索引迭代快。

  • xrange()类似range(),不过当你有一个很大范围的列表时,前者可能更适合,因为它不会在内存里创建列表的完整拷贝,它的性能远高出后者。它只被用在for循环中。

  • pass不做任何事情,也可标记以后要完成的代码,先把结构定下来。

  • Python 可以在whilefor循环中使用else语句,只在循环完成后执行,也就是说break语句也会跳过else块。

  • 根本上说,迭代器就是有一个next()方法的对象,而不是通过索引来计数。

  • 对一个对象调用iter()就可以得到它的迭代器。
    iter(obj)会检查obj是不是一个序列,是则根据索引从0一直迭代到序列结束。
    iter(func, sentinel)会重复的调用func,直到迭代器的下个值等于sentinel

  • 列表解析[expr for iter_var in iterable if cond_expr]的核心是for循环,expr应用于序列的每个成员,最后的结果值是应用后产生的列表。

  • 列表解析支持多重嵌套for循环以及多个if子句,如

f = open('xxx.txt', 'r')
len([word for line in f for word in line.split()])

f.seek(0)
sum([len(word) for line in f for word in line.split()])
  • 生成器表达式(expr for iter_var in iterable if cond_expr)并不真正创建列表,而是返回一个生成器,在使用内存上更有效。如上例中的文本文件很大,则最后一句可改为
sum(len(word) for line in f for word in line.split())

再如找到一个文件的最长行的长度

max(len(x.strip()) for x in open('/path/xxx'))

第9章 文件和输入输出

  • 文件打开模式可以包含b,即以二进制模式访问,但它不能作为第一个字符出现。
    如果要处理二进制文件,并希望可移植到非 Unix 的环境中,加上b是不错的主意。

  • open()file()具有相同的功能,可以任意替换。
    建议使用open()来读写文件,在想说明在处理文件对象时使用file(),例如if instance(f, file)

  • 当使用U模式打开文件的时候,所有的行分隔符(或行结束符)都会被替换为换行符NEWLINE(\n)。文件对象的newlines属性会记录它曾“看到的”文件的行结束符。

  • sys.argv返回命令行参数的列表,len(sys.argv)是命令行参数的个数,sys.argv[0]是程序的名称。

  • marshalpickle模块的区别在于,前者只能处理简单的 Python 对象(数字、序列、映射以及代码对象),而后者还可以处理递归对象、被不同地方多次引用的对象,以及用户定义的类和实例。

  • Python 提供了 DBM 的多种实现,如果不确定,最好使用anydbm模块。它会自动检测系统上已安装的 DBM 兼容模块,并选择“最好”的一个。

  • shelve模块使用anydbm模块寻找合适的 DBM 模块,然后使用cPickle来完成储存转换过程。cPicklepickle的一个更快的 C 语言编译版本。

  • os.path.expanduser()函数传递一个带波浪号~的目录,它会返回对应的绝对路径。

第10章 错误和异常

  • SyntaxError异常是唯一不在运行时发生的异常,一般都是在编译时发生,Python 解释器无法把脚本转化为 Python 字节代码。

  • 如果一个异常没有找到合适的处理器,就向上移交给调用者处理;如果也没找到,继续向上移交;如果到达最顶层仍然没有找到,就认为这个异常是未处理的,Python 解释器会显示“跟踪记录Traceback”,然后退出。

  • except语句在处理多个异常时,要求异常被放在一个元组里。

  • Python 提供的try-except语句是为了更好的跟踪错误,并在代码里准备好处理异常的逻辑。它的目的是减少程序出错的次数,并在出错后仍能保证程序正常执行。
    它的作用是提供一个可以提示错误或处理错误的机制,而不是一个错误过滤器。
    可以捕获特定的异常并忽略它们,或是捕获所有异常并采取特定的动作。不要捕获所有异常,然后忽略掉它们。

  • try范围中没有异常被检测到时,执行else子句。

  • finally子句是无论异常是否发生、是否捕捉,都会执行的一段代码。

  • try-except-else-finally语法示例:

try:
    A
except MyException:
    B
else:
    C
finally:
    D

可以有不止一个except子句,正常时执行A-C-D,异常时执行A-B-D

  • try-finally语句,当在try范围中产生一个异常时,会立即跳转到finally语句,当finally中的所有代码都执行完毕后,会继续向上一层引发异常。
    如果finally中的代码引发了另一个异常,或由于returnbreakcontinue而终止,原来的异常将丢失而且无法重新引发。

  • Python 提供了一种机制明确的触发异常,就是raise语句。

  • 断言语句assert如果断言成功,不采取任何措施,否则触发AssertionError

  • 运行环境必须足够强健,来处理应用级别的错误,并提供用户级别的错误信息。

第11章 函数和函数式编程

  • 过程是简单、特殊、没有返回值的函数。Pthon 的过程就是函数,因为解释器会隐式的返回默认值None

  • func是函数对象的引用,func()是函数对象的调用。

  • 定义函数时,所有必需的参数都要在默认参数之前。
    非关键字可变长参数(元组)前加*,关键字可变长参数(字典)前加**且其应为最后的参数。

  • 如果将全局变量的名字声明在一个函数体内的时候,全局变量的名字能被局部变量给覆盖掉。
    为了明确的引用一个已命名的全局变量,必须使用global语句。

  • 如果在一个内部函数里,对在外部作用域(但不是全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。

第12章 模块

  • 模块是用来组织代码的,包是用来组织模块的。

  • 模块的搜索路径被保存在sys模块的sys.path变量里。
    sys.modules给出当前导入了哪些模块和它们的地址。

  • 名称空间是纯粹意义上的名字和对象间的映射关系,而作用域还指出了从用户代码的哪些物理位置可以访问到这些名字。

  • 推荐使用此顺序导入模块:Python 标准库模块、Python 第三方模块、应用程序自定义模块,并使用一个空行分割这三类模块的导入语句。

  • 如果一个模块在顶层导入,它的作用域是全局的;如果在函数中导入,作用域是局部的。

  • 加载模块会导致被导入模块的顶层代码直接执行。
    只把函数和模块定义放入模块的顶层是良好的模块编程习惯。

  • 只在两种场合下建议使用from module import *,一个是目标模块中的属性非常多,反复键入模块名很不方便,如TkinterNumpy,可能还有socket;另一个是在交互解释器下。

  • 只从模块导入名字的另一个副作用是那些名字会成为局部名称空间的一部分,而且这些变量的改变只影响它的局部拷贝,而不是所导入模块的原始名称空间。

  • globals()locals()内建函数分别返回调用者全局和局部名称空间的字典。

  • reload()内建函数重新导入一个已经导入的模块。该模块必须是全部导入,且必须被成功导入。

第13章 面向对象编程

  • 未绑定的方法调用需要传递一个适当的实例(self)给方法。

  • 类名通常由大写字母打头,可以帮助区别于函数调用。
    数据值应该使用名词作为名字,方法使用谓词(动词加对象)。

  • 考虑用面向对象设计(OOD)来工作的一个最重要的原因,在于它直接提供建模和解决现实世界问题与情形的途径。

  • Python 并不支持纯虚函数(像 C++)或者抽象方法(如 Java),这些都强制程序员在子类中定义方法。作为替代方法,可以简单的在基类方法中引发NotImplementedError

  • 类属性包括数据属性(如复数的实部和虚部)和函数属性(方法)。

  • 访问一个属性时,它同时也是一个对象,拥有自己的属性,可以访问,这导致了一个属性链。

  • 类数据属性仅当需要有更加“静态”数据类型时才变得有用。
    通常,所有方法都有一个限制:在调用前,需要创建一个实例。

  • 查看类的属性有两种方法:dir()内建函数返回对象属性的名字列表;__dict__属性返回一个字典,键是属性名,键值是相应的属性对象的数据值。

  • 任何类都有的特殊属性:__name____doc____bases____dict____module____class__

  • 实例仅拥有数据属性(方法严格来说是类属性)。
    实例仅有两个特殊属性:__class____dict__

  • 静态类属性只有类引用才能更新,在实例中设定或更新静态类属性会创建一个实例属性,遮蔽静态类属性。

  • 调用一个还没有任何实例的类中的方法的一个主要场景是:在派生一个子类时,而且要覆盖父类的方法。

  • 静态方法是类中的函数(不需要实例),由staticmethod()内建函数显式声明,或使用函数修饰符@staticmethod
    类方法需要类而不是实例作为第一参数,由classmethod()内建函数显式声明,或使用函数修饰符@classmethod

  • 组合就是通过利用已有类创建实例的方式,来给出新类中的变量。

  • 调用被覆盖的父类方法可以使用super()内建函数。

  • __mro__属性用于查看继承类调用的方法的查找顺序。
    经典类使用深度优先算法查找,新式类使用广度优先算法查找。

  • 由双下划线开始的属性在运行时被“混淆”,直接访问是不允许的。实际上,解释器会在名字前面加上下划线和类名,可以防止在祖先类或子孙类中的同名冲突。

  • 授权是包装的一个特性。
    实现授权的关键点就是覆盖__getattr__()方法,在代码中包含一个对getattr()内建函数的调用。

  • isinstance()没有执行严格匹配,子类的实例也会返回True。如果想严格匹配,仍然需要使用is

  • __slots__是一个类变量,用以限定类属性,主要目的是节约内存,副作用是防止用户随意增加实例属性。

  • __getattribute__()方法类似__getattr__(),不同之处在于,当属性被访问时,它就一直都可以被调用,而不局限于不能找到的情况。
    如果类同时定义了两者,一般前者会遮蔽后者。

  • __getattribute__()的优先级别:类属性 > 数据描述符 > 实例属性 > 非数据描述符 > 默认为__getattr__()
    __get__()__set__()__delete__()这三个特殊方法充当描述符协议的作用。那些同时覆盖前两者的类被称作数据描述符。

  • property()内建函数有四个参数:property(fget=None, fset=None, fdel=None, doc=None),可以把自定义的函数fgetfsetfdel影射为描述符的__get__()__set__()__delete__()方法。
    利用它可以避免搞乱类的名字空间,并很好的控制属性访问。

第14章 执行环境

  • 默认情况下,类的__call__()方法是没有实现的。如果在类定义中覆盖了它,这个类的实例就成为可调用的。调用这样的实例对象等同于调用__call__()方法。

  • compile()函数提供了一次性字节代码预编译,以后每次调用都不用重新编译。用法示例:
    1、可求值表达式

eval_code = compile('100+200', '', 'eval')
eval(eval_code)

2、单一可执行语句

single_code = compile('print "Hello World!"', '', 'single')
exec single_code

3、可执行语句组

exec_code = compile("""
req = input('Count how many numbers?')
for eachNum in range(req):
    print eachNum
""", '', 'exec')
exec exec_code
  • 以交互方式输入的表达式,按下回车后的结果应和eval()返回的结果相同。

  • 内建函数input()等价于eval(raw_input()),即它把输入作为 Python 对象来求值并返回表达式的结果。

  • 可以将字符串形式的测试代码作为函数属性,并通过exec语句执行。

  • execfile(filename)相当于f = open(filename, 'r'); exec f; f.close()

  • 如果想把外部命令的返回值读入变量并执行内部操作或存储到日志文件中,可以使用popen(),它返回一个类文件对象。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容

  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,708评论 2 59
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,900评论 25 707
  • 【day 69 菇酱】 《影响力》 一旦我们作出了一个选择,或采取了某种立场,我们立刻就会碰到来自内心和外部的压...
    阿阿阿囡酱阅读 110评论 0 0
  • 总述:首先介绍了NLP是什么,以及NLP所面临的问题。讨论了用将单词表征为数字向量的概念,最后介绍了构建词向量的比...
    J_Y_Peng阅读 1,115评论 0 1
  • 中国的新中产阶级,是中国改革开放以来,也就是1978年以来,经济高速发展的产物。 在新浪财经和吴晓波频道联合发布的...
    可可林阅读 679评论 4 1