Python核心编程笔记

http://corepython.com/

(5)类定义语句
所有的类都需要在这里定义。当模块被导入时 class 语句会被执行, 类也就会被定义。类
的文档变量是 class.__doc__。
(7) 主程序
无论这个模块是被别的模块导入还是作为脚本直接执行,都会执行这部分代码。通常这里
不会有太多功能性代码,而是根据执行的模式调用不同的函数。

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

时刻记住一个事实,那就是所有的模块都有能力来执行代码。最高级别的 Python 语句--
也就是说, 那些没有缩进的代码行在模块被导入时就会执行, 不管是不是真的需要执行。由
于有这样一个“特性”,比较安全的写代码的方式就是除了那些真正需要执行的代码以外, 几
乎所有的功能代码都在函数当中。 再说一遍,  通常只有主程序模块中有大量的顶级可执行代码, 
所有其它被导入的模块只应该有很少的顶级执行代码,所有的功能代码都应该封装在函数或类
当中。

核心笔记:__name__ 指示模块应如何被加载
由于主程序代码无论模块是被导入还是被直接执行都会运行,  我们必须知道模块如何决定
运行方向。一个应用程序可能需要导入另一个应用程序的一个模块,以便重用一些有用的代码
(否则就只能用拷贝粘贴那种非面向对象的愚蠢手段)。这种情况下,你只想访问那些位于其
它应用程序中的代码,而不是想运行那个应用程序。因此一个问题出现了,“Python 是否有
一种方法能在运行时检测该模块是被导入还是被直接执行呢?” 答案就是......(鼓声雷
动).....没错! __name__ 系统变量就是正确答案。
 
如果模块是被导入, __name__ 的值为模块名字
如果模块是被直接执行, __name__ 的值为 '__main__'

变量无须事先声明
变量无须指定类型
变量在第一次被赋值时自动声明

注意任何追踪或调试程序会给一个对象增加一个额外的引用, 这会推迟该
对象被回收的时间。

它也负责检查那些虽然引用计数大于 0 但也应该被销毁的对象。 特定情形会导
致循环引用。

类似 os.linesep 这样的名字需要解释器做两次查询: (1) 查找 os 以确认它是一个模块,
(2)在这个模块中查找 linesep 变量。因为模块也是全局变量, 我们多消耗了系统资源。如
果你在一个函数中类似这样频繁使用一个属性,我们建议你为该属性取一个本地变量别名。 变
量查找速度将会快很多--在查找全局变量之前, 总是先查找本地变量。 这也是一个让你的
程序跑的更快的技巧: 将经常用到的模块属性替换为一个本地引用。代码跑得更快,而也不用
老是敲那么长的变量名了。在我们的代码片段中,并没有定义函数,所以不能给你定义本地别
名的示例。不过我们有一个全局别名,至少也减少了一次名字查询

异常处理最适用的场合,是在没有合适的函数处理异常状况的时候。
这时程序员必须识别这些非正常的错误,并做出相应处理。

你一定还记得,对象的一系列固有行为和特性(比如支持哪些运算,具
有哪些方法)必须事先定义好。从这个角度看,类型正是保存这些信息的最佳位置。描述一种
类型所需要的信息不可能用一个字符串来搞定,所以类型不能是一个简单的字符串,这些信息
不能也不应该和数据保存在一起, 所以我们将类型定义成对象。

随着 Python 2.2 中类型和类的统一,类型对象在面向对象编程和日常对象使用中扮演着
更加重要的角色。从现在起, 类就是类型,实例是对应类型的对象。

我们会注意到比较操作是针对对象的值进行的, 也就是说比较的是对象的数值而不是对象
本身。在后面的部分我们会研究对象身份的比较。

Python 通过传递引用来处理对象。

在上面的例子中,您会注意到我们使用的是浮点数而不是整数。为什么这样?整数对象和
字符串对象是不可变对象,所以 Python 会很高效的缓存它们。这会造成我们认为 Python应该
创建新对象时,它却没有创建新对象的假象。看下面的例子:
>>> a = 1
>>> id(a)
8402824
>>> b = 1
>>> id(b)
8402824
>>>
>>> c = 1.0
>>> id(c)
8651220
>>> d = 1.0
>>> id(d)
8651204
Python 仅缓存简单整数,因为它认为在 Python 应用程序中这些小整数会经常被用到。当
我们在写作本书的时候,Python 缓存的整数范围是(-1, 100),不过这个范围是会改变的,所
以请不要在你的应用程序使用这个特性。

在学习编程的过程中, 我们一直接受这样的教育, 变量就像一个盒子, 里面装着变量的
值。在 Python 中, 变量更像一个指针指向装变量值的盒子。 对不可改变类型来说, 你无法
改变盒子的内容, 但你可以将指针指向一个新盒子。每次将另外的数字赋给变量的时候,实际
上创建了一个新的对象并把它赋给变量.(不仅仅是数字,对于所有的不可变类型,都是这么回
事)

