该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!
2018-05-07——2018-05-28
参考内容《Python tutorial 2.7.13》
实践源码 Python2.7.13 初识
#coding=utf-8
# 《Python入门指南》
# Release 2.7.13
# Date 2017-02-13
# Python简单易学 且功能强大
# 1.拥有高效的高级数据结构,能以简单且高效的方式进行面向对象编程;
# 2.语法优雅、动态类型,结合它的解释性,是大多数平台或领域中编写脚本和开发应用程序的理想语言;
# Python官方点 http:www.python,以源码或二进制的形式提供Python解释器及其标准扩展库,并可以自由分发;
# 该站点也提供了大量的第三方Python模块、程序、工具及其附加文档;
# 可以使用C C++(其他可以通过C调用的语言)为Python解释器扩展函数和数据类型;
# Python还可以被用作定制应用程序的一门扩展语言
# Python 参考文档:https://docs.python/2.7/library/index.html
# Python 参考文档:https://docs.python/2.7/index.html
# Python 解释器扩展和集成章节:https://docs.python/2.7/extending/index.html#extending-index
# C API参考手册 https://docs.python/2.7/c-api/index.html#c-api-index
# 或其他进阶书籍
# 本学习过程学完之后,能阅读并编写Pyhton程序,并为以后使用《Python参考手册》继续学习Python模块库做准备;
#coding=utf-8
# 一、开胃菜
# Python可以快速的开发应用程序,实现扩展,在主流的操作系统上都可以帮助更快的完成任务;
# Python虽然简单,却是一门完整的语言:
# 1.为编写大型程序踢提供了更多的结构和支持;
# 2.作为一门高级语言,内置支持高级的数据结构和类型;
# 3.Python允许将程序分割成不同的模块,以便在其他Python程序中重用;
# 同时内置了大量的标准模块;如 文件I/O、系统调用、Socket支持,甚至GUI用户图形界面工具包;
# Python是一门解释型语言,因此无需编译和链接;
# 使用Python编程更加紧凑和可读:
# 1.高级数据结构使你可以在一条语句中表达复杂的操作;
# 2.语句组使用缩进代替开始和结束大括号组织;
# 3.变量和参数无需声明;
# Python是可扩展的:
# 使用C语言可以轻而易举地为解释器添加内置函数或模块,或者对性能瓶颈进行优化;或者将Python程序与只有二进制形式的库链接起来;
# -*- coding: utf-8 -*-
# 二、使用Python解释器
# 2.1 调用Python解释器
# Python一般安装在目标机器的/usr/local/bin/python目录下;以确保可以输入python命令;
# windows下可以配置环境变量
# Python解释器可以交互式的解释并执行终端输入的命令;当使用文件名参数或以文件作为标准输入调用时,它读取文件并将文件作为脚本执行;
# python -c commond [arg] ... :这种方式可以在命令行执行Python语句;为了避免特殊字符冲突,一般将命令行用单引号包裹起来;
# python -m module [arg] ... :将一些Python模块当做脚本使用;使用脚本文件时,经常会运行脚本然后进入交互模式,这也可以通过在脚本之
# 前加上-参数来实现;
# 2.1.1 参数传递
# 调用解释器时,脚本名和附加参数传入一个名为sys.argv的字符串列表;可以通过import sys来获取此列表,列表长度>=1,无给定脚本和参数
# 时,argv[0]为空字符串;
# 脚本名指定为'-'(表示标准输入)时,sys.argv[0]被指定为'-';使用-c指令时,sys.argv[0]被指定为'-c';
# 使用-m模块参数时,sys.argv[0]被设定为指定模块的全名;-c指令或-m模块之后的参数不会被Python解释器的选项处理机制所截获,而是留
# 在sys.argv中,供脚本命令操作;
# 2.1.2 交互模式
# 从tty(命令行)读取命令时,我们称解释器工作于 交互模式;
# 这种模式下它根据主提示符来执行,主提示符通常标识为3个大于号>>>,继续的部分成为从属提示符,由三个点标识(...);
# 输入多行(缩进)时就需要从属提示符了;
# 2.2 解释器及其环境
# 2.2.1 源程序编码
# 在Python源代码使用非ASCII编码,最好的方式是在#!行后边增加一行特殊的注释来定义源文件中的编码(即便只是注释也不可以,编码方式必须支持
# 文件中所有字符的字体);
# 如果编辑器支持保存为带有UTF-8字节顺序标记(也叫做BOM)的UTF-8格式的文件,则可以使用这种功能为不必编码声明;
# IDLE如果设置了Default Source Encoding/UTF-8也可以;
# -*- coding: utf-8 -*-
# 三、Python简介
# 关于注释:以#开头直至行尾,出现在代码中的#仅作为正常的#号;
SPAN = 1
STRING = "# This is not a comment."
# 3.1 将Python当做计算器
# 由于之前已经练习过一部分Python,就不再在命令行中做相应的实践,而是使用PCharm IDE;
# 3.1.1 数字
print 2 + 2
print 50 - 5 * 6
print (50 - 5.0 * 6)/4
# 整数类型是int
# 带有小数的是float,后续我们会学习到更多的数字类型;
# /法的返回类型取决于它的操作数,两个int,采用floor division(可向下取整的除法)返回的也是一个int;float参与的返回的就是一个float;
# //运算符也是除法,用于floor division而无论操作数是什么类型;
# 余数使用%操作符计算;
print 17/3
print 17.0//3
print 17%3
# 可以使用**运算符计算幂乘方
print 5**2
# 变量在使用前必须"定义"(赋值),否则会出错
n = 3
print n
# 浮点数与整数混合计算时,自动转换为浮点数
# 交互模式中,最近一个表达式的值赋给变量'_';这样可以把它当做一个桌面计算器,用于连续计算;此变量对于用户时只读的;如果对他进行赋值的
# 话,只会创建一个独立的同名局部变量;
# 除了int和float,Python还支持其他数字类型,如Decimal何Fraction;
# Python还内建支持复数,使用后缀j或J表示虚数部分
f = 5 + 3j
print f.real
print abs(f) #实部的平方 + 虚部的平方开根号
# 3.1.2 字符串
# Python支持几种不同的方式表示字符串;
# 可以使用单引号('...')或双引号("...");\可以用来转义引号;
print "doesn't"
print 'doesn\'t'
# 如果你前面带有的\字符被当做特殊字符,你可以使用原始字符,方法是在第一个引号前面加上一个r;
print 'C:\some\name'
print r'C:\some\name'
# 字符串文本能够分成多行,一种方法就是使用3引号:"""..."""或者'''...''';
# 行尾换行符会被自动包含到字符串中,也可以在行尾加上\来避免这个行为;
# 这里\反斜杠表示下一行在逻辑上是本行的后续内容;
print """abc
def\
ghi
jkl"""
# 字符串可以由+操作符连接,用*表示重复
print 3*'UN' + 'China' 'is great country!'
# 相邻的两个字符串会自动连接在一起
# 如果是一段很长的字符串文本你可以这样写
print ('1awefjaiwjefiaw-'
'2awefahwuefhawuefhaw-'
'3awefhahwefhawuefhawu-')
# 值得注意的是 上边说的这种相邻的两个字符串会自动连接的方式只能用于两个字符串文本,不能用于字符串表达式,或者是字符串文本和变量之间;
# 如我们将3*'UN'加上一个括号就变成了一个表达式,此时上边的这个例子就不可行了;
# 如果想连接多个变量或者是字符串文本,可以使用+
# 字符串也可以被截取(检索);第一个字符索引为0;
# Python没有单独的字符类型,一个字符就是一个简单的长度为1的字符串;
# 索引也可以是负数,这也将导致从右边开始计算;注意-0就是0
word = "Python"
print word[0]
print word[-1]
# 除了索引,还支持切片;索引用于获取单个字符,切片让你获得一个子字符串;
print word[0:2]
print word[:2]# 省略第一个索引值默认0
print word[2:]# 省略第二个索引值默认为切片的字符串大小
print word[:2] + word[2:]
# 切片的范围包含起始字符,不包含末尾字符;
# 理解索引和切片的工作方式:
# P y t h o n
# 0 1 2 3 4 5 6
# -6 -5 -4 -3 -2 -1
print word[-6:5]
print word[0:5]
# 试图使用太大的索引会导致错误
# Python能够优雅地处理那些没有意义的切片索引:
# 切片中过大的索引值会被替换为字符串的实际长度;
# 切片中上边界>下边界时返回空字符串;
# Python字符串是不可以被修改的,试图赋值给字符串索引的位置会导致错误;
# 如果你需要一个不同的字符串,你应该创建一个新的;
# 内置函数len()返回字符串的长度
print len(word)
# 旧的字符串格式化操作,是在%操作符的左操作数时调用;
# 3.1.3 关于Unicode
# 从Python2.0开始,可以使用Unicode对象存储文本数据类型;用来存储和维护Unicode数据;
# 与现有的字符串对象有良好的集成,必要时提供自动转换
# Unicode的先进之处在于为每一种现代或古代使用的文字系统中出现的每一个字符都提供了统一的序列号;
print "花 强"
print u"花 强"
# 引号前的'u'表示这会创建一个Unicode字符串;
# 和普通字符一样,Unicode字符串也有原始字符;可以在引号前加'ur'
# 如果你需要大量输入反斜杠,原始模式很有用,这在正则表达式中几乎是必须的;
print str(u"abc")#字符串转换
# print str(u"啊喔阿") # 会报错 这几个Unicode字符不在ASCII的编码范围
print u"啊喔阿"
print u"啊喔阿".encode('utf-8')
# 为了将一个Unicode字符串转换为一个使用特定编码的8位字符串,Unicode对象提供一个encode()方法,它接收编码名作为参数;编码名应该小写;
# 如果有一个其他编码的数据,希望可以从中生成一个Unicode字符串,你可以使用unicode函数,它接收编码名作为第二个参数;
# 3.1.4 列表
# Python中最为通用的复合数据类型就是list(列表)
# 他可以写作中括号之间的一列逗号分隔的值;值得注意的是 列表的元素不必是同一类型
squares = [1,2,3,4,5,6]
print squares
print ['a',"b",1,2]
# 就像字符串一样,列表也可以被索引和切片:
print squares[0]
print squares[1:3]
print squares[-3:]
# 所有的切片操作都会返回一个包含请求的元素的新列表
# 列表也支持+连接这样的操作:
print squares + ['h','e','l','l','o']
# 与字符串的不可变不一样的是,列表允许修改元素
cubes = [2**0,2**1,2**2,2**3,2**4]
cubes[1] = 4
print cubes
# 可以使用append()方法,在列表的末尾添加新的元素
cubes.append(32)
print cubes
# 内置函数len()同样适用于列表
print len(cubes)
# 列表允许嵌套
a_list = ['a','b','c']
b_list = [1,2,3]
n_list = [a_list,b_list]
print n_list[1][1]
# 3.2 编程的第一步
# 生成一个菲波那切数列
a_ep,b_ep = 0,1
while b_ep < 10:
print b_ep
a_ep,b_ep = b_ep,a_ep + b_ep
# 和C语言中类似,0表示false(列表为空也是false),非0为true
# 缩进是Python的组织语法,Python不提供集成的行编辑功能;
# 字符串打印的时候不用引号包围,每两个子项之间插入空格;
print "hahaha",1
a_ep,b_ep = 0,1
while b_ep < 10:
print b_ep,
a_ep,b_ep = b_ep,a_ep + b_ep
# 用一个逗号结尾就可以禁止输出换行
# 注意:
# **的优先级高于加减乘除等基本运算,所以请使用括号,避免歧义;
# \n在''和""中意义是一样的,''和""的唯一区别在于''中你不需要转义",但必须转义\'
# -*- coding:utf-8 -*-
# 四、深入Python流程控制
# 除了前面用过的while,Python还从其他语言借鉴了一些流程控制功能,但有所改变
# 4.1 if语句
# x = int(raw_input("Please enter an integer:"))
x = 5
if x > 0:
print x
elif x <= 0:
print 0
else:
print "default"
# 可能会有0-多个elif部分,else是可选的;
# 关键字elif是else if的缩写,这个可以有效的避免过深的缩进
# if...elif...elif...序列用于代替其他语言中的switch或case语句
# 4.2 for语句
# Python中的for循环和其他语言略有不同;
# Python中的for语句依据任意序列(链表或字符串)中的子项,按它们在序列中的顺序来进行迭代;
words =['cat','dog','elephont']
for w in words:
print w,len(w)
# 在迭代过程中修改序列是不安全的(只有在链表这样的可变序列的遍历过程中才会出现这样的情况);
# 如果你要修改你迭代的序列,可以迭代它的副本;使用切割标识可以很方便的做到这一点;
for w in words[:]:
if len(w) > 4:
words.insert(0,w)
print words
# 这样 我们通过对副本的判断 实现了对原列表的修改
# 4.3 range()函数
# 如果你需要一个数值序列,内置函数range()会很方便,他会生成一个等差级数列表:
print range(10)
# range(10)是一个包含10个值的链表,他用链表的索引值填充了这个长度为10的列表;
# 所生成的链表中不包括范围中的结束值
# 也可以让range操作从另一个数值开始,到指定的数值;
# 或者可以指定一个不同的步进值;(步进值可以是负数)
print range(10,100,10)
# 我们可以这样迭代列表的索引值
for i in range(len(words)):
print i,words[i]
# 不过这种场合更多见的是使用enumerate();
# 4.4 break和continue语句,以及循环中的else子句
# break语句和C中类似,用于跳出最近的一级for或while循环;
# 循环也可以有一个else子句:它会在循环迭代完 整个列表(对于for)后执行;这种情况的else子句是属于for循环的;
# 如果for中使用了break,则else不会执行;
# continue语句是从C语言中借鉴过来的,他表示循环继续执行下一次迭代
for num in range(2,10):
if num % 2 == 0:
continue
print num,"even number!"
# 4.5 pass语句
# pass语句什么也不做,他用在那些语法上必须要有什么的、但程序什么也不做的场合;
# while True:
# pass
# 这通常用于创建最小结构的类
# class MyEmptyClass:
# pass
# 另一方面,pass可以在创建新代码时用来做函数或控制体的占位符;可以让你在更抽象的级别上思考;pass可以默默地被忽视;
# def initlog(* args):
# pass
# 4.6 定义函数
# 我们定义一个函数:用来生成任意上界的斐波那契数列
def fib(n):
"""Do Fibonacci series
para number of upfloor:n
"""
print "Print a Fibonacci series up to",n
a,b = 0,1
while a < n:
print a,#,号可以去掉换行
a,b = b,a+b
fib(10)
# 关键字def引入了一个函数定义:在其后必须跟着 函数名 和 包括形式参数的圆括号;
# 函数体从下一行开始,必须是缩进的;
# 函数体的第一行语句可以是可选的字符串文本——即函数的文档字符串(docstring);
# 有些在线工具通过docstring可以自动生成在线的可打印的文档;
# 函数调用会为函数局部变量生成一个新的符号表;
# 确切的来说,所有函数中的变量赋值都是将值存储在局部符号表,变量引用首先在局部符号表中查找,然后是包含函数的局部符号表;
# 然后是全局符号表,最后是内置名字表;
# 因此,全局变量不能在函数中直接赋值(除非用global语句命名),尽管他们可以被这样用;
# 函数引用的实际参数在函数调用时引入局部符号表,因此,实参总是传值调用(这里的值总是一个对象的引用,而非对象的值);
# 一个函数被另一个函数调用时,一个新的饿局部符号表在调用过程中被创建
# 一个函数定义会在当前符号表内引用函数名;函数名指代的值(即函数体)存在一个被Python解释器认定为用户自定义函数的类型;
# 这个值可以赋予其他的名字(即变量名),然后它也可以被当做函数使用;
f = fib
print f(10)
# 我们看到fib()函数并没有return语句;
# 事实上,没有return语句的函数确实会返回一个值,虽然是一个相当令人厌恶的值——None(内建的一个名称);
# 我们使用print就可以看到这个值的输出;
print f(0)
# 我们修改下之前的代码,以返回一个包含斐波那契数列的值的链表,而不是打印他
def fibs(n):
"""Return a list containing the Fibonacci series up to N."""
result = []
a,b = 0,1
while a < n:
result.append(a)
a,b = b,a+b
return result
f100 = fibs(100)
print f100
# return语句从函数中返回一个值,不带return则返回None,过程结束后也会返回None;
# append是链表对象result的一个方法;作用类似result = result + 【b】,不过效率更高;
# 4.7 深入Python函数定义
# 在Python中,你可以定义包含若干参数的函数;
# 这里有三种可用的形式,也可以混合使用
# 4.7.1 默认参数值
# 最常用的一种形式是为一个或多个参数指定默认值;
# 这会创建一个可以使用比定义时允许的参数更少的参数调用的函数;
def ask_ok(prompt,retries = 4,complaint = 'Yes or No,please!'):
while True:
ok = raw_input(prompt)
if ok in ('y','ye','yes'):
return True
if ok in ('n','no','nop','nope'):
return False
retries = retries - 1
if retries < 0:
raise IOError('refuse input user')
print complaint
# ask_ok("Do you want to quit? ")
# ask_ok("Do you want to quit? ",2)
# ask_ok("Do you want to quit? ",2,"Come on,only yes or no!")
# 这里介绍下in关键字,他测定序列中是否包含某个确定的值;
# 值得注意的是:默认值在函数定义作用域被解析(下面示例将打印5)
i= 5
def f_i(arg = i):
print arg
i = 6
f_i()
# 重要警告:
# 默认值只被赋值一次;这使得当默认值是可变对象时会有所不同;
# 如列表、字典、大多数类的实例等;
# 下面的这个函数在后续调用中会累计(前面)传给他的参数;
def f_list(a,L = []):
L.append(a)
return L
f_list(1)
f_list(2)
f_list(3)
print f_list(4)
# 如果你不想在随后的调用中共享默认值,你可以这样修改
def f_list_n(a,L = None):
if L is None:
L = []
L.append(a)
return L
# 4.7.2 关键字参数
# 函数可以通过关键字参数的形式来调用,形如keyword = value;
def parrot(voltage,state = 'a stiff',action = 'voom',type = 'Norwegian blue!'):
print '1',voltage,'2',state,'3',action,'4',type
parrot(1000)
parrot(voltage=1000)
parrot('a','b','c')
parrot('a',state=1000)
# 因为非默认值参数的存在,不可以不传参数
# 调用时,在一个关键字参数后边的入参也必须是关键参数的方式
# 在函数调用中,关键字的参数必须跟随在位置参数的后面;
# 传递的所有关键字参数必须与参数接收的某个参数相匹配;他们的顺序并不重要;
# 任何参数都不可以多次赋值;
# 引入一个形如 **name 的参数:
# 它接收一个字典,该字典包含了所有未出现在形式参数列表中的关键字参数;
# 这里还会组合使用一个形如 *name的形式参数;
# 包含了所有没有出现在形式参数列表中的参数值;
# *name必须在**name之前出现;
def cheeseshop(kind , *arguments,**keywordDic):
for arg in arguments:
print arg
print "-"*40
keys = sorted(keywordDic.keys())
for key in keys:
print key,':',keywordDic[key]
return
cheeseshop("Flower","a","b","c",k_a = "k_a_value",k_b = "k_b_value",k_c = "k_c_value")
# 这里我们对字典keys进行了排序,使输出按照key有序;
# 4.7.3 可变参数列表
# 最后一个最不常用的选择是让函数调用可变个数的参数;
# 这些参数被包装进一个元组(和Swift的语法如此相似!)
# 在这些可变参数之前,可能有0-多个普通的参数;
# 就是前面我们引入的形如*name形式的参数,对应的就是一个元组;(这里不再举例)
# 4.7.4 参数列表的分拆
# 另一种相反的情况:当你要传递的参数已经是一个列表,但要调用的函数却接受分开一个个的参数值;
# 这时候需要把已有的列表拆开来;
# 例如内建函数range()需要独立的start,stop参数;你可以在调用函数时加一个*操作符来自动把参数列表拆开;
print list(range(3,6))
args = [3,6]
print range(*args)
# 以同样的方式,可以使用**操作符分拆关键字参数为字典;
def parrots(voltage,state = 'abc',action = 'def'):
print voltage,state,action
d = {"voltage":"v_voltage","state":"v_state","action":"v_action"}
parrots(**d)
# 4.7.5 Lambda形式
# 函数式编程:通过lambda关键字,可以创建短小的匿名函数;
lambda a,b:a+b
# lambda形式可以用于任何需要的函数对象;
# 处于语法限制,他们只能有一个单独的表达式;
# 语义上,他们只是普通函数定义中的一个语法技巧;
# 类似嵌套函数定义,lambda形式可以从外部作用域引用变量;
def make_incrementor(n):
return lambda x:x+n
f = make_incrementor(40)
print f(0)
print f(1)
print f(2)
# 使用lambda表达式除了能返回一个函数,还可以将一个函数最为参数
pairs = [(1,"one"),(2,"two"),(3,"three"),]
pairs.sort(key=(lambda pair:pair[1]))#传入的key是一个函数
# 这个函数的入参是排序过程中遍历的每一个元组 返回的是这个元组的索引1位置的值
print pairs
# 4.7.6 文档字符串
# 这里介绍下文档字符串的概念和格式
def my_func():
"""第一行是关于对象用途的简介,简短期间,首字母大写,./。结尾
第二行空出来
后续是一或多段描述对象的调用约定、边界效应等"""
# Python解释器不会从多行的文档字符串中去除缩进,所以必要时需要自己清除;
# 第一行之后的第一个非空行决定了整个文档的缩进格式;
# 4.8 插曲:编码风格
# 1.使用4个空格缩进,而非Tab;
# 2.折行以确保不会超过79个字符;
# 3.使用空行分隔函数和类,以及函数中的大块代码;
# 4.使用文档字符串
# 5.把空格放到操作符两边,以及逗号后面,但是括号里侧不加空格;a = f(1, 2) + g(3, 4)
# 6.统一函数和类的命名;
# 推荐类名使用驼峰命名;
# 函数和方法名用小写_和_下划线;
# 总是使用self作为方法的一个参数;
# 7.不要使用花哨的编码,默认情况utf-8,甚至普通的ASCII总是工作的最好;
# 同样也不要使用非ASCII字符的标识符;
# -*- coding:utf-8 -*-
# 五、数据结构
# 5.1 关于列表更多的内容
# 列表对象方法
list_a = ['a','b','c']
list_a.append(1)#添加一个元素至列表末尾
list_a.extend(['m','n'])#将一个列表中的数据添加到另一个列表中
list_a.insert(2,"flower")#在指定位置插入元素
list_a.remove("flower")#删除链表中值为"flower"的第一个元素,如果没有这样的元素,就会返回一个错误;
list_a.pop()#从链表的指定位置list.pop([i])删除元素,并将其返回;如果没有指定索引,默认返回最后一个元素;
# 元素随机从链表中被删除;
# 方法中i两边的[]表示这个参数是可选的,不是要你真的输入一对方括号;
list_a.index('m')#返回链表中第一个值为'm'元素的索引,如果没有则会返回一个错误;
list_a.count('n')#返回'n'在链表中出现的次数;
list_a.sort()#对链表中的元素就地进行排序,参数可以指定自定义的排序方法
list_a.reverse()#就地倒排链表中元素
print list_a
# 像insert,remove或者sort这样的修改列表的方法没有打印返回值——他们的返回值是None;
# 在Python中对所有可变的数据类型这是统一的设计原则;
# 5.1.1 把链表当做堆栈使用
stack = [3,4,5]
stack.append(6)#入栈
stack.pop()#出栈
# 5.1.2 把链表当做队列使用
# 队列有先进先出的特点,列表这样用效率并不高;
# 相对来说从列表末尾添加和弹出很快;在头部插入和弹出很慢,因为为了一个元素,要移动整个列表中的所有元素;
# 要实现队列,使用collections.deque,他为在首尾两端快速插入和删除而设计;
from collections import deque
queue = deque(['a','b','c'])
queue.append('Fl')
print queue
queue.popleft()
print queue
# 5.1.3 函数式编程工具
# 对于链表来讲,有三个非常有用的内置函数:filter(),map()以及redece();(关联Swift中的相关语法)
# filter(func,sequence):过滤
# 返回一个序列sequence,包含给定序列中所有调用传入函数后返回值为true的元素;
def f_test(x):
return x % 3 == 0 and x % 5 == 0
print filter(f_test,range(5,25))
# 上述示例会返回既能被3、也能被5整除的原序列中的元素所组成的序列;
# map(func,sequence):扩展
# 为每一个元素依次调用func并将返回值组成一个链表返回;
def f_test_cube(x):
return x**2 * x
print map(f_test_cube,range(1,10))
def f_test_add(x, y):
return x + y
print map(f_test_add,range(8),range(8,16))
# 也可以传入多个序列,函数也必须要有对应数量的参数;
# reduce(func,sequence):展平
# reduce返回一个单值,它是这样的结构:首先以序列的前两个元素调用函数func,再以返回值和第三个参数调用,依次执行下去;
# 计算1-10的整数之和
print reduce(f_test_add,range(1,11))
# 由于合计数值是一个通用的需求,早已有内置的sum(sequence)函数,很好用;
# 5.1.4 列表推导式
# 列表推导式为从序列中创建列表提供了一个简单的方法
# 举个简单的例子
squares = [x**2 for x in range(10)]
print 'squares',squares
# 这样我们就很简洁的创建了一个列表,否则的话 我们可能需要for循环,append元素;
# 我们通过lambda表达式也可以实现,但上边的方式更简洁
squares_1 = map(lambda x:x**2,range(10))
print 'squares_1',squares_1
# 再举一个 使用列表推导式 生成一个元组列表的例子
print [(x,y) for x in range(5) for y in range(4) if x != y]
# 值得注意的是上面两个方法中的for和if语句的顺序;因为要得到元素所以需要对x,y加上括号;
# 列表推导式可使用复杂的表达式和嵌套函数
from math import pi
print [str(round(pi,i)) for i in range(1,6)]
# 5.1.4.1 嵌套的列表推导式
# 列表推导式可以嵌套
matris = [
[1,2,3,4],
[5,6,7,8],
[9,10,11,12]
]
print matris
# 现在想交换行和列,就可以通过嵌套的列表推导式
print [[row[i] for row in matris] for i in range(4)]
# 转换成我们传统的多层for循环的话 最内层的for循环 对应 列表推导式最近的for循环;
# 实际中,你应该更喜欢使用内置函数组成复杂流程语句;对此情况zip()函数将会做的更好;
# 内置的zip()函数实现了我们之前说的交换行列的需求;
print list(zip(*matris))
# 5.2 del语句
# del语句:可以从列表中按照指定的索引而不是值来删除一个子项;
# 语句del可以从列表中删除切片或清空整个列表
list_a = [1,2,3,4,5,6,7,8]
del list_a[0]
print list_a
del list_a[1:4]
print list_a
del list_a[:]
print list_a
# del也可以删除整个变量
del list_a
# 知道再次赋值,否则引用list_a将会报错,提示not defined;后续我们还会看见del的其他用法;
# 5.3 元组和序列
# 我们知道链表和字符串有很多通用的属性,例如索引和切割操作——它们都是序列类型
# Sequence Type-str,unicode,list,tuple,bytearray,buffer,xrange
# 这里先介绍下其中的一个标准数据类型:元组;
# 元组输出的时候总是有括号,以表达正确的嵌套结构;输入时经常括号也是必须的;
# 元组和链表不同的是,元组是不可变的;
# 元组通常包含不同类型的元素,列表的元素通常是相同类型的;
# 一个空的括号可以创建一个空元组;
# 要创建一个单元素 元组,可以在值后面跟一个逗号(在括号中的单独一个值不够明确,不可以这样做);
empty = ()
tuple_1 = ('tuple')
tuple_2 = 'tuple',
tuple_3 = 'tuple1','tuple2','tuple3'
print len(empty)
print len(tuple_1)#这就不是一个元组!!!
print len(tuple_2)
print len(tuple_3)
# 可以通过tuple_3赋值的逆操作将元组的值取出
x_t_1, x_t_2, x_t_3 = tuple_3
print x_t_1,x_t_2,x_t_3
# 实际上,这个调用的等号右边可以是任何线性序列——称之为 序列拆封 最为恰当;
# 序列拆封 要求左侧的变量数目与序列的元素个数相同;
# 值得注意的是,可变参数其实就是元组封装和序列拆封的一个结合;
# 5.4 集合
# Python还包含了一个数据类型——集合(set)
# 集合是一个无需不重复元素的集;基本功能包括关系测试和消除重复元素;
# 集合对象还支持union(联合),intersection(交),different(差)和sysmmetric difference(对称差集)等数学运算;
# 大括号或set()函数可以用来创建集合;
# 值得注意的是要想创建空的集合必须使用set();{}是用来创建空字典的;
basket = ['apple','orange','apple','pear']
fruit = set(basket)
print fruit
print 'apple' in fruit
set_a = set('abcde')
set_b = set('cdefg')
print set_a - set_b
print set_a | set_b
print set_a & set_b
print set_a ^ set_b
# 类似列表推导式,这里有一种集合推导式语法;
print {x for x in 'abcde' if x not in 'abc'}
# 5.5 字典
# 字典:也是一个Python非常有用的内建数据类型;
# 字典在某些语言中可能称为联合内存(associative memories)或联合数组(associative arrays);
# 序列是以连续的整数作为索引,与此不同的是,字典以关键字为索引,关键字可以是任何不可变类型,通常用字符串或数值;
# 如果元组中只包含字符串和数字,他也可以作为关键字,如果它直接或间接的包含了可变对象,就不能当做关键字;
# 不能有链表做关键字,因为链表可以改变;
# 理解字典的最佳方式是把它看做无序的键:值对集合(key:value);
# key不相同,使用{}创建空字典;
# 初始化链表时,在大括号内放置一组逗号分割的 键:值 对,这也是字典的输出的方法;
# 字典的主要操作是依据键来存储和析取值;也可以用del来删除键:值对;
# 对一个字典执行keys()将返回一个字典中所有关键字组成的无序列表,可以再使用sorted()排序;
# 使用in关键字可以检查字典中是否存在某个关键字;
dic_tel= {'a':11,'b':12}
dic_tel['c'] = 13
print dic_tel
del dic_tel['a']
print dic_tel
print 'b' in dic_tel
# dict()构造函数可以直接从key-value对(元组s)中创建字典:
print dict([('A',65),('B',66)])
# 字典推导式可以从任意的键值表达式中创建字典
print {x:x**2 for x in (2,4,6)}
# 如果关键字都是简单的字符串,有时通过 关键字参数 指定key-value更方便
print dict(sap = 1, sbp = 2, scp = 3)
# 5.6 循环技巧
# 在序列中循环时,索引位置和对应值可以使用enumerate()函数同时得到
for i, v in enumerate(['a','b','c']):
print i,v
# 同时循环两个或更多序列时,可以使用zip()整体打包
questions = ['name','quest','answer']
answers = ['labd','scape','blue']
for q, a in zip(questions, answers):
print q, a
print 'question is {0},answer is {1}'.format(q, a)
# 需要逆向循环序列的话,先正向定位序列,然后调用reversed()函数;
for i in reversed(range(1,10,2)):
print (i)
# 要按排序后的顺序循环序列的话,使用sorted()函数,它不改动原序列,而是生成一个新的已排序的序列;
for f in sorted(set(basket)):
print f
# 变量字典时,使用iteritems()方法同时得到键和对应的值;
knights = {'a':'white','b':'blue'}
for k, v in knights.iteritems():
print k, v
# 如果需要在循环内部修改正在遍历的序列,可以使用切片制作副本
words = ['cat','dog']
for w in words[:]:
words.insert(0,w)
print words
# 5.7 深入条件控制
# while和if语句中使用的条件不仅可以使用比较,而且可以包含任意的操作;
# 比较操作符in和not in用来判断值是否在一个区间之内;
# 操作符is和is not 比较两个对象是否相同;这只和诸如链表这样的可变对象有关;
# 所有的比较操作符具有相同的优先级,低于所有的数值操作;
# 比较操作可以传递,例如a < b == c 判断的是a<b并且b==c
# 比较操作可以通过逻辑操作符and和or组合;比较的结果可以用not取反义;not优先级高于or;
# 逻辑运算符and和or也被称为短路运算符:从左向右解析,一旦确定结果将停止;
# 可以把比较或其他逻辑表达式的返回值赋给一个变量;
string1, string2, string3 = '','T','H'
non_null = string1 or string2 or string3
print non_null# T
# 值得注意的是 Python和C不同,在表达式内部不能赋值;这也避免了使用==误用=的问题;
# 5.8 比较序列和其他类型
# 序列对象可以与相同类型的其他对象进行比较;
# 比较是按照字典序进行;如果两个元素本身就是同样类型的序列,就递归字典序比较;
# 如果两个序列的所有子项都相等,就认为序列相等;
print (1,2) < (1,2,3)
print 'ABC' < 'C' < 'P'
print (1,2) < (1,3)
print (1) < (1,-1)
print (1,2,('a'),4) < (1,2,('b'))
# 比较不同类型的对象虽然合法 但是不合理
# -*- coding:utf-8 -*-
# 六、模块
# 在我们编写大程序时,为准备解释器输入使用一个文本编辑器会更好;
# 并以那个文件代替输入执行——这就是传说中的脚本;
# 我们可已将程序分割,更加利于维护;
# 为此,Python提供了一个方法可以从文件中获取定义,在脚本或解释器的一个交互式实例中使用;
# 这样的文件被称为——模块;
# 模块中的定义可以导入到另一个模块或主模块中;
# 模块是包括Python定义和声明的文件;文件名就是模块名加上.py后缀;
# 模块的模块名可以由全局变量 __name__ 得到;
import Py2_6_fibo
# 我们创建了一个模块,使用import命令导入到当前模块;
# 这样做不会直接把fibo中的函数导入当前的语义表;他只是引入了模块名fibo;
# 你可以通过模块名按如下方式访问这个函数;
print Py2_6_fibo.fib2(1000)
print Py2_6_fibo.__name__
# 如果打算频繁的使用一个函数,可以将它赋值给一个本地变量
fibo = Py2_6_fibo.fib2
print fibo(100)
# 6.1 深入模块
# 除了包含函数定义外,模块也可以包含可执行语句;
# 这些语句一般用来初始化模块;他们仅在第一次被导入的地方执行一次;
# 每个模块都有自己的私有的符号表,被模块内所有的函数定义作为全局符号表使用;
# 因此,模块的作者可以在模块内部使用全局变量,而无需担心他与某个用户的全局变量意外冲突;
# 模块可以导入其他模块,一个好的习惯是将所有的import语句放在模块的开始(或者是脚本);这并非强制;
# 被导入的模块名会放入当前模块的全局符号表中;
# import语句的一个变体直接从被导入的模块中导入命名到本模块的语义表中;
#Py2_6_fibo.py(如下)
# -*- coding:utf-8 -*-
def fib(n):
a, b = 0, 1
while b < n:
print b
a, b = b, a + b
def fib2(n):
result = []
a, b = 0, 1
while b < n:
result.append(b)
a, b = b, a + b
return result
# if __name__ == "__main__":
# import sys
# fib(int(sys.argv[1]))
from Py2_6_fibo import fib, fib2
fib(10)
# 甚至可以使用 import * 导入from模块中的所有定义(这样可以导入所有除了以下划线_开头的命名);
# 这样做的好处是 不会从局部语义表中导入模块名;
# 并不推荐使用import *的方式,因为这样会使代码比较难读;
# 出于性能考虑,每个模块在每个解释器会话中只导入一遍;因此,如果你修改了你的模块,需要重启解释器或者
# 如果想交互式的测试一个模块,可以用reload()重新加载;
reload(Py2_6_fibo)
# 6.1.1 作为脚本来执行模块
# 我们在模块后添加代码如下
# if __name__ == "__main__":
# import sys
# fib(int(sys.argv[1]))
# 并以如下方式运行该Python模块,模块中的代码就会运行
# python Py2_6_fibo.py 50
# 此时,模块会被作为'main'文件执行,__name__也被这是成"__main__";
# 如果模块使用import被导入,这段代码就不会执行了;
# 这通常为模块提供一个便于测试的用户接口(将模块作为脚本执行测试需求);
# 6.1.2 模块的搜索路径
# 导入一个叫spam的模块时,解释器先在当前目录中搜索名为spam.py的文件;
# 如果没有,会接着到sys.path变量中给出的目录列表中查找;
# sys.path初始值来自如下:
# 1.输出脚本的目录(当前目录);
# 2.环境变量PYTHONPATH表示的目录列表中搜索;(PATH就是一系列目录名的列表)
# 3.Python默认的安装路径中搜索;
# 需要注意的是,由于这些目录中包含有搜索路径中运行的脚本,所以这些脚本不应该和标准模块重名;
# 否则在导入模块时Python会尝试把这些脚本当做模块来加载,这通常会引发错误;
# 6.1.3 "编译的"Python文件
# 对于大量引入标准模块的短程序,有一个提高启动速度的重要方法:
# 在spam.py所在的目录下存在一个名为spam.pyc的文件,他会被视为spam模块的预编译(二进制编译)版本;
# 用于创建spam.pyc的这一版spam.py的修改时间记录在spam.pyc文件中,如果两者不匹配,.pyc文件就会被忽略;
# C中的静态库就是这个套路,很多编程语言都有类似的策略;
# 通常不需要为创建.pyc文件做什么;一旦spam.py文件编译成功,就会尝试生成对应版本的.pyc文件;
# spam.pyc文件的内容是平台独立的,所以Python模块目录可以在不同架构的机器之间共享;
# 部分高级技巧:
# 1.以-O参数调用Python解释器时,会编译生成优化代码并保存在.pyo文件中;(删除断言assert)
# 2.向Python解释器传递两个-O(即-OO)会执行完全优化的二进制优化编译,这偶尔也会生成错误的程序;(删除__doc__造成的)
# 生成的是更加紧凑的.pyo文件;
# 3.来自.pyo或.pyc文件中的程序不会比来自.py文件的运行更快;只是加载的时候更快一点;
# 4.通过脚本名在命令行中运行脚本时,不会将为该脚本创建二进制代码写入.pyc或.pyo文件;
# 当然,把脚本的主要代码移进一个模块里,然后用一个小的启动脚本导入这个模块,就可以提高脚本的启动速度;
# 也可以直接在命令行中指定一个.pyc或.pyo文件;
# 5.对于同一个模块,可以只有.pyc/.pyo文件,而没有.py文件,这样可以打包发布比较难于逆向工程的Python代码;
# 6plieall模块可以为指定目录中的所有模块创建.pyc文件(或者使用-O参数创建.pyo文件);
# 6.2 标准模块
# Python带有一个标准模块库,并发布有独立文档——Python库参考手册;
# 有一些模块内置于解释器之中。这些操作的访问接口不是语言内核的一部分,但是已经内置于解释器了;
# 这既提高了效率,也是给系统调用等操作系统原生访问提供接口;
# 这类模块集合是一个依赖底层平台的配置选型;
# 如:winreg模块只在windows系统上才有;sys模块内置于所有的Pyhton解释器;
# 变量sys.ps1 和sys.ps2定义了主提示符和辅助提示符字符串
import sys
# print sys.ps1
# print sys.ps2
# 这两个变量只有在解释器的交互模式下才有意义;
# 变量sys.path是解释器模块搜索路径的字符串列表;它由环境变量PYTHONPATH初始化,可以用标准的列表操作来修改:
# print sys.path.append('/test')
print sys.path
# 6.3 dir()函数
# 内置函数dir()用于按模块名搜索模块定义,他返回一个排好序的字符串类型的存储列表
print dir(Py2_6_fibo)
print dir()
# 无参数调用时,dir()函数返回当前定义的命名列表;
# 注意该列表列出了所有类型的名称:变量,模块,函数,等等;
# dir()不会列出内置函数和变量名;如果需要则需使用标准模块__builtin__;
import __builtin__
print dir(__builtin__)
# 6.4 包
# 包 通常是使用"圆点模块名"的结构化模块命名空间;
# 例如A.B模块表示 包A中的子模块B;
# 使用不同的类库架构(包)可以避免模块之间的命名冲突;
# 可能的包结构举例
print '''
sound/
__init__.py
formats/
__init__.py
wavred.py
wavwrite.py
effects/
__init__.py
echo.py
surround.py
filters/
__init__.py
vocoder.py
equalizer.py
'''
# 当导入这个包时,Python会通过sys.path搜索路径查找包含这个包的子目录;
# 为了让Python将目录当做内容包,目录中必须包含__init__.py文件;
# 这是为了避免一个含有烂俗名字的目录无意中隐藏了稍后在模块搜索路径中出现的有效模块;
# 最简单的,只需要一个空的__init__文件即可;当然他也可以执行包的初始化代码,或者定义稍后介绍的__all__变量;
# 用户可以每次只导入包里的特定模块;
# import sound.formats.wavred
# 这样导入的是 sound.formats.wavred子模块,调用也需要使用sound.formats.wavred;
# 另一种可选的方式:
# from sound.formats import wavred
# 这样加载的是 wavred 子模块,并且可以在没有包前缀的情况下也可以使用;
# 值得注意的是:使用 from package import item 导入时;子项 item可以是一个子模块、包;
# 也可以是包中定义的其它命名,像函数、类或变量;
# import语句首先核对是否包中有这样一个子项,如果没有,就假定它是一个模块,并尝试加载它;
# 如果没有找到它,会引发一个ImportError异常;
# 而使用import sound.formats.wavred这样的语法,前面的子项必须是包,最后的子项可以是包或模块,但不能是前面子项中定义的类、函数或变量;
# 6.4.1 从*导入包
# from package import *
# 这种希望在文件系统中找出包中所有的子模块,然后倒入它们;
# 这可能会花掉很长时间,并且出现期待之外的边界效应;
# 执行from package import *时,如果包中的__init__.py代码定义了名为__all__的列表;
# 就会按照列表中给出的模块名进行导入;
# __all__ = ["wavred","wavwrite"]
# 并不建议使用import *的方式;
# from Package import specific_submodules,除非导入的模块需要使用其他包中的同名子模块,否则这是推荐的写法;
# 6.4.2 保内引用
# 如果包中使用了子包结构,可以按照绝对位置从相邻的包中引入子模块;
# 如sound.filter.vocoder 中需要使用sound.effects包中的echo模块,则可以:
# from sound.effects import echo
# 还可以用.号标明关联上级包;
# from . import echo #当前包 导入其他模块
# from .. import formats #上级包 导入其他包
# from ..filters import equalizer #上级包 子包 导入 子包中的模块
# 需要注意的是显示或隐式相对位置导入都是基于当前模块的命名;
# 因为主模块的名字总是"__main__",Python应用程序的主模块应该总是用绝对导入;
# 6.4.3 多重目录中的包
# 包支持一个特殊的特性:__path__;
# 在包中的__init__.py文件代码执行之前,该变量初始化一个目录名列表;
# 该变量可以修改,作用于包中的子包和模块的搜索功能;
# 这个功能可以用于扩展包中的模块集,不过它不常用;
# -*- coding:utf-8 -*-
# 七、输入和输出
# 一个程序的输出方式可能是人类可读的方式打印,或者写入一个文件供以后使用,我们来讨论几种可能性
# 7.1 格式化输出
# 先说两种大相径庭的输出值方法:表达式语句和print语句;
# 第三种方法是使用文件对象的write()方法,标准文件输出可以参考sys.stdout
# 格式化输出方式:
# 1.自己处理整个字符串,通过使用字符串切割和连接操作可以创建任何你想要的输出形式;
# string类型包含一些将字符串填充到指定列宽度的有用操作,后续讨论;
# 2.使用str.format()方法;
# Python有办法将任意值转为字符串:将它传入repr()或str()函数;
# 函数str()用于将值转化为适合人阅读的形式;
# repr()转化为供阅读器读取的形式(如果没有等价语法,则会发生SyntaxError异常);
# 某对象没有适于人阅读的解释形式的话,两者会返回等同的值;
# 很多类型,诸如 数值 链表 字典 这样的结构,针对各函数都有统一的解读方式;
# 字符串和浮点数,有着独特的解读方式;
s = 'Hello world!\n'
print str(s)
print repr(s)
print str(1.0/7)
print repr(1.0/7)
print repr((1,3,('a','b')))#repr的参数可以是任何Python对象
# 写平方和立方表
for x in range(1,11):
print repr(x).rjust(2),repr(x*x).rjust(3),
print repr(x*x*x).rjust(4)#行占4位 ,号间隔的代码输出是间隔空格
for x in range(1,11):
print '{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x)#行占2、3、4位 间隔空格输出
# str.rjust()方法可以把字符串输出到1列,并通过向左侧填充空格来使其右对齐;
# 类似的方法还有str.ljust()和str.center();
# 这些函数并不改变字符串 而是返回新的 且全显示(格式可能会有错乱)
# 如果想截断显示可以使用【:】切片;
# 还有一个方法,str.zfill()它用于向数值的字符串表达式左侧填充0;
# 该函数可以正确理解正负号;
print '12'.zfill(5)
print '-3.14'.zfill(6)
# 方法str.format()的基本用法如下:
print 'We war falling in {}!'.format('love')
# 大括号和其中的字符会被替换成传入str.format()的参数;
# 大括号中的数值可以指明使用传入参数对象中的哪一个;
print '{1} and {0}'.format('dog','cat')
print '{0} and {1}'.format('dog','cat')
# 如果在str.format()调用时使用关键字参数,可以通过参数名来引用值;
print 'This {food} is {adjective}'.format(food = 'span',adjective = 'delicius')
# 且,定位和关键字参数可以组合使用
# '!s'表示应用str()
# '!r'表示应用repr()
# 他们可以在格式化之前转换值
import math
print 'The value of PI is approxumately {}.'.format(math.pi)
print 'The value of PI is approxumately {!r}.'.format(math.pi)
# 字段名后允许可选的':'和格式指令;
# 这允许对值的格式化加以更深入的控制;
print 'The value of PI is approxumately {:.3f}.'.format(math.pi)
print 'The value of PI is approxumately {0:.2f}.'.format(math.pi)
# 我们还可以格式化字段,通过key操作value
table = {'a':123,'b':456,'c':789}
print 'table is a = {0[a]:d}, b = {0[b]:f}, c = {0[c]}'.format(table)
# 也可以用**标志将这个字典以关键字参数的方式传入
print 'table1 is a = {a:d}, b = {b:.2f}, c = {c}'.format(**table)
# 这种方式与新的内置函数vars()组合使用非常有效;
# 该函数返回包含所有局部变量的字典
# 7.1.1 旧式的字符串格式化
# 操作符%也可以用于字符串格式化;
print '%5.3f %5.2f'%(math.pi, math.pi)
# 7.2 文件读写
# 函数open()返回文件对象,通常的用法需要两个参数:
# open(filename,mode)
f = open('workfile','r+')
print f
# 第一个参数是一个标识文件名的字符串;
# 第二个参数是由有限个字母组成的字符串,描述了文件将会被如何使用;
# 可选的模式有
# 'r'——使文件只读(缺省,默认为此模式)
# 'w'——使文件只写,对于同名文件,该操作使原文件被覆盖
# 'a'——以追加方式打开文件
# 'r+'——以读写方式打开文件
# 在windows平台上,'b'模式以二进制方式打开文件,类似的可能会有'rb','wb','r+b'等组合模式;
# windows平台上文本文件与二进制文件是由区别的,读写文本文件时,行尾会自动添加行结束符;
# 这种后台操作方式对于ASCII文本文件时没有问题的,但对于JPEG或EXE这样的二进制文件就会产生破坏;
# 这些操作时一定要记得以二进制模式打开;(在linux上亦然)
# 7.2.1 文件对象方法
# 读取内容:f.read(size)
# 该方法读取若干数量的数据并以字符串形式返回其内容;size是可选数值,指定字符串长度;
# 如果没有指定size或设置为负数,就会读取并返回整个文件;(当超出机器内存的两倍时,就会出问题)
# 到了文件末尾会返回一个空字符串("");
print f.read(2)
# 读取单独一行:f.readline()
# 从文件中读取单独一行,字符串结尾会自动加上一个换行符(\n)
# 只有当文件最后一行没有以换行符结尾时,这一操作才会被忽略;这样返回值就不会混淆
# 到达文件末尾,readline()会返回空串;
# 返回空行的话,就是'\n',一个只包含换行符的字符串;
print f.readline()
print f.readline()
# f.readlines(sizehint)返回一个列表,其中包含了文件中所有的数据行;
print f.readlines()
# 这个功能通常用于高效读取大型行文件;避免了将整个文件读入内存;(这种操作只返回完整的行)
# 一种替代的方式是 通过遍历文件对象来读取文件行;
# 这是一种内存高效、快捷的方式;
f.close()
f1 = open('workfile1','r+')
for line in f1:
print (line)
# 虽然这种代替方式更简单,但不具备细节控制能力;
# 两种方式的缓存方式不同,别搞混;
# 写入:f.write(string)
# 将字符串写入文件
# print f1.write('flower\n')
# 要想写入非字符串内容,首先需要将它转换为字符串
value = ('the answer',42)
s = str(value)
# f1.write(s)#('the answer', 42)
# f.tell()返回一个整数,代表文件对象在文件中的指针位置;
# 该值计量了自文件开头到指针处的比特数;
# 改变文件对象指针:f.seek(offset,from_what)
# 指针在该操作中从指定的引用位置移动offset比特;
# from_what = 0表示自文件起始处开始;
# 1表示自当前文件指针位置开始;
# 2表示自文件末尾开始;
# from_what可忽略,默认从文件头开始,值为0
f1.close()
f2 = open('workfile2','r+')
# f2.write('0123456789abcdef')
f2.seek(5)
print f2.read(1)
f2.close()
# 在文本文件中(那些没有使用b模式选项打开的文件);
# 只允许使用从文件头开始计算相对位置,使用seek(-3,2)会报异常;
# 当使用完一个文件时,使用f.close()方法可以关闭文件,并释放其占有的全部资源;
# 在调用f.close()之后,试图再次使用文件会自动失败;
# 使用关键字with处理文件对象是个好习惯;
# 它的先进之处在于文件用完之后会自动关闭;发生异常也没关系 相当于try-finally块的简写
# with open('/tmp/workfile','r') as f3:
# read_data = f3.read()
# f3.close()
# 7.2.2 使用json存储结构化数据
# Python允许使用常用的数据交换格式JSON(JavaScript Object Notation);
# 标准模块json可以接受Python数据结构,并转换为字符串表示形式——此过程成为 序列化;
# 从字符串表示形式重新构建数据结构——反序列化;
# 序列化和反序列化的过程:
# 表示该对象的字符串可以存储在文件或数据中,也可以通过网络连接传送给远程的机器;
# 对于一个对象x,你可以这样查看他的JSON字符串形式
import json
x = {'name':'foot'}
x_s = json.dumps(x)
print x_s
# dumps()函数的另一个变体是dump():直接将一个对象序列化到一个文件
json.dump(x,open('workfile4','w'))#文件中有了对应的json字符串
# 重新解码对象
x_o = json.loads(x_s)#从字符串中读取
print x_o
# 如果f是为读取而打开的文件对象
x_o = json.load(open('workfile4','r'))#从json文件中读取
print x_o,type(x_o)
# 这种简单的序列化技术可以处理列表和字典
# 但序列化任意类实例为json需要一点额外的努力;
# json模块的手册对此有详细的描述
# pickle - pickle模块
# 与JSON不同,pickle是一个协议,它允许任意复杂Python对象的序列化;
# 因此,他只能用于Pyhton而不能用来与其他语言编写的应用程序进行通信;
# 默认情况下它是不安全的,如果数据由熟练的攻击者精心设计,反序列化来自一个不受信任源的pickle数据可以执行任意代码;
# -*- coding:utf-8 -*-
# 八、错误和异常
# Python中有两种错误:语法错误和异常(syntax errors 和 exceptions)
# 8.1 语法错误
# 也叫做解析错误;
# 语法分析器指出错误行,并且在检测到错误的位置前面显示一个小箭头;错误一般就在箭头标识的前面;
# 错误会返回文件名和行号;
# 8.2 异常
# 运行期检测到的错误称为 异常;并且程序不会无条件的崩溃;
# 大多数异常都不会被程序处理,我们需要捕捉和处理他们;
# 异常信息通畅作为错误信息的一部分显示出来;
# 如 除错误ZeroDivisionError、命名错误NameError、异常错误TypeError;
# 打印错误信息时,异常的类型作为异常的内置名显示;
# 内置异常均是如此,用户自定义的异常则不一定;
# 标准异常名是内置的标识(没有保留关键字);
# 异常的描述依赖于异常类型
# 8.3 异常处理
# 通过编程处理选择的异常是可行的;
# 举一个例子:我们循环让用户输入一个数值,直到输入内容合法,或中断程序;
# 注意:用户产生的中断会引发一个KeyboardInterrupt异常;
while True:
try:
# x = int(raw_input('Please input a number :'))
break
except ValueError:
print "Try again..."
# try语句的工作方式
# 1.执行try子句;
# 2.若无异常,except子句在try子句执行完之后被忽略;
# 3.如果try子句发生异常,那么该子句的其余部分就会被忽略;
# 4.如果匹配于except关键字后面指定的异常类型,则执行except子句;
# 然后继续执行try-except语句之后的代码;
# 5.如果发生了一个无匹配的异常,则会上传至上一级try语句中;
# 如果最终找不到,会成为一个未处理异常,终止程序,显示提示信息;
# 一个try语句可能包含多个except子句;
# 至多只有一个分支被执行
# 异常处理程序只会处理对应的try子句中发生的异常;
# 一个except子句可以在括号中列出多个异常的名字
import sys
try:
print 'normal'
except (ValueError,KeyboardInterrupt):
print 'error'
except:
print "Unexpected error:",sys.exc_info()[0]#打印异常信息
raise #产生异常 后边可跟异常名
else:
print 'else normal'
# 注意此元组的括号是必须的;
# 最后一个except子句可以省略异常名称,以作为通配符使用;
# 慎用此方法,因为他会轻易隐藏一个实际的错误;
# 可以使用它打印一个错误信息,然后重新抛出异常
# try...except语句可以带一个else子句,该子句只能出现在except子句之后;
# 当try没有抛出异常时,需要执行一些代码 可以使用这个子句
for arg in sys.argv[1:]:
try:
f = open(arg,'r')
except IOError:
print "can not open",arg
else:
print arg,'has',len(f.readlines()),'lines'
f.close()
# 使用else子句比在try子句中附加代码要好;
# 因为这样可以避免try...except意外的截获本来不属于他们保护的那些代码抛出的异常(放在else子句里)
# 发生异常时,可能会有一个附属值,作为异常的参数存在;
# 这个参数是否存在、什么类型,依赖于异常的类型
# 在异常名(列表)之后,可以为except子句指定一个变量;这个变量绑定于一个异常实例;
# 存储在instance.args的参数中;(为方便 异常实例定义了__str__(),这样可以直接访问打印参数而不必引用.args);
# instance.args更推荐;
# 给异常传递一个参数(多个异常传递的是一个元组)
try:
raise Exception('spam','eggs')
except Exception as inst:
print type(inst)
x, y = inst.args
print 'x = ', x, 'y = ', y
# 异常处理器不仅仅处理那些在try子句中立刻发生的异常,也会处理那些try子句中调用的函数内部发生的异常
def this_fails():
x = 1/0
try:
this_fails()
except ZeroDivisionError as detail:
print 'Handing run-time error',detail
# 8.4 抛出异常
# raise语句允许程序员强制抛出一个指定的异常;
# 要抛出的异常又raise的唯一参数标识;它必须是一个异常实例或异常类(继承自Exception的类);
# 如果你需要明确一个异常是否抛出,但不想处理它,raise语句可以让你很简单的重新抛出该异常
try:
raise NameError('Flower')
except NameError:
print "An exception flew by!"
# raise
# 8.5 用户自定义异常
# 在程序中可以通过创建新的异常类型来命名自己的异常;
# 异常类通常应该直接或间接的从Exception类派生;
class MyError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
try:
raise MyError(2*2)
except MyError as e:
print 'My exception occurred, value:', e.value
# 在这个例子中,Exception默认的__init__()被覆盖;
# 新的方式简单的创建value属性;这就替换了原来创建args属性的方式;
# 异常类中可以定义任何其他类中可以定义的东西,但通常为了保持简单,只加入几个属性信息,供异常处理语句句柄提取;
# 如果一个新创建的模块中需要抛出几种不同的错误时,一个通常的做法是为该模块定义一个异常基类;
# 然后针对不同的错误类型派生出对应的异常子类;
# 与标准异常类似:大多数异常的命名 都以'Error'结尾;
# 很多标准模块中都定义了自己的异常,用以报告在他们所定义的函数中可能发生的错误
class FlowerError(Exception):
"""Base class for exceptions in this module."""
pass
class FlowerInputError(FlowerError):
"""Exception raised for errors in the input.
Attributes:
expr -- input expression in which the error occurred
msg -- explanation of the error
"""
def __init__(self, expr, msg):
self.expr = expr
self.msg = msg
class FlowerTransitionError(FlowerError):
"""Raised when an operation attempts a state transition that is not allowed.
Attributes:
prev -- state at beginning of transition
next -- attempted new state
msg -- explanation of why the specific transition is not allowed
"""
def __init__(self, prev, next, msg):
self.prev = prev
self.next = next
self.msg = msg
# 8.6 定义清理行为
# try语句还有另外一个可选的子句——finally,目的在于定义在任何情况下都一定要执行的功能;
def divide(x, y):
try:
result = x / y
except ZeroDivisionError:
print "division error"
else:
print 'result is ',result
finally:
print 'finally play'
divide(2, 1)
divide(2, 0)
# 不管有没有发生异常,finally子句在程序离开try后都一定会被执行;
# 当try中发生了未被捕获的异常,在finally执行之后,它会重新被抛出;
# try语句经由break、continue、return语句退出也一样会执行finally子句;
# 真实的场景中,finally子句会被用于释放外部资源(文件或网络连接之类的),无论他们的使用过程中是否出错;
# 8.7 预定义清理行为
# 有些对象 定义了标准的清理行为,无论对象操作是否成功,不再需要该对象的时候就会起作用;
for line in open('myfile.txt'):
print line
# 这段代码的问题在于代码执行完之后没有立即关闭打开的文件;
# 相比之下,with...as语句使得文件之类的对象总能及时准确地被清理;
with open('myfile.txt') as f:
for line in f:
print line
# 类似上边示例,语句执行后,文件f总会被关闭,即使是在处理文件中的数据时出错也一样;
# 其他对象是否提供了预定义的清理行为则需要查阅文档;
# -*- coding:utf-8 -*-
# 九、类
# Python的类并没有在用户和定义之间设立绝对的屏障,而是依赖于用户不去"强行闯入定义"的优雅;
# 且类中的大多数特性都被保留:
# 1.类继承机制允许多重继承,派生类可以覆盖基类中的任何方法或类;
# 2.可以使用相同的方法名称调用基类的方法;对象可以包含任意数量的私有数据;
# 用C++术语:类中成员都是公共的,成员函数都是虚(virtual)的;
# 方法函数在定义时需要以引用的对象作为第一个参数,调用时则会隐式引用对象;
# 大多数带有特殊语法的内置操作(运算符、下标等)都可以针对类的需要重新定义;
# 9.1 术语相关
# 当一个对象有多个引用的时候,并且引用有不同的名称,我们称这个对象有别名(aliase);
# 9.2 Pyhton作用域和命名空间
# 命名空间 是从命名到对象的映射;
# 一些命名空间的例子:
# 1.内置命名集(向abs()这样的函数、内置异常名);
# 2.模块中的全局命名;
# 3.函数调用的局部命名;
# 莫种意义上 对象的属性集也是一个命名空间;
# 值得注意的是,不同命名空间中的命名没有任何关系;为了不混淆,用户必须以模块名为前缀来引用它们;
# 我们称Python中任何一个.之后的命名为属性;
# 模块对象.属性;
# 模块属性和模块中的全局命名共享同一个命名空间;
# 属性可以是只读或写的;
# 赋值:modname.answer = 42
# 可写属性可以使用del语句删除,del modname.answer;
# 不同的命名空间在不同的时刻创建,有不同的生存期:
# 1.包含内置命名的命名空间 在Pyhton解释器启动时创建,会一直保留;
# 内置命名被包含在一个模块中,它被称作__builtin__
# 2.模块的全局命名在模块定义被读入时创建,通常,模块命名空间也会一直保存到解释器退出;
# 3.由解释器在最高层调用执行的语句,不管它是从脚本文件中读入还是来自交互式输入,都是__main__模块的一部分;
# 他们也有自己的命名空间;
# 当调用函数时,会为他创建一个局部命名空间;
# 并且在函数返回或抛出一个没有在函数内部处理的异常时删除;
# 每个递归调用都有自己的命名空间;
# 作用域就是一个Python程序可以直接访问命名空间的正文区域;
# 每次执行时,至少有三个命名空间可以直接访问的作用域嵌套在一起;
# 1.包含局部命名的使用域在最里面;首先被搜索;其次搜索的是中层的作用域,这里包含了同级的函数;
# 最后搜索最外边的作用域,它包含内置命名;
# 2.最内层作用域:包含局部命名,任意函数包含的都是这种;
# 3.接下来的作用域包含当前模块的全局命名;
# 4.最外层的作用域(最后搜索)是包含内置命名的命名空间;
# 如果一个命名声明为全局的,那么所有的赋值和引用都直接针对包含模块全局命名的中级作用域;
# 另外从外部访问到的所有内层作用域的变量都是只读的;
# 类定义也是局部作用域中的另一个命名空间;
# 作用域决定源程序的意义:
# 一个定义于某模块中的函数的全局作用域是该模块的命名空间;而不是该函数的别名被定义或调用的位置;
# 事实上,所有引入新命名的操作都作用于局部作用域;
# 特别是import语句和函数名将模块名或函数绑定于局部作用域(可以使用global语句将变量引入到全局作用域);
# 9.3 初识类
# 9.3.1 类定义语法
class ClassName:
pass
# 类的定义类似函数定义(def语句),要执行才能生效;
# 你可以把类的定义放到if语句的一个分支,也可以放到一个函数的内部;
# 类中的函数定义通常包括了一个特殊形式的参数列表,用于方法调用约定;
# 进入类定义部分后,会创建一个命名空间 作为局部作用域;
# 所有的赋值成为这个新命名空间的局部变量;特别是函数定义在此绑定了新的命名;
# 类定义完成时(正常退出),就创建了一个类对象;在这里类对象绑定到类定义头部的类名;
# 9.3.2 类对象
# 类对象支持两种操作:属性引用和实例化;
# 类对象创建后,类命名空间中所有的命名都是有效属性名;
class MyClass:
"""A simple example class"""
i = 12345
def f(self):
return 'hello world'
print MyClass.i
print MyClass.f,'方法参数self为空 所以是未绑定的方法'
i = MyClass.i
f = MyClass().f
print i
print f()
MyClass.i = 123
print MyClass.i
print i
# MyClass.i MyClass().f都是有效的属性引用;我们可以通过赋值修改MyClass.i;
# MyClass.__doc__也是一个有效的属性,返回类的文档字符串;
print MyClass.__doc__
# 类的实例化
x = MyClass()
print x.i
x.i = 1
print x.i
print MyClass.i
y = MyClass()
print y.i
y.i = 2
print y.i
print MyClass.i
# 很多类都倾向于将对象创建为有初始状态的;
# 因此类可能会定义一个名为__init__()的特殊方法;
class MyClassTest:
def __init__(self):
self.data = []
# 定义了__init__()方法的话,类的实例化操作会自动为新创建的类实例调用__init__()方法;
x_test= MyClassTest()
print x_test.data
# __init__()方法也可以待参数
class MyClassTest1:
def __init__(self, realpart, imagpart):
self.r = realpart
self.i = imagpart
x_test_1 = MyClassTest1("a","b")
print x_test_1.r, x_test_1.i
# 9.3.3 实例对象
# 实例对象唯一可用的操作就是属性引用;
# 9.3.4 方法对象
# 方法通过右绑定方式调用;x.f()
# x.f是一个方法对象,可以存起来再调用;xf = x.f
# 我们来看前面的MyClass例子:
# 我们在f()的函数定义中指明了一个参数,这个参数我们并没有传,而实际上,Python对于函数调用缺少参数会抛出异常;
# 方法的特别之处在于:实例对象作为函数的第一个参数传给了函数;x.f() <=> MyClass.f(x)
# 以n个参数的列表去调用一个方法,就相当于将方法的对象插入到参数列表的最前面;
# 原理:
# 引用非数据属性的实例属性时,会搜索它的类;
# 如果这个命名确认为一个有效的函数对象类属性,就会将实例对象和函数对象封装进一个抽象对象:这就是方法对象;
# 以一个参数列表调用方法对象时,它被重新拆封,用实例对象和原始的参数列表构造一个新的参数列表;
# 然后函数对象调用这个新的参数列表;
# 9.3.5 类和实例变量
# 类变量用于类的所有实例共享属性和方法;
# 前面MyClass x, y实例的例子,相应的x.i 和 y.i 共享的是MyClass.i;
# 即实例属性共享了类属性的值;
# 同时我们也看到,之后的实例属性可以修改i的值,但并不影响MyClass.i的值;
# 我们重新举一个例子说明:
class Cat:
kind = 'canine'
def __init__(self, name):
self.name = name
c1 = Cat('Fibo')
c2 = Cat('Buddy')
print c1.kind, c1.name
print c2.kind, c2.name
# 但对于一些可变的共享数据可能会有意外的效果;
class Elephont:
tricks = []
def __init__(self, name):
self.name = name
def add_tricks(self, trick):
self.tricks.append(trick)
e1 = Elephont('Fibo')
e2 = Elephont('Buddy')
e1.add_tricks('roll over')
e2.add_tricks('play dead')
print e1.tricks
# 这个类的正确设计应该是
class Dog:
def __init__(self, name):
self.name = name
self.tricks = []
def add_tricks(self, trick):
self.tricks.append(trick)
d1 = Dog('Fibo')
d2 = Dog('Buddy')
d1.add_tricks('roll over')
d2.add_tricks('play dead')
print d1.tricks
print d2.tricks
# 9.4 一些说明
# 数据属性会覆盖同名的方法属性,使用一些约定来减少冲突是明智的;
# 1.大写方法名称的首字母;
# 2.使用唯一的小字符串(也许只是一个下划线)作为数据属性名称的前缀;
# 3.或者方法使用动词,数据属性使用名词;
# Python中不能强制隐藏数据(使用C编写的Python实现可以完全隐藏实现细节,并控制对象的访问,通过C语言扩展Python);
# 客户应该谨慎的使用数据属性,通过践踏数据属性会使那些由方法维护的常量变得混乱;
# 一般,方法的第一个参数被命名为self;这只是一个约定,名称self并没有特殊性(可以有效改善代码可读性)
# 方法可以像引用普通的函数那样引用全局命名;
# 函数定义代码不一定非得定义在类中,也可以将一个函数对象赋值给类中的一个局部变量;
# Function defined outside the class
def func1(self, x, y):
return min(x, x + y)
class ClassF:
f = func1
def func2(self):
return 'Hello world!'
h = func2
# f和h都是类ClassF的属性,引用的都是函数对象,因此它们都会ClassF的实例的方法;
# 通过self参数的方法属性,方法可以调用其他的方法
class Bag:
def __init__(self):
self.data = []
def add(self, x):
self.data.append(x)
def addtwice(self, x):
self.add(x)
self.add(x)
b = Bag()
print b.__class__
# 每个值都有一个类(class),也称为它的类型(type),存储为object.__class__
# 9.5 继承
# 如果一个语言不支持继承,那么类也就没了意义;
# 派生类定义如下:
# class DerivedClassName(BaseClassName):
# 派生类定义的执行过程和基类是一样的;
# 构造派生类,就记住了基类;这在解析属性引用的时候尤其有用;
# 如果类中找不到请求调用的属性,就会搜索基类;如果基类是由别的类派生而来,这个规则会递归的应用上去;
# 派生类可能会覆盖其基类的方法;
# 虚方法:对非虚函数的调用在编译的时候进行了绑定;调用虚方法时,则会在运行时解析该调用;
# 对应运行时决定调用的类型——即动态类型;
# Python中的所有方法本质都是虚方法;
# 调用基类方法:
# BaseClassName.methodname(self, arguments)
# 要注意,只有BaseClassName在同一全局作用域定义或导入时才能这样用;
# Python有两个用于运行时判断的继承关系的函数:
# 1.函数isinstance()用于检查实例类型:如isinstance(obj,int) 判断obj是否是int或其他从int继承来的类型的实例;
# 2.函数issubclass()用于检查类继承:如issubclass(bool, int),为True,因为bool是int的子类;
# 但是issubclass(unicode, str)是False,因为unicode不是str的子类,他们只是共享一个通用的祖先类basestring;
# 9.5.1 多继承
# Pyhton同样有限的支持多继承形式;
# 多继承的类定义形如下例:
# class DerivedClassName(Base1, Base2, Base3):
# 对于旧风格的类,规则是 深度优先+从左到右;
# 即先Base1及其基类,在Base2及其基类 的顺序进行属性名称搜索;
# 对于新风格的类,方法的解析顺序动态变化地支持合作对super()的调用;
# 对于新风格的类,动态调整顺序是必要的,因为菱形关系,至少会有一个父类可以通过多条路径访问到;;
# 例如,新的风格的类,都继承自object;为了防止基类被重复访问,使用动态算法线性化搜索顺序
# 使每个类都按从左到右的顺序被指定,保证每个父类只调用一次;
# 所有这些特性使得设计可靠并且可扩展的多继承类成为可能;
# 9.6 私有变量和类本地引用
# Pyhton中不存在 只能从对象内部访问的私有实例变量;
# 然而,有一种变通的方式应用于大多数Python代码:
# 以一个下划线开头的命名(如_spam)会被处理为API的非公开部分,无论它是一个函数、方法或数据成员;
# 它会被视为一个实现细节,无需公开;
# 一个正当的私有成员用途是:避免子类里定义的命名与之冲突;
# 因此,Python提供了对这种结构的有限支持,称为——name mangling(命名编码);
# 任何形如__spam的标识(前面两条下划线,后面最多一个),被替换为_classname_spam;
# 此语法不关注标识的位置,只要求在类定义内;
# 我们通过一个例子理解下:
class Mapping:
def __init__(self, iterable):
self.items_list = []
self.__update(iterable)
def update(self, iterable):
for item in iterable:
self.items_list.append(item)
__update = update#private copy of original update() method
class MappingSubclass(Mapping):
def update(self, keys, values):
# private new sinature for update()
# but does not break __init__()
for item in zip(keys, values):
self.items_list.append(item)
m = Mapping([2,3])
print m.items_list
m1 = MappingSubclass([2,3])
print m1.items_list
m1.update(['key1','key2'], ['value1', 'value2'])
print m1.items_list
# 需要注意的是 编码规则设计为尽可能的避免冲突,被认作为私有的变量仍然有可能被访问或修改;
# 在一些特定场合就很有用,比如调试的时候;
# 要注意的是 代码传入exex、eval()或execfile()时不考虑所调用的类的类名,视其为当前类;
# 这类似于global语句的效应,已经按字节编译的部分也有同样的限制;这也同样作用于getattr(),setattr()和delattr();
# 就像直接引用__dict__一样;
# 模块对象有一个隐秘的只读对象,名为 __dict__ ,它返回用于实现模块命名空间的字典;
# 命名 __dict__ 是一个属性而非全局命名。
# 显然,使用它违反了命名空间实现的抽象原则,应该被严格限制于调试中。
# 9.7 补充
# 有时,类似C中的结构struct,将一组已命名的数据项绑定在一起;
# 一个空的类定义可以很好的实现;
class Employee:
pass
john = Employee()
john.name = "Flower"
john.salary = 1000
print john.name, john.salary
# 9.8 异常也是类
# 用户自定义异常也可以是类;利用这个机制可以创建可扩展的异常体系;
# 以下是良好总新的、有效的(语义上的)抛出异常形式
# 1. raise Class, instance
# 2. raise instance
# 第一种形式,instance必须是Class或其派生类的一个实例;
# 第二种则是 raise instance.__class__, instance 的简写;
# 抛出一个异常,与except子句列出的类相符合需要 满足 抛出的异常时类或其派生类的子类;
class B:
pass
class C(B):
pass
class D(C):
pass
for c in [B, C, D]:
try:
raise c()
except D:
print 'D'
except C:
print 'C'
except B:
print 'B'
# 简单来说就是 D是B,但B不是D;上述示例如果将except的B/C/D颠倒,打印的就是B B B了;
# 打印一个异常类的错误信息时,先打印类名,然后是一个空格、一个冒号,然后是用内置函数str()将类转换得到的完整字符串;
# 9.9 迭代器
# 我们已经注意到 大多数容器类对象 都可以用for遍历;(序列 元组 字典 字符串 文件。。。)
for key in {'one':1,'tow':2}:
print key
# for语句在容器对象中调用 iter();该函数返回一个定义了next()方法的迭代器对象,他可以在容器中逐一访问元素;
# 没有后续元素时,next()抛出一个StopIteration异常通知for语句循环结束;(和Swift同理)
# 给自己的类添加迭代器行为:
# 定义一个__iter__()方法,使其返回一个带有next()方法的对象;如果这个类定义了next(),那么__iter__()只需要返回self;
class Reverse:
"""Iterator for looping over a sequence backwards."""
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def next(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
rev = Reverse('spam')
# iter(rev)
for char in rev:
print char#实现了字符串的倒序遍历
# 9.10 生成器
# Generator是创建迭代器的简单而强大的工具;
# 他们写起来就像是正规的函数,需要返回数据时使用yield语句;
# 每次next()被调用时,生成器回复它脱离的位置(记忆位置和数据值);
def reverse(data):
for index in range(len(data) - 1, -1, -1):
yield data[index]
for char in reverse('flower'):
print char # 实现了字符串的倒序遍历
# 前一节基于类的迭代器,生成器能实现相同的功能;因为自动创建了__iter__()和 next()方法,生成器很简洁;
# 生成器的关键功能在于两次执行之间,局部变量和执行状态都自动的保存下来;相比类的迭代器都更清晰;
# 生成器 终结时,还会自动抛出StopIteration异常;
# 可见编写一个函数是创建迭代器最简单的方法;
# 9.11 生成器表达式
# 生成器表达式比完整的生成器定义更简洁,但是没有那么多变,而且通常比等价的链表推导式更容易记
print sum(i*i for i in range(10))
xvec = [10, 20 ,30]
yvec = [7, 5, 3]
print sum(x*y for x, y in zip(xvec, yvec))
# unique_words = set(word for line in page for word in line.split())
data = 'golf'
print list(data[i] for i in range(len(data) - 1, -1, -1))
# -*- coding:utf-8 -*-
# 十、Python标准库概览
# 10.1 操作系统接口
# 在学习C语言时,也有类似的操作,即直接可以运行系统命令;
# Python的os模块提供了很多与操作系统交互的函数;
import os
print os.getcwd()#打印当前的工作目录
# os.chdir('/test')改变工作目录
# os.system('mkdir today')执行命令创建路径
# 使用import os风格而非from os import *;
# 这样可以保证随操作系统不同而有所变化的os.open()不会覆盖内置函数open()
# 在使用一些像os这样的大型模块时内置的dir()和help()函数非常有用
# print dir(os)
# print help(os)
# 针对日常的文件和目录管理任务,shutil模块提供了一个易于使用的高级接口
import shutil
# shutil.copyfile('data.db','archive.db')
# shutil.move('/build/executables','installdir')
# 10.2 文件通配符
# glob模块提供了一个函数用于从目录通配符搜索中生成文件列表
import glob
print glob.glob('*.py')#当前目录下所有的py文件列表
# 10.3 命令行参数
# 通过工具脚本经常调用命令行参数;这些命令行参数以链表的形式存储在sys模块的argv变量;
# 如命令行中执行 python demo.py one two three
import sys
print sys.argv#['demo.py', 'one', 'two', 'three'],当然 我们并没有通过命令行运行,所以只会打印出模块名的链表;
# getopt模块使用Unix getopt()函数处理sys.argv;更多的复杂命令行处理有argparse模块提供;
# 10.4 错误输出重定向和程序终止
# sys还有stdin,stdout和stderr属性;(和C语言重定向基本一样)
# sys.stderr.write("Warning!!!!!!")
# 大多数脚本的定向终止都使用sys.exit();
# 10.5 字符串正则匹配
# re 模块为高级字符串处理提供了正则表达式工具;
# 针对复杂的匹配和处理,正则表达式提供了简洁,优化的解决方案;
import re
print re.findall(r'\bf[a-z]*','which foot or hand fell fastest f1')
print re.sub(r'(\b[a-z]+) \1',r'\1','cat in the the hat')
# 当然 如果只是简单的操作,使用字符串的方法更好用
print 'tea for too'.replace('too','two')
# 10.6 数学
# math模块为浮点运算提供了底层C函数库的访问;
import math
print math.cos(math.pi / 4.0)
print math.log(1024, 2)
# random模块提供了生成随机数的工具
import random
print random.choice(['apple','pear','banana'])
print random.sample(xrange(10),10)
print random.random()
print random.randrange(6)
# 10.7 互联网访问
# 有几个模块用于访问互联网以及处理网络通信协议;
# 其中最简单的两个是用于处理从urls接收的数据的 urllib2 以及用于发送电子邮件的 smtplib
import urllib2
for line in urllib2.urlopen('http://192.168.10.70:8888/appapi/v3/platform/getGraphicValideCode'):
line = line.decode('utf-8')
if 'code' in line or '系统异常'.decode('utf-8') in line:
print line
import smtplib
# server = smtplib.SMTP('smtp.exmail.qq', 465)
# server.sendmail('huaqiang@tongqiangaun',
# 'hua_qiang_work@163',
# 'Test smtp')
# server.quit()
# 第二个实例可以在本地运行一个邮件服务器 参数传递'localhost'
# 10.8 日期和时间
# datetime模块 为日期和时间处理同时提供了简单和复杂的方法;
# 支持日期和时间算法的同时,实现的重点放在更有效的处理和格式化输出;
# 该模块还支持时区处理;
from datetime import date
now = date.today()
print now
print date(2003, 12, 1)
print now.strftime('%m-%d-%y. %d %b %Y is a %A on the %d day of %B.')
birthday = date(1991, 05, 06)
age = now - birthday
print age.days
# 10.9 数据压缩
# 以下模块直接支持通用的数据打包和压缩格式:zlib,gzip,bz2,zipfile 以及 tarfile
import zlib
s = b'witch which has which witches wrist watch'
print len(s)
t = zlibpress(s)
print t
print len(t)
print zlib.decompress(t)
print zlib.crc32(s)#循环冗余校验码
# 10.10 性能度量
# Python提供了一个工具:对比同一问题不同方法之间的性能差异;
# 如 使用元组封装和拆封来交换元素和传统方式的对比
from timeit import Timer
print Timer('''a = 1
b = 2
t=a
a=b
b=t''').timeit()
print Timer('''a = 1
b = 2
a, b = b, a''').timeit()
# 显然使用元组的方式更快;
# 相对于timeit的细粒度,profile和pstats模块提供了更大代码块的实践度量工具;
# 10.11 质量控制
# 开发高质量软件的方法之一就是为每一个函数开发测试代码;并且在开发过程中经常进行测试;
# doctest模块 提供了一个工具;
# 扫描模块并根据程序中内嵌的文档字符串执行测试;
# 测试结构如同简单的将它的输出结果剪切并粘贴到文档字符串中;
# 通过用户提供的例子,它发展了文档,允许doctest模块确认代码的结果是否与文档一致;
def average(values):
"""Computes the arithmetic mean of a list of numbers.
>>> print average([20,30,70])
40.0
"""
return sum(values,0.0) / len(values)
import doctest
print doctest.testmod()
# unittest模块不想doctest模块那么容易使用,不过它可以在一个独立的文件里提供一个更全面的测试集;
import unittest
class TestStatisticalFunctions(unittest.TestCase):
def test_average(self):
self.assertEqual(average([20, 30, 70]), 40.0)
self.assertEqual(round(average([1, 5, 7]),1),4.3)
self.assertRaises(ZeroDivisionError, average, [])
self.assertRaises(TypeError, average, 20, 30, 70)
# print unittest.main()#callling from the commend line invokes all tests
# 我们使用终端运行当前模块
# ----------------------------------------------------------------------
# Ran 1 test in 0.000s
#
# OK
# 或者直接在终端 运行脚本unittest.py(如下)
# -*- coding:utf-8 -*-
import unittest
def average(values):
return sum(values,0.0) / len(values)
class TestStatisticalFunctions(unittest.TestCase):
def test_average(self):
self.assertEqual(average([10, 30, 70]), 40.0)
self.assertEqual(round(average([1, 5, 7]),1),4.3)
self.assertRaises(ZeroDivisionError, average, [])
self.assertRaises(TypeError, average, 20, 30, 70)
unittest.main()
# 10.12 "瑞士军刀"
# Python展示了"瑞士军刀"的哲学;这可以通过它更大的包的高级和健壮功能来得到最好的展现;
# xmlrpclib 和 SimpleXMLRPCServer模块让远程过程调用变得轻而易举;
# email包是一个管理邮件信息的库,包括MIME和其他基于RFC2822的信息文档;
# 不同于实际发送和接收信息的smtplib和poplib模块,email包包含一个构造和解析复杂消息结构(包括附件)及实现
# 互联网编码和头协议的完整工具集;
# xml.dom和xml.sax包为流行的信息交换格式提供了强大的支持;
# 同样,csv模块支持在通用数据库格式中直接读写;
# 所以,这些模块和包大大简化了Python应用程序和其他工具之间的数据交换;
# 国际化可由gettext、locale和codecs包支持;
# -*- coding:utf-8 -*-
# 十一、标准库浏览 - Part II
# 第二部分包含了支持专业编程工作所需的更高级的模块;
# 这些模块很少出现在小版本中;
# 11.1 输出格式
# repr模块为大型的或深度嵌套的容器缩写显示提供了repr()函数的一个定制版本;
import repr
print repr.repr(set('seraweurawheuhfaiuehfawue'))
# pprint模块给老手提供了一种解释器可读的方式 深入控制内置和用户自定义对象的打印;
# 当输出超过一行的时候,美化打印(pretty printer)添加断行和标识符,使得数据结构显示的更清晰;
import pprint
t = [[[['black','cyan'],'while',['green','red']],[['magenta','yellow'],'blue']]]
pprint.pprint(t, width=30)
# textwrap模块格式化文本段落以使用设定的屏幕
import textwrap
doc = """awoehfuaweuifhawuefaiwuehfiawuehfawiuehfauwehfawuefh
aweifhauwehfauiwehhaaaawihefiuawhefuawheifuhawiuefhawiuefha
awefhawiuehfuawhefuawhefiuhawuiefhawuefhuawehfuaweiufhhfawue"""
print textwrap.fill(doc, width=40)
# locale模块按访问预定好的国家信息数据库;
# local的格式化函数属性集 提供了一个直接方式以分组格式格式化数字;
import locale
locale.setlocale(locale.LC_ALL)
conv = locale.localeconv()
print conv
x = 1234567.8
print locale.format("%d", x, grouping=True)#我们还可以 以'$1,234,567.80'这样的格式 格式化输出(可以自行查阅)
# 11.2 模板
# string提供了一个灵活多变的模板类Template;
# 使用它最终用户可以简单地进行编辑;以到达定制程序的目的;
# 格式使用$为开头的Python合法标识(数字 字母 下划线)作为占位符;;
# 占位符外边的大括号使他可以和其他的字符不加空格混在一起;
# $$创建一个单独的$
from string import Template
t = Template('${village}folk send $$10 to $cause')
print t.substitute(village = 'Nottingham', cause = 'the ditch fund')
# 当一个占位符在字典或关键字参数中没有被提供时,substitute()方法就会抛出一个 KeyError异常;
# 对于邮件风格的应用程序,用户提供的数据可能并不完整,这时使用safe_substitute()方法可能更合适;
# 如果数据不完整,他就不会改变占位符;
t = Template('Return the $item to $ owner')
d = dict(item = 'unladen swallow')
print t.safe_substitute(d)
# 模板子类可以指定一个自定义分隔符;
# 例如,图像查看器的批量重命名工具可能选择使用%作为占位符,像当前日期、图片序列号或文件格式;
import time, os.path
photofiles = ['img_1011.jpg', 'img_1012.jpg', 'img_1013.jpg']
class BatchRename(Template):
delimiter = '%'
fmt = '(%d-%n%f)'
print fmt
t1 = BatchRename(fmt)
date = time.strftime('%d%b%y')
print t1, date
for i, filename in enumerate(photofiles):
base, ext = os.path.splitext(filename)
newname = t1.substitute(d = date, n = i, f = ext)
print('{0} --> {1}'.format(filename, newname))
# 模板的另一个应用是把多样的输出格式细节 从程序逻辑汇总分类出来;
# 这使得XML文件,纯文本报表和HTML WEB 报表定制模板成为可能;
# 11.3 使用二进制数据记录布局
# struct 模块为使用变长的二进制记录格式提供了 pack()和unpack()函数;
# 下面演示的是 在不使用zipfile模块的情况下 如何迭代一个ZIP文件的头信息;
# 压缩码H和I分别表示2和4字节无符号数字;
# '<'表明他们都是标准大小并且按照little-endian字节排序;
# import struct
# with open('myfile.zip', 'rb') as f:
# data = f.read()
#
# start = 0
# for i in range(3):
# start += 14
# fields = struct.unpack('<IIIHH', data[start:start + 16])
# crs32, comp_size, uncomp_size, filenamesize, extra_size = fields
#
# start += 16
# filename = data[start:start + filenamesize]
# start += filenamesize
# extra = data[start:start + extra_size]
#
# print filename, hex(crs32), comp_size, uncomp_size
# start += extra_size + comp_size
# 11.4 多线程
# 线程是一个分离无顺序依赖关系任务的技术;
# 下面演示高级模块threading如何在主程序运行的同时运行任务;
import threading, zipfile
class AsyncZip(threading.Thread):
def __init__(self, infile, outfile):
threading.Thread.__init__(self)
self.infile = infile
self.outfile = outfile
def run(self):
f = zipfile.ZipFile(self.outfile, 'w', zipfile.ZIP_DEFLATED)
f.write(self.infile)
f.close()
print 'Finish background zip of:', self.infile
background = AsyncZip('myfile', 'myfile.zip')
background.start()
print "The main program continues to run in foreground"
background.join()# Wait for the background task to finish
# Join方法:主线程要调用其他线程,当前子线程需要执行完才能继续后续执行
# 那么在调用之前,可以使用子线程的join方法。
print "Main program waited until background was done"
# 多线程应用程序的主要挑战是协调编程,诸如线程间共享数据或其他资源;
# 为达到那个目的,线程模块提供了许多同步化的原生支持,包括:锁,事件,条件变量和信号灯;
# 任务协调的首选方法是把对一个资源的所有访问几种在一个单独的线程中;
# 然后使用Queue模块用那个线程服务其他线程的请求;
# 为内部线程通信和协调而使用Queue.Queue对象的应用程序更容易设计,更可读,并且更可靠;
# 11.5 日志
# logging模块 提供了完整和灵活的日志系统;
# 它最简单的用法是记录信息并发送到一个文件或sys.stderr
import logging
logging.debug("Debugging information")
logging.info('Informational message')
logging.warning("Warning:config file %s not found",'server.cof')
logging.error('Error occurred')
logging.critical('Critical error -- shutting down')
# 默认,捕获信息和调试信息 将输出发送到标准错误流;
# 其他可选的路由信息方式通过email,数据报文,socket或者HTTP Server;
# 基于消息属性,新的过滤器可以选择不同的路由:DEBUG INFO WARNING ERROR CRITICAL
# 日志系统可以直接在Python代码中定制,也可以不经过应用程序直接在一个用户可便捷的配置文件中加载;
# 11.6 弱引用
# Python自动进行内存管理(对大多数的对象进行引用计数和垃圾回收以循环利用)
# 在最后一个引用消失后,内存会很快释放;
# 有时需要跟踪对象而创建引用,为避免其长期存在,weakref模块提供了不用创建引用的跟踪对象工具;
# 一旦对象不再存在,它自动从弱引用表上删除并触发回调;
# 典型的应用包括捕获难以构造的对象;
import weakref, gc
class A:
def __init__(self, value):
self.value = value
def __repr__(self):
return str(self.value)
a = A(10)
print a
d = weakref.WeakValueDictionary()
d['primary'] = a #does not create a refrence
print d['primary']
del a
gc.collect()
# print d['primary']#KeyError
# 11.7 列表工具
# 很对数据结构可能会用到内置列表类型;
# array模块提供了一个类似列表的array()对象;
from array import array
array = array('H',[4000,10,700,22222])#存储双字节无符号整数的数组 H为类型编码
print sum(array)
print array[1:3]
# collections模块提供了类似列表deque()对象;
# 从左边添加append和弹出pop更快,但内部查询更慢;
# 这些对象更适合实现队列 和 广度优先的搜索树;
from collections import deque
deque = deque(['task1','task2','task3'])
deque.append("task4")
print deque
print deque.popleft()
# 除了链表的替代实现,该库还提供了bisect这样的模块 以操作存储链表
import bisect
scores = [(100, 'per1'),(200, 'per2'),(400, 'per4'),(500, 'per5')]
bisect.insort(scores,(300, 'per3'))
print scores
# heapq提供了基于正规链表的堆实现;
# 最小值总是保持在0点;
# 这在希望循环访问最小元素但是不想执行完整堆排序的时候很有用
from heapq import heapify, heappop, heappush
data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
heapify(data)
print data
heappush(data, -5)
print data
print [heappop(data) for i in range(3)]
print data
# 11.8 十进制浮点数算法
# decimal模块提供一个Decimal数据类型 用于浮点数计算;
# 相比float,这个类型适合:
# 1.金融应用和其他需要精确十进制表示的场合;
# 2.控制精度;
# 3.红纸舍入以适应法律或者规定要求;
# 4.确保十进制数位精度
# 用户希望计算结果与手算相符的场合;
from decimal import *
print round(Decimal('0.70') * Decimal('1.05'),2)
print Decimal('0.740010')
print round(.70 * 1.05, 2)
# Decimal的结果总是保持结尾的0,自动从两位精度延伸到4位;
# Decimal重现了手工的数学运算,这就确保了二进制浮点数无法精确保有的主句精度;
# 高精度使Decimal可以执行二进制浮点数无法进行的模运算和等值测试
print Decimal('1.00') % Decimal('.10')
print 1.00 % 0.10
print sum([Decimal('0.1')] * 10) == Decimal('1.0')
print sum([0.1]*10) == 1.0
# 此外,Decimal还提供了必须的高精度算法
# -*- coding:utf-8 -*-
# Python标准库: https://docs.python/2.7/library/index.html#library-index
# 安装Python模块:python setup.py install
# Python官方网站:http://www.python
# Python快速访问文档:http://docs.python
# Python包索引:http://pypi.python
# Python 食谱是大量的示例代码、大型的集合,和有用的脚本: http://aspn.activestate/ASPN/Python/Cookbook/
# 执行Pyhton脚本
# BSD 类的 Unix 系统中,Python 脚本可以像 Shell 脚本那样直接执行。只要在脚本文件开头写一行命令,指定文件和模式:
# #! /usr/bin/env python
更多推荐
Python2.7.13 初识
发布评论