A new start on learning Python

从哲学理念上讲,python采用了一种所谓极简主义的设计理念。EIBTI(Explicit Is Better Than Implicit)的意思是明了胜于晦涩。
import this 会触发python内部的一个彩蛋,它将显示python语言层面之下的设计哲学。
Industrial Light & Magic、Pixar用python制作动画电影。
python已经证明它是无所不能的。
python的缺点是它的性能:它不像C和C++这类常规的编译语言运行得那么快。


python的应用领域:
  1. 系统编程
  2. 用户图形接口
  3. Internet脚本
  4. 组件集成
  5. 数据库编程
  6. 快速原型
  7. 数值计算和科学计算编程
  8. 游戏、图像、人工智能、XML、机器人等
python名字源于Monty Python


python解释器:
解释器是一种让其他程序运行起来的程序。python也是一个名为解释器的软件包。解释器是代码与机器的计算机硬件之间的软件逻辑层。


接下去开始编写程序:
  1. 每一个以扩展名.py结尾的Python源代码文件都是一个模块,其他的文件可以通过导入一个模块读取这个模块的内容。导入从本质上来讲,就是载入另一个文件,并能够读取那个文件的内容。这种基于模块的方式使模块变成了python程序架构的一个核心概念。更大的程序往往以多个模块文件的形式出现,并且导入了其他模块文件的工具。其中的而一个模块文件被设计成主文件,或叫顶层文件。
  2. 导入是一个开销很大的操作以致于每个程序运行不能够重复多于一次。但是如果真的想要python在同一次会话中再次运行文件(不停止和重新启动会话),需要调用内置的reload函数。
  3. 模块的显要特性:属性。
  4. 模块和命名空间:模块将变量封装为不同部分,python具有了能够避免命名冲突的优点。模块是一个不需要重复输入而可以反复运行代码的方法。
  5. IDLE没有清屏选项。