拥有 C 背景的程序员一定熟悉传统除法――也就是说, 对整数操作数,会执行“地板除”
(floor,取比商小的最大整数。例如 5 除以 2 等于 2.5,其中“2”就称为商的“地板” ,即“地
板除”的结果。本书中使用“地板除”的说法是为了沿用原作者的风格,译者注) 。对浮点操作
数会执行真正的除法。然而,对第一次学编程的人或者那些依赖精确计算的人来说,可能就需
要多次调整代码才能得到自己想要的结果。

所谓工厂函数就是指这些内建函数都是类对象,  当你调用它们时, 实际上是创建了一个类实例。 

Python 不支持方法或函数重载, 因此你必须自己保证调用的就是你想要的函数或对象。
(参阅 Python 常见问答 4.75 节) 。幸运的是, 我们前面 4.3.1 小节提到的 type()内建函数可
以帮助你确认这一点。 一个名字里究竟保存的是什么?相当多, 尤其是这是一个类型的名字时。
确认接收到的类型对象的身份有很多时候都是很有用的。为了达到此目的,Python 提供了一
个内建函数 type(). type()返回任意 Python 对象对象的类型,而不局限于标准类型。让我们
通过交互式解释器来看几个使用 type()内建函数返回多种对象类型的例子:

sequence1 + sequence2
该表达式的结果是一个包含 sequence1 和 sequence2 的内容的新序列.注意,这种方式看
起来似乎实现了把两个序列内容合并的概念,但是这个操作不是最快或者说最有效的。对字符
串来说,这个操作不如把所有的子字符串放到一个列表或可迭代对象中,然后调用一个 join
方法来把所有的内容连接在一起节约内存;类似地,对列表来说,我们推荐读者用列表类型的
extend()方法来把两个或者多个列表对象合并.当你需要简单地把两个对象的内容合并, 或者说
不能依赖于可变对象的那些没有返回值(实际上它返回一个 None)的内建方法来完成的时候时,
连接操作符还是很方便的一个选择。

因为 Python 是面向对象的, 所以你可以像下面这样直接访问一个序列的元素(不用先把它
赋值给一个变量):
>>> print ('Faye', 'Leanna', 'Daylen')[1] 
Leanna
这个特性在你调用一个返回值是序列类型的函数,并且你只对返回的序列中的一个或某几
个元素感兴趣时特别有用.

所谓浅拷贝就是只拷贝了对对象的索引,而不是重新建立了一个对象!如果你想完全的拷
贝一个对象(包括递归,如果你的对象是一个包含在容器中的容器),你需要用到深拷贝,关于浅
拷贝和深拷贝的更多信息会在本章的末尾讲到。

字符串是不可变类型,就是说改变一个字符串的元素需要新建一个新的字符串.

核心笔记:那些可以改变对象值的可变对象的方法是没有返回值的!
Python 初学者经常会陷入一个误区:调用一个方法就返回一个值.最明显的例子就是
sort():
>>> music_media.sort()# 没有输出?
>>>
在使用可变对象的方法如 sort(),extend()和 reverse()的时候要注意,这些操作会在列表
中原地执行操作,也就是说现有的列表内容会被改变,但是没有返回值!是的,与之相反,字符串
方法确实有返回值:
>>> 'leanna, silly girl!'.upper()
'LEANNA, SILLY GIRL!'
温习一下,字符串是不可变的 -- 不可变对象的方法是不能改变它们的值的,所以它们必须
返回一个新的对象.如果你确实需要返回一个对象,那么我们建议你看一下 Python2.4 以后加入
的 reversed()和 sorted()内建函数.
它们像列表的方法一样工作,不同的是它们可以用做表达式,因为它们返回一个对象.同时
原来的那个列表还是那个列表,没有改变,而你得到的是一个新的对象.

当处理一组对象时,这个组默认是元组类型.

只有一个元素的元组需要在元组分割符里面加一个逗号(,)用以防止跟普通的分组操作符混淆.
如a = (1),a是int类型,a = (1,),a是元组.

虽然元组对象本身是不可变的,但这并不意味着元组包含的可变对象也不可变了。
所有的多对象的,逗号分隔的,没有明确用符号定义的,比如说像用方括号表示列表和用
圆括号表示元组一样,等等这些集合默认的类型都是元组.

对一个对象进行浅拷贝其实是新创建了一个类型跟原对
象一样,其内容是原来对象元素的引用,换句话说,这个拷贝的对象本身是新的,但是它的内容不
是.序列类型对象的浅拷贝是默认类型拷贝,并可以以下几种方式实施:(1)完全切片操作[:],(2)
利用工厂函数,比如 list(),dict()等,(3)使用 copy 模块的 copy 函数.

以下有几点关于拷贝操作的警告。 第一,非容器类型(比如数字,字符串和其他"原子"类型的
对象,像代码,类型和 xrange 对象等)没有被拷贝一说,浅拷贝是用完全切片操作来完成的.第二
如果元组变量只包含原子类型对象,对它的深拷贝将不会进行.如果我们把账户信息改成元组类
型,那么即便按我们的要求使用深拷贝操作也只能得到一个浅拷贝:

字典键必须是可哈希的,即每次哈希的结果都一样.
一个要说明的是问题是数字:值相等的数字表示相同的键。换句话来说,整型数字 1 和 浮点数 1.0 的哈希值是相同的,即它们
是相同的键。

当使用输入方法如 read() 或者 readlines() 从文件中读取行时, Python 并不会删除行结束
符. 这个操作被留给了程序员. 例如这样的代码在 Python 程序中很常见:
f = open('myFile', 'r')
data = [line.strip() for line in f.readlines()]
f.close()
类似地, 输出方法 write() 或 writelines() 也不会自动加入行结束符. 你应该在向文件写
入数据前自己完成:

try 语句块中异常发生点后的剩余语句永远不会到达(所以也永远不会执行). 一旦一个异常被
引发, 就必须决定控制流下一步到达的位置. 剩余代码将被忽略, 解释器将搜索处理器, 一旦找到
就开始执行处理器中的代码.
如果没有找到合适的处理器, 那么异常就向上移交给调用者去处理, 这意味着堆栈框架立即回
到之前的那个. 如果在上层调用者也没找到对应处理器, 该异常会继续被向上移交, 直到找到合适
处理器. 如果到达最顶层仍然没有找到对应处理器, 那么就认为这个异常是未处理的,  Python 解释
器会显示出跟踪返回消息, 然后退出.

__builtins__ 模块和 __builtin__ 模块不能混淆。 虽然它们的名字相似——尤其对于新手来
说。 __builtins__ 模块包含内建名称空间中内建名字的集合。 其中大多数(如果不是全部的话)来
自 __builtin__ 模块, 该模块包含内建函数, 异常以及其他属性。 在标准 Python 执行环境下,
__builtins__ 包含 __builtin__ 的所有名字。

加载顺序:名称空间是名称(标识符)到对象的映射。 向名称空间添加名称的操作过程涉及到绑定标识符到
指定对象的操作(以及给该对象的引用计数加 1 )。   《Python语言参考》 (Python Language Reference)
有如下的定义: 改变一个名字的绑定叫做重新绑定, 删除一个名字叫做解除绑定。

Python 解释器首先加载内建名称空间。 它由 __builtins__ 模块中的名字构成。 随后加载执
行模块的全局名称空间, 它会在模块开始执行后变为活动名称空间。 这样我们就有了两个活动的名
称空间。 如果在执行期间调用了一个函数, 那么将创建出第三个名称空间, 即局部名称空间。

那么确定作用域的规则是如何联系到名称空间的呢? 它所要做的就是名称查询. 访问一个属性
时, 解释器必须在三个名称空间中的一个找到它。 首先从局部名称空间开始, 如果没有找到, 解释
器将继续查找全局名称空间. 如果这也失败了, 它将在内建名称空间里查找。 如果最后的尝试也失
败了, 你会得到这样的错误:

Python 的一个有用的特性在于你可以在任何需要放置数据的地方获得一个名称空间。 我们已
经在前一章见到了这一特性, 你可以在任何时候给函数添加属性(使用熟悉的句点属性标识)。 

一个模块只被加载一次, 无论它被导入多少次。 这可以阻止多重导入时代码被多次执行。 例
如你的模块导入了 sys 模块, 而你要导入的其他 5 个模块也导入了它, 那么每次都加载 sys (或
是其他模块)不是明智之举! 所以, 加载只在第一次导入时发生。

因为 import 语句总是绝对导入的, 所以相对导入只应用于 from-import 语句。
语法的第一部分是一个句点, 指示一个相对的导入操作。 之后的其他附加句点代表当前 from
起始查找位置后的一个级别。

如果你不想让某个模块属性被 "from module import *" 导入 , 那么你可以给你不想导入的属
性名称加上一个下划线( _ )。 不过如果你导入了整个模块或是你显式地导入某个属性(例如 import
foo._bar ), 这个隐藏数据的方法就不起作用了。

你有一个本身没有任何属性的类,使用它仅对数据提供一个名字空间,让你的类拥有像 Pascal 中的记录集(records)和 C
语言中的结构体(structures)一样的特性,或者换句话说,这样的类仅作为容器对象来共享名字空间。

如果需要,每个子类最好定义它自己的构造器,不然,基类的构造器会被调用。然而,如果子
类重写基类的构造器,基类的构造器就不会被自动调用了--这样,基类的构造器就必须显式写出
才会被执行.

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值