(第三章完P76




第二部分:类型和运算


python对象类型:数据以对象的形式出现。

  • 程序由模块构成
  • 模块包含语句
  • 语句包含表达式
表达式建立并处理对象
为什么使用内置类型:

python提供了强大的对象类型作为语言的zuchengbuf,在你开始解决问题之前往往没有必要编写对象的实现。

  • 内置对象使程序更容易编写。(列表、字典等)
  • 内置对象是扩展的组件。
  • 内置对象往往比定制的数据结构更有效率。
  • 内置对象是语言的标准的一部分。

python的核心数据类型:

  • 数字
  • 字符串     'spam' , "guido's"
  • 列表         [1,[2, 'three'], 4]
  • 字典         {'food': 'spam', 'taste': 'yum'}
  • 元组         (1, 'spam', 4, 'U')
  • 文件          myfile = open('eggs', 'r')
  • 其他类型  集合、类型、None、布尔型
其他的类型的对象往往是通过导入或使用模块来建立的,而且它们都有各自的行为。


python中没有类型申明。
一旦创建了一个对象,它就和操作集合绑定了。
python是动态类型的(它自动地跟踪你的类型而不是要求声明代码),但是它也是强类型语言(你只能对一个对象进行有效的操作)。


字符串:
  • 序列的操作:一个负的索引号简单地与字符串的长度相加,得到两个操作是等效的。除了简单的从位置进行索引,序列也支持一种所谓的分片(slice)的操作,这是一种一步就能够提取整个分片的方法。X[ I : J ]。表示“取出在X中从偏移为I,直到但不包括J的内容”。在一个分片中,左边界默认为0,并且右边界默认为分片序列的长度。负偏移量也可以作为分片的边界。作为一个序列,字符串也支持使用加号进行合并,或者重复(用*)。
  • 不可变性:字符串在python中具有不可变性——在其创建后值不能改变。例如:你不能通过对其某一位置进行赋值而改变字符串。


动态类型简介(P129):

动态类型一集由它提供的多态性,这个概念无疑是python语言的简洁性和灵活性的基础。


变量、对象和引用:
类型的概念是存在于对象中而不是变量名中。例如:a = 3
python语言中所有赋值的操作:
  1. 创建一个对象来代表值3。
  2. 创建一个变量a,如果它还没有创建的话。
  3. 将变量与新的对象3相连接。
  • 变量是一个系统表的元素,拥有指向对象的连接的空间。
  • 对象是被分配的一块内存,有足够的空间去表现它们所代表的值。
  • 引用是自动形成的从变量到对象的指针。
类型属于对象,而不是变量:
例如我们只是把a引用了不同的对象。因为变量没有类型,我们实际上并没有改变变量a的类型,只是让变量引用了不同类型的对象而已。实际上,python的变量就是在特定的时间引用了一个特定的对象。
另一方面,对象知道自己的类型。每个对象都包含了一个头部信息,其中标记了这个对象的类型。因此,python中的类型是与对象相关联的,而不是和变量关联。

对象的垃圾收集:
在内部,python是通过保持用每个对象中的计数器记录引用指到这个对象上的次数来完成这一功能的。一旦这个计数器被设置成零,这个对象的内存空间就会自动回收。

共享引用和在原处修改:
有一些对象和类型确实会在实地改变对象。
python拷贝对象,而不是创建引用。拷贝列表的方法:如从头到尾的分片。
L1 = [2, 3, 4]
L2=L1[ : ]      # make a copy of L1, L2 is not changed.
L1[0] = 24
对字典应该使用D.copy()方法。注意标准库中的copy模块有一个通用的拷贝任意对象类型的调用,也有一个拷贝嵌套对象结构的调用。

共享引用和相等:
“==操作符”  测试两个被引用的对象是否有相同的值;
“is操作符    检查对象的同一性,如果两个变量名精确地指向同一对象,它会返回TRUE,所以这是一种更严格形式的相等测试。

第七章 字符串:单引号、双引号、转义字符、raw字符串、块字符串、unicode字符串
在python字符串中,单引号和双引号字符是可以互换的。
如果字母r(大写或小写)出现在字符串的第一引号的前面,它将会关闭转义机制。这个结果就是python会将反斜线作为常量保持,就像输入的那样。
三重引号字符串常用于文档字符串;(另外,三重引号字符串经常在开发过程中作为一种恐怖的黑客风格的方法去废除一些代码。)

索引和分片:
分片表达式能够返回除了第一项之外的所有元素的列表;
分片也常常用作清理输入文件的内容。如:line[ : -1]把这行除去最后一个字符之外的所有内容提取出来。(为了去掉换行符常常推荐line.rstrip()方法,因为这个调用将会留下没有换行字符那行的最后一个字符。)


字符串格式化:
%s  :  字符串(或任何对象)
%r  :  s,但使用repr,而不是str
%c  :  字符
%d  :  十进制整数
%i  :  整数
%u  :  无符号整数
%o  :  八进制整数
%x  :  十六进制整数
%X  :  x,但打印大写
%e  :  浮点指数
%E :  e,但打印大写
%f  :  浮点十进制
%g  :  浮点e或f
%G  :  浮点E或f
%%  :  常量%


序列:支持索引、分片、合并;
映射:支持通过键的索引。
可变类型能够在原处修改。


第八章:列表与字典
这两种类型相当灵活,它们可以在原处修改,也可以按需求增长或缩短,而且可以包含任何种类的对象或者被嵌套。


列表:
列表是python中最具灵活性的有序集合对象类型。可以包含任何种类的对象:数字、字符串甚至其他列表。


任意对象的有序集合;
通过偏移读取;
可变长度、异构以及任意嵌套;
属于可变序列的分类;
对象引用数组;
对列表进行索引的结果就是你指定的偏移处的对象(不管是什么类型),而对列表进行分片时往往返回一个新的列表。
索引和分片的赋值都是原地修改,它们对列表进行直接修改,而不是生成一个新的列表作为结果。

字典:
字典是python中最具灵活的内置数据结构类型。如果把列表看作是有序的对象集合,那么就可以把字典当成是无序的集合。它们主要的差别在于:字典当中的元素是通过键来存取的,而不是通过偏移存取。


作为内置类型,字典可以取代许多搜索算法和数据结构。
主要属性如下:
通过键而不是偏移量来读取;
任意对象的无序集合;
可变长、异构、任意嵌套;
属于可变映射类型;
对象引用表(哈希表);


与列表不同的是,当对新字典键进行赋值(之前没有被赋值的键),就会再字典内生成一个新的元素;在列表中情况不同,因为python会将超出列表末尾的偏移视为越界并报错。要想扩展列表,你需要使用append方法或分片赋值来实现。


其他字典方法:
>>> d2 = {'spam': 2, 'ham': 1, 'eggs' : 3}
>>> d2.values()
dict_values([3, 1, 2])
>>> d2.items()
dict_items([('eggs', 3), ('ham', 1), ('spam', 2)])

>>> d2.get('spam')
2


字典的update方法有点类似于合并,它把一个字典的键和值合并到另一个,盲目地覆盖相同键的值。

>>> d2
{'eggs': 3, 'ham': 1, 'spam': 2}
>>> d3 = {'toast': 4, 'mu':5}
>>> d2.update(d3)
>>> d2
{'toast': 4, 'mu': 5, 'eggs': 3, 'ham': 1, 'spam': 2}
>>> 

字典pop方法能够从字典中删除一个键并返回它的值。这类似于列表的pop方法,只不过删除的是一个键而不是一个可选的位置。

>>> d2
{'toast': 4, 'mu': 5, 'eggs': 3, 'ham': 1, 'spam': 2}
>>> d2.pop('mu')
5
>>> d2
{'toast': 4, 'eggs': 3, 'ham': 1, 'spam': 2}
>>>


>>> # pop a list by position
>>> L = ['aa', 'bb', 'cc', 'dd']
>>> L.pop()
'dd'
>>> L
['aa', 'bb', 'cc']
>>> L.pop(1)
'bb'
>>> L
['aa', 'cc']
>>>


语言表:

>>> table ={'python' : 'Guido van Rossum',
'perl': 'Larry Wall',
'Tcl': 'John Ousterhout'}
>>> language = 'python'
>>> creator = table[language]
>>> creator
'Guido van Rossum'
>>> for lang in table.keys():
print(lang, '\t', table[lang])


python   Guido van Rossum
Tcl   John Ousterhout
perl   Larry Wall
>>>



字典用法注意事项:

  • 序列运算无效。字典是映射机制,不是序列。
  • 对新索引赋值会添加项。
  • 键不一定总是字符串。
避免missing-key错误三种方法:

  1. 在if语句中预先对键进行测试
  2. 使用try语句明确地捕获并修复这一异常
  3. get方法为不存在的键提供一个默认值。
四种形式建立相同的字典:
>>> {'name': 'mel', 'age': '45'}
{'age': '45', 'name': 'mel'}


>>> d ={}
>>> d['name'] = 'mel'
>>> d['age'] = '45'
>>> d
{'age': '45', 'name': 'mel'}
>>>




>>> dict(name = 'mel', age = '45')
{'age': '45', 'name': 'mel'}
>>>


>>> dict([('name', 'mel'), ('age', '45')])
{'age': '45', 'name': 'mel'}
>>>

这四种形式都会建立相同的两键字典:

  • 如果你可以事先拼出整个字典,那么第一种是很方便的;
  • 如果你需要一次动态地建立字典的一个字段,第二种比较合适;
  • 第三种关键字形式所需的代码比较常量少,但是键必须都是字符串;
  • 如果你需要在程序运行时把键和值逐步建成序列,那么最后一种形式比较有用。
如果所有键的值都相同,你也可以用这个特殊的形式对字典进行初始化——简单地传入一个键列表,以及所有键的初始值(默认为空):

>>> dict.fromkeys(['a', 'b'], 0)
{'a': 0, 'b': 0}
>>>

第九章 元组(tuple)、文件及其他

元组:
python集合类型是元组(tuple)。元组由简单的对象组构成,元组与列表非常类似,只不过元组不能在原处修改(它们是不可变的),并且通常写成圆括号(而不是方括号)中的一系列项。元组不支持任何方法调用,但元组具有列表的大多数属性。

任意对象的有序集合:与字符串和列表类似,元组是一个位置有序的对象的集合。
通过偏移存取:同字符串、列表一样,支持所有基于偏移的操作:索引和分片。
属于不可变序列类型:类似于字符串,不支持应用在任何原处修改操作。
固定长度、异构、任意嵌套:因为元组是不可变的,在不生成一个拷贝的情况下不能增长或缩短。另一方面,元组可以包含其他的复合对象(例如:列表、字典、其他元组等),因此支持嵌套。
对象引用的数组:与列表相似,元组最好被认为是对象引用的数组。

元组的特殊语法:逗号和圆括号
如果圆括号里的单一对象是元组对象而不是一个简单的表达式时,需要对python进行特别说明。如果确实想得到一个元组,只要在这个单个元素之后、关闭圆括号之前加一个逗号就可以了。

>>> x = (40)
>>> x
40
>>> x = (40,)
>>> x
(40,)
>>>

作为特殊情况,在不引起语法冲突的情况下,python允许忽略元组的圆括号。仅当元组作为文字传给函数调用(圆括号很重要)以及当元组在print语句中列出(逗号很重要)的特殊情况时,圆括号才是必不可少的。

转换以及不可变性:
如果想对元组进行排序,通常先得将它转换为列表才能获得使用排序方法调用的权限,并将它变为一个可变对象。

>>> T = ('cc', 'aa', 'dd', 'bb')
>>> tmp = list(T)
>>> tmp.sort()
>>> tmp
['aa', 'bb', 'cc', 'dd']
>>> T = tuple(tmp)
>>> T
('aa', 'bb', 'cc', 'dd')
>>>

列表解析(list comprehension)也可以用于元组的转换。



>>> T = (1, 2, 3, 4, 5)
>>> L = [x + 20 for x in T]
>>> L
[21, 22, 23, 24, 25]
>>>
列表解析是名副其实的序列操作——它们总会创建新的列表,但也可以用于遍历包括元组、字符串以及其他列表在内的任何序列对象。列表解析甚至可以用在某些并非实际存储的序列之上——任何可遍历的对象都可以,包括可自动逐行读取的文件。

注意元组的不可变性只适用于元组本身顶层而并非其内容。例如,元组内部的列表是可以像往常那样修改的。


>>> T = (1, [2, 3, 4], 5)
>>> T[1] = 'spam'
Traceback (most recent call last):
  File "<pyshell#61>", line 1, in <module>
    T[1] = 'spam'
TypeError: 'tuple' object does not support item assignment
>>> T[1][1] = 'spam'
>>> T
(1, [2, 'spam', 4], 5)
>>>

为什么有了列表还要元组:
元组的不可变性提供了某种完整性。元组也可以用在列表无法使用的地方。例如,作为字典键。

文件:
内置open函数会创建一个python文件对象,可以作为计算机上的一个文件链接。文件对象不是数字、序列也不是映射。

记住:现在从文本文件读取文字行的最佳方式是根本不要读取该文件。文件也有个迭代器会自动地在for循环、列表解析或者其他迭代语句中对文件进行逐行读取。

实际应用中的文件:

>>> myfile = open('myfile', 'w')
>>> myfile.write('hello text file\n')
16
>>> myfile.close()
>>> myfile = open('myfile')
>>> myfile.readline()
'hello text file\n'
>>> myfile.readline()
''

写入方法不会为我们添加行终止符,所以程序必须包含它来严格地终止行。

在文件中存储并解析python对象:

>>> F = open('datafile.txt', 'w')
>>> F.write(S + '\n')
5
>>> F.write('%s,%s,%s\n' % (X, Y, Z))
9
>>> F.write(str(L) + '$' + str(D) + '\n')
27
>>> F.close()


>>> bytes = open('datafile.txt').read()
>>> bytes
"spam\n43,44,45\n[1, 2, 3]${'a': 1, 'b': 2}\n"
>>> print(bytes)
spam
43,44,45
[1, 2, 3]${'a': 1, 'b': 2}



>>> F = open('datafile.txt')
>>> line = F.readline()
>>> line
'spam\n'
>>> line.rstrip()
'spam'
>>>


>>> line = F.readline()
>>> line
'43,44,45\n'
>>> parts = line.split(',')
>>> parts
['43', '44', '45\n']
>>>
#int 能够把数字字符串转换为整数对象,注意:我们不一定非要运行rstrip来删除最后部分的“\n”,int 和一些其他转换方法会忽略数字旁边的空白。

['43', '44', '45\n']
>>> int(parts[1])
44
>>> number = [int(p) for p in parts]
>>> number
[43, 44, 45]

#转换列表和字典

#使用eval可以把字符串转换成对象
>>> parts = line.split('$')
>>> parts
['[1, 2, 3]', "{'a': 1, 'b': 2}\n"]
>>> eval(parts[0])
[1, 2, 3]
>>> objects = [eval(p) for p in parts]
>>> objects
[[1, 2, 3], {'a': 1, 'b': 2}]
>>>

用pickle存储python的原生对象:
pickle模块是能够让我们直接在文件中存储几乎任何python对象的高级工具,也并不要求我们把字符串转换来转换去。它就像是超级通用的数据格式化和解析工具。例如,想要在文件中存储字典,就直接用pickle来存储。

对象灵活性:

  • 列表、字典和元组可以包含任何种类的对象
  • 列表、字典和元组可以任意嵌套
  • 列表和字典可以动态地扩大和缩小
引用VS拷贝
赋值操作总是存储对象的引用,而不是这些对象的拷贝。

拷贝:

  • 没有限制条件的分片表达式(L[ : ])能够复制序列
  • 字典copy方法(D.copy())能够复制字典
  • 有些内置函数(例如,list)能够生成拷贝(list(L))
  • copy标准库模块能够生成完整拷贝
拷贝需要注意的是:无条件值的分片以及字典copy方法只能做顶层复制,也就是说,不能够复制嵌套的数据结构。如果你需要一个深层嵌套的数据结构的完整的,完全独立的拷贝,那么久要使用标准的copy模块。

比较、相等性和真值

>>> L1 = [1, ('a', 3)]
>>> L2 = [1, ('a', 3)]
>>> L1 == L2, L1 is L2
(True, False)
L1,L2被赋值为列表,虽然相等,却是不同的对象。

  • “==”操作符测试值的相等性。python运行相等测试,递归地比较所有内嵌对象
  • “is”表达式测试对象的一致性。python测试二者是否是同一对象。
>>> S1 = 'spam'
>>> S2 = 'spam'
>>> S1 ==S2, S1 is S2
(True, True)
在这里,两个截然不同的对象碰巧有着相同的值:但是因为在python内部暂时存储并重复使用短字符串作为最佳化,事实上内存里只有一个字符串‘spam’供S1和S2分享。因此,“is”一致性测试结果为真,为了得到更一般的结果,我们需要使用更长的字符串:

>>> S1 = 'a longer string'
>>> S2 = 'a longer string'
>>> S1 == S2, S1 is S2
(True, False)

相对大小的比较也能够递归地应用于嵌套的数据结构。

>>> L1 = [1, ('a', 3)]
>>> L2 = [1, ('a', 2)]
>>> L1 < L2, L1 == L2, L1> L2
(False, False, True)

一般来说,python中不同的类型的比较方法如下:

  • 数字通过相对大小进行比较
  • 字符串是按照字典顺序,一个字符接一个字符地对比进行比较
  • 列表和元组从左到右对每部分的内容进行比较
  • 字典通过排序之后的(键、值)列表进行比较
python中真和假的含义:

>>> L = [] * 100
>>> L
[]
>>> L = [None] * 100
>>> L
[None, None, None, ……]
对于列表来说,只能给已经存在的偏移赋值的。预先分配一个100项的列表,这样你可以在100个偏移的任何一个加上None对象。

重复能够增加层次深度:
序列重复就好像是多次将一个序列加到自己身上。






>>> L = [4,5,6]
>>> X = L * 4
>>> Y = [L] * 4
>>> X
[4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6]
>>> Y
[[4, 5, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]



>>> L[1] = 0
>>> X
[4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6]
>>> Y
[[4, 0, 6], [4, 0, 6], [4, 0, 6], [4, 0, 6]]
重复、合并以及分片只是在复制操作数对象的顶层。


留意循环数据结构
>>> L = ['grail']
>>> L.append(L)
>>> L
['grail', [...]]




第三部分:语句和语法
第十章:python语句简介:
python程序结构:

  1. 程序由模块构成
  2. 模块包含语句
  3. 语句包含表达式
  4. 表达式建立并处理对象
python是WYSIWYG语言——所见即所得(what you see is what you get)。因为不管是谁写的,程序看上去的样子就是运行得方式。

第十一章:赋值、表达式和打印:
赋值语句:

  • 赋值语句建立对象引用值。python变量更像是指针,而不是数据存储区域
  • 变量名在首次赋值时会被创建。一旦赋值了,每当这个变量名出现在表达式时,就会被其所引用值取代
  • 变量名在引用前必须先赋值。
  • 隐式赋值语句:import、from、def、class、for、函数参数。赋值语句会在许多情况下使用。例如:模块导入、函数和类的定义、for循环变量以及函数参数全都是隐式赋值运算。
增强赋值以及共享引用:

>>> L = [1, 2]
>>> M = L               # L和M引用相同的对象
>>> L = L + [3, 4]      # 合并生成一个新的对象,运行起来更慢一些
>>> L, M
([1, 2, 3, 4], [1, 2])
>>> L = [1, 2]
>>> M = L
>>> L += [3, 4]         # 但是增强赋值+=真正意味着扩展
>>> L,M                 # M原地修改
([1, 2, 3, 4], [1, 2, 3, 4])


变量命名规则:

1、语法:(下划线或字母)+ (任意数目的字母、数字或下划线)
变量名必须以下划线或字母开头,而后面接任意数目的字母、数字或下划线。
2、区分大小写。
3、禁止使用保留字。python保留字都是小写的,无法对保留字做赋值运算。

命名惯例:

  • 以单一下划线开头的变量名(_x)不会被from module import *语句导入的
  • 前后有下划线的变量名(_ _x_ _)是系统定义的变量名,对解释器有特殊意义
  • 以两下划线开头、但结尾没有两个下划线的变量名(_ _x)是类的本地变量
  • 通过交互模式运行时,只有单个下划线的变量名(_)会保存最后表达式的结果
类变量名通常以一个大写字母开头,而模块变量名以小写字母开头。变量名self虽然并非保留字,但在类中一般有特殊的角色。
变量名没有类型,但对象有。

表达式语句和在原处的修改

>>> L = [1, 2]
>>> L.append(3)
>>> L
[1, 2, 3]
>>> L = [1, 2]
>>> L = L.append(3)
>>> L
>>>
>>> print(L)
None
对列表调用append、sort、reverse这类在原处的修改的运算,一定是对列表做原处的修改,但这些方法在列表修改后并不会立即把列表返回。事实上,它们返回的是none对象。

为什么要注意print和stdout:
print语句和sys.stdout之间的等效是很重要的额,这样才有可能把sys.stdout重新赋值给用户定义的对象(提供和文件相同的方法,就像write)。因为print语句只是传送文本给sys.stdout.write方法,可以把sys.stdout赋值给一个对象,而由该对象的write方法通过任意方式处理文字,通过这个对象捕捉程序中打印的文本。

第十二章:if测试

python语法规则:

  • 语句是逐个运行得,除非你不这样编写。
  • 块和语句的边界会自动被检测。
  • 复合语句=首行+“:”+缩进语句。
  • 空白行、空格以及注释通常都会被忽略。
  • 文档字符串(docstring)会被忽略,但会被保存并由工具显示。
  • python没有变量类型声明。
语句的分隔符:

  • 如果使用语法括号对,语句就可横跨数行。
  • 如果语句以反斜杠结尾,就可横跨数行。(反斜杠几乎都不再使用了)
  • 三重引号字符串常量可以横跨数行。


第十三章:while和for循环

关于循环else分句是python特有的,简而言之,循环else分句提供了常见的编写代码的明确语法;这是编写代码的结构,让你捕捉循环的“另一条”出路,而不是通过设定和检查标志位或条件。

迭代器:
迭代工具包括了for循环、列表解析、in成员关系测试以及map内置函数等。
“可迭代对象”的概念在python中是相当新颖的。基本上,这就是序列观念的通用化:如果对象是实际保存的序列,或者可以在迭代工具环境中一次产生一个结果的对象,就被看作是可迭代。总之,可迭代对象包括实际序列和按照需求而计算的虚拟序列。

文件迭代器:
了解迭代器含义的最简单的方法之一就是看一看它是如何与内置类型一起工作的。每一种有左至右扫描对象的工具都会使用迭代协议。python提供了两个内置函数,在for循环内定制迭代:

  • 内置range函数返回连续整数列表,可作为for中的索引。
  • 内置zip函数返回并行的元素元组的列表,可用于在for中内遍历数个序列。

for循环一般都比while计数器循环运行得更快。

循环计数器:while和range

非完备遍历:range
修改列表:range
并行遍历:zip和map

>>> L1 = [1, 2, 3, 4]
>>> L2 = [5, 6, 7, 8]
>>> zip(L1, L2)
<zip object at 0x023583A0>
>>> for (x,y) in zip(L1,L2):
print(x, y, '-->', x+y)


1 5 --> 6
2 6 --> 8
3 7 --> 10
4 8 --> 12



>>> T1,T2, T3 = (1, 2, 3), (4, 5, 6), (7, 8, 9)
>>> T3
(7, 8, 9)
>>> zip(T1, T2, T3)
<zip object at 0x023585F8>
>>> #当参数长度不同时,zip会以最短序列的长度为准来截断所得到的元组
>>> S1 = 'abc'
>>> S2 = 'xyz123'
>>> zip(S1, S2)
<zip object at 0x023586E8>

使用zip构造字典:


>>> keys = ['spam', 'eggs', 'toast']
>>> vals = [1, 3, 5]
>>> D = dict(zip(keys, vals))
>>> D
{'toast': 5, 'eggs': 3, 'spam': 1}

产生偏移和元素:enumerate


>>> S = 'spam'
>>> for (offset, item) in enumerate(S):
print(item, 'appears at offset', offset)


s appears at offset 0
p appears at offset 1
a appears at offset 2
m appears at offset 3


enumerate函数返回一个生成器对象:这种对象支持迭代协议,有一个next方法,每次遍历列表时,会返回一个(index,value)的元组,而我们能在for中通过元组赋值运算将其分解(很像是使用zip)

列表解析初探:


>>> L = [x + 10 for x in range(10)]
>>> L
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

列表解析和for循环语句并不完全相同,因其创建新的列表对象(如果原始列表有多个引用值,这一点可能就很重要)。

列表解析基础:
从语法上讲,列表解析的语法是从集合理论表示法中的一种结构中衍生出来的,也就是对集合中的每个元素应用某一种运算,但不需要懂得集合理论就能运用。在python中,多数人会觉得列表解析看起来就像是倒过来的for循环。列表解析是写在方括号中的,因为它毕竟是一种创建新的列表的方式。为了执行表达式,python会在解释器内通过L来执行迭代,依次把x赋值给每个元素,然后通过左侧的表达式运行每一个元素并且存储其结果。我们所得到的结果列表就是列表解析所说的内容:新列表表,内含x+10,而x是在L中的每个元素。
从技术角度来说,列表解析绝不是必须要求的,因为我们可以使用for循环,在遍历过程中把表达式结果加在列表上。

>>> res = []
>>> for x in L:
res.append(x+10)
>>> res
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
事实上,这正是列表解析内部所做的事。

扩展列表解析语法

第十四章:文档

dir函数是抓取对象内可用属性列表的简单方式。塌能够调用任何有属性的对象。

>>> import sys
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__', '__package__', '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames', '_getframe', '_mercurial', '_xoptions', 'api_version', 'argv', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dllhandle', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'getcheckinterval', 'getdefaultencoding', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'getwindowsversion', 'hash_info', 'hexversion', 'int_info', 'intern', 'last_traceback', 'last_type', 'last_value', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'setcheckinterval', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'subversion', 'version', 'version_info', 'warnoptions', 'winver']

内置文档字符串:

>>> print(sys.__doc__)
This module provides access to some objects used or maintained by the
interpreter and to functions that interact strongly with the interpreter.

Dynamic objects:

argv -- command line arguments; argv[0] is the script pathname if known
path -- module search path; path[0] is the script directory, else ''
modules -- dictionary of loaded modules
……

PyDoc:help函数


>>> help(sys.getrefcount)
Help on built-in function getrefcount in module sys:

getrefcount(...)
    getrefcount(object) -> integer
  
    Return the reference count of object.  The count returned is generally
    one higher than you might expect, because it includes the (temporary)
    reference as an argument to getrefcount().

python的标准手册:www.python.org上在线阅读


常见编写代码的陷阱:

  • 别忘了冒号;
  • 从第1行开始;
  • 空白行在交互模式提示符下很重要;
  • 缩进要一致;
  • 不要在python中写C代码:(例如:if (x == 1): )不需要(),不要在while循环测试中嵌入赋值语句;
  • 使用简单的for循环,而不是while或range;
  • 要注意赋值语句中的可变对象:(例如:a = b =[],以及在增强指定语句中a += [1, 2],在原处的修改会影响其他变量。)
  • 不要期待进行在原处的修改的函数会返回结果(像list.append和List.sort方法这种的修改,并不会有返回值(除了None)。所以调用时不要对其赋值。)
  • 一定要使用括号调用函数。(必须在函数名称后面加括号才能对它进行调用,无论它是否带参数。函数也是对象,通过括号触发对它的调用)
  • 不要在导入和重载中使用扩展名或路径(在import语句中省略目录路径和文件字尾,如:import mod,而不是import mod.py)

第四部分:函数

第十五章:函数基础
函数是python为了代码最大程度的重用和最小化代码冗余而提供的最基本的程序结构。
概念介绍:

  • def是可执行的代码。def是一个可执行的语句——函数并不存在,直到python运行了def后才存在。在典型的操作中,def语句在模块文件中编写,并自然而然地在模块文件第一次被导入的时候生成定义的函数。
  • def创建了一个对象并将其赋值给某一变量名。当python运行到def语句时,它将会生成一个新的函数对象并将其赋值给这个函数名。就像所有的赋值一样,函数名变成了某一函数的引用。函数也可以通过lambda表达式来创建。
  • return将一个结果对象发送给调用者。
  • 函数是通过赋值(对象引用)传递的。在python中,参数通过赋值传递给函数。
  • global声明了一个模块级的变量并被赋值。在默认情况下,所有在一个函数中被赋值的对象,是这个函数的本地变量,并且仅在这个函数运行得过程中存在。变量名需要关注它的作用域(也就是变量存储的地方),并且是通过实赋值语句将变量名绑定至作用域的。
  • 参数、返回值以及变量并不是声明。在函数中一样没有类型约束。可以传递任意类型的参数给函数,函数也可以返回任意类型的对象。
def语句时实时执行的。

python中的多态:
python将对某一对象在某种语法的合理性交由对象自身来判断。这种依赖类型的行为成为多态,其含义是一个操作的意义取决于被操作对象的类型。

第十六章:作用域和参数
变量名解析:LEGB原则
对于一个def语句:

  • 变量名引用分为三个作用域进行查找:首先是本地,之后是函数内(如果有的话),之后是全局,最后是内置。
  • 在默认情况下,变量名赋值会创建或者改变本地变量。
  • 全局声明将赋值变量名映射到模块文件内部的作用域。
所有在函数def语句或者lambda表达式内赋值的变量名默认均为本地变量。函数能够在函数内部以及全局作用域直接使用变量名,但是必须声明为全局变量去改变其属性,python的变量名解析机制有时成为LEGB法则,这由作用域的命令而来。

  • 当在函数中使用未认证的变量名时,python搜索4个作用域【本地作用域(L),之后是上一层结构中def或lambda的本地作用域(E),之后是全局作用域(G),最后是内置作用域(B)】并且在第一处能够找到这个变量名的地方停下来。变量名在使用前首先必须被赋值过。
  • 当在函数中给一个变量名赋值时(而不是在一个表达式中对其进行引用),python总是创建或改变本地作用域的变量名,除非它已经在那个函数中声明为全局变量。
  • 当在函数之外给一个变量名赋值时(也就是,在一个模块文件的顶层,或者是在交互提示模式下),本地作用域与全局作用域(这个模块的命名空间)是相同的。
global语句:不是一个类型或大小的声明,它是一个命名空间的声明。
全局变量和模块的属性石的等效的!

作用域和嵌套函数:

嵌套作用域的细节:对于一个函数:

  • 在默认情况下,一个赋值(X = value)创建或者改变了变量名X的当前作用域。如果X在函数内部声明为全局变量,它将会创建或改变变量名X为整个模块的作用域。
  • 一个引用(X)首先在本地(函数内)作用域查找变量名X,之后会在代码的语法上嵌套了的函数中的本地作用域,从内到外,之后查找当前的全局作用域(模块文件),最后再内置作用域内(模块__builtin__)。全局声明将会直接从全局(模块文件)作用域进行搜索。
工厂函数:根据要求的对象,这种行为有时也叫做闭合或者工厂函数——一个能够记住嵌套作用域的变量值的函数,尽管那个作用域或许已经不存在了。尽管类是最适合用作记忆状态的,因为它们通过属性赋值让这个过程变得很明了,像这样的函数也提供了一种替代的解决方法。
例如:
工厂函数有时用于需要及时生成事件处理,实时对不同情况进行反馈的程序中(例如,用户的输入是无法进行预测的)。如:


>>> def maker(N):
def action(X):
return X ** N
return action
#这定义了一个外部的函数,这个函数简单的生成并返回了一个嵌套的函数,却并不调用这个内嵌的函数。如果我们调用外部的函数:
>>> f = maker(2)
>>> f
<function action at 0x0250A108>
我们得到的是生成的内嵌函数的一个引用。一个内嵌函数是通过运行内嵌的def而创建的,如果现在调用从外部得到的那个函数的话:
>>> f(3)
9
>>> f(4)
16


使用默认参数来保留嵌套作用域的状态。

(P344)嵌套作用域和lambda:

>>> def func():
x = 4
action = (lambda n: x ** n)
return action

>>> x = func()
>>> print(x(2))
16

传递参数:

  • 参数的传递是通过自动将对象赋值给本地变量来实现的。作为参数被传递的对象从来不自动被拷贝。
  • 在函数内部的参数名的赋值不会影响调用者。在函数运行时,在函数头部的参数名是一个新的、本地的变量名,这个变量名是在函数的本地作用域内的。函数参数名和调用者的变量名是没有别名的。
  • 改变函数的可变对象参数的值也许会对调用者有影响。因为参数是简单的通过赋值进行对象的传递的,函数能够改变传入的可变对象,因此其结果会影响调用者。可变参数对于函数来说是可以做输入和输出的。
python在实际中与C语言的参数传递模型相当相似。

  • 不可变参数是“通过值”进行传递。
  • 可变对象是通过“指针”进行传递的。

(P381)对参数输出进行模拟

函数参数匹配:
语法              位置   解释
func(value)       调用者 常规参数:通过位置进行匹配
func(name = value)调用者 关键字参数:通过变量名匹配
func(*name)       调用者 以name传递所有的对象,并作为独立的基于位置的参数   
func(**name)      调用者 以name成对的传递所有的关键字/值,并作为独立的关键字参数

def func(name)      函数   常规参数:通过位置或变量名进行匹配
def func(name=value)函数   默认参数值,如果没有在调用中传递的话
def func(*name)     函数   匹配并收集(在元组中)所有包含位置的参数
def func(**name)    函数   匹配并收集(在字典中)所有包含位置的参数

注意:在调用中,通过变量名进行匹配关键字,而在函数头部,它为一个可选的参数定义了默认值。无论是哪种情况,这都不是一个赋值语句。它是在这两种情况下的特定语法,改变了默认的参数匹配机制。

分解参数:
在调用函数时能够使用*语法。在这种情况下与函数定义的意思相反,它会分解参数的集合,而不是创建参数的集合。相似地,在函数调用时,**会以键值对的形式分解一个字典。

>>> def func(a, b, c, d):
print(a, b, c, d)


>>> args = {'a': 1, 'b': 2, 'c': 3}
>>> args['d'] = 4
>>> func(**args)
1 2 3 4
>>>

min调用:



>>> def min1(*args):
res = args[0]
for arg in args[1:]:
if arg < res:
res = arg


>>> def min1(*args):
res = args[0]
for arg in args[1:]:
if arg < res:
res = arg
return res

>>> def min2(first, *rest):
for arg in rest:
if arg < first:
first = arg
return first

>>> def min3(*args):
tmp = list(args)
tmp.sort()
return tmp[0]

>>> print(min1(3, 4, 1, 2))
1
>>> print(min2("bb", "aa"))
aa
>>> print(min3([2,2],[1,1],[3,3]))
[1, 1]
>>>


>>> def minmax(test, *args):
res = args[0]
for arg in args[1:]:
if test(arg, res):
res = arg
return res

>>> def lessthan(x, y):
return x<y

>>> def morethan(x, y):
return x>y

>>> print(minmax(lessthan, 4, 2, 1, 5, 6, 3))
1
>>> print(minmax(morethan, 4, 2, 1, 5, 6, 3))
6




第十七章:函数的高级话题

匿名函数:lambda
lambda就像def一样,这个表达式创建了一个之后能够调用的函数,但是它返回了一个函数而不是将这个函数赋值给一个变量名。这也就是lambda有时被称作匿名的函数的原因。实际上,它常以一种行内进行函数定义的形式被使用,或者用作推迟执行一些代码。

  • lambda是一个表达式,而不是一个语句。lambda能够出现在python语法不允许def出现的地方——例如,在一个列表常量中或函数调用中。此外,作为一个表达式,lambda反悔了一个值(一个新的函数),可以选择性地赋值给一个变量名。相反,def语句总是得在头部将一个新的函数赋值给一个变量名,而不是将这个函数作为结果返回。
  • lambda的主体是一个单个的表达式,而不是一个代码块。lambda限制了程序的嵌套:lambda是一个为编写简单的函数而设计的,而def用来处理更大的任务。
在序列中映射函数:map
在程序对列表和其他序列常常要做的一件事就是对每一个元素进行一个操作并把其结果集合起来。例如:

>>> counters = [1, 2, 3, 4]
>>> update = []
>>> for x in counters:
update.append(x+10)


>>> update
[11, 12, 13, 14]


>>> def inc(x):
return x+10

>>> map(inc,counters)
<map object at 0x023D08D0>

函数式编程工具:filter和reduce

重访列表解析:映射

列表解析和矩阵

重访迭代器:生成器
不像一般的函数会生成值后退出,生成器函数在生成值后自动挂起并暂停它们的执行和状态。
生成器和一般的函数之间代码上的最大不同就是一个生成器yield一个值,而不是return一个值。yield语句将会将函数关起,并向它的调用者返回一个值,但是保存足够的状态信息为了让其能够在函数从它挂起的地方恢复。这能够允许这些函数不断的产生一系列的值,而不是一次计算所有的值,之后将值以类似列表之类的形式来返回。

生成器函数在python中与迭代器协议的概念联系在一起,简而言之,包含了yield语句的函数将会特地编译为生成器,当调用时,它们返回了一个生成器对象,这个生成器对象支持迭代器对象接口。生成器函数也许也有一个return语句,这个语句就是用来终止产生值的。

迭代器对象,依次定义了下一个方法,这些方法既要返回在迭代中的下一个元素,又要抛出一个特定的异常(StopIteration)来终结这个迭代,迭代器是通过内置的iter函数获取的。如果协议支持的话,python的for循环使用了这个迭代接口协议来步进处理一个序列(或者序列生成器);如果不支持的话,for将会重复对序列进行索引运算。

扩展生成器函数协议:send和next

生成器表达式:迭代器遇到列表解析
在最新版本的python中,迭代器和列表解析的概念形成了这个语言的而一个新的特性,生成器表达式,从语法上来讲,生成器表达式就像一般的列表解析一样,但是它们是括在圆括号而不是方括号中的。

>>> [x ** 2 for x in range(4)]
[0, 1, 4, 9]
>>> (x ** 2 for x in range(4))
<generator object <genexpr> at 0x023D1FA8>
从执行过程上来讲,生成器表达式很不相同:不是在内存中构建结果,而是返回一个生成器对象,这个对象将会支持迭代协议并在任意的迭代语境的操作中,获得最终结果列表的一部分。

对迭代的各种方法进行计时:
列表解析要比for循环语句有速度方面的性能优势,而且map会依据调用方法的不同表现出更好或更差的性能。生成器表达式看起来比列表解析速度更慢一些,但是它们把内存需求降到了最小。

函数设计概念:
如何将任务分解成更有针对性的函数(导致了聚合性)、函数将如何通信(耦合性)等。

  • 耦合性:对于输入使用参数并且对于输出使用return语句。一般来讲,你需要力求让函数独立于它外部的东西。参数和return语句通常就是隔离对外部的依赖关系的最好的办法,从而让代码中只剩少量醒目位置。
  • 耦合性:只有在真正必要的情况下使用全局变量。全局变量(在整个模块中的变量名)通常是一种蹩脚的函数间进行通信的办法。它们引发了依赖关系和计时的问题,会导致程序调试和修改的困难。
  • 耦合性:不要改变可变类型的参数,除非调用者希望这样做。函数会改变传入的可变类型对象,但是就像全局变量一样,这会导致很对调用者和被调用者之间的耦合性,这种耦合性会导致一耳光函数过于特殊和不友好。
  • 聚合性:每一个函数都应该有一个单一的、统一的目标。在设计完美的情况下,每一个函数中都应该做一件事:这件事可以用一个简单说明句来总结。
  • 大小:每一个函数应该相对较小。保持简单,保持简短。
  • 耦合:避免直接改变在另一个模块文件中的变量。在可能的时候使用读取函数,而不是直接进行赋值语句。
函数是对象:简洁调用
函数陷阱:
本地变量是静态检测的
没有return语句的函数

>>> list = [1, 2, 3]
>>> list = list.append(4)
>>> print(list)
None
>>> list.append(5)
Traceback (most recent call last):
  File "<pyshell#88>", line 1, in <module>
    list.append(5)
AttributeError: 'NoneType' object has no attribute 'append'
>>> list = [1, 2,3]
>>> list.append(4)
>>> print(list)
[1, 2, 3, 4]
嵌套作用域的循环变量


第五部分:模块

模块:宏伟蓝图
import :使客户端(导入者)以一个整体获取一个模块。
from :允许客户端从一个模块文件中获取特定的变量名。
reload : 在不中止python程序的情况下,提供了一种重新载入模块文件代码的方法。

模块至少有三个角色:

  1. 代码重用:模块可以在文件中永久保存代码。除此之外,模块还是定义变量名的空间,被认作是属性,可以被多个外部的客户端引用。
  2. 系统命名空间的划分:模块还是在python中最高级别的程序组织单元。从根本上来讲,它们不过是变量名的软件包。
  3. 实现共享服务和数据:从操作的角度来看,模块对实现跨系统共享的组件是很方便的,而且只需要一个拷贝即可。
python程序架构:
(409/PDF440)import如何工作

第十九章:模块代码编写基础
模块怎么命名都可以,但是如果打算将其导入,模块文件名就应该以.py结尾,对于会执行但不会被导入的顶层文件而言,.py从技术上来讲是可有可无的,但是每次都加上去,可以确保文件类型更醒目,并允许以后可以导入任何文件。

导入只发生一次,因为模块文件中的顶层程序代码通常只执行一次,你可以凭借这种特性对变量进行初始化。

import和from是赋值语句:
就像def一样,import和from是可执行的语句,而不是编译期间的声明,而且它们可以嵌套在if测试中,出现在函数def之中等等,直到执行程序时,python执行到这些语句,才会进行解析,换句话说,被导入的模块和变量名直到它们所对应的import或from语句执行后,才可以使用,此外,就像def一样,import和from都是隐性的赋值语句。

  • import将整个模块对象赋值给一个变量名。
  • from将一个或多个变量名赋值给另一个模块中同名的对象。
import和from的对等性:
from只是把变量名从一个模块复制到另一个模块,并不会对模块本身进行赋值。

第二十章 模块包
import dir1.dir2.mod
必须遵循下列规则:

  • dir1和dir2中必须都含有一个__init__.py文件。
  • dir0是容器,不需要__init__.py文件;如果有的话,这个文件也会被忽略。
  • dir0(而非dir0\dir1)必须列在模块搜索路径上(也就是此目录必须是主目录,或者列在PYTHONPATH之中)。
__init__.py文件扮演了包初始化的挂钩、替目录产生模块命名空间以及使用目录导入时实现from*行为的角色。

第二十一章 高级模块话题

在模块中隐藏数据:python的封装更像是打包,而不是约束。
最小化from*的破坏:_X和__all__
__all__是指出要复制的变量名,而_X是指出不被复制的变量名。
培养他还能会先寻找模块内的__all__列表,如果没有定义的话,from*就会
复制出开头没有单下划线的所有变量名。就像_X惯例一样,__all__列表只对from*语句这种形式有效,它并不是私有声明。

启用以后的语言特性:

from __future__ import featurename

混合用法模式:__name__和__main__
以__name__进行单元测试

修改模块搜索路径:模块搜索路径是一个目录列表,可以通过环境变量PYTHONPATH以及可能的.pth路径文件进行定制。sys.path的设置方法只在修改的python会话或程序中才会存续。在python结束后,不会保留下来。

import as 扩展:

import longmodulename as name相当于:
import longmodulename
name = longmodulename
del longmodulename #don't keep original name

from module import longname as name

相对导入语法
模块设计理念:

  • 总是在python的模块内编写代码。
  • 模块耦合要降到最低:全局变量。
  • 最大化模块的粘合性:统一目标。
  • 模块应该少去修改其他模块的变量。

模块式对象:元程序

reload的使用没有传递性:当重载一个模块时,python只会重载那个模块的文件,不会自动重载该文件重载时碰巧还要导入的模块。

递归形式的from import无法工作。

第六部分 类和OOP
第二十二章 OOP:宏伟蓝图

类是在python实现支持继承的新种类的对象的部件。

属性继承搜索

类:
类是实例工厂。类的属性提供了行为(数据以及函数),所有从类产生的实例都继承该类的属性。
实例:
代表程序中具体的元素。实例属性记录数据,而每个特定对象的数据都不同。
类和实例的主要差别在于,类是一种产生实例的工厂。
类和模块的另一个差异:内存中特定模块只有一个实例(所以我们得重载模块以取得其新代码),但是,对于类而言,只要有需要,制作多少实例都可以。

在OOP中,实例就像是带有“数据”的记录,而类是处理这些记录的“程序”。

编写类树

  • 每个class语句会生成一个新的类对象。
  • 每次类调用时,就会生成一个新的实例对象。
  • 实例自动连结至创建了这些实例的类。
  • 类连结至超类的方式是,将超类列在类头部的括号里。其从左到右的顺序会决定树中的次序。



  • 属性通常是在class语句中通过赋值语句添加在类中,而不是嵌入在函数的def语句内。
  • 属性通常是在类内,对传给函数的特殊参数(也就是self),做赋值运算而添加在实例中的。

就像简单变量一样,类和实例属性并没有事先声明,而是在首次赋值时它的值才会存在,类树中所有对象都不过是命名空间对象,我们可以通过恰当的变量名读取或设置其任何属性。

大体而言,OOP就是在树中搜索属性。

第二十三章 类代码编写基础


类产生多个实例对象(P516)
在python中,实例从类中继承,而类继承于超类。
1、超类列在了类开头的括号中。
2、类从其超类中继承属性。
3、实例会继承所有可读取类的属性。
4、每个object.attribute都会开启新的独立搜索。
5、逻辑的修改是通过创建子类,而不是修改超类。
这种搜索的结果和主要目的就是,类支持了程序的分解和定制。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章