大家好,又见面了,我是你们的朋友全栈君。
第一章 Python基础知识
1.1 介绍
1.1.1 特点
Python是一种面向对象、解释型计算机程序设计语言。语法简洁清晰,强制用空白符作为语句缩进。
Python具有丰富和强大的库,又被称为胶水语言。能把其他语言(主要C/C++)写的模块很轻松的结合在一起。
1.1.2 应用领域
Web网站:有很多优秀的开源Web框架,比如Django(最流行)、Tornado(轻量级、异步)、Flask(微型)、Web.py(简单)等。
数据采集:有好用的http库,比如urllib2、requests等。还有高级的屏幕爬取及网页采集框架scrapy。并对网页解析也有很多库,比如lxml、xpath、BeautifulSoup等。
大数据分析:常用模块有Numpy、Pandas。并支持写MapReduce、PySpark处理Spark RDD(弹性分布式数据集)。
运维自动化:编写脚本、Web平台,自动化日常工作。
科学计算:在科学计算也应用越来越广泛,常用的模块有Numpy、SciPy。
等等…可见Python是一门通用语言!
1.1.3 为什么选择Python?
运维的目的呢,主要还是学习Python用来实现运维自动化了。大多数人除了shell脚本外有其他语言基础的应该占少数。
我们以Python作为第一门语言是很好的选择。为什么呢?
1) 语法简洁,易于学习。
2) 广泛的标准库,适合快速开发,不就追求极快处理速度。
3) 跨平台,基本所有的所有的操作系统都能运行。
4) 运维领域Python最流行。
1.2
安装Python
操作系统采用CentOS6.5,默认安装了Python2.6.6,那我们升级到Python2.7最新版Python2.7.12
1. 安装Python2.7
# wget https://www.python.org/ftp/python/2.7.12/Python-2.7.12.tgz # tar zxvf Python-2.7.12.tgz
# cd Python-2.7.12
# ./configure
# make && make install
# mv /usr/bin/python /usr/bin/python2.6.6
# ln -s /usr/local/bin/python2.7 /usr/bin/python
# python -V
Python 2.7.12
注意:软链接指向Python2.7版本后,yum将不能正常工作,因为yum不兼容2.7的,所有需要指定下yum命令里默认Python版本为2.6.6版本
# sed -i ‘1s/$/2.6.6/’ /usr/bin/yum
2. 安装setuptools
# yum install python-devel zlib-devel openssl-devel -y
# wget https://pypi.python.org/packages/32/3c/e853a68b703f347f5ed86585c2dd2828a83252e1216c1201fa6f81270578/setuptools-26.1.1.tar.gz
# tar zxvf setuptools-26.1.1.tar.gz
# cd setuptools-26.1.1
# python setup.py install
……
“Compression requires the (missing) zlib module”
RuntimeError: Compression requires the (missing) zlib module
解决方法,进入刚解压的Python2.7目录重新编译安装:
# cd ../Python-2.7.12
# make && make install
# python setup.py install
3. 安装pip2.7
# wget https://pypi.python.org/packages/e7/a8/7556133689add8d1a54c0b14aeff0acb03c64707ce100ecd53934da1aa13/pip-8.1.2.tar.gz
# tar zxvf pip-8.1.2.tar.gz
# cd pip-8.1.2
# python setup.py install
1.3
解释器
1.3.1 Python解释器几种实现版本
1) CPython
当我们装完Python后,其默认解释就是CPython,也是官方默认解释器。CPython是C语言写的,当执行代码时会将代码转化成字节码(ByteCode)。
2) IPython
基于CPython之上的一个交互式解释器,相当于默认解释器的一个增强版,最显著的功能就是自动补全,挺好用的。
3) PyPy
PyPy本身是由Python编写的,使用了JIT编译器(即时编译器)技术,当执行代码时JIT编译器将代码翻译成机器码。性能相比CPython要好。JAVA也采用了JIT编译器。
4) Jython
Jython是由JAVA编写的一个解释器,可以把JAVA模块加载到Python的模块中使用,也可以把Python代码打包成JAR包,意味着允许用Python写JAVA程序了。当执行代码时会将代码转化成JAVA字节码,然后使用JRE执行。
5) IronPython
在.NET平台上工作的Python语言。
1.3.2 Python代码执行过程
大致流程:源代码编译成字节码(.pyc文件)–> Python虚拟机 –> 执行编译好的字节码 –> Python虚拟机将字节码翻译成对应的机器指令(机器码)
运行Python程序时,先编译成字节码并保存到内存中,当程序运行结束后,Python解释器将内存中字节码对象写到.pyc文件中。
第二次再运行此程序时,先回从硬盘中寻找.pyc文件,如果找到,则直接载入,否则就重复上面的过程。
这样好处是,不重复编译,提供执行效率。
1) 字节码
字节码是一种包含执行程序、由一序列op代码/数据对组成的二进制文件。字节码是一种中间码,比机器码更抽象。
2) 机器码
机器码是一种指令集,让CPU可直接解读的数据。也称为原生码。
1.4 代码风格
1.4.1 代码风格有毛用?
个人觉得有以下几个作用:
1) 团队协作
在企业中,一个团队开发一个项目很正常不过了,刚入职是不是会先让你熟悉本公司的编码规范文档呢,作为纯开发来说,我相信大多数公司都会这么做,其中目的是让团队中的每个成员,写代码时能够统一,避免项目中出现几个编码风格版本,不利用后期维护和交接。
2) 有利于解决问题
草泥马,又出问题了,代码运行不起来了,怎么办?百度、谷歌无解…是时候求助大神了,来看看我的代码吧!大神一看,琢磨了一会,你想多了,不是再想你的问题,而是在梳理你的代码实现的功能和逻辑关系。结果发现,多了括号。擦,我怎么就没看到呢!~
3) 未雨绸缪
功能终于实现了,发布到线上运行也挺正常,过了半年后,突然跑不起来了,赶紧排查问题,代码看着看着自己就懵逼了,这还是自己写的代码嘛,长的这么不像我,是亲生的嘛!
小结:只要人人都献出一点爱,世界将会变成美好的人间。
1.4.2 编写代码怎么能更规范化?
1) 缩进
Python以空白符作为语句缩进,意味着语句没有结尾符,给往往因为少写个fi的人带来了福利,在Python中最好以4个空格作为缩进符。
2) 代码注释
据说优质的代码,注释说明要比代码量多,详细的代码说明不管对自己后期维护还是开源,都是有必要的。就像一个流行的软件,如果没有丰富的使用文档,你认为会有多少耐心的人去花大把的时间研究它呢!
3) 空格使用
在操作符两边,以及逗号后面,加1个空格。但是在括号左右不加空格。
在函数、类、以及某些功能代码块,空出一行,来分隔它们。
4) 命名
模块:自己写的模块,文件名全部小写,长名字单词以下划线分隔。
类:大/小驼峰命名。我一般采用大驼峰命名,也就是每个单词首字母大写。类中私有属性、私有方法,以双下划线作为前缀。
函数:首单词小写,其余首字母大写。
变量:都小写,单词以下划线分隔。
提醒:所有的命名必须能简要说明此代码意义。
5) 代码换行
按照语法规则去换行,比如一个很长的表达式,可以在其中某个小表达式两边进行换行,而不是将小表达式拆分,这样更容易阅读。
1.5 交互式解释器
直接执行Python命令就启动默认的CPython解释器:
# python
Python 2.7.12 (default, Sep 3 2016, 21:51:00)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-17)] on linux2
Type “help”, “copyright”, “credits” or “license” for more information.
>>> print “Hello World”
Hello World
配置自动补全:
# pip2.7 install readline
# pip2.7 install rlcompleter2
>>> import readline, rlcompleter
>>> readline.parse_and_bind(“tab: complete”)
1.6 运算操作符
运算符 |
描述 |
示例 |
+ |
加法 |
1 + 1 = 2 |
– |
减法 |
3 – 1 = 2 |
* |
乘法 |
2 * 1 = 2 |
/ |
除法 |
2 / 1 = 2 |
% |
取余/模 |
2 % 1 = 2 |
** |
指数/幂 |
2 ** 1 = 2 |
1.7 赋值操作符
操作符 |
描述 |
示例 |
= |
变量赋值 |
a = b + c |
+= |
加法 |
a += b 等同于 a = a + b |
-= |
减法 |
a -= b 等同于 a = a – b |
*= |
乘法 |
a *= b 等同于 a = a * b |
/= |
除法 |
a /= b 等同于 a = a / b |
%= |
模 |
a %= b 等同于 a = a % b |
**= |
指数/幂 |
a **= b 等同于 a = a ** b |
赋值操作符,操作符左边运算右边,然后将结果赋值给操作符左边。
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ群:323779636(Shell/Python运维开发群)
1.8 变量
1.8.1 变量赋值
>>> xxoo = 2
>>> print xxoo
>>> 2
说明:等号左边是变量名,等号右边是值
# 多重赋值
>>> xx, oo = 1, 2
>>> print xx
1
>>> print oo
2
>>> xx = oo = 2
>>> print xx
2
>>> print oo
2
1. 8.2 变量引用
上面打印时就是在引用变量了,可见Python引用变量不用加$什么特殊字符,不像Shell、PHP那样,还要加$。
的确,直接用变量名即是引用,下面说一种常用的字符串格式输出时引用变量的方法。
>>> xxoo = 2
>>> print “xxoo: %d” % xxoo
xxoo: 2
>>> xxoo = “xo”
>>> print “xxoo: %s” % xxoo
xxoo: xo
>>> x = “abc”
>>> o = 123
>>> print “str: %s, int: %d” %(x, o)
str: abc, int: 123
说明:双引号里面%操作符算是占位符吧,d代表数字,s代表字符串。双引号外面%加上后面的变量名对应里面的第一个%。
下面同时引用了两个变量,外面%()里变量名位置对应双引号里面的%位置。
1.8.3 局部变量
>>> xxoo = 2
>>> print xxoo
2
1.8.4 全局变量
>>> global xxoo # 声明为全局变量
>>> print xxoo
2
说明:从上面并不能看出什么区别,后续在函数章节中会讲解局部变量和全局变量的区别和使用。
1.9 转义字符(列出一些常用的)
符号 |
描述 |
\ |
字符串太长,换一行接着输入 |
\’ \” |
单引号和双引号 |
\r |
光标 |
\t |
横向制表符(tab键) |
\v |
纵向制表符 |
\n |
换行符,打印到下一行 |
示例:
>>> print “Hello \
… World”
Hello World
>>> print “Hello \”World!”
Hello “World!
>>> print “Hello \rWorld!”
World!
>>> print “Hello\tWorld!”
Hello World!
>>> print “Hello \vWorld!”
Hello
World!
>>> print “Hello \nWorld!”
Hello
World!
如果不想让转义字符生效,可以用r指定显示原始字符串:
>>> print r”Hello \nWorld!”
Hello \nWorld!
>>> print “Hello \nWorld!”
Hello
World!
1.10 获取用户输入
1.10.1 raw_input()
>>> name = raw_input(“My name is: “)
My name is: xiaoming
>>> print name
xiaoming
1.10.2 input()
>>> name = input(“My name is: “)
My name is: xiaoming
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
File “<string>”, line 1, in <module>
NameError: name ‘xiaoming’ is not defined
>>> name = input(“My name is: “)
My name is: “xiaoming”
>>> print name
xiaoming
>>> name = input(“My name is: “)
My name is: 1 + 2
>>>
>>> print name
3
1.10.3 raw_input()与input()函数区别
可以看到两个函数用同样的方式输入,结果input()报错!
原因是因为raw_input()把任何输入的都转成字符串存储。
而input()接受输入的是一个表达式,否则就报错。
1.11 运行第一个程序
# vi test.py
#!/usr/bin/env python # 说明用什么可执行程序运行它,env会自动寻找python解释器的绝对路径
print “Hello World!”
# python test.py
Hello World!
easy!打印Hello world已经没什么难度了,那改进下刚学接受用户输入。
# vi test.py
#!/usr/bin/env python
name = raw_input(“My name is: “)
print name
# python test.py
My name is: xiaoming
xiaoming
1.12 注释
单行注释:井号(”#”)开头
多行注释:三单引号或三双引号
#!/usr/bin/env python
# -*- coding: utf-8 -*- # 设置解释器默认编码,下一章会讲到
# 单行注释
”’
多行注释
多行注释
”’
“””
多行注释
多行注释
第二章 字符串处理与编码不再发愁
2.1 字符串
2.1.1 字符串转换
>>> a = 123
>>> b = 1.23
>>> type(a)
<type ‘int’>
>>> type(b)
<type ‘float’>
>>> type(str(a))
<type ‘str’>
>>> type(str(b))
<type ‘str’>
说明:先定义个整数和浮点数,再查看类型,用str()函数将对象转成字符串。
这里的用到了type()函数,用于查看对象类型。这个type()在以后学习中很用的,刚开始学习时候,往往因为对象类型不对,导致程序运行报错,这时可以用它来排查问题。
2.1.2 字符串连接
# 加号字符将同类型字符连接到一起
>>> hw = “Hello” + “World!”
>>> print hw
HelloWorld!
# 两个相邻的字符串自动连接一起
>>> hw = “Hello””World!”
>>> print hw
HelloWorld!
# 如果字符串内包括单引号或双引号,要用\转义,否则报错,上一章也讲过。
>>> hw = “Hello \”World!\””
>>> print hw
Hello “World!”
# 不同字符串类型拼接
>>> a = “abc”
>>> b = 1
>>> print a + b
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
TypeError: cannot concatenate ‘str’ and ‘int’ objects
说明:不同字符串类型不允许连接,想要连接可以下面这么做。
方法1:
>>> c = “%s%d” %(a,b)
>>> print c
abc1
方法2:
>>> c = a + str(b)
>>> print c
abc1
2.1.3 格式化输出
操作符号 |
说明 |
%s |
字符串(str()) |
%r |
字符串(repr()) |
%d |
整数 |
%f |
浮点数,可指定小数点后的精度 |
? |
|
1) 字符串格式输出三种方法
>>> xxoo = “string”
>>> print “%s” %xxoo
string
>>> print “%r” %xxoo
‘string’
>>> print `xxoo`
‘string’
说明:%s采用str()函数显示,%r采用repr()函数显示。repr()和反撇号把字符串转为Python表达式。
2) 保留小数点数
>>> ‘%.1f’ %(float(100)/1024)
‘0.1’
2.1.4 字符串处理
上图是字符串处理的方法,红色框框中大概有一半经常用的,我们就拿一部分常用的来举例说明。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
xxoo = “Hello world!”
print “字符串长度: %s” % len(xxoo)
print “首字母大写: %s” % xxoo.capitalize()
print “字符l出现次数: %s” % xxoo.count(‘l’)
print “感叹号是否结尾: %s” % xxoo.endswith(‘!’)
print “w字符是否是开头: %s” % xxoo.startswith(‘w’)
print “w字符索引位置: %s” % xxoo.find(‘w’) # xxoo.index(‘W’)
print “格式化字符串: Hello{0} world!”.format(‘,’)
print “是否都是小写: %s” % xxoo.islower()
print “是否都是大写: %s” % xxoo.isupper()
print “所有字母转为小写: %s” % xxoo.lower()
print “所有字母转为大写: %s” % xxoo.upper()
print “感叹号替换为句号: %s” % xxoo.replace(‘!’,’.’)
print “以空格分隔切分成列表: %s” % xxoo.split(‘ ‘)
print “转换为一个列表: %s” % xxoo.splitlines()
print “去除两边空格: %s” % xxoo.strip()
print “大小写互换: %s” % xxoo.swapcase()
print “只要Hello字符串: %s” % xxoo[0:5]
print “去掉倒数第一个字符: %s” % xxoo[0:-1]
# python test.py
字符串长度: 12
首字母大写: Hello world!
字符l出现次数: 3
感叹号是否结尾: True
w字符是否是开头: False
w字符索引位置: 6
格式化字符串: Hello, world!
是否都是小写: False
是否都是大写: False
所有字母转为小写: hello world!
所有字母转为大写: HELLO WORLD!
感叹号替换为句号: Hello world.
以空格分隔切分成列表: [‘Hello’, ‘world!’]
转换为一个列表: [‘Hello world!’]
去除两边空格: Hello world!
大小写互换: hELLO WORLD!
只要Hello字符串: Hello
去掉倒数第一个字符: Hello world
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ群:323779636(Shell/Python运维开发群)
2.2 编码
2.2.1 常见字符编码类型
ASCII:美国信息交换标准码,是目前计算机中最广泛使用的字符集编码。每个ASCII码以1个字节存储,例如数字字符0的ASCII码是0110000,十进制表示为48。
Unicode:为解决世界上上百种语言带来混合、冲突,各国有各国的标准,显示很容易出现乱码。Unicode就出现了,它把所有语言的字符都统一到一套Unicode编码中,并定义每个语言字符的标准,所以Unicode又称统一码,万国码。大部分编程语言都支持Unicode,Python内部编码也支持Unicode。
GB2312:中国国家标准总局发布处理汉字的标准编码。
GBK:GB2312的扩展,向下兼容GB2312。
UTF-8:针对Unicode的可变长度字符编码,又称万国码。支持中文简体繁体及其它语言(如英文,日文,韩文)。
2.2.
3 decode()
decode()函数作用是将其他编码(比如ACSII、Byte String)的字符串解码成Unicode。
2.2.
4 encode()
encode()函数作用是将Unicode编码成终端软件能是识别的编码,就能正常显示了,比如UTF-8、GBK。
2.2.
5 Python编码处理
#!/usr/bin/env python
c = “中文”
print c
# python test.py
File “test.py”, line 2
SyntaxError: Non-ASCII character ‘\xe4’ in file test.py on line 3, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details
说明:在程序里面直接打印中文,会报语法错误,这是因为Python默认编码是ASCII,无法处理其他编码。
如果想打印中文,需要声明编码为utf-8,上面也有写过:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
c = “中文”
print c
print type(c)
# python test.py
中文
<type ‘str’>
可以正常输出中文了,类型是字符串,这个字符串是经过Python unicode编码后字节组成的。
虽然可以正常输入中文,并不意味的就万事大吉了,如果终端编码不是utf-8或其他软件也不确定编码还会出现乱码情况。所以还是要明白Python处理编码逻辑关系,才能更好的应对编码问题。
切换到交互式解释器:
>>> c = “中文”
>>> c.encode(‘utf-8’)
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe4 in position 0: ordinal not in range(128)
如果直接转成utf-8是不允许的,报错Unicode解码错误,大概意思是说ascii码不能解码字节字符串。
上面讲到encode()函数作用是将Unicode码解码,而现在的c变量并非是Unicode码,而是字节字符串,算是Unicode的一种吧?。
故此,不能使用encode(),而是先使用decode()先解码陈Unicode再用encode()编码成utf-8。
>>> c.decode(‘utf-8’)
u’\u4e2d\u6587′ # 4e2d对应unicode值是”中”,6587对应unicdoe值是”文”
>>> type(c.decode(‘utf-8’))
<type ‘unicode’>
>>> print c.decode(‘utf-8’) ?
中文
>>> print c.decode(‘utf-8’).encode(‘utf-8’)
中文
如果是Unicode字符串可直接通过encode()函数转码其他编码。
>>> c = u’中文’
>>> c.encode(‘utf-8’)
‘\xe4\xb8\xad\xe6\x96\x87’
>>> print c.encode(‘utf-8’)
中文
看下字节字符串和unicode字符串区别:
>>> c = ‘中文’
>>> u = u’中文’
>>> c
‘\xe4\xb8\xad\xe6\x96\x87’
>>> u
u’\u4e2d\u6587′
>>> len(c)
6
>>> len(u)
2
字节字符串长度要比unicode长的多,而unicode长度就是字符长度。
总结下:Python处理编码流程大致是这样的,ascii –> decode() –> unicode –> encode() –> 终端是能识别的编码,unicode算是一个中间码,有着承上启下的作用。
第三章 Python丰富的数据类型
什么是数据类型?
前两章里面包含的字符串、布尔类型、整数、浮点数都是数据类型。数据类型在一个编程语言中必不可少,也是使用最多的。
而且数据类型的数据都是存放在内存中的,我们一般操作都是在对内存里对象操作。
什么是数组?
数组也是一种数据类型,为了方便处理数据,把一些同类数据放到一起就是数组,是一组数据的集合,数组内的数据称为元素,每个元素都有一个下标(索引),从0开始。
在Python中,内建数据结构有列表(list)、元组(tuple)、字典(dict)、集合(set)。
3.1 列表[List]
3.1.1 定义列表
>>> lst = [‘a’,’b’,’c’,1,2,3]
用中括号括起来,元素以逗号分隔,字符串用单引号引起来,整数不用。
3.1.2 基本操作
# 追加一个元素
>>> lst.append(4)
>>> lst
[‘a’, ‘b’, ‘c’, 1, 2, 3, 4]
# 统计列表中a字符出现的次数
>>> lst.count(‘a’)
1
# 将一个列表作为元素添加到lst列表中
>>> a = [5,6]
>>> lst.extend(a)
>>> lst
[‘a’, ‘b’, ‘c’, 1, 2, 3, 4, 5, 6]
# 查找元素3的索引位置
>>> lst.index(1)
3
# 在第3个索引位置插入一个元素
>>> lst.insert(3, 0)
>>> lst
[‘a’, ‘b’, ‘c’, 0, 1, 2, 3, 4, 5, 6]
# 删除最后一个元素和第3个下标元素
>>> lst.pop()
6
>>> lst.pop(3)
0
>>> lst
[‘a’, ‘b’, ‘c’, 1, 2, 3, 4, 5]
# 删除元素是5,如果没有会返回错误
>>> lst.remove(“5”)
>>> lst
[‘a’, ‘b’, ‘c’, 1, 2, 3, 4]
# 倒序排列元素
>>> lst.reverse()
>>> lst
[4, 3, 2, 1, ‘c’, ‘b’, ‘a’]
# 正向排序元素
>>> lst.sort()
>>> lst
[1, 2, 3, 4, ‘a’, ‘b’, ‘c’]
# 列表连接
>>> a = [1,2,3]
>>> b = [‘a’,’b’,’c’]
>>> a + b
[1, 2, 3, ‘a’, ‘b’, ‘c’]
3.1.3 学习新函数对列表排序
# reversed()函数倒序排列
使用此函数会创建一个迭代器,遍历打印才能输出:
>>> lst = [‘a’, ‘b’, ‘c’, 1, 2, 3, 4, 5]
>>> type(reversed(lst))
<type ‘listreverseiterator’>
>>> lst2 = []
>>> for i in reversed(lst):
… lst2.append(i)
…
>>> lst2
[5, 4, 3, 2, 1, ‘c’, ‘b’, ‘a’]
# sorted()函数正向排列
>>> lst2 = []
>>> for i in sorted(lst):
… lst2.append(i)
…
>>> lst2
[1, 2, 3, 4, 5, ‘a’, ‘b’, ‘c’]
这里在讲解一个序列生成器range()函数,生成的是一个列表:
>>> type(range(5))
<type ‘list’>
>>> for i in range(1,5):
… print i
…
1
2
3
4
当然也可以用上面的排序函数来排序这个生成的序列了:
>>> for i in reversed(range(1,10,3)):
… print i
…
7
4
1
range()函数用法:range(start,end,step)
说明:是不是和列表内置方法结果一样!区别是内置函数不改动原有序列。
3.1.4 切片
>>> lst
[1, 2, 3, 4, ‘a’, ‘b’, ‘c’]
# 返回第一个元素
>>> lst[0]
1
# 返回倒数第一个元素
>>> lst[-1]
‘c’
# 取出倒数第一个元素
>>> lst[0:-1]
[1, 2, 3, 4, ‘a’, ‘b’]
# 返回第一个至第四个元素
>>> lst[0:4]
[1, 2, 3, 4]
3.1.5 清空列表
方法1:
>>> lst = [1, 2, 3, 4, ‘a’, ‘b’, ‘c’]
>>> lst = []
>>> lst
[]
方法2:
>>> lst = [1, 2, 3, 4, ‘a’, ‘b’, ‘c’]
>>> del lst[:]
>>> lst
[]
# 删除列表
>>> lst = [1, 2, 3, 4, ‘a’, ‘b’, ‘c’]
>>> del lst
>>> lst
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
NameError: name ‘lst’ is not defined
3.1.6 del语句
del语句也可以删除一个下标范围的元素
>>> lst = [1, 2, 3, 4, ‘a’, ‘b’, ‘c’]
>>> del lst[0:4]
>>> lst
[‘a’, ‘b’, ‘c’]
3.1.7 列表推导式
利用其它列表推导出新的列表。
# 通过迭代对象方法
方法1:
>>> lst = []
>>> for i in range(5):
… lst.append(i)
…
>>> lst
[0, 1, 2, 3, 4]
方法2:
>>> lst = []
>>> lst = [i for i in range(5)]
>>> lst
[0, 1, 2, 3, 4]
说明:方法1和方法2,实现方式是一样的,只是方法2用简洁的写法。for循环在下一章会讲。
# 通过已有的列表生成新列表
>>> lst
[0, 1, 2, 3, 4]
>>> lst2 = [i for i in lst if i > 2]
>>> lst2
[3, 4]
3.1.8 遍历列表
如果既要遍历索引又要遍历元素,可以这样写。
方法1:
>>> lst = [‘a’,’b’,’c’,1,2,3]
>>> for i in range(len(lst)):
… print i,lst[i]
…
0 a
1 b
2 c
3 1
4 2
5 3
方法2:
>>> for index, value in enumerate(lst):
… print index,value
…
0 a
1 b
2 c
3 1
4 2
5 3
又学了一个新函数enumrate(),可遍历列表、字符串的下标和元素。
3.2 元组(Tuple)
元组与列表类型,不同之处在于元素的元素不可修改。
2.1 定义元组
t = (‘a’,’b’,’c’,1,2,3)
用小括号括起来,元素以逗号分隔,字符串用单引号引起来,整数不用。
2.2 基本操作
count()和index()方法和切片使用方法与列表使用一样,这里不再讲解。
3.3 集合(set)
集合是一个无序不重复元素的序列,主要功能用于删除重复元素和关系测试。
集合对象还支持联合(union),交集(intersection),差集(difference)和对称差集(sysmmetric difference)数学运算。
需要注意的是,集合对象不支持索引,因此不可以被切片。
3.3.1 定义集合
>>> s = set()
>>> s
set([])
使用set()函数创建集合。
3.3.2 基本操作
# 添加元素
>>> s.add(‘a’)
>>> s
set([‘a’])
>>> s.add(‘b’)
>>> s
set([‘a’, ‘b’])
>>> s.add(‘c’)
>>> s
set([‘a’, ‘c’, ‘b’])
>>> s.add(‘c’)
>>> s
set([‘a’, ‘c’, ‘b’])
说明:可以看到,添加的元素是无序的,并且不重复的。
# update方法事把传入的元素拆分为个体传入到集合中。与直接set(‘1234’)效果一样。
>>> s.update(‘1234’)
>>> s
set([‘a’, ‘c’, ‘b’, ‘1’, ‘3’, ‘2’, ‘4’])
# 删除元素
>>> s.remove(‘4’)
>>> s
set([‘a’, ‘c’, ‘b’, ‘1’, ‘3’, ‘2’])
# 删除元素,没有也不会报错,而remove会报错
>>> s.discard(‘4’)
>>> s
set([‘a’, ‘c’, ‘b’, ‘1’, ‘3’, ‘2’])
# 删除第一个元素
>>> s.pop()
‘a’
>>> s
set([‘c’, ‘b’, ‘1’, ‘3’, ‘2’])
# 清空元素
>>> s.clear()
>>> s
set([])
# 列表转集合,同时去重
>>> lst = [‘a’,’b’,’c’,1,2,3,1,2,3]
>>> s = set(lst)
>>> s
set([‘a’, 1, ‘c’, ‘b’, 2, 3])
3.3.3 关系测试
符号 |
描述 |
– |
差集 |
& |
交集 |
| |
合集、并集 |
!= |
不等于 |
== |
等于 |
in |
是成员为真 |
not in |
不是成员为真 |
示例:
# 返回差集
>>> a – b
set([‘1’, ‘3’, ‘2’])
>>> b – a
set([‘9’, ‘8’, ‘7’])
# 返回交集
>>> a & b
set([‘5’, ‘4’, ‘6’])
# 返回合集
>>> a | b
set([‘1’, ‘3’, ‘2’, ‘5’, ‘4’, ‘7’, ‘6’, ‘9’, ‘8’])
# 不等于
>>> a != b
True
# 等于
>>> a == b
False
# 存在为真
>>> ‘1’ in a
True
# 不存在为真
>>> ‘7’ not in a
True
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ群:323779636(Shell/Python运维开发群)
3.4 字典{Dict}
序列是以连续的整数位索引,与字典不同的是,字典以关键字为索引,关键字可以是任意不可变对象(不可修改),通常是字符串或数值。
字典是一个无序键:值(Key:Value)集合,在一字典中键必须是互不相同的,
3.4.1 定义字典
>>> d = {‘a’:1, ‘b’:2, ‘c’:3}
用大括号括起来,一个键对应一个值,冒号分隔,多个键值逗号分隔。
3.4.2 基本操作
# 返回所有键值
>>> d.items()
[(‘a’, 1), (‘c’, 3), (‘b’, 2)]
# 返回所有键
>>> d.keys()
[‘a’, ‘c’, ‘b’]
# 查看所有值
>>> d.values()
[1, 3, 2]
# 添加键值
>>> d[‘e’] = 4
>>> d
{‘a’: 1, ‘c’: 3, ‘b’: 2, ‘e’: 4}
# 获取单个键的值,如果这个键不存在就会抛出KeyError错误
>>> d[‘a’]
>>> 1
# 获取单个键的值,如果有这个键就返回对应的值,否则返回自定义的值no
>>> d.get(‘a’,’no’)
1
>>> d.get(‘f’,’no’)
no
# 删除第一个键值
>>> d.popitem()
(‘a’, 1)
>>> d
{‘c’: 3, ‘b’: 2, ‘e’: 4}
# 删除指定键
>>> d.pop(‘b’)
2
>>> d
{‘c’: 3, ‘e’: 4}
# 添加其他字典键值到本字典
>>> d
{‘c’: 3, ‘e’: 4}
>>> d2 = {‘a’:1}
>>> d.update(d2)
>>> d
{‘a’: 1, ‘c’: 3, ‘e’: 4}
# 拷贝为一个新字典
>>> d
{‘a’: 1, ‘c’: 3, ‘e’: 4}
>>> dd = d.copy()
>>> dd
{‘a’: 1, ‘c’: 3, ‘e’: 4}
>>> d
{‘a’: 1, ‘c’: 3, ‘e’: 4}
# 判断键是否在字典
>>> d.has_key(‘a’)
True
>>> d.has_key(‘b’)
False
3.4.3 可迭代对象
字典提供了几个获取键值的迭代器,方便我们在写程序时处理,就是下面以iter开头的方法。
d.iteritems()
# 获取所有键值,很常用
d.iterkeys() # 获取所有键
d.itervalues() # 获取所有值
# 遍历iteritems()迭代器
>>> for i in d.iteritems():
… print i
…
(‘a’, 1)
(‘c’, 3)
(‘b’, 2)
说明:以元组的形式打印出了键值
如果我们只想得到键或者值呢,就可以通过元组下标来分别获取键值:
>>> for i in d.iteritems():
… print “%s:%s” %(i[0],i[1])
…
a:1
c:3
b:2
有比上面更好的方法实现:
>>> for k, v in d.iteritems():
… print “%s: %s” %(k, v)
…
a: 1
c: 3
b: 2
这样就可以很方面处理键值了!
# 遍历其他两个迭代器也是同样的方法
>>> for i in d.iterkeys():
… print i
…
a
c
b
>>> for i in d.itervalues():
… print i
…
1
3
2
说明:上面用到了for循环来遍历迭代器,for循环的用法在下一章会详细讲解。
3.4.4 一个键多个值
一个键对应一个值,有些情况无法满足需求,字典允许一个键多个值,也就是嵌入其他数组,包括字典本身。
# 嵌入列表
>>> d = {‘a’:[1,2,3], ‘b’:2, ‘c’:3}
>>> d[‘a’]
[1, 2, 3]
>>> d[‘a’][0] # 获取值
1
>>> d[‘a’].append(4) # 追加元素
>>> d
{‘a’: [1, 2, 3, 4], ‘c’: 3, ‘b’: 2}
# 嵌入元组
>>> d = {‘a’:(1,2,3), ‘b’:2, ‘c’:3}
>>> d[‘a’][1]
2
# 嵌入字典
>>> d = {‘a’:{‘d’:4,’e’:5}, ‘b’:2, ‘c’:3}
>>> d[‘a’]
{‘e’: 5, ‘d’: 4}
>>> d[‘a’][‘d’] # 获取值
4
>>> d[‘a’][‘e’] = 6 # 修改值
>>> d
{‘a’: {‘e’: 6, ‘d’: 4}, ‘c’: 3, ‘b’: 2}
3.5 额外的数据类型
colloctions()函数在内置数据类型基础上,又增加了几个额外的功能,替代内建的字典、列表、集合、元组及其他数据类型。
3.5.1 namedtuple
namedtuple函数功能是使用名字来访问元组元素。
语法:namedtuple(“名称”, [名字列表])
>>> from collections import namedtuple
>>> nt = namedtuple(‘point’, [‘a’, ‘b’, ‘c’])
>>> p = nt(1,2,3)
>>> p.a
1
>>> p.b
2
>>> p.c
3
namedtuple函数规定了tuple元素的个数,并定义的名字个数与其对应。
3.5.2 deque
当list数据量大时,插入和删除元素会很慢,deque的作用就是为了快速实现插入和删除元素的双向列表。
>>> from collections import deque
>>> q = deque([‘a’, ‘b’, ‘c’])
>>> q.append(‘d’)
>>> q
deque([‘a’, ‘b’, ‘c’, ‘d’])
>>> q.appendleft(0)
>>> q
deque([0, ‘a’, ‘b’, ‘c’, ‘d’])
>>> q.pop()
‘d’
>>> q.popleft()
0
实现了插入和删除头部和尾部元素。比较适合做队列。
3.5.3 Counter
顾名思义,计数器,用来计数。
例如,统计字符出现的个数:
>>> from collections import Counter
>>> c = Counter()
>>> for i in “Hello world!”:
… c[i] += 1
…
>>> c
Counter({‘l’: 3, ‘o’: 2, ‘!’: 1, ‘ ‘: 1, ‘e’: 1, ‘d’: 1, ‘H’: 1, ‘r’: 1, ‘w’: 1})
结果是以字典的形式存储,实际Counter是dict的一个子类。
3.5.4 OrderedDict
内置dict是无序的,OrderedDict函数功能就是生成有序的字典。
例如,根据前后插入顺序排列:
>>> d = {‘a’:1, ‘b’:2, ‘c’:3}
>>> d # 默认dict是无序的
{‘a’: 1, ‘c’: 3, ‘b’: 2}
>>> from collections import OrderedDict
>>> od = OrderedDict()
>>> od[‘a’] = 1
>>> od[‘b’] = 2
>>> od[‘c’] = 3
>>> od
OrderedDict([(‘a’, 1), (‘b’, 2), (‘c’, 3)])
# 转为字典
>>> import json
>>> json.dumps(od)
‘{“a”: 1, “b”: 2, “c”: 3}’
OrderedDict输出的结果是列表,元组为元素,如果想返回字典格式,可以通过json模块进行转化。
3.6 数据类型转换
3.6.1 常见数据类型转换
# 转整数
>>> i = ‘1’
>>> type(i)
<type ‘str’>
>>> type(int(i))
<type ‘int’>
# 转浮点数
>>> f = 1
>>> type(f)
<type ‘int’>
>>> type(float(f))
<type ‘float’>
# 转字符串
>>> i = 1
>>> type(i)
<type ‘int’>
>>> type(int(1))
<type ‘int’>
# 字符串转列表
方式1:
>>> s = ‘abc’
>>> lst = list(s)
>>> lst
[‘a’, ‘b’, ‘c’]
方式2:
>>> s = ‘abc 123’
>>> s.split()
[‘abc’, ‘123’]
# 列表转字符串
>>> s = “”
>>> s = ”.join(lst)
>>> s
‘abc’
# 元组转列表
>>> lst
[‘a’, ‘b’, ‘c’]
>>> t = tuple(lst)
>>> t
(‘a’, ‘b’, ‘c’)
# 列表转元组
>>> lst = list(t)
>>> lst
[‘a’, ‘b’, ‘c’]
# 字典格式字符串转字典
方法1:
>>> s = ‘{“a”: 1, “b”: 2, “c”: 3}’
>>> type(s)
<type ‘str’>
>>> d = eval(s)
>>> d
{‘a’: 1, ‘c’: 3, ‘b’: 2}
>>> type(d)
<type ‘dict’>
方法2:
>>> import json
>>> s = ‘{“a”: 1, “b”: 2, “c”: 3}’
>>> json.loads(s)
{u’a’: 1, u’c’: 3, u’b’: 2}
>>> d = json.loads(s)
>>> d
{u’a’: 1, u’c’: 3, u’b’: 2}
>>> type(d)
<type ‘dict’>
3.6.2 学习两个新内建函数
1) join()
join()函数是字符串操作函数,用于字符串连接。
# 字符串时,每个字符作为单个体
>>> s = “ttt”
>>> “.”.join(s)
‘t.t.t’
# 以逗号连接元组元素,生成字符串,与上面的列表用法一样。
>>> t = (‘a’, ‘b’, ‘c’)
>>> s = “,”.join(t)
>>> s
‘a,b,c’
# 字典
>>> d = {‘a’:1, ‘b’:2, ‘c’:3}
>>> “,”.join(d)
‘a,c,b’
2) eval()
eval()函数将字符串当成Python表达式来处理。
>>> s = “abc”
>>> eval(‘s’)
‘abc’
>>> a = 1
>>> eval(‘a + 1’)
2
>>> eval(‘1 + 1’)
2
第四章 Python运算符和流程控制
在第一章的时候讲解了运算操作符和赋值操作符,这章来学习下其他常用操作符。
4.1 基本运算符
4.1.1 比较操作符
操作符 |
描述 |
示例 |
== |
相等 |
>>> 1 == 1 True |
!= |
不相等 |
>>> 1 != 1 False |
> |
大于 |
>>> 2 > 1 True |
< |
小于 |
>>> 2 < 1 False |
>= |
大于等于 |
>>> 1 >= 1 True |
<= |
小于等于 |
>>> 1 <= 1 True |
4.1.2 逻辑运算符
逻辑运算符常用于表达式判断。
示例:
>>> a = “a”
>>> b = “b”
>>> a and b
‘b’
>>> a or b
‘a’
>>> a = “”
>>> b = “b”
>>> a and b
”
>>> a or b
‘b’
and操作符判断表达式,如果a和b都为真,返回b的值,否则返回a的值。
or操作符也是判断表达式,如果a和b都为真,返回a的值,否则返回b的值。
类似于shell里的&&和||:[ ‘a’ == ‘b’ ] && echo no || echo yes
>>> a = “”
>>> if not a:
… print “yes”
… else:
… print “no”
…
yes
>>> a = “a”
>>> if not a:
… print “yes”
… else:
… print “no”
…
no
not操作符用于布尔值(true和false)判断不为真,与if语句连用。上面是不为真用not,那为真时怎么弄呢?
>>> a = “a”
>>> if a:
… print “yes”
… else:
… print “no”
…
yes
>>> a = “”
>>> if a:
… print “yes”
… else:
… print “no”
…
no
4.1.3 成员运算符
操作符 |
描述 |
in |
在对象里 |
not in |
不在对象里 |
示例:
>>> ‘a’ in ‘abc’
True
>>> ‘d’ in ‘abc’
False
>>> lst = [‘a’,’b’,’c’]
>>> ‘a’ in lst
True
>>> ‘d’ in lst
False
>>> ‘a’ not in ‘abc’
False
>>> ‘d’ not in ‘abc’
True
>>> ‘d’ not in lst
True
4.1.4 标识运算符
操作符 |
描述 |
is |
内存地址相等 |
is not |
内存地址不相等 |
示例:
>>> a = []
>>> b = []
>>> id(a)
139741563903296
>>> id(b)
139741563902144
>>> a is b
False
>>> a is not b
True
这里用到了id()函数,用于获取对象在内存的地址。
4.2 条件判断
4.2.1 单分支
>>> a = 20
>>> if a < 18:
… print “no”
… else:
… print “yes”
…
yes
有时候一个简单的判断语句,感觉这样写麻烦,有没有一条命令搞定的。
有的,简写if语句:
>>> a = 20
>>> result = (“yes” if a == 20 else “no”)
>>> result
‘yes’
>>> type(result)
<type ‘str’>
# 有时会看到别人代码用中括号,意思把结果存储为一个列表
>>> result = [“yes” if a == 20 else “no”]
>>> result
[‘yes’]
>>> type(result)
<type ‘list’>
4.2.2 多分支
>>> a = 20
>>> if a < 18:
… print “no”
… elif a == 20:
… print “yes”
… else:
… print “other”
…
yes
4.2.3 pass语句
>>> a = 20
>>> if a < 18:
… print “no”
… elif a == 20:
… pass
… else:
… print “other”
…
pass语句作用是不执行当前代码块,与shell中的冒号做作用一样。
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ群:323779636(Shell/Python运维开发群)
4.3 循环语句
4.3.1 for
1)迭代对象
遍历字符串,每个字符当做单个遍历:
>>> for i in “abc”:
… print i
…
a
b
c
使用range()函数生成一个数字序列列表,并遍历:
>>> for i in range(1,5):
… print i
…
1
2
3
4
回顾下第三章讲的遍历字典:
>>> d = {‘a’:1, ‘b’:2, ‘c’:3}
>>> for i in d.iteritems():
… print “%s:%s” %(i[0],i[1])
…
a:1
c:3
b:2
2)嵌套循环
逐个循环判断外层列表里元素是否存在内层列表:
>>> for i in range(1,6):
… for x in range(3,8):
… if i == x:
… print i
…
3
4
5
3)简写语句
简写for语句:
>>> result = (x for x in range(5))
>>> result
<generator object <genexpr> at 0x030A4FD0>
>>> type(result)
<type ‘generator’>
说明:在这里用小括号,会生成一个生成器,在这里知道下就可以了,不过多讲解,后面会专门生成器用途。
# 同样用中括号会以列表存储
>>> result = [ x for x in range(5)]
>>> type(result)
<type ‘list’>
>>> result
[0, 1, 2, 3, 4]
for和if语句写一行:
>>> result = [ x for x in range(5) if x % 2 == 0]
>>> result
[0, 2, 4]
4.3.2 while
语法:
while 表达式:
执行语句…
1)输出序列
当条件满足时,停止循环:
>>> while count < 5:
… print count
… count += 1
…
0
1
2
3
4
2)死循环
>>> import time
>>> i = 1
>>> while True:
… print i
… i += 1
… time.sleep(0.5)
…
1
2
3
…… # 会一直循环,直到海枯石烂,天荒地老…
注意:当表达式值为true或者非零时,都会一直循环。
4.3.3 continue和break语句
continue当满足条件时,跳出本次循环。
break当满足条件时,跳出所有循环。
for和while用法一样。
1)基本使用
满足条件跳出当前循环:
#!/usr/bin/env python
for i in range(1,6):
if i == 3:
continue
else:
print i
# python test.py
1
2
4
5
#!/usr/bin/env python
count = 0
while count < 5:
count += 1
if count == 3:
continue
else:
print count
# python test.py
1
2
4
5
满足条件终止循环:
#!/usr/bin/env python
for i in range(1,6):
if i == 3:
break
else:
print i
# python test.py
1
2
#!/usr/bin/env python
count = 0
while count < 5:
count += 1
if count == 3:
break
else:
print count
# python test.py
1
2
2)输入错误次数超过三次退出
例如:提示用户输入名字,如果名字是xiaoming输入正确退出,否则一直提示重新输入,直到三次退出。
#!/usr/bin/env python
count = 0
while 1:
if count < 3:
name = raw_input(“Please input your name: “).strip() # .strip()去除首尾空格
if len(name) == 0:
print “Input can not be empty!”
count += 1
continue
elif name == “xiaoming”:
print “OK.”
break
else:
print “Name input error, please input again!”
count += 1
else:
print “Error three times, Exit!”
break
4.3.4 else语句
else语句会在循环正常执行完才执行。在for循环用法也一样。
>>> count = 0
>>> while count < 5:
… print count
… count += 1
… else:
… print “end”
…
0
1
2
3
4
end
>>> count = 0
>>> while count < 5:
… print count
… break
… else:
… print “end”
…
0
第五章 Python函数你知多少
函数作用:把一些复杂的代码封装起来,函数一般都是一个功能,用的时候才调用,提高重复利用率和简化程序结构。
5.1 语法
def functionName(parms1, parms2, …):
code block
return expression
函数以def关键字开头,空格后跟函数名,括号里面是参数,用于传参,函数代码段里面引用。
5.2 函数定义与调用
# 定义函数
>>> def func():
… print “Hello world!”
… return “Hello world!”
…
# 调用函数
>>> func()
Hello world!
‘Hello world!’
当我们定义好函数,是不执行的,没有任何输出。当输入函数名后跟双小括号才会执行函数里写的代码。
顺便说下print和return区别:
有没有点奇怪!为什么print和return输出一样呢,return就加个单引号,貌似也没啥明显区别啊!其实在解释器下所有的结果都会输出的。
先了解下return作用:结束函数,并返回一个值。如果不跟表达式,会返回一个None。
好,那么我们深入了解下他们区别,举个例子,写个py程序:
#!/usr/bin/env python
def func():
print “1: Hello world!”
return “2: Hello world!”
func()
# python test.py
1: Hello world!
明白点了嘛?print是打印对象的值,而return是返回对象的值。也就是说你return默认是将对象值存储起来,要想知道里面的值,可以用print可以打印。
#!/usr/bin/env python
def func():
print “1: Hello world!”
return “2: Hello world!”
print func()
# python test.py
1: Hello world!
2: Hello world!
为什么函数里面不用print就在这里,往往我们定义一个函数是不需要打印的,而是交给其他代码去处理这个函数返回值。当然,print在调试函数代码时会起到很好的帮助。
5.3 函数参数
5.3.1 接受参数
>>> def func(a, b):
… print a + b
…
>>> func(1, 2)
3
>>> func(1, 2, 3)
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
TypeError: func() takes exactly 2 arguments (3 given)
a和b可以理解为是个变量,可由里面代码块引用。调用函数时,小括号里面的表达式数量要对应函数参数数量,并且按传参按位置赋予函数参数位置。如果数量不对应,会抛出TypeError错误。
当然,函数参数也可以是数组:
>>> def func(a):
… print a
…
>>> func([1,2,3])
[1, 2, 3]
>>> func({‘a’:1,’b’:2})
{‘a’: 1, ‘b’: 2}
如果不想一一对应传参,可以指定参数值:
>>> def func(a,b):
… print a + b
…
>>> func(b=2,a=1)
3
5.3.2 函数参数默认值
参数默认值是预先定义好,如果调用函数时传入了这个值,那么将以传入的为实际值,否则是默认值。
>>> def func(a, b=2):
… print a + b
…
>>> func(1)
3
>>> func(1, 3)
4
5.3.3 接受任意数量参数
上面方式固定了参数多个,当不知道多少参数时候可以用以下方式。
单个星号使用:
>>> def func(*a):
… print a
…
>>> func(1,2,3)
(1, 2, 3)
单个星号存储为一个元组。
两个星号使用:
>>> def func(**a):
… print a
…
>>> func(a=1, b=2, c=3)
{‘a’: 1, ‘c’: 3, ‘b’: 2}
两个星号存储为一个字典。可见它们都是以数组的形式传入。
你也许在查资料的时候,会看到这样写的函数参数(*args, **kwargs),与上面只是名字不一样罢了 :
>>> def func(*args, **kwargs):
… print args
… print kwargs
…
>>> func(1,2,3,a=1,b=2,c=3)
(1, 2, 3)
{‘a’: 1, ‘c’: 3, ‘b’: 2}
与普通参数一起使用:
>>> def func(a, b, *c):
… print a + b
… print c
…
>>> func(1,2,3,5,6)
3
(3, 5, 6)
>>> def func(a, b, **c):
… print a + b
… print c
…
>>> func(1,2,a=1,b=2,c=3)
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
TypeError: func() got multiple values for keyword argument ‘a’
>>> func(1,2,c=3,d=4,e=5)
3
{‘c’: 3, ‘e’: 5, ‘d’: 4}
抛出异常,是因为传入的第一个参数1,和第三个参数a=1,都认为是传入函数参数a了。请注意下这点。
5.4 作用域
作用域听着挺新鲜,其实很简单,就是限制一个变量或一段代码可用范围,不在这个范围就不可用。提高了程序逻辑的局部性,减少名字冲突。
作用域范围一般是:全局(global)->局部(local)->内置(build-in)
先看看全局和局部变量:
>>> a = 2
>>> def func():
… b = 3
…
>>> a
2
>>> b
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
NameError: name ‘b’ is not defined
a变量的作用域是整个代码中有效,称为全局变量,也就是说一段代码最开始定义的变量。
b变量的作用域在函数内部,也就是局部变量,在函数外是不可引用的。
这么一来,全局变量与局部变量即使名字一样也不冲突。
如果函数内部的变量也能在全局引用,需要使用global声明:
>>> def func():
… global b
… b = 3
…
>>> b
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
NameError: name ‘b’ is not defined
>>> func()
>>> b
3
抛出异常,说明一个问题,当函数没引用使用,里面的代码块是没有解释的。
使用global声明变量后外部是可以调用函数内部的变量的。
5.5 嵌套函数
# 不带参数
>>> def func():
… x = 2
… def func2():
… return x
… return func2 # 返回func2函数
…
>>> func()()
2
>>> func2()
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
NameError: name ‘func2’ is not defined
>>> def func():
… x = 2
… global func2
… def func2():
… return x
… return func2
…
>>> func()()
2
>>> func2()
2
内层函数可以访问外层函数的作用域。内嵌函数只能被外层函数调用,但也可以使用global声明全局作用域。
调用内部函数的另一种用法:
# 带参数
>>> def func(a):
… def func2(b):
… return a * b
… return func2
…
>>> f = func(2) # 变量指向函数。是的,变量可以指向函数。
>>> f(5)
10
>>> func(2)(5)
10
内层函数可以访问外层函数的作用域 。但
变量不能重新赋值,举例说明:
>>> def func():
… x = 2
… def func2():
… x = 3
… func2()
… return x
…
>>> func()
2
>>> def func():
… x = 2
… def func2():
… x += 1
… func2()
… return x
…
>>> func()
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
File “<stdin>”, line 5, in func
File “<stdin>”, line 4, in func2
UnboundLocalError: local variable ‘x’ referenced before assignment
5.6 闭包
“官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
其实,上面嵌套函数就是闭包一种方式:
>>> def func(a):
… def func2(b):
… return a * b
… return func2
…
>>> f = func(2) # 变量指向函数。是的,变量可以指向函数。
>>> f(5)
10
func是一个函数,里面又嵌套了一个函数func2,外部函数传过来的a参数,这个变量会绑定到函数func2。func函数以内层函数func2作为返回值,然后把func函数存储到f变量中。当外层函数调用内层函数时,内层函数才会执行(func()()),就创建了一个闭包。
5.7 高阶函数
高阶函数是至少满足这两个任意中的一个条件:
1) 能接受一个或多个函数作为输入。
2)输出一个函数。
abs、map、reduce都是高阶函数,后面会讲解。
其实,上面所讲的嵌套函数也是高阶函数。
举例说明下高阶函数:
>>> def f(x):
… return x * x
…
>>> def f2(func, y):
… return func(y)
…
>>> f2(f, 2)
4
这里的f2就是一个高阶函数,因为它的第一个参数是一个函数,满足了第一个条件。
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ群:323779636(Shell/Python运维开发群)
5.8 函数装饰器
装饰器(decorator)本身是一个函数,包装另一个函数或类,它可以让其他函数在不需要改动代码情况下动态增加功能,装饰器返回的也是一个函数对象。
先举一个例子,说明下装饰器的效果,定义两个函数,分别传参计算乘积:
#!/usr/bin/python
# -*- coding: utf-8 -*-
def f1(a, b):
print “f1 result: ” + str(a * b)
def f2(a, b):
print “f2 result: ” + str(a * b)
f1(1, 2)
f2(2, 2)
# python test.py
f1 result: 2
f2 result: 4
跟预期的那样,打印出了乘积。
如果我想给这两个函数加一个打印传入的参数,怎么办,应该这样:
#!/usr/bin/python
# -*- coding: utf-8 -*-
def f1(a, b):
print “f1 parameter: %d %d” %(a, b)
print “f1 result: ” + str(a * b)
def f2(a, b):
print “f2 parameter: %d %d” %(a, b)
print “f2 result: ” + str(a * b)
f1(1, 2)
f2(2, 2)
# python test.py
f1 parameter: 1 2
f1 result: 2
f2 parameter: 2 2
f2 result: 4
按照所想的打印了传入的参数,有没有方法能更简洁点呢,来看看装饰器后的效果。
#!/usr/bin/python
# -*- coding: utf-8 -*-
def deco(func):
def f(a, b):
print “%s parameter: %d %d” %(func.__name__, a, b)
return func(a, b)
return f
@deco
def f1(a, b):
print “f1 result: ” + str(a * b)
@deco
def f2(a, b):
print “f2 result: ” + str(a * b)
f1(1, 2)
f2(2, 2)
# python test.py
f1 parameter: 1 2
f1 result: 2
f2 parameter: 2 2
f2 result: 4
可见用装饰器也实现了上面方法,给要装饰的函数添加了装饰器定义的功能,这种方式显得是不是更简洁呢!
好,那么我们继续深入学习装饰器用法。
5.8.1 无参数装饰器
方式1:函装饰器函数装饰函数
#!/usr/bin/python
# -*- coding: utf-8 -*-
def deco(func):
return func
def f1():
print “Hello world!”
myfunc = deco(f1)
myfunc()
# python test.py
Hello world!
方式2:使用语法糖”@”来装饰函数
#!/usr/bin/python
# -*- coding: utf-8 -*-
def deco(func):
return func
@deco
def f1():
print “Hello world!”
f1()
# python test.py
Hello world!
方式1是将一个函数作为参数传给装饰器函数。
方式2使用了语法糖,也实现同样效果。
其实两种方式结果一样,方式1需要每次使用装饰器时要先变量赋值下,而方式2使用装饰器时直接用语法糖”@”引用,会显得更方便些,实际代码中一般也都是用语法糖。
5.8.2 带参数装饰器
#!/usr/bin/python
# -*- coding: utf-8 -*-
def deco(func):
def f(a, b):
print “function name: %s” % func.__name__ # __name__属性是获取函数名,为了说明执行了这个函数
return func(a, b) # 用接受过来的func函数来处理传过来的参数
return f
@deco
def f1(a, b):
print “Hello world!”
print a + b
f1(2, 2)
# python test.py
function name: f1
Hello world!
4
3)不固定参数
#!/usr/bin/python
# -*- coding: utf-8 -*-
def log(func):
def deco(*args, **kwargs):
print “function name: %s” % func.__name__
return func(*args, **kwargs)
return deco
@log
def f1(a, b):
print “f1() run.”
print a + b
f1(1,2)
# python test.py
function name: f1
f1() run.
3
4)装饰器加参数
#!/usr/bin/python
# -*- coding: utf-8 -*-
# 三层函数,调用log函数返回deco函数,再调用返回的函数deco,则返回值是_deco函数
def log(arg):
def deco(func):
def _deco(*args, **kwargs):
print “%s – function name: %s” % (arg, func.__name__)
return func(*args, **kwargs)
return _deco
return deco
@log(“info”)
def f1(a, b):
print “f1() run.”
print a + b
f1(1,2)
# python test.py
info – function name: f1
f1() run.
3
再举一个例子,给函数输出字符串带颜色:
#!/usr/bin/python
# -*- coding: utf-8 -*-
def fontColor(color):
begin = “\033[“
end = “\033[0m”
d = {
‘red’:’31m’,
‘green’:’32m’,
‘yellow’:’33m’,
‘blue’:’34m’
}
def deco(func):
print begin + d[color] + func() + end
return deco
@fontColor(“red”)
def f():
return “Hello world!”
@fontColor(“green”)
def f2():
return “Hello world!”
可以看出装饰器处理方式满足了高阶函数的条件,所以装饰器也是一种高阶函数。
装饰器优点:灵活给装饰器增加功能,而不修改函数,提高代码可重复利用性,增加可读性。
5.9 匿名函数
匿名函数:定义函数的一种形式,无需定义函数名和语句块,因此代码逻辑会受到局限,同时也减少代码量,增加可读性。
在Python中匿名函数是lambda。
举例子说明def关键字与lambda函数定义函数区别:
# 普通函数
>>> def func():
… return “Hello world!”
…
>>> func()
>>> def func(a, b):
… return a * b
…
>>> func(2, 2)
4
# 匿名函数
>>> f = lambda:”Hello world!”
>>> f()
‘Hello world!’
>>> f = lambda a, b: a * b # 冒号左边是函数参数,右边是返回值
>>> f(2, 2)
4
lambda函数一行就写成一个函数功能,省去定义函数过程,让代码更加精简。
5.10 内置高阶函数
5.10.1 map()
语法:map(function, sequence[, sequence, …]) -> list
将序列中的元素通过函数处理返回一个新列表。
例如:
>>> lst = [1,2,3,4,5]
>>> map(lambda x:str(x)+”.txt”, lst)
[‘1.txt’, ‘2.txt’, ‘3.txt’, ‘4.txt’, ‘5.txt’]
5.10.2 filter()
语法:filter(function or None, sequence) -> list, tuple, or string
将序列中的元素通过函数处理返回一个新列表、元组或字符串。
例如:过滤列表中的奇数
>>> lst = [1,2,3,4,5]
>>> filter(lambda x:x%2==0, lst)
[2, 4]
5.10.3 reduce()
语法:reduce(function, sequence[, initial]) -> value
reduce()是一个二元运算函数,所以只接受二元操作函数。
例如:计算列表总和
>>> lst = [1,2,3,4,5]
>>> reduce(lambda x,y:x+y, lst)
15
先将前两个元素相加等于3,再把结果与第三个元素相加等于6,以此类推。这就是reduce()函数功能。
第六章 Python类(面向对象编程)
什么是面向对象编程?
面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是一种计算机编程架构。Python就是这种编程语言。
面向对象程序设计中的概念主要包括:对象、类、继承、动态绑定、封装、多态性、消息传递、方法。
1)对象:类的实体,比如一个人。
2)类:一个共享相同结构和行为的对象的集合。通俗的讲就是分类,比如人是一类,动物是一类。
3)继承:类之间的关系,比如猫狗是一类,他们都有四条腿,狗继承了这个四条腿,拥有了这个属性。
4)动态绑定:在不修改源码情况下,动态绑定方法来给实例增加功能。
5)封装:把相同功能的类方法、属性封装到类中,比如人两条腿走路,狗有四条腿走路,两个不能封装到一个类中。
6)多态性:一个功能可以表示不同类的对象,任何对象可以有不同的方式操作。比如一个狗会走路、会跑。
7)消息传递:一个对象调用了另一个对象的方法。
8)方法:类里面的函数,也称为成员函数。
对象=属性+方法。
属性:变量。
方法:函数。
实例化:创建一个类的具体实例对象。比如一条泰迪。
什么是类?
类是对对象的抽象,对象是类的实体,是一种数据类型。它不存在内存中,不能被直接操作,只有被实例化对象时,才会变的可操作。
类是对现实生活中一类具有共同特征的事物的抽象描述。
6.1 类和类方法语法
# 类
class ClassName():
pass
# 类中的方法
def funcName(self):
pass
self代表类本身。类中的所有的函数的第一个参数必须是self。
6.2 类定义与调用
#!/usr/bin/python
# -*- coding: utf-8 -*-
class MyClass():
x = 100
def func(self, name):
return “Hello %s!” % name
def func2(self):
return self.x
mc = MyClass() # 类实例化,绑定到变量mc
print mc.x # 类属性引用
print mc.func(“xiaoming”) # 调用类方法
print mc.func2()
# python test.py
100
Hello xiaoming!
100
上面示例中,x变量称为类属性,类属性又分为类属性和实例属性:
1)类属性属于类本身,通过类名访问,一般作为全局变量。比如mc.x
2)如果类方法想调用类属性,需要使用self关键字调用。比如self.x
3)实例属性是实例化后对象的方法和属性,通过实例访问,一般作为局部变量。下面会讲到。
4)当实例化后可以动态类属性,下面会讲到。
类方法调用:
1)类方法之间调用:self.<方法名>(参数),参数不需要加self
2)外部调用:<实例名>.<方法名>
6.3 类的说明
给类添加注释,提高可阅读性,可通过下面方式查看。
方法1:
>>> class MyClass:
… “””
… 这是一个测试类.
… “””
… pass
…
>>> print MyClass.__doc__
这是一个测试类.
>>>
方法2:
>>> help(MyClass)
Help on class MyClass in module __main__:
class MyClass
| 这是一个测试类.
6.4 类内置方法
内置方法 |
描述 |
__init__(self, …) |
初始化对象,在创建新对象时调用 |
__del__(self) |
释放对象,在对象被删除之前调用 |
__new__(cls, *args, **kwd) |
实例的生成操作,在__init__(self)之前调用 |
__str__(self) |
在使用print语句时被调用,返回一个字符串 |
__getitem__(self, key) |
获取序列的索引key对应的值,等价于seq[key] |
__len__(self) |
在调用内建函数len()时被调用 |
__cmp__(str, dst) |
比较两个对象src和dst |
__getattr__(s, name) |
获取属性的值 |
__setattr__(s, name, value) |
设置属性的值 |
__delattr__(s, name) |
删除属性 |
__gt__(self, other) |
判断self对象是否大于other对象 |
__lt__(self, other) |
判断self对象是否小于other对象 |
__ge__(self, other) |
判断self对象是否大于或等于other对象 |
__le__(self, other) |
判断self对象是否小于或等于other对象 |
__eq__(self, other) |
判断self对象是否等于other对象 |
__call__(self, *args) |
把实例对象作为函数调用 |
6.5 初始化实例属性
很多类一般都有初始状态的,常常定义对象的共同特性,也可以用来定义一些你希望的初始值。
Python类中定义了一个构造函数__init__,对类中的实例定义一个初始化对象,常用于初始化类变量。当类被实例化,第二步自动调用的函数,第一步是__new__函数。
__init__构造函数也可以让类传参,类似于函数的参数。
__init__构造函数使用:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class MyClass():
def __init__(self):
self.name = “xiaoming”
def func(self):
return self.name
mc = MyClass()
print mc.func()
# python test.py
xiaoming
__init__函数定义到类的开头.self.name变量是一个实例属性,只能在类方法中使用,引用时也要这样self.name。
类传参:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class MyClass():
def __init__(self, name):
self.name = name
def func(self, age):
return “name: %s,age: %s” %(self.name, age)
mc = MyClass(‘xiaoming’) # 第一个参数是默认定义好的传入到了__init__函数
print mc.func(’22’)
# python test.py
Name: xiaoming, Age: 22
6.6 类私有化(私有属性)
6.6.1 单下划线
实现模块级别的私有化,以单下划线开头的变量和函数只能类或子类才能访问。当from modulename import * 时将不会引入以单下划线卡头的变量和函数。
#!/usr/bin/python
# -*- coding: utf-8 -*-
class MyClass():
_age = 21
def __init__(self, name=None):
self._name = name
def func(self, age):
return “Name: %s, Age: %s” %(self._name, age)
mc = MyClass(‘xiaoming’)
print mc.func(’22’)
print mc._name
print mc._age
# python test.py
Name: xiaoming, Age: 22
xiaoming
21
_age和self._name变量其实就是做了个声明,说明这是个内部变量,外部不要去引用它。
6.6.2 双下划线
以双下划线开头的变量,表示私有变量,受保护的,只能类本身能访问,连子类也不能访问。避免子类与父类同名属性冲突。
#!/usr/bin/python
# -*- coding: utf-8 -*-
class MyClass():
__age = 21
def __init__(self, name=None):
self.__name = name
def func(self, age):
return “Name: %s, Age: %s” %(self.__name, age)
mc = MyClass(‘xiaoming’)
print mc.func(’22’)
print mc.__name
print mc.__age
# python test.py
Name: xiaoming, Age: 22
Traceback (most recent call last):
File “test.py”, line 12, in <module>
print mc.__name
AttributeError: MyClass instance has no attribute ‘__name’
可见,在单下划线基础上又加了一个下划线,同样方式类属性引用,出现报错。说明双下划线变量只能本身能用。
如果想访问私有变量,可以这样:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class MyClass():
__age = 21
def __init__(self, name=None):
self.__name = name
def func(self, age):
return “Name: %s, Age: %s” %(self.__name, age)
mc = MyClass(‘xiaoming’)
print mc.func(’22’)
print mc._MyClass__name
print mc._MyClass__age
# python test.py
Name: xiaoming, Age: 22
xiaoming
21
self.__name变量编译成了self._MyClass__name,以达到不能被外部访问的目的,并没有真正意义上的私有。
6.6.3 特殊属性(首尾双下划线)
一般保存对象的元数据,比如__doc__、__module__、__name__:
>>> class MyClass:
“””
这是一个测试类说明的类。
“””
pass
# dic()返回对象内变量、方法
>>> dir(MyClass)
[‘__doc__’, ‘__module__’]
>>> MyClass.__doc__
‘\n\t\xd5\xe2\xca\xc7\xd2\xbb\xb8\xf6\xb2\xe2\xca\xd4\xc0\xe0\xcb\xb5\xc3\xf7\xb5\xc4\xc0\xe0\xa1\xa3\n\t’
>>> MyClass.__module__
‘__main__’
>>> MyClass.__name__
‘MyClass’
这里用到了一个新内置函数dir(),不带参数时,返回当前范围内的变量、方法的列表。带参数时,返回参数的属性、方法的列表。
Python自己调用的,而不是用户来调用。像__init__ ,你可以重写。 博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ群:323779636(Shell/Python运维开发群)
6.7 类的继承
子类继承父类,子类将继承父类的所有方法和属性,提高代码重用。
1)简单继承
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Parent():
def __init__(self, name=None):
self.name = name
def func(self, age):
return “Name: %s, Age: %s” %(self.name, age)
class Child(Parent):
pass
mc = Child(‘xiaoming’)
print mc.func(’22’)
print mc.name
# python test.py
Name: xiaoming, Age: 22
xiaoming
2)子类实例初始化
如果子类重写了构造函数,那么父类的构造函数将不会执行:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Parent():
def __init__(self):
self.name_a = “xiaoming”
def funcA(self):
return “function A: %s” % self.name_a
class Child(Parent):
def __init__(self):
self.name_b = “zhangsan”
def funcB(self):
return “function B: %s” % self.name_b
mc = Child()
print mc.name_b
print mc.funcB()
print mc.funcA()
# python test.py
zhangsan
function B: zhangsan
Traceback (most recent call last):
File “test2.py”, line 17, in <module>
print mc.funcA()
File “test2.py”, line 7, in funcA
return “function A: %s” % self.name_a
AttributeError: Child instance has no attribute ‘name_a’
抛出错误,提示调用funcA()函数时,没有找到name_a属性,也就说明了父类的构造函数并没有执行。
如果想解决这个问题,可通过下面两种方法:
方法1:调用父类构造函数
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Parent():
def __init__(self):
self.name_a = “xiaoming”
def funcA(self):
return “function A: %s” % self.name_a
class Child(Parent):
def __init__(self):
Parent.__init__(self)
self.name_b = “zhangsan”
def funcB(self):
return “function B: %s” % self.name_b
mc = Child()
print mc.name_b
print mc.funcB()
print mc.funcA()
# python test.py
zhangsan
function B: zhangsan
function A: xiaoming
方法2:使用supper()函数继承
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Parent(object):
def __init__(self):
self.name_a = “xiaoming”
def funcA(self):
return “function A: %s” % self.name_a
class Child(Parent):
def __init__(self):
super(Child, self).__init__()
self.name_b = “zhangsan”
def funcB(self):
return “function B: %s” % self.name_b
mc = Child()
print mc.name_b
print mc.funcB()
print mc.funcA()
# python test.py
zhangsan
function B: zhangsan
function A: xiaoming
6.8 多重继承
每个类可以拥有多个父类,如果调用的属性或方法在子类中没有,就会从父类中查找。多重继承中,是依次按顺序执行。
类简单的继承:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class A:
def __init__(self):
self.var1 = “var1”
self.var2 = “var2”
def a(self):
print “a…”
class B:
def b(self):
print “b…”
class C(A,B):
pass
c = C()
c.a()
c.b()
print c.var1
print c.var2
# python test.py
a…
b…
var1
var2
类C继承了A和B的属性和方法,就可以像使用父类一样使用它。
子类扩展方法,直接在子类中定义即可:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class A:
def __init__(self):
self.var1 = “var1”
self.var2 = “var2”
def a(self):
print “a…”
class B:
def b(self):
print “b…”
class C(A,B):
def test(self):
print “test…”
c = C()
c.a()
c.b()
c.test()
print c.var1
print c.var2
# python test.py
a…
b…
test…
var1
var2
在这说明下经典类和新式类。
经典类:默认没有父类,也就是没继承类。
新式类:有继承的类,如果没有,可以继承object。在Python3中已经默认继承object类。
经典类在多重继承时,采用从左到右深度优先原则匹配,而新式类是采用C3算法(不同于广度优先)进行匹配。两者主要区别在于遍历父类算法不同,具体些请在网上查资料。
6.9 方法重载
直接定义和父类同名的方法,子类就修改了父类的动作。
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Parent():
def __init__(self, name=’xiaoming’):
self.name = name
def func(self, age):
return “Name: %s, Age: %s” %(self.name, age)
class Child(Parent):
def func(self, age=22):
return “Name: %s, Age: %s” %(self.name, age)
mc = Child()
print mc.func()
# python test.py
Name: xiaoming, Age: 22
6.10 修改父类方法
在方法重载中调用父类的方法,实现添加功能。
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Parent():
def __init__(self, name=’xiaoming’):
self.name = name
def func(self, age):
return “Name: %s, Age: %s” %(self.name, age)
class Child(Parent):
def func(self, age):
print “——”
print Parent.func(self, age) # 调用父类方法
print “——”
mc = Child()
mc.func(’22’)
# python test.py
——
Name: xiaoming, Age: 22
——
还有一种方式通过super函数调用父类方法:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Parent():
def __init__(self, name=’xiaoming’):
self.name = name
def func(self, age):
return “Name: %s, Age: %s” %(self.name, age)
class Child(Parent):
def func(self, age):
print “——”
print super(Child, self).func(age)
print “——”
mc = Child()
mc.func(’22’)
# python test.py
——
Traceback (most recent call last):
File “test2.py”, line 15, in <module>
mc.func(’22’)
File “test2.py”, line 11, in func
print super(Child, self).func(age)
TypeError: must be type, not classobj
抛出错误,因为super继承只能用于新式类,用于经典类就会报错。
那我们就让父类继承object就可以使用super函数了:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Parent(object):
def __init__(self, name=’xiaoming’):
self.name = name
def func(self, age):
return “Name: %s, Age: %s” %(self.name, age)
class Child(Parent):
def func(self, age):
print “——“
print super(Child, self).func(age) # 调用父类方法。在Python3中super参数可不用写。
print “——“
mc = Child()
mc.func(’22’)
# python test.py
——
Name: xiaoming, Age: 22
——
6.11 属性访问的特殊方法
有四个可对类对象增删改查的内建函数,分别是getattr()、hasattr()、setattr()、delattr()。
6.11.1 getattr()
返回一个对象属性或方法。
>>> class A:
… def __init__(self):
… self.name = ‘xiaoming’
… def method(self):
… print “method…”
…
>>> c = A()
>>> getattr(c, ‘name’, ‘Not find name!’)
‘xiaoming’
>>> getattr(c, ‘namea’, ‘Not find name!’)
>>> getattr(c, ‘method’, ‘Not find method!’)
<bound method A.method of <__main__.A instance at 0x93fa70>>
>>> getattr(c, ‘methoda’, ‘Not find method!’)
‘Not find method!’
6.11.2 hasattr()
判断一个对象是否具有属性或方法。返回一个布尔值。
>>> hasattr(c, ‘name’)
True
>>> hasattr(c, ‘namea’)
False
>>> hasattr(c, ‘method’)
True
>>> hasattr(c, ‘methoda’)
False
6.11.3 setattr()
给对象属性重新赋值或添加。如果属性不存在则添加,否则重新赋值。
>>> hasattr(c, ‘age’)
False
>>> setattr(c, ‘age’, 22)
>>> c.age
22
>>> hasattr(c, ‘age’)
True
6.11.4 delattr()
删除对象属性。
>>> delattr(c, ‘age’)
>>> hasattr(c, ‘age’)
False
6.12 类装饰器
与函数装饰器类似,不同的是类要当做函数一样调用:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Deco:
def __init__(self, func):
self._func = func
self._func_name = func.__name__
def __call__(self):
return self._func(), self._func_name
@Deco
def f1():
return “Hello world!”
print f1()
# python test.py
(‘Hello world!’, ‘f1’)
6.13 类内置装饰器
下面介绍类函数装饰器,在实际开发中,感觉不是很常用。
6.10.1 @property
@property属性装饰器的基本功能是把类中的方法当做属性来访问。
在没使用属性装饰器时,类方法是这样被调用的:
>>> class A:
… def __init__(self, a, b):
… self.a = a
… self.b = b
… def func(self):
… print self.a + self.b
…
>>> c = A(2,2)
>>> c.func()
4
>>> c.func
<bound method A.func of <__main__.A instance at 0x7f6d962b1878>>
使用属性装饰器就可以像属性那样访问了:
>>> class A:
… def __init__(self, a, b):
… self.a = a
… self.b = b
… @property
… def func(self):
… print self.a + self.b
…
>>> c = A(2,2)
>>> c.func
4
>>> c.func()
4
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
TypeError: ‘NoneType’ object is not callable
6.10.2 @staticmethod
@staticmethod是静态方法装饰器,可以通过类对象访问,也可以通过实例化后类对象实例访问。
实例方法的第一个参数是self,表示是该类的一个实例,称为类对象实例。
而使用静态方法装饰器,第一个参数就不用传入实例本身(self),那么这个方法当做类对象,由Python自身处理。
看看普通方法的用法:
>>> class A:
… def staticMethod(self):
… print “not static method…”
…
>>> c = A()
>>> c.staticMethod()
not static method…
使用静态方法则是这么用:
>>> class A:
… @staticmethod
… def staticMethod():
… print “static method…”
…
>>> A.staticMethod() # 可以通过类调用静态方法
static method…
>>> c = A()
>>> c.staticMethod() # 还可以使用普通方法调用
static method…
静态方法和普通的非类方法作用一样,只不过命名空间是在类里面,必须通过类来调用。一般与类相关的操作使用静态方法。
6.10.3 @classmethod
@classmethod是类方法装饰器,与静态方法装饰器类似,也可以通过类对象访问。主要区别在于类方法的第一个参数要传入类对象(cls)。
>>> class A:
… @classmethod
… def classMethod(cls):
… print “class method…”
… print cls.__name__
…
>>> A.classMethod()
class method…
A
6.14 __call__方法
可以让类中的方法像函数一样调用。
>>> class A:
… def __call__(self, x):
… print “call…”
… print x
…
>>> c = A()
>>> c(123)
call…
123
>>> class A:
… def __call__(self, *args, **kwargs):
… print args
… print kwargs
…
>>> c = A()
>>> c(1,2,3,a=1,b=2,c=3)
(1, 2, 3)
{‘a’: 1, ‘c’: 3, ‘b’: 2}
第七章 Python异常处理
什么是异常?
顾名思义,异常就是程序因为某种原因无法正常工作了,比如缩进错误、缺少软件包、环境错误、连接超时等等都会引发异常。一个健壮的程序应该把所能预知的异常都应做相应的处理,应对一些简单的异常情况,使得更好的保证程序长时间运行。即使出了问题,也可让维护者一眼看出问题所在。因此本章节讲解的就是怎么处理异常,让你的程序更加健壮。
7.1 捕捉异常语法
try…except…
try:
expression
except [Except Type]:
expression
7.2 异常类型
常见的异常类型:
异常类型 | 用途 |
SyntaxError | 语法错误 |
IndentationError | 缩进错误 |
TypeError
| 对象类型与要求不符合 |
ImportError | 模块或包导入错误;一般路径或名称错误 |
KeyError
| 字典里面不存在的键 |
NameError | 变量不存在 |
IndexError | 下标超出序列范围 |
IOError | 输入/输出异常;一般是无法打开文件 |
AttributeError | 对象里没有属性 |
KeyboardInterrupt | 键盘接受到Ctrl+C |
Exception | 通用的异常类型;一般会捕捉所有异常 |
还有一些异常类型,可以通过dir查看:
>>> import exceptions
>>> dir(exceptions)
[‘ArithmeticError’, ‘AssertionError’, ‘AttributeError’, ‘BaseException’, ‘BufferError’, ‘BytesWarning’, ‘DeprecationWarning’, ‘EOFError’, ‘EnvironmentError’, ‘Exception’, ‘FloatingPointError’, ‘FutureWarning’, ‘GeneratorExit’, ‘IOError’, ‘ImportError’, ‘ImportWarning’, ‘IndentationError’, ‘IndexError’, ‘KeyError’, ‘KeyboardInterrupt’, ‘LookupError’, ‘MemoryError’, ‘NameError’, ‘NotImplementedError’, ‘OSError’, ‘OverflowError’, ‘PendingDeprecationWarning’, ‘ReferenceError’, ‘RuntimeError’, ‘RuntimeWarning’, ‘StandardError’, ‘StopIteration’, ‘SyntaxError’, ‘SyntaxWarning’, ‘SystemError’, ‘SystemExit’, ‘TabError’, ‘TypeError’, ‘UnboundLocalError’, ‘UnicodeDecodeError’, ‘UnicodeEncodeError’, ‘UnicodeError’, ‘UnicodeTranslateError’, ‘UnicodeWarning’, ‘UserWarning’, ‘ValueError’, ‘Warning’, ‘ZeroDivisionError’, ‘__doc__’, ‘__name__’, ‘__package__’]
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ群:323779636(Shell/Python运维开发群) 7.3 异常处理
例如:打印一个没有定义的变量
>>> print a
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
NameError: name ‘a’ is not defined
会抛出异常,提示名字没有定义。如果程序遇到这种情况,就会终止。
那我们可以这样,当没有这个变量的时候就变量赋值,否则继续操作。
>>> try:
… print a
… except NameError:
… a = “”
…
>>> a
”
这样就避免了异常的发生。在开发中往往不知道什么是什么异常类型,这时就可以使用Exception类型来捕捉所有的异常:
例如:打印一个类对象里面没有的属性
>>> class A:
… a = 1
… b = 2
…
>>> c = A()
>>> try:
… print c.c
… except Exception:
… print “Error…”
…
Error…
有时也想把异常信息也打印出来,怎么做呢?
可以把错误输出保存到一个变量中,根据上面例子来:
>>> try:
… print c.c
… except Exception, e:
… print “Error: ” + str(e)
…
Error: A instance has no attribute ‘c’
# 也可以使用as关键字将错误出输出保存到变量中
>>> try:
… print c.c
… except Exception as e:
… print “Error: ” + str(e)
…
Error: A instance has no attribute ‘c’
当出现的异常类型有几种可能性时,可以写多个except:
>>> try:
… print a
… except NameError, e:
… print “NameError: ” + str(e)
… except KeyError, e:
… print “KeyError: ” + str(e)
…
NameError: name ‘a’ is not defined
注意:except也可以不指定异常类型,那么会忽略所有的异常类,这样做有风险的,它同样会捕捉Ctrl+C、sys.exit等的操作。所以使用except Exception更好些。
7.4 else和finally语句
7.4.1 else语句
表示如果try中的代码没有引发异常,则会执行else。
继续按照上面定义的类举例:
>>> try:
… print c.a
… except Exception as e:
… print e
… else:
… print “else…”
…
1
else…
7.4.2 finally语句
表示无论是否异常,都会执行finally。
>>> try:
… print c.c
… except Exception as e:
… print e
… finally:
… print “finally…”
…
A instance has no attribute ‘c’
finally…
一般用于清理工作,比如打开一个文件,不管是否文件是否操作成功,都应该关闭文件。
7.4.3 try…except…else…finally
这是一个完整的语句,当一起使用时,使异常处理更加灵活。
#!/usr/bin/python
# -*- coding: utf-8 -*-
try:
print a
except Exception as e:
print “Error: ” + str(e)
else:
print “else…”
finally:
print “finally…”
# python test.py
python test.py
Error: name ‘a’ is not defined
finally…
需要注意的是:它们语句的顺序必须是try…except…else…finally,否则语法错误!里面else和finally是可选的。
7.5 自定义异常类
raise语句用来手动抛出一个异常,使用方法:
raise ExceptType(ExceptInfo)
例如:抛出一个指定的异常
>>> raise NameError(‘test except…’)
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
NameError: test except…
raise参数必须是一个异常的实例或Exception子类。
上面用的Exception子类,那么我定义一个异常的实例,需要继承Exception类:
>>> class MyError(Exception):
… def __init__(self, value):
… self.value = value
… def __str__(self):
… return self.value
…
>>> raise MyError(“MyError…”)
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
__main__.MyError: MyError…
7.6 assert语句
assert语句用于检查条件表达式是否为真,不为真则触发异常。又称断言语句。
一般用在某个条件为真才能正常工作。
>>> assert 1==1
>>> assert 1!=1
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
AssertionError
>>> assert range(4)==[0,1,2]
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
AssertionError
# 添加异常描述信息
>>> assert 1!=1, “assert description…”
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
AssertionError: assert description…
第八章 Python可迭代对象、迭代器和生成器
- 云栖社区>
- 博客>
- 正文
第八章 Python可迭代对象、迭代器和生成器
李振良
2016-10-22 10:40:11 浏览2276
8.1 可迭代对象(Iterable)
大部分对象都是可迭代,只要实现了__iter__方法的对象就是可迭代的。
__iter__方法会返回迭代器(iterator)本身,例如:
>>> lst = [1,2,3]
>>> lst.__iter__()
<listiterator object at 0x7f97c549aa50>
Python提供一些语句和关键字用于访问可迭代对象的元素,比如for循环、列表解析、逻辑操作符等。
判断一个对象是否是可迭代对象:
>>> from collections import Iterable # 只导入Iterable方法
>>> isinstance(‘abc’, Iterable)
True
>>> isinstance(1, Iterable)
False
>>> isinstance([], Iterable)
True
这里的isinstance()函数用于判断对象类型,后面会讲到。
可迭代对象一般都用for循环遍历元素,也就是能用for循环的对象都可称为可迭代对象。
例如,遍历列表:
>>> lst = [1, 2, 3]
>>> for i in lst:
… print i
…
1
2
3
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ群:323779636(Shell/Python运维开发群) 8.2 迭代器(Iterator)
具有next方法的对象都是迭代器。在调用next方法时,迭代器会返回它的下一个值。如果next方法被调用,但迭代器没有值可以返回,就会引发一个StopIteration异常。
使用迭代器的好处:
1)如果使用列表,计算值时会一次获取所有值,那么就会占用更多的内存。而迭代器则是一个接一个计算。
2)使代码更通用、更简单。
8.2.1 迭代器规则
回忆下在Python数据类型章节讲解到字典迭代器方法,来举例说明下迭代器规则:
>>> d = {‘a’:1, ‘b’:2, ‘c’:3}
>>> d.iteritems()
<dictionary-itemiterator object at 0x7f97c3b1bcb0>
# 判断是否是迭代器
>>> from collections import Iterator
>>> isinstance(d, Iterator)
False
>>> isinstance(d.iteritems(), Iterator)
True
# 使用next方法。
>>> iter_items = d.iteritems()
>>> iter_items.next()
(‘a’, 1)
>>> iter_items.next()
(‘c’, 3)
>>> iter_items.next()
(‘b’, 2)
由于字典是无序的,所以显示的是无序的,实际是按照顺序获取的下一个元素。
8.2.2 iter()函数
使用iter()函数转换成迭代器:
语法:
iter(collection) -> iterator
iter(callable, sentinel) -> iterator
>>> lst = [1, 2, 3]
>>> isinstance(lst, Iterator)
False
>>> lst.next() # 不是迭代器是不具备next()属性的
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
AttributeError: ‘list’ object has no attribute ‘next’
>>> iter_lst = iter(lst)
>>> isinstance(iter_lst, Iterator)
True
>>> iter_lst.next()
1
>>> iter_lst.next()
2
>>> iter_lst.next()
3
8.2.3 itertools模块
itertools模块是Python内建模块,提供可操作迭代对象的函数。可以生成迭代器,也可以生成无限的序列迭代器。
有下面几种生成无限序列的方法:
count([n]) –> n, n+1, n+2, …
cycle(p) –> p0, p1, … plast, p0, p1, …
repeat(elem [,n]) –> elem, elem, elem, … endlessly or up to n times
也有几个操作迭代器的方法:
islice(seq, [start,] stop [, step]) –> elements from
chain(p, q, …) –> p0, p1, … plast, q0, q1, …
groupby(iterable[, keyfunc]) –> sub-iterators grouped by value of keyfunc(v)
imap(fun, p, q, …) –> fun(p0, q0), fun(p1, q1), …
ifilter(pred, seq) –> elements of seq where pred(elem) is True
1)count生成序列迭代器
>>> from itertools import * # 导入所有方法
# 用法 count(start=0, step=1) –> count object
>>> counter = count()
>>> counter.next()
0
>>> counter.next()
1
>>> counter.next()
2
……
可以使用start参数设置开始值,step设置步长。
2)cycle用可迭代对象生成迭代器
# 用法 cycle(iterable) –> cycle object
>>> i = cycle([‘a’, ‘b’, ‘c’])
>>> i.next()
‘a’
>>> i.next()
‘b’
>>> i.next()
‘c’
3)repeat用对象生成迭代器
# 用法 repeat(object [,times]) -> create an iterator which returns the object,就是任意对象
>>> i = repeat(1)
>>> i.next()
1
>>> i.next()
1
>>> i.next()
1
……
可使用无限次。
也可以指定次数:
>>> i = repeat(1, 2)
>>> i.next()
1
>>> i.next()
1
>>> i.next()
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
StopIteration
4)islice用可迭代对象并设置结束位置
# 用法 islice(iterable, [start,] stop [, step]) –> islice object
>>> i = islice([1,2,3],2)
>>> i.next()
1
>>> i.next()
2
>>> i.next()
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
StopIteration
正常的话也可以获取的3。
5)chain用多个可迭代对象生成迭代器
# 用法 chain(*iterables) –> chain object
>>> i = chain(‘a’,’b’,’c’)
>>> i.next()
‘a’
>>> i.next()
‘b’
>>> i.next()
‘c’
6)groupby将可迭代对象中重复的元素挑出来放到一个迭代器中
# 用法 groupby(iterable[, keyfunc]) -> create an iterator which returns
>>> for key,group in groupby(‘abcddCca’):
… print key,list(group)
…
a [‘a’]
b [‘b’]
c [‘c’]
d [‘d’, ‘d’]
C [‘C’]
c [‘c’]
a [‘a’]
groupby方法是区分大小写的,如果想把大小写的都放到一个迭代器中,可以定义函数处理下:
>>> for key,group in groupby(‘abcddCca’, lambda c: c.upper()):
… print key, list(group)
…
A [‘a’]
B [‘b’]
C [‘c’]
D [‘d’, ‘d’]
C [‘C’, ‘c’]
A [‘a’]
7)imap用函数处理多个可迭代对象
# 用法 imap(func, *iterables) –> imap object
>>> a = imap(lambda x, y: x * y,[1,2,3],[4,5,6])
>>> a.next()
4
>>> a.next()
10
>>> a.next()
18
8)ifilter过滤序列
# 用法 ifilter(function or None, sequence) –> ifilter object
>>> i = ifilter(lambda x: x%2==0,[1,2,3,4,5])
>>> for i in i:
… print i
…
2
4
当使用for语句遍历迭代器时,步骤大致这样的,先调用迭代器对象的__iter__方法获取迭代器对象,再调用对象的__next__()方法获取下一个元素。最后引发StopIteration异常结束循环。
8.3 生成器(Generator)
什么是生成器?
1)任何包含yield语句的函数都称为生成器。
2)生成器都是一个迭代器,但迭代器不一定是生成器。
8.3.1 生成器函数
在函数定义中使用yield语句就创建了一个生成器函数,而不是普通的函数。
当调用生成器函数时,每次执行到yield语句,生成器的状态将被冻结起来,并将结果返回__next__调用者。冻结意思是局部的状态都会被保存起来,包括局部变量绑定、指令指针。确保下一次调用时能从上一次的状态继续。
以生成斐波那契数列举例说明yield使用:
斐波那契(Fibonacci)数列是一个简单的递归数列,任意一个数都可以由前两个数相加得到。
#!/usr/bin/python
# -*- coding: utf-8 -*-
def fab(max):
n, a, b = 0, 0, 1
while n < max:
print b
a, b = b, a + b
n += 1
fab(5)
# python test.py
1
1
2
3
5
使用yied语句,只需要把print b改成yield b即可:
#!/usr/bin/python
# -*- coding: utf-8 -*-
def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b
# print b
a, b = b, a + b
n += 1
print fab(5)
# python test.py
<generator object fab at 0x7f2369495820>
可见,调用fab函数不会执行fab函数,而是直接返回了一个生成器对象,上面说过生成器就是一个迭代器。那么就可以通过next方法来返回它下一个值。
>>> import test
>>> f = test.fab(5)
>>> f.next()
1
>>> f.next()
1
>>> f.next()
2
>>> f.next()
3
>>> f.next()
5
每次fab函数的next方法,就会执行fab函数,执行到yield b时,fab函数返回一个值,下一次执行next方法时,代码从yield b的吓一跳语句继续执行,直到再遇到yield。
8.3.2 生成器表达式
在第四章 Python运算符和流程控制章节讲过,简化for和if语句,使用小括号()返回一个生成器,中括号[]生成一个列表。
回顾下:
# 生成器表达式
>>> result = (x for x in range(5))
>>> result
<generator object <genexpr> at 0x030A4FD0>
>>> type(result)
<type ‘generator’>
# 列表解析表达式
>>> result = [ x for x in range(5)]
>>> type(result)
<type ‘list’>
>>> result
[0, 1, 2, 3, 4]
第一个就是生成器表达式,返回的是一个生成器,就可以使用next方法,来获取下一个元素:
>>> result.next()
0
>>> result.next()
1
>>> result.next()
2
……
第九章 Python自定义模块及导入方法
9.1 自定义模块
自定义模块你已经会了,平常写的代码放到一个文件里面就是啦!
例如,写个简单的函数,作为一个模块:
#!/usr/bin/python
# -*- coding: utf-8 -*-
def func(a, b):
return a * b
class MyClass:
def __init__(self, a, b):
self.a = a
self.b = b
def method(self):
return self.a * self.b
导入模块:
>>> import test
>>> test.func(2, 2)
4
>>> c = test.MyClass(2, 2)
>>> c.method()
4
是不是很简单!是的,没错,就是这样。
需要注意的是,test就是文件名。另外,模块名要能找到,我的是在当前目录下。
有时经常from…import…,这又是啥呢,来看看:
>>> from test import func, MyClass # 多个函数或类以逗号分隔
>>> test.func(2, 2)
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
NameError: name ‘test’ is not defined
>>> func(2, 2)
4
>>> c = MyClass(2, 2)
>>> c.method()
4
看到了吧!如果你不想把模块里的函数都导入,就可以这样。一方面避免导入过多用不到的函数增加负载,另一方面引用时可不加模块名。
如果想调用不加模块名,也想导入所有模块,可以这样:
>>> from test import *
>>> func(2, 2)
4
>>> c = MyClass(2, 2)
>>> c.method()
4
使用个星号就代表了所有。
提醒:在模块之间引用也是同样的方式。
博客地址:http://lizhenliang.blog.51cto.com
QQ群:323779636(Shell/Python运维开发群)
9.2 作为脚本来运行程序
所有的模块都有一个内置属性__name__,如果import一个模块,那么模块的__name__属性返回值一般是文件名。如果直接运行Python程序,__name__的值将是一个”__mian__”。
举例说明,根据上面程序做一个测试:
#!/usr/bin/python
# -*- coding: utf-8 -*-
def func(a, b):
return a * b
class MyClass:
def __init__(self, a, b):
self.a = a
self.b = b
def method(self):
return self.a * self.b
print __name__
# python test.py
__main__
与预期一样,打印出了“__main__”,再创建一个test2.py,导入这个模块:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import test
# python test2.py
test
打印出了模块名,这个结果输出就是test.py中的print __name__。
所以,我们在test.py里面判断下__name__值等于__main__时说明在手动执行这个程序:
#!/usr/bin/python
# -*- coding: utf-8 -*-
def func(a, b):
return a * b
class MyClass:
def __init__(self, a, b):
self.a = a
self.b = b
def method(self):
return self.a * self.b
if __name__ == “__main__”:
print “我在手动执行这个程序…”
# python test.py
我在手动执行这个程序…
此时再运行test2.py试试,是不是打印为空!明白了吧!
9.3 安装第三方模块
在Python中安装外部的模块有几种方式:
1)下载压缩包,通过setuptools工具安装,这个在第一章Python基础知识里面用到过。推荐下载地址:http://pypi.python.org
2)easy_install工具安装,也依赖setuptools。
3)pip工具安装。推荐使用这个方式。
4)直接将压缩包解压到Python模块目录。但常常会出现import失败,不推荐。
5)在Windows下,除了上面几种方式以外,可以直接下载exe文件点击一步步安装。
pip与easy_install安装方式类似,主要区别在于easy_install不支持卸载软件,而pip支持。
推荐使用pip命令安装,简单方便。如果安装失败可以按顺序这么尝试:方式1 –> 方式2 –> 方式4
以安装setuptools举例上面几种安装方式:
方式1:
# wget https://pypi.python.org/packages/32/3c/e853a68b703f347f5ed86585c2dd2828a83252e1216c1201fa6f81270578/setuptools-26.1.1.tar.gz
# tar zxvf setuptools-26.1.1.tar.gz
# cd setuptools-26.1.1
# python setup.py install
方式2:
# easy_install setuptools
方式3:
# pip install setuptools
# pip uninstall setuptools # 卸载
# pip search setuptools # 搜索
方式3:
cp -rf setuptools-26.1.1 /usr/local/lib/python2.7/dist-packages
9.4 查看模块帮助文档
前面几个章节已经使用几个内置模块了,比如collections、itertools等,导入与上面一样,这里不再过多说明了。
1)help()函数
当一个模块对其语法不了解时,可以查看帮助,以collections举例:
>>> import collections
>>> help(collections)
Help on module collections:
NAME
collections
FILE
/usr/lib/python2.7/collections.py
MODULE DOCS
http://docs.python.org/library/collections # 注意:这里是这个模块的帮助文档,很详细的哦!
CLASSES
__builtin__.dict(__builtin__.object)
Counter
OrderedDict
defaultdict
__builtin__.object
_abcoll.Callable
_abcoll.Container
……
使用help()就能查看这个模块的内部构造,包括类方法、属性等信息。
也可以再对某个方法查看其用法:
>>> help(collections.Counter())
Help on Counter in module collections object:
class Counter(__builtin__.dict)
| Dict subclass for counting hashable items. Sometimes called a bag
| or multiset. Elements are stored as dictionary keys and their counts
| are stored as dictionary values.
|
| >>> c = Counter(‘abcdeabcdabcaba’) # count elements from a string
|
| >>> c.most_common(3) # three most common elements
| [(‘a’, 5), (‘b’, 4), (‘c’, 3)]
| >>> sorted(c) # list all unique elements
| [‘a’, ‘b’, ‘c’, ‘d’, ‘e’]
| >>> ”.join(sorted(c.elements())) # list elements with repetitions
| ‘aaaaabbbbcccdde’
| >>> sum(c.values()) # total of all counts
| 15
|
| >>> c[‘a’] # count of letter ‘a’
……
一般里面都是举例说明,可快速帮助我们回忆使用方法。
2)dir()函数查看对象属性
这个在前面也用到过,能看到对象的方法、属性等信息:
>>> dir(collections)
[‘Callable’, ‘Container’, ‘Counter’, ‘Hashable’, ‘ItemsView’, ‘Iterable’, ‘Iterator’, ‘KeysView’, ‘Mapping’, ‘MappingView’, ‘MutableMapping’, ‘MutableSequence’, ‘MutableSet’, ‘OrderedDict’, ‘Sequence’, ‘Set’, ‘Sized’, ‘ValuesView’, ‘__all__’, ‘__builtins__’, ‘__doc__’, ‘__file__’, ‘__name__’, ‘__package__’, ‘_abcoll’, ‘_chain’, ‘_class_template’, ‘_eq’, ‘_field_template’, ‘_get_ident’, ‘_heapq’, ‘_imap’, ‘_iskeyword’, ‘_itemgetter’, ‘_repeat’, ‘_repr_template’, ‘_starmap’, ‘_sys’, ‘defaultdict’, ‘deque’, ‘namedtuple’]
3)github上查看模块用法
Python官方模块下载地址http://pypi.python.org,所有的模块在这里都有。
打开网站后,在搜索框搜索你的模块名,在结果找到模块名点进去,会有一个 Home Page的连接,Python大多数模块都是托管在github上面,这个链接就是这个模块在github上面的地址,点击后跳转到github对应的模块页面,里面也有很详细模块使用方法。
9.5 导入模块新手容易出现的问题
还有一个新手经常犯的问题,写一个模块,比如使用itertools模块,为了说明这个测试文件是这个模块,就把文件名写成了这个模块名,于是就造成了下面错误:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import collections
c = collections.Counter()
for i in “Hello world!”:
c[i] += 1
print c
# python collections.py
Traceback (most recent call last):
File “collections.py”, line 3, in <module>
import collections
File “/home/user/collections.py”, line 4, in <module>
c = collections.Counter()
AttributeError: ‘module’ object has no attribute ‘Counter’
抛出异常,明明在解释器里面可以正常导入使用啊,怎么会提示没Counter属性呢,问题就出现你的文件名与导入的模块名重名,导致程序import了这个文件,上面讲过文件名就是模块名。所以文件名不要与引用的模块名相同。
还有一个使用方法也说明下,使用as关键字设置模块别名,这样使用中就不用输入那么长的模块名了,按照上面的例子,把名字先改成collections1.py,做测试:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import collections as cc
c = cc.Counter()
for i in “Hello world!”:
c[i] += 1
print c
# python collections1.py
Counter({‘l’: 3, ‘o’: 2, ‘!’: 1, ‘ ‘: 1, ‘e’: 1, ‘d’: 1, ‘H’: 1, ‘r’: 1, ‘w’: 1})
第十章 Python常用标准库/模块使用(必会)
本章涉及标准库:
1、sys
2、os
3、glob
4、math
5、random
6、platform
7、pikle与cPikle
8、subprocess
9、Queue
10、StringIO
11、logging
12、ConfigParser
13、urllib与urllib2
14、json
15、time
10.1 sys
1)sys.argv
命令行参数。
argv[0] #代表本身名字
argv[1] #第一个参数
argv[2] #第二个参数
argv[3] #第三个参数
argv[N] #第N个参数
argv #参数以空格分隔存储到列表。
看看使用方法:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
print sys.argv[0]
print sys.argv[1]
print sys.argv[2]
print sys.argv[3]
print sys.argv
print len(sys.argv)
# python test.py
test.py
a
b
c
c
[‘test.py’, ‘a’, ‘b’, ‘c’]
4
值得注意的是,argv既然是一个列表,那么可以通过len()函数获取这个列表的长度从而知道输入的参数数量。可以看到列表把自身文件名也写了进去,所以当我们统计的使用应该-1才是实际的参数数量,因此可以len(sys.argv[1:])获取参数长度。
2)sys.path
模块搜索路径。
>>> sys.path
[”, ‘/usr/local/lib/python2.7/dist-packages/tornado-3.1-py2.7.egg’, ‘/usr/lib/python2.7’, ‘/usr/lib/python2.7/plat-x86_64-linux-gnu’, ‘/usr/lib/python2.7/lib-tk’, ‘/usr/lib/python2.7/lib-old’, ‘/usr/lib/python2.7/lib-dynload’, ‘/usr/local/lib/python2.7/dist-packages’, ‘/usr/lib/python2.7/dist-packages’]
输出的是一个列表,里面包含了当前Python解释器所能找到的模块目录。
如果想指定自己的模块目录,可以直接追加:
>>> sys.path.append(‘/opt/scripts’)
>>> sys.path
[”, ‘/usr/local/lib/python2.7/dist-packages/tornado-3.1-py2.7.egg’, ‘/usr/lib/python2.7’, ‘/usr/lib/python2.7/plat-x86_64-linux-gnu’, ‘/usr/lib/python2.7/lib-tk’, ‘/usr/lib/python2.7/lib-old’, ‘/usr/lib/python2.7/lib-dynload’, ‘/usr/local/lib/python2.7/dist-packages’, ‘/usr/lib/python2.7/dist-packages’, ‘/opt/scripts’]
3)sys.platform
系统平台标识符。
系统 | 平台标识符 |
Linux | linux |
Windows | win32 |
Windows/Cygwin | cygwin |
Mac OS X | darwin |
>>> sys.platform
‘linux2’
Python本身就是跨平台语言,但也不就意味着所有的模块都是在各种平台通用,所以可以使用这个方法判断当前平台,做相应的操作。
4)sys.subversion
在第一章讲过Python解释器有几种版本实现,而默认解释器是CPython,来看看是不是:
>>> sys.subversion
(‘CPython’, ”, ”)
5)sys.version
查看Python版本:
>>> sys.version
‘2.7.6 (default, Jun 22 2015, 17:58:13) \n[GCC 4.8.2]’
6)sys.exit()
退出解释器:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
print “Hello world!”
sys.exit()
print “Hello world!”
# python test.py
Hello world!
代码执行到sys.exit()就会终止程序。
7)sys.stdin、sys.stdout和sys.stderr
标准输入、标准输出和错误输出。
标准输入:一般是键盘。stdin对象为解释器提供输入字符流,一般使用raw_input()和input()函数。
例如:让用户输入信息
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
name = raw_input(“Please input your name: “)
print name
# python test.py
Please input your name: xiaoming
xiaoming
import sys
print “Please enter your name: “
name = sys.stdin.readline()
print name
# python b.py
Please enter your name:
xiaoming
xiaoming
再例如,a.py文件标准输出作为b.py文件标准输入:
# cat a.py
import sys
sys.stdout.write(“123456\n”)
sys.stdout.flush()
# cat b.py
import sys
print sys.stdin.readlines()
# python a.py | python b.py
[‘123456\n’]
sys.stdout.write()方法其实就是下面所讲的标准输出,print语句就是调用了这个方法。
标准输出:一般是屏幕。stdout对象接收到print语句产生的输出。
例如:打印一个字符串
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
print “Hello world!”
# python test.py
Hello world!
sys.stdout是有缓冲区的,比如:
import sys
import time
for i in range(5):
print i,
# sys.stdout.flush()
time.sleep(1)
# python test.py
0 1 2 3 4
本是每隔一秒输出一个数字,但现在是循环完才会打印所有结果。如果把sys.stdout.flush()去掉,就会没执行到print就会刷新stdout输出,这对实时输出信息的程序有帮助。
错误输出:一般是错误信息。stderr对象接收出错的信息。
例如:引发一个异常
>>> raise Exception, “raise…”
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
Exception: raise…
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ群:323779636(Shell/Python运维开发群) 10.2 os os模块主要对目录或文件操作。
方法 | 描述 | 示例 |
os.name | 返回操作系统类型 | 返回值是”posix”代表linux,”nt”代表windows |
os.extsep | 返回一个”.”标识符 | |
os.environ | 以字典形式返回系统变量 | |
os.devnull | 返回/dev/null标识符 | |
os.linesep | 返回一个换行符”\n” | >>> print “a” + os.linesep + “b” a b |
os.sep | 返回一个路径分隔符正斜杠”/” | >>> “a” + os.sep + “b” ‘a/b’ |
os.listdir(path) | 列表形式列出目录 | |
os.getcwd() | 获取当前路径 | >>> os.getcwd() ‘/home/user’ |
os.chdir(path) | 改变当前工作目录到指定目录 | >>> os.chdir(‘/opt’) >>> os.getcwd() ‘/opt’ |
os.mkdir(path [, mode=0777]) | 创建目录 | >>> os.mkdir(‘/home/user/test’) |
os.makedirs(path [, mode=0777]) | 递归创建目录 | >>> os.makedirs(‘/home/user/abc/abc’) |
os.rmdir(path) | 移除空目录 | >>> os.makedirs(‘/home/user/abc/abc’) |
os.remove(path) | 移除文件 | |
os.rename(old, new) | 重命名文件或目录 | |
os.stat(path) | 获取文件或目录属性 | |
os.chown(path, uid, gid) | 改变文件或目录所有者 | |
os.chmod(path, mode) | 改变文件访问权限 | >>> os.chmod(‘/home/user/c/a.tar.gz’, 0777) |
os.symlink(src, dst) | 创建软链接 | |
os.unlink(path) | 移除软链接 | >>> os.unlink(‘/home/user/ddd’) |
urandom(n) | 返回随机字节,适合加密使用 | >>> os.urandom(2) ‘%\xec’ |
os.getuid() | 返回当前进程UID | |
os.getlogin() | 返回登录用户名 | |
os.getpid() | 返回当前进程ID | |
os.kill(pid, sig) | 发送一个信号给进程 | |
os.walk(path) | 目录树生成器,返回格式:(dirpath, [dirnames], [filenames]) | >>> for root, dir, file in os.walk(‘/home/user/abc’): … print root … print dir … print file |
os.statvfs(path) | | |
os.system(command) | 执行shell命令,不能存储结果 | |
popen(command [, mode=’r’ [, bufsize]]) | 打开管道来自shell命令,并返回一个文件对象 | >>> result = os.popen(‘ls’)
>>> result.read() |
os.path类用于获取文件属性。
os.path.basename(path) |
返回最后一个文件或目录名 |
>>> os.path.basename(‘/home/user/a.sh’) ‘a.sh’ |
os.path.dirname(path) |
返回最后一个文件前面目录 |
>>> os.path.dirname(‘/home/user/a.sh’) ‘/home/user’ |
os.path.abspath(path) |
返回一个绝对路径 |
>>> os.path.abspath(‘a.sh’) ‘/home/user/a.sh’ |
os.path.exists(path) |
判断路径是否存在,返回布尔值 |
>>> os.path.exists(‘/home/user/abc’) True |
os.path.isdir(path) |
判断是否是目录 |
|
os.path.isfile(path) |
判断是否是文件 |
|
os.path.islink(path) |
判断是否是链接 |
|
os.path.ismount(path) |
判断是否挂载 |
|
os.path.getatime(filename) |
返回文件访问时间戳 |
>>> os.path.getctime(‘a.sh’) 1475240301.9892483 |
os.path.getctime(filename) |
返回文件变化时间戳 |
|
os.path.getmtime(filename) |
返回文件修改时间戳 |
|
os.path.getsize(filename) |
返回文件大小,单位字节 |
|
os.path.join(a, *p) |
加入两个或两个以上路径,以正斜杠”/”分隔。常用于拼接路径 |
>>> os.path.join(‘/home/user’,’test.py’,’a.py’) ‘/home/user/test.py/a.py’ |
os.path.split( |
分隔路径名 |
>>> os.path.split(‘/home/user/test.py’) (‘/home/user’, ‘test.py’) |
os.path.splitext( |
分隔扩展名 |
>>> os.path.splitext(‘/home/user/test.py’)
(‘/home/user/test’, ‘.py’) |
10.3 glob
文件查找,支持通配符(*、?、[])
# 查找目录中所有以.sh为后缀的文件
>>> glob.glob(‘/home/user/*.sh’)
[‘/home/user/1.sh’, ‘/home/user/b.sh’, ‘/home/user/a.sh’, ‘/home/user/sum.sh’]
# 查找目录中出现单个字符并以.sh为后缀的文件
>>> glob.glob(‘/home/user/?.sh’)
[‘/home/user/1.sh’, ‘/home/user/b.sh’, ‘/home/user/a.sh’]
# 查找目录中出现a.sh或b.sh的文件
>>> glob.glob(‘/home/user/[a|b].sh’)
[‘/home/user/b.sh’, ‘/home/user/a.sh’]
10.4 math
数字处理。
下面列出一些自己决定会用到的:
方法 | 描述 | 示例 |
math.pi | 返回圆周率 | >>> math.pi 3.141592653589793 |
math.ceil(x) | 返回x浮动的上限 | >>> math.ceil(5.2) 6.0 |
math.floor(x) | 返回x浮动的下限 | >>> math.floor(5.2) 5.0 |
math.trunc(x) | 将数字截尾取整 | >>> math.trunc(5.2) 5 |
math.fabs(x) | 返回x的绝对值 | >>> math.fabs(-5.2) 5.2 |
math.fmod(x,y) | 返回x%y(取余) | >>> math.fmod(5,2) 1.0 |
math.modf(x) | 返回x小数和整数 | >>> math.modf(5.2) (0.20000000000000018, 5.0) |
math.factorial(x) | 返回x的阶乘 | >>> math.factorial(5) 120 |
math.pow(x,y) | 返回x的y次方 | >>> math.pow(2,3) 8.0 |
math.sprt(x) | 返回x的平方根 | >>> math.sqrt(5)
2.2360679774997898 |
10.5 random
生成随机数。
常用的方法:
方法 |
描述 |
示例 |
random.randint(a,b) |
返回整数a和b范围内数字 |
>>> random.randint(1,10) 6 |
random.random() |
返回随机数,它在0和1范围内 |
>>> random.random() 0.7373251914304791 |
random.randrange(start, stop[, step]) |
返回整数范围的随机数,并可以设置只返回跳数 |
>>> random.randrange(1,10,2) 5 |
random.sample(array, x) |
从数组中返回随机x个元素 |
>>> random.sample([1,2,3,4,5],2) [2, 4] |
10.6 platform
获取操作系统详细信息。
方法 |
描述 |
示例 |
platform.platform() |
返回操作系统平台 |
>>> platform.platform() ‘Linux-3.13.0-32-generic-x86_64-with-Ubuntu-14.04-trusty’ |
platform.uname() |
返回操作系统信息 |
>>> platform.uname() (‘Linux’, ‘ubuntu’, ‘3.13.0-32-generic’, ‘#57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014’, ‘x86_64’, ‘x86_64’) |
platform.system() |
返回操作系统平台 |
>>> platform.system() ‘Linux’ |
platform.version() |
返回操作系统版本 |
>>> platform.version() ‘#57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014’ |
platform.machine() |
返回计算机类型 |
>>> platform.machine() ‘x86_64’ |
platform.processor() |
返回计算机处理器类型 |
>>> platform.processor() ‘x86_64’ |
platform.node() |
返回计算机网络名 |
>>> platform.node() ‘ubuntu’ |
platform.python_version() |
返回Python版本号 |
>>> platform.python_version()
‘2.7.6’ |
10.7 pickle与cPickle
创建可移植的Python序列化对象,持久化存储到文件。
1)pickle
pickle库有两个常用的方法,
dump()、load() 和
dumps()、
loads()
,下面看看它们的使用方法:
dump()方法是把对象保存到文件中。
格式:dump(obj, file, protocol=None)
load()方法是从文件中读数据,重构为原来的Python对象。
格式:load(file)
示例,将字典序列化到文件:
>>> import pickle
>>> dict = {‘a’:1, ‘b’:2, ‘c’:3}
>>> output = open(‘data.pkl’, ‘wb’) # 二进制模式打开文件
>>> pickle.dump(dict, output) # 执行完导入操作,当前目录会生成data.pkl文件
>>> output.close() # 写入数据并关闭
看看pickle格式后的文件:
# cat data.pkl
(dp0
S’a’
p1
I1
sS’c’
p2
I3
sS’b’
p3
I2
s.
读取序列化文件:
>>> f = open(‘data.pkl’)
>>> data = pickle.load(f)
>>> print data
{‘a’: 1, ‘c’: 3, ‘b’: 2}
用法挺简单的,就是先导入文件,再读取文件。
接下来看看序列化字符串操作:
dumps()返回一个pickle格式化的字符串
格式:dumps(obj, protocol=None)
load()解析pickle字符串为对象
示例:
>>> s = ‘abc’
>>> pickle.dumps(s)
“S’abc’\np0\n.”
>>> pkl = pickle.dumps(s)
>>> pkl
“S’abc’\np0\n.”
>>> pickle.loads(pkl)
‘abc’
需要注意的是,py2.x使用的是pickle2.0格式版本,如果用3.0、4.0版本的pickle导入会出错。可以通过pickle.format_version 查看版本。
2)cPickle
cPickle库是C语言实现,对pickle进行了优化,提升了性能,建议在写代码中使用。
cPicke提供了与pickle相同的dump()、load() 和dumps()、 loads()方法,用法一样,不再讲解。
10.8 subprocess
subprocess库会fork一个子进程去执行任务,连接到子进程的标准输入、输出、错误,并获得它们的返回代码。这个模块将取代os.system、os.spawn*、os.popen*、popen2.*和commands.*。
提供了以下常用方法帮助我们执行bash命令的相关操作:
subprocess.call():运行命令与参数。等待命令完成,返回执行状态码。
>>> import subprocess
>>> retcode = subprocess.call([“ls”, “-l”])
total 504
-rw-r–r– 1 root root 54 Nov 2 06:15 data.pkl
>>> retcode
0
>>> retcode = subprocess.call([“ls”, “a”])
ls: cannot access a: No such file or directory
>>> retcode
2
# 也可以这样写
>>> subprocess.call(‘ls -l’, shell=True)
subprocess.check_call():运行命令与参数。如果退出状态码非0,引发CalledProcessError异常,包含状态码。
>>> subprocess.check_call(“ls a”, shell=True)
ls: cannot access a: No such file or directory
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
File “/usr/lib/python2.7/subprocess.py”, line 540, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command ‘ls a’ returned non-zero exit status 2
subprocess.Popen():这个类我们主要来使用的,参数较多。
参数 |
描述 |
args |
命令,字符串或列表 |
bufsize |
0代表无缓冲,1代表行缓冲,其他正值代表缓冲区大小,负值采用默认系统缓冲(一般是全缓冲) |
executable |
|
stdin stdout stderr |
默认没有任何重定向,可以指定重定向到管道(PIPE)、文件对象、文件描述符(整数),stderr还可以设置为STDOUT |
preexec_fn |
钩子函数,在fork和exec之间执行 |
close_fds |
|
shell |
为True,表示用当前默认解释器执行。相当于args前面添加“/bin/sh”“-c或win下”cmd.exe /c ” |
cwd |
指定工作目录 |
env |
设置环境变量 |
universal_newlines |
换行符统一处理成”\n” |
startupinfo |
在windows下的Win32 API 发送CreateProcess()创建进程 |
creationflags |
在windows下的Win32 API 发送CREATE_NEW_CONSOLE()创建控制台窗口 |
subprocess.Popen()类又提供了以下些方法:
方法 |
描述 |
Popen.communicate(input=None) |
与子进程交互。读取从stdout和stderr缓冲区内容,阻塞父进程,等待子进程结束 |
Popen. kill() |
杀死子进程,在Posix系统上发送SIGKILL信号 |
Popen.pid |
获取子进程PID |
Popen.poll() |
如果子进程终止返回状态码 |
Popen.returncode |
返回子进程状态码 |
Popen.send_signal(signal) |
发送信号到子进程 |
Popen.stderr |
如果参数值是PIPE,那么这个属性是一个文件对象,提供子进程错误输出。否则为None |
Popen.stdin |
如果参数值是PIPE,那么这个属性是一个文件对象,提供子进程输入。否则为None |
Popen.stdout |
如果参数值是PIPE,那么这个属性是一个文件对象,提供子进程输出。否则为None |
Popen.terminate() |
终止子进程,在Posix系统上发送SIGTERM信号,在windows下的Win32 API发送TerminateProcess()到子进程 |
Popen.wait() |
等待子进程终止,返回状态码 |
示例:
>>> p = subprocess.Popen(‘dmesg |grep eth0’, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
>>> p.communicate()
…… # 元组形式返回结果
>>> p.pid
57039
>>> p.wait()
0
>>> p.returncode
0
subprocess.PIPE提供了一个缓冲区,将stdout、stderr放到这个缓冲区中,p.communicate()方法读取缓冲区数据。
缓冲区的stdout、stderr是分开的,可以以p.stdout.read()方式获得标准输出、错误输出的内容。
再举个例子,我们以标准输出作为下个Popen任务的标准输入:
>>> p1 = subprocess.Popen(‘ls’, stdout=subprocess.PIPE, shell=True)
>>> p2 = subprocess.Popen(‘grep data’, stdin=p1.stdout, stdout=subprocess.PIPE, shell=True)
>>> p1.stdout.close() # 调用后启动p2,为了获得SIGPIPE
>>> output = p2.communicate()[0]
>>> output
‘data.pkl\n’
p1的标准输出作为p2的标准输入。这个p2的stdin、stdout也可以是个可读、可写的文件。
10
.9 Queue
队列,数据存放在内存中,一般用于交换数据。
类 |
描述 |
Queue.Empty |
当非阻塞get()或get_nowait()对象队列上为空引发异常 |
Queue.Full |
当非阻塞put()或put_nowait()对象队列是一个满的队列引发异常 |
Queue.LifoQueue(maxsize=0) |
构造函数为后进先出队列。maxsize设置队列最大上限项目数量。小于或等于0代表无限。 |
Queue.PriorityQueue(maxsize=0) |
构造函数为一个优先队列。级别越高越先出。 |
Queue.Queue(maxsize=0) |
构造函数为一个FIFO(先进先出)队列。maxsize设置队列最大上限项目数量。小于或等于0代表无限。 |
Queue.deque |
双端队列。实现快速append()和popleft(),无需锁。 |
Queue.heapq |
堆排序队列。 |
用到比较多的是Queue.Queue类,在这里主要了解下这个。
它提供了一些操作队列的方法:
方法 |
描述 |
Queue.empty() |
如果队列为空返回True,否则返回False |
Queue.full() |
如果队列是满的返回True,否则返回False |
Queue.get(block=True, timeout=None) |
从队列中删除并返回一个项目。没有指定项目,因为是FIFO队列,如果队列为空会一直阻塞。timeout超时时间 |
Queue.get_nowait() |
从队列中删除并返回一个项目,不阻塞。会抛出异常。 |
Queue.join() |
等待队列为空,再执行别的操作 |
Queue.put(item, block=True, timeout=None) |
写入项目到队列 |
Queue.put_nowait() |
写入项目到队列,不阻塞。与get同理 |
Queue.qsize() |
返回队列大小 |
Queue.task_done() |
表示原队列的任务完成 |
示例:
>>> from Queue import Queue
>>> q = Queue.Queue()
>>> q.put(‘test’)
>>> q.qsize()
1
>>> q.get()
‘test’
>>> q.qsize()
0
>>> q.full()
False
>>> q.empty()
True
10.10 StringIO
StringIO库将字符串存储在内存中,像操作文件一样操作。主要提供了一个StringIO类。
方法 |
描述 |
StringIO.close() |
关闭 |
StringIO.flush() |
刷新缓冲区 |
StringIO.getvalue() |
获取写入的数据 |
StringIO.isatty() |
|
StringIO.next() |
读取下一行,没有数据抛出异常 |
StringIO.read(n=-1) |
默认读取所有内容。n指定读取多少字节 |
StringIO.readline(length=None) |
默认读取下一行。length指定读取多少个字符 |
StringIO.readlines(sizehint=0) |
默认读取所有内容,以列表返回。sizehint指定读取多少字节 |
StringIO.seek(pos, mode=0) |
在文件中移动文件指针,从mode(0代表文件起始位置,默认。1代表当前位置。2代表文件末尾)偏移pos个字节 |
StringIO.tell() |
返回当前在文件中的位置 |
StringIO.truncate() |
截断文件大小 |
StringIO.write(str) |
写字符串到文件 |
StringIO.writelines(iterable) |
写入序列,必须是一个可迭代对象,一般是一个字符串列表 |
可以看到,StringIO方法与文件对象方法大部分都一样,从而也就能方面的操作内存对象。
示例:
>>> f = StringIO()
>>> f.write(‘hello’)
>>> f.getvalue()
‘hello’
像操作文件对象一样写入。
用一个字符串初始化StringIO,可以像读文件一样读取:
>>> f = StringIO(‘hello\nworld!’)
>>> f.read()
‘hello\nworld!’
>>> s = StringIO(‘hello world!’)
>>> s.seek(5) # 指针移动到第五个字符,开始写入
>>> s.write(‘-‘)
>>> s.getvalue()
‘hello-world!’
10
.11 logging
记录日志库。
有几个主要的类:
logging.Logger |
应用程序记录日志的接口 |
logging.Filter |
过滤哪条日志不记录 |
logging.FileHandler |
日志写到磁盘文件 |
logging.Formatter |
定义最终日志格式 |
日志级别:
级别 |
数字值 |
描述 |
critical |
50 |
危险 |
error |
40 |
错误 |
warning |
30 |
警告 |
info |
20 |
普通信息 |
debug |
10 |
调试 |
noset |
0 |
不设置 |
Formatter类可以自定义日志格式,默认时间格式weight%Y-%m-%d %H:%M:%S,有以下这些属性:
%(name)s |
日志的名称 |
%(levelno)s |
数字日志级别 |
%(levelname)s |
文本日志级别 |
%(pathname)s |
调用logging的完整路径(如果可用) |
%(filename)s |
文件名的路径名 |
%(module)s |
模块名 |
%(lineno)d |
调用logging的源行号 |
%(funcName)s |
函数名 |
%(created)f |
创建时间,返回time.time()值 |
%(asctime)s |
字符串表示创建时间 |
%(msecs)d |
毫秒表示创建时间 |
%(relativeCreated)d |
毫秒为单位表示创建时间,相对于logging模块被加载,通常应用程序启动。 |
%(thread)d |
线程ID(如果可用) |
%(threadName)s |
线程名字(如果可用) |
%(process)d |
进程ID(如果可用) |
%(message)s |
输出的消息 |
示例:
#!/usr/bin/python
# -*- coding: utf-8 -*-
#————————————————–
# 日志格式
#————————————————–
# %(asctime)s 年-月-日 时-分-秒,毫秒 2013-04-26 20:10:43,745
# %(filename)s 文件名,不含目录
# %(pathname)s 目录名,完整路径
# %(funcName)s 函数名
# %(levelname)s 级别名
# %(lineno)d 行号
# %(module)s 模块名
# %(message)s 消息体
# %(name)s 日志模块名
# %(process)d 进程id
# %(processName)s 进程名
# %(thread)d 线程id
# %(threadName)s 线程名
import logging
format = logging.Formatter(‘%(asctime)s – %(levelname)s %(filename)s [line:%(lineno)d] %(message)s’)
# 创建日志记录器
info_logger = logging.getLogger(‘info’)
# 设置日志级别,小于INFO的日志忽略
info_logger.setLevel(logging.INFO)
# 日志记录到磁盘文件
info_file = logging.FileHandler(“info.log”)
# info_file.setLevel(logging.INFO)
# 设置日志格式
info_file.setFormatter(format)
info_logger.addHandler(info_file)
error_logger = logging.getLogger(‘error’)
error_logger.setLevel(logging.ERROR)
error_file = logging.FileHandler(“error.log”)
error_file.setFormatter(format)
error_logger.addHandler(error_file)
# 输出控制台(stdout)
console = logging.StreamHandler()
console.setLevel(logging.DEBUG)
console.setFormatter(format)
info_logger.addHandler(console)
error_logger.addHandler(console)
if __name__ == “__main__”:
# 写日志
info_logger.warning(“info message.”)
error_logger.error(“error message!”)
# python test.py
2016-07-02 06:52:25,624 – WARNING test.py [line:49] info message.
2016-07-02 06:52:25,631 – ERROR test.py [line:50] error message!
# cat info.log
2016-07-02 06:52:25,624 – WARNING test.py [line:49] info message.
# cat error.log
2016-07-02 06:52:25,631 – ERROR test.py [line:50] error message!
上面代码实现了简单记录日志功能。分别定义了info和error日志,将等于或高于日志级别的日志写到日志文件中。在小项目开发中把它单独写一个模块,很方面在其他代码中调用。
需要注意的是,在定义多个日志文件时,getLogger(name=None)类的name参数需要指定一个唯一的名字,如果没有指定,日志会返回到根记录器,也就是意味着他们日志都会记录到一起。
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ群:323779636(Shell/Python运维开发群) 10
.12 ConfigParser 配置文件解析。
这个库我们主要用到ConfigParser.ConfigParser()类,对ini格式文件增删改查。
ini文件固定结构:有多个部分块组成,每个部分有一个[标识],并有多个key,每个key对应每个值,以等号”=”分隔。值的类型有三种:字符串、整数和布尔值。其中字符串可以不用双引号,布尔值为真用1表示,布尔值为假用0表示。注释以分号”;”开头。
方法 | 描述 |
ConfigParser.add_section(section) | 创建一个新的部分配置 |
ConfigParser.get(section, option, raw=False, vars=None) | 获取部分中的选项值,返回字符串 |
ConfigParser.getboolean(section, option) | 获取部分中的选项值,返回布尔值 |
ConfigParser.getfloat(section, option) | 获取部分中的选项值,返回浮点数 |
ConfigParser.getint(section, option) | 获取部分中的选项值,返回整数 |
ConfigParser.has_option(section, option) | 检查部分中是否存在这个选项 |
ConfigParser.has_section(section) | 检查部分是否在配置文件中 |
ConfigParser.items(section, raw=False, vars=None) | 列表元组形式返回部分中的每一个选项 |
ConfigParser.options(section) | 列表形式返回指定部分选项名称 |
ConfigParser.read(filenames) | 读取ini格式的文件 |
ConfigParser.remove_option( section, option) | 移除部分中的选项 |
ConfigParser.remove_section(section, option) | 移除部分 |
ConfigParser.sections() | 列表形式返回所有部分名称 |
ConfigParser.set(section, option, value) | 设置选项值,存在则更新,否则添加 |
ConfigParser.write(fp) | 写一个ini格式的配置文件 |
举例说明,写一个ini格式文件,对其操作:
# cat config.ini
[host1]
host = 192.168.1.1
port = 22
user = zhangsan
pass = 123
[host2]
host = 192.168.1.2
port = 22
user = lisi
pass = 456
[host3]
host = 192.168.1.3
port = 22
user = wangwu
pass = 789
1)获取部分中的键值
#!/usr/bin/python
# -*- coding: utf-8 -*-
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read(“config.ini”)
section = conf.sections()[0] # 获取随机的第一个部分标识
options = conf.options(section) # 获取部分中的所有键
key = options[2]
value = conf.get(section, options[2]) # 获取部分中键的值
print key, value
print type(value)
# python test.py
port 22
<type ‘str’>
这里有意打出来了值的类型,来说明下get()方法获取的值都是字符串,如果有需要,可以getint()获取整数。测试发现,ConfigParser是从下向上读取的文件内容!
2)遍历文件中的每个部分的每个字段
#!/usr/bin/python
# -*- coding: utf-8 -*-
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read(“config.ini”)
sections = conf.sections() # 获取部分名称 [‘host3’, ‘host2’, ‘host1’]
for section in sections:
options = conf.options(section) # 获取部分名称中的键 [‘user’, ‘host’, ‘port’, ‘pass’]
for option in options:
value = conf.get(section, option) # 获取部分中的键值
print option + “: ” + value
print “————-”
# python test.py
user: wangwu
host: 192.168.1.3
port: 22
pass: 789
————-
user: lisi
host: 192.168.1.2
port: 22
pass: 456
————-
user: zhangsan
host: 192.168.1.1
port: 22
pass: 123
————-
通过上面的例子,熟悉了sections()、options()和get(),能任意获取文件的内容了。
也可以使用items()获取部分中的每个选项:
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read(“config.ini”)
print conf.items(‘host1’)
# python test.py
[(‘user’, ‘zhangsan’), (‘host’, ‘192.168.1.1’), (‘port’, ’22’), (‘pass’, ‘123’)]
3)更新或添加选项
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read(“config.ini”)
fp = open(“config.ini”, “w”) # 写模式打开文件,供后面提交写的内容
conf.set(“host1”, “port”, “2222”) # 有这个选项就更新,否则添加
conf.write(fp) # 写入的操作必须执行这个方法
4)添加一部分,并添加选项
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read(“config.ini”)
fp = open(“config.ini”, “w”)
conf.add_section(“host4”) # 添加[host4]
conf.set(“host4”, “host”, “192.168.1.4”)
conf.set(“host4”, “port”, “22”)
conf.set(“host4”, “user”, “zhaoliu”)
conf.set(“host4”, “pass”, “123”)
conf.write(fp)
5)删除一部分
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read(“config.ini”)
fp = open(“config.ini”, “w”)
conf.remove_section(‘host4’) # 删除[host4]
conf.remove_option(‘host3’, ‘pass’) # 删除[host3]的pass选项
conf.write(fp)
10
.13 urllib与urllib2
打开URL。urllib2是urllib的增强版,新增了一些功能,比如Request()用来修改Header信息。但是urllib2还去掉了一些好用的方法,比如urlencode()编码序列中的两个元素(元组或字典)为URL查询字符串。
一般情况下这两个库结合着用,那我们也结合着了解下。
类 | 描述 |
urllib.urlopen(url, data=None, proxies=None) | 读取指定URL,创建类文件对象。data是随着URL提交的数据(POST) |
urllib/urllib2.quote(s, safe=’/’) | 将字符串中的特殊符号转十六进制表示。如: quote(‘abc def’) -> ‘abc%20def’ |
urllib/urllib2.unquote(s) | 与quote相反 |
urllib.urlencode(query, doseq=0) | 将序列中的两个元素(元组或字典)转换为URL查询字符串 |
urllib.urlretrieve(url, filename=None, reporthook=None, data=None) | 将返回结果保存到文件,filename是文件名 |
urllib2.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False) | 一般访问URL用urllib.urlopen(),如果要修改header信息就会用到这个。 data是随着URL提交的数据,将会把HTTP请求GET改为POST。headers是一个字典,包含提交头的键值对应内容。 |
urllib2.urlopen(url, data=None, timeout=<object object>) | timeout 超时时间,单位秒 |
urllib2.build_opener(*handlers) | 构造opener |
urllib2.install_opener(opener) | 把新构造的opener安装到默认的opener中,以后urlopen()会自动调用 |
urllib2.HTTPCookieProcessor(cookiejar=None) | Cookie处理器 |
urllib2.HTTPBasicAuthHandler | 认证处理器 |
urllib2.ProxyHandler | 代理处理器 |
urllib.urlopen()有几个常用的方法:
方法 | 描述 |
getcode() | 获取HTTP状态码 |
geturl() | 返回真实URL。有可能URL3xx跳转,那么这个将获得跳转后的URL |
info() | 返回服务器返回的header信息。可以通过它的方法获取相关值 |
next() | 获取下一行,没有数据抛出异常 |
read(size=-1) | 默认读取所有内容。size正整数指定读取多少字节 |
readline(size=-1) | 默认读取下一行。size正整数指定读取多少字节 |
readlines(sizehint=0) | 默认读取所有内容,以列表形式返回。sizehint正整数指定读取多少字节 |
示例:
1)请求URL
>>> import urllib, urllib2
>>> response = urllib.urlopen(“http://www.baidu.com”) # 获取的网站页面源码
>>> response.readline()
‘<!DOCTYPE html>\n’
>>> response.getcode()
200
>>> response.geturl()
‘http://www.baidu.com’
2)伪装chrome浏览器访问
>>> user_agent = “Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36”
>>> header = {“User-Agent”: user_agent}
>>> request = urllib2.Request(“http://www.baidu.com”, headers=header)
>>> response = urllib2.urlopen(request)
>>> response.geturl()
‘https://www.baidu.com/’
>>> print respose.info() # 查看服务器返回的header信息
Server: bfe/1.0.8.18
Date: Sat, 12 Nov 2016 06:34:54 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: close
Vary: Accept-Encoding
Set-Cookie: BAIDUID=5979A74F742651531360C08F3BE06754:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BIDUPSID=5979A74F742651531360C08F3BE06754; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: PSTM=1478932494; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: BD_HOME=0; path=/
Set-Cookie: H_PS_PSSID=1426_18240_17945_21118_17001_21454_21408_21394_21377_21525_21192; path=/; domain=.baidu.com
P3P: CP=” OTI DSP COR IVA OUR IND COM “
Cache-Control: private
Cxy_all: baidu+a24af77d41154f5fc0d314a73fd4c48f
Expires: Sat, 12 Nov 2016 06:34:17 GMT
X-Powered-By: HPHP
X-UA-Compatible: IE=Edge,chrome=1
Strict-Transport-Security: max-age=604800
BDPAGETYPE: 1
BDQID: 0xf51e0c970000d938
BDUSERID: 0
Set-Cookie: __bsi=12824513216883597638_00_24_N_N_3_0303_C02F_N_N_N_0; expires=Sat, 12-Nov-16 06:34:59 GMT; domain=www.baidu.com; path=/
3)提交用户表单
>>> post_data = {“loginform-username”:”test”,”loginform-password”:”123456″}
>>> response = urllib2.urlopen(“http://home.51cto.com/index”, data=(urllib.urlencode(post_data)))
>>> response.read() # 登录后网页内容
提交用户名和密码表单登录到51cto网站,键是表单元素的id。其中用到了urlencode()方法,上面讲过是用于转为字典格式为URL接受的编码格式。
例如:
>>> urllib.urlencode(post_data)
‘loginform-password=123456&loginform-username=test’
4)保存cookie到变量中
#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib, urllib2
import cookielib
# 实例化CookieJar对象来保存cookie
cookie = cookielib.CookieJar()
# 创建cookie处理器
handler = urllib2.HTTPCookieProcessor(cookie)
# 通过handler构造opener
opener = urllib2.build_opener(handler)
response = opener.open(“http://www.baidu.com”)
for item in cookie:
print item.name, item.value
# python test.py
BAIDUID EB4BF619C95630EFD619B99C596744B0:FG=1
BIDUPSID EB4BF619C95630EFD619B99C596744B0
H_PS_PSSID 1437_20795_21099_21455_21408_21395_21377_21526_21190_21306
PSTM 1478936429
BDSVRTM 0
BD_HOME 0
urlopen()本身就是一个opener,无法满足对Cookie处理,所有就要新构造一个opener。这里用到了cookielib库,cookielib库是一个可存储cookie的对象。CookieJar类来捕获cookie。
cookie存储在客户端,用来跟踪浏览器用户身份的会话技术。
5)保存cookie到文件
#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib, urllib2
import cookielib
cookie_file = ‘cookie.txt’
# 保存cookie到文件
cookie = cookielib.MozillaCookieJar(cookie_file)
# 创建cookie处理器
handler = urllib2.HTTPCookieProcessor(cookie)
# 通过handler构造opener
opener = urllib2.build_opener(handler)
response = opener.open(“http://www.baidu.com”)
# 保存
cookie.save(ignore_discard=True, ignore_expires=True) # ignore_discard默认是false,不保存将被丢失的。ignore_expires默认flase,如果cookie存在,则不写入。
# python test.py
# cat cookie.txt
# Netscape HTTP Cookie File
# http://curl.haxx.se/rfc/cookie_spec.html
# This is a generated file! Do not edit.
.baidu.com TRUE / FALSE 3626420835 BAIDUID 687544519EA906BD0DE5AE02FB25A5B3:FG=1
.baidu.com TRUE / FALSE 3626420835 BIDUPSID 687544519EA906BD0DE5AE02FB25A5B3
.baidu.com TRUE / FALSE H_PS_PSSID 1420_21450_21097_18560_21455_21408_21395_21377_21526_21192_20927
.baidu.com TRUE / FALSE 3626420835 PSTM 1478937189
www.baidu.com FALSE / FALSE BDSVRTM 0
www.baidu.com FALSE / FALSE BD_HOME 0
MozillaCookieJar()这个类用来保存cookie到文件。
6)使用cookie访问URL
#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib2
import cookielib
# 实例化对象
cookie = cookielib.MozillaCookieJar()
# 从文件中读取cookie
cookie.load(“cookie.txt”, ignore_discard=True, ignore_expires=True)
# 创建cookie处理器
handler = urllib2.HTTPCookieProcessor(cookie)
# 通过handler构造opener
opener = urllib2.build_opener(handler)
# request = urllib2.Request(“http://www.baidu.com”)
response = opener.open(“http://www.baidu.com”)
7)使用代理服务器访问URL
import urllib2
proxy_address = {“http”: “http://218.17.252.34:3128”}
handler = urllib2.ProxyHandler(proxy_address)
opener = urllib2.build_opener(handler)
response = opener.open(“http://www.baidu.com”)
print response.read()
8)URL访问认证
import urllib2
auth = urllib2.HTTPBasicAuthHandler()
# (realm, uri, user, passwd)
auth.add_password(None, ‘http://www.example.com’,’user’,’123456′)
opener = urllib2.build_opener(auth)
response = opener.open(‘http://www.example.com/test.html’)
10
.14 json
JSON是一种轻量级数据交换格式,一般API返回的数据大多是JSON、XML,如果返回JSON的话,将获取的数据转换成字典,方面在程序中处理。
json库经常用的有两种方法dumps和loads():
# 将字典转换为JSON字符串
>>> dict = {‘user’:[{‘user1’: 123}, {‘user2’: 456}]}
>>> type(dict)
<type ‘dict’>
>>> json_str = json.dumps(dict)
>>> type(json_str)
<type ‘str’>
# 把JSON字符串转换为字典
>>> d = json.loads(json_str)
>>> type(d)
<type ‘dict’>
JSON与Python解码后数据类型:
JSON |
Python |
object |
dict |
array |
list |
string |
unicode |
number(int) |
init,long |
number(real) |
float |
true |
Ture |
false |
False |
null |
None |
10.15 time
这个time库提供了各种操作时间值。
方法 |
描述 |
示例 |
time.asctime([tuple]) |
将一个时间元组转换成一个可读的24个时间字符串 |
>>> time.asctime(time.localtime()) ‘Sat Nov 12 01:19:00 2016’ |
time.ctime(seconds) |
字符串类型返回当前时间 |
>>> time.ctime() ‘Sat Nov 12 01:19:32 2016’ |
time.localtime([seconds]) |
默认将当前时间转换成一个(struct_timetm_year,tm_mon,tm_mday,tm_hour,tm_min, tm_sec,tm_wday,tm_yday,tm_isdst) |
>>> time.localtime() time.struct_time(tm_year=2016, tm_mon=11, tm_mday=12, tm_hour=1, tm_min=19, tm_sec=56, tm_wday=5, tm_yday=317, tm_isdst=0) |
time.mktime(tuple) |
将一个struct_time转换成时间戳 |
>>> time.mktime(time.localtime()) 1478942416.0 |
time.sleep(seconds) |
延迟执行给定的秒数 |
>>> time.sleep(1.5) |
time.strftime(format[, tuple]) |
将元组时间转换成指定格式。[tuple]不指定默认以当前时间 |
>>> time.strftime(‘%Y-%m-%d %H:%M:%S’) ‘2016-11-12 01:20:54’ |
time.time() |
返回当前时间时间戳 |
>>> time.time() 1478942466.45977 |
strftime():
指令 |
描述 |
%a |
简化星期名称,如Sat |
%A |
完整星期名称,如Saturday |
%b |
简化月份名称,如Nov |
%B |
完整月份名称,如November |
%c |
当前时区日期和时间 |
%d |
天 |
%H |
24小时制小时数(0-23) |
%I |
12小时制小时数(01-12) |
%j |
365天中第多少天 |
%m |
月 |
%M |
分钟 |
%p |
AM或PM,AM表示上午,PM表示下午 |
%S |
秒 |
%U |
一年中第几个星期 |
%w |
星期几 |
%W |
一年中第几个星期 |
%x |
本地日期,如’11/12/16′ |
%X |
本地时间,如’17:46:20′ |
%y |
简写年名称,如16 |
%Y |
完整年名称,如2016 |
%Z |
当前时区名称(PST:太平洋标准时间) |
%% |
代表一个%号本身 |
10.16 datetime
datetime库提供了以下几个类:
类 |
描述 |
datetime.date() |
日期,年月日组成 |
datetime.datetime() |
包括日期和时间 |
datetime.time() |
时间,时分秒及微秒组成 |
datetime.timedelta() |
时间间隔 |
datetime.tzinfo() |
|
datetime.date()类:
方法 |
描述 |
描述 |
date.max |
对象所能表示的最大日期 |
datetime.date(9999, 12, 31) |
date.min |
对象所能表示的最小日期 |
datetime.date(1, 1, 1) |
date.strftime() |
根据datetime自定义时间格式 |
>>> date.strftime(datetime.now(), ‘%Y-%m-%d %H:%M:%S’) ‘2016-11-12 07:24:15 |
date.today() |
返回当前系统日期 |
>>> date.today() datetime.date(2016, 11, 12) |
date.isoformat() |
返回ISO 8601格式时间(YYYY-MM-DD) |
>>> date.isoformat(date.today()) ‘2016-11-12’ |
date.fromtimestamp() |
根据时间戳返回日期 |
>>> date.fromtimestamp(time.time()) datetime.date(2016, 11, 12) |
date.weekday() |
根据日期返回星期几,周一是0,以此类推 |
>>> date.weekday(date.today()) 5 |
date.isoweekday() |
根据日期返回星期几,周一是1,以此类推 |
>>> date.isoweekday(date.today()) 6 |
date.isocalendar() |
根据日期返回日历(年,第几周,星期几) |
>>> date.isocalendar(date.today()) (2016, 45, 6) |
datetime.datetime()类:
方法 |
描述 |
示例 |
datetime.now()/datetime.today() |
获取当前系统时间 |
>>> datetime.now() datetime.datetime(2016, 11, 12, 7, 39, 35, 106385) |
date.isoformat() |
返回ISO 8601格式时间 |
>>> datetime.isoformat(datetime.now()) ‘2016-11-12T07:42:14.250440’ |
datetime.date() |
返回时间日期对象,年月日 |
>>> datetime.date(datetime.now()) datetime.date(2016, 11, 12) |
datetime.time() |
返回时间对象,时分秒 |
>>> datetime.time(datetime.now()) datetime.time(7, 46, 2, 594397) |
datetime.utcnow() |
UTC时间,比中国时间快8个小时 |
>>> datetime.utcnow() datetime.datetime(2016, 11, 12, 15, 47, 53, 514210) |
datetime.time()类:
方法 |
描述 |
示例 |
time.max |
所能表示的最大时间 |
>>> time.max datetime.time(23, 59, 59, 999999) |
time.min |
所能表示的最小时间 |
>>> time.min datetime.time(0, 0) |
time.resolution |
时间最小单位,1微妙 |
>>> time.resolution datetime.timedelta(0, 0, 1) |
datetime.timedelta()类:
# 获取昨天日期
>>> date.today() – timedelta(days=1)
datetime.date(2016, 11, 11)
>>> date.isoformat(date.today() – timedelta(days=1))
‘2016-11-11’
# 获取明天日期
>>> date.today() + timedelta(days=1)
datetime.date(2016, 11, 13)
>>> date.isoformat(date.today() + timedelta(days=1))
‘2016-11-13’
第十一章 Python常用内建函数
内建函数,可以直接使用,而不需要import。
在前面章节学过的sorded()、reversed()、range(),filter()、reduce()、map()等内建函数,下面再回顾下及学习一些新的内置函数。
函数 |
描述 |
示例 |
sorded(iterable, cmp=None, key=None, reverse=False) |
正序排序可迭代对象,生成新的列表 |
>>> lst = [2,3,4,1,5] >>> sorted(lst) [1, 2, 3, 4, 5] 对字典value排序: >>> dict = {‘a’:86, ‘b’:23, ‘c’:45} >>> sorted(dict.iteritems(), key=lambda x:x[1], reverse=True) [(‘a’, 86), (‘c’, 45), (‘b’, 23)] |
reversed(sequence) |
反向排序序列,返回一个可迭代对象 |
>>> lst = [1,2,3,4,5] >>> lst2 = [] >>> for i in reversed(lst): … lst2.append(i) … >>> lst2 [5, 4, 3, 2, 1] |
range(start, stop[, step]) |
生成整数列表 |
>>> range(0,5) [0, 1, 2, 3, 4] >>> range(0,5, 2) [0, 2, 4] |
xrange(start, stop[, step]) |
生成可迭代对象,比range节省内存资源 |
>>> type(xrange(0,5)) <type ‘xrange’> >>> for i in xrange(0,5): … print i … 0 1 2 3 4 |
filter(function or None, sequence) |
将序列中的元素通过函数处理返回一个新列表、元组或字符串 |
例如:过滤列表中的奇数 >>> lst = [1,2,3,4,5] >>> filter(lambda x:x%2==0, lst) [2, 4] |
reduce(function, sequence[, initial]) |
二元运算函数,所以只接受二元操作函数 |
例如:计算列表总和 >>> lst = [1,2,3,4,5] >>> reduce(lambda x,y:x+y, lst) 15 先将前两个元素相加等于3,再把结果与第三个元素相加等于6,以此类推 |
map(function, sequence[, sequence, …]) |
将序列中的元素通过函数处理返回一个新列表 |
>>> lst = [1,2,3,4,5] >>> map(lambda x:str(x)+”.txt”, lst) [‘1.txt’, ‘2.txt’, ‘3.txt’, ‘4.txt’, ‘5.txt’] |
len(object) |
返回序列的数量 |
>>> len([1,2,3]) 3 |
abs(number) |
返回参数的绝对值 |
>>> abs(-2) 2 |
eval(source[, globals[, locals]]) |
把字符串当成Python表达式处理并返回计算结果 |
>>> a = ‘1 + 2’ >>> eval(a) 3 |
repr(object) |
把象转为字符串表示 |
>>> repr(3) ‘3’ >>> repr(‘1+2’) “‘1+2′” |
round(number[, ndigits]) |
number四舍五入计算,返回浮点数。ndigits是保留几位小数 |
>>> round(1.6) 2.0 |
min(iterable[, key=func]) min(a, b, c, …[, key=func] |
返回最小项。可以是可迭代对象,也可以是两个或两个以上参数。 |
>>> min([1,2,3]) 1 >>> min(‘a’, ‘b’, ‘c’) ‘a’ |
max(iterable[, key=func]) max(a, b, c, …[, key=func]) |
返回最大项。与min使用方法一样。 |
|
sum(sequence[, start]) |
返回序列合,start在计算结果上加的数 |
>>> sum([1,2,3]) 6 |
isinstance(object, class-or-type-or-tuple) |
判断object类型,返回布尔值 |
>>> isinstance([1,2,3],list) True >>> isinstance([1,2,3],tuple) False |
hex(number) |
返回整数十六进制表示 |
>>> hex(18) ‘0x12’ |
zip(seq1 [, seq2 […]]) |
返回一个合并的列表元组,每个元组里面是每个seq对应的下标值,在长度最短的seq结束。 |
>>> zip(range(5),[‘a’,’b’,’c’]) [(0, ‘a’), (1, ‘b’), (2, ‘c’)] |
cmp(x, y) |
比较两个对象,x==y等于返回0,x>y返回整数,x<y返回负数 |
>>> cmp(1,1) 0 >>> cmp(1,2) -1 >>> cmp(1,0) 1 |
locals() |
返回当前局部变量字典 |
>>> a = 1 >>> b = 2 >>> locals() {‘a’: 1, ‘b’: 2,…… |
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
第十二章 Python文件操作
12.1 open()
open()函数作用是打开文件,返回一个文件对象。
用法格式:open(name[, mode[, buffering[,encoding]]]) -> file object
name 文件名
mode 模式,比如以只读方式打开
buffering 缓冲区
encoding 返回数据采用的什么编码,一般utf8或gbk
Mode |
Description |
r |
只读,默认 |
w |
只写,打开前清空文件内容 |
a |
追加 |
a+ |
读写,写到文件末尾 |
w+ |
可读写,清空文件内容 |
r+ |
可读写,能写到文件任何位置 |
rb |
二进制模式读 |
wb |
二进制模式写,清空文件内容 |
例如:打开一个文件
>>> f = open(‘test.txt’, ‘r’)
>>> f.
f.__class__( f.__new__( f.encoding f.readinto(
f.__delattr__( f.__reduce__( f.errors f.readline(
f.__doc__ f.__reduce_ex__( f.fileno( f.readlines(
f.__enter__( f.__repr__( f.flush( f.seek(
f.__exit__( f.__setattr__( f.isatty( f.softspace
f.__format__( f.__sizeof__( f.mode f.tell(
f.__getattribute__( f.__str__( f.name f.truncate(
f.__hash__( f.__subclasshook__( f.newlines f.write(
f.__init__( f.close( f.next( f.writelines(
f.__iter__( f.closed f.read( f.xreadlines(
open()函数打开文件返回一个文件对象,并赋予遍历f,f就拥有了这个文件对象的操作方法。
方法 |
描述 |
f.read([size]) |
读取size字节,当未指定或给负值时,读取剩余所有的字节,作为字符串返回 |
f.readline([size]) |
从文件中读取下一行,作为字符串返回。如果指定size则返回size字节 |
f.readlines([size]) |
读取size字节,当未指定或给负值时,读取剩余所有的字节,作为列表返回 |
f.write(str) |
写字符串到文件 |
f.writelines(seq) |
写序列到文件,seq必须是一个可迭代对象,而且要是一个字符串序列 |
f.seek(offset[, whence=0]) |
在文件中移动文件指针,从whence(0代表文件起始位置,默认。1代表当前位置。2代表文件末尾)偏移offset个字节 |
f.tell() |
返回当前在文件中的位置 |
f.close()
|
关闭文件 |
f.flush |
刷新缓冲区到磁盘 |
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ群:323779636(Shell/Python运维开发群)
12.2 文件对象操作
写一个测试文件test.txt举例:
# cat test.txt
1.Python
2.Java
3.C++
4.Ruby
12.2.1 read()读取所有内容
>>> f = open(‘test.txt’, ‘r’)
>>> f.read()
‘1.Python\n2.Java\n3.C++\n4.Ruby\n’
# 获取指定字节
指定读取多少字节:
>>> f = open(‘test.txt’, ‘r’)
>>> f.read(9)
‘1.Python\n’
12.2.2 readline()读取下一行内容
>>> f = open(‘test.txt’, ‘r’)
>>> f.readline()
‘1.Python\n’
>>> f.readline()
‘2.Java\n’
12.2.3 readlines()读取所有内容返回一个列表
>>> f = open(‘test.txt’, ‘r’)
>>> f.readlines()
[‘1.Python\n’, ‘2.Java\n’, ‘3.C++\n’, ‘4.Ruby\n’]
12.2.4 wirte()写入字符串到文件
>>> f = open(‘test.txt’, ‘a’) # 以追加方式打开文件
>>> f.write(“5.Shell\n”) # 这一步并没有真正写到文件
>>> f.flush() # 刷新到磁盘才写到文件
# cat test.txt
1.Python
2.Java
3.C++
4.Ruby
5.Shell
12.2.5 wirtelines()写入一个序列字符串到文件
>>> f = open(‘test.txt’, ‘a’)
>>> f.writelines([‘a’,’b’,’c’])
>>> f.flush()
# cat test.txt
1.Python
2.Java
3.C++
4.Ruby
5.Shell
abc
12.2.6 seek()从指定位置读取
>>> f = open(‘test.txt’, ‘r’)
>>> f.tell()
0
>>> f.seek(9)
>>> f.tell()
9
>>> f.seek(5,1) # 1表示从当前位置开始
>>> f.tell()
14
12.2.7 tell()返回当前指针位置
>>> f = open(‘test.txt’, ‘r’)
>>> f.tell()
0
>>> f.readline()
‘1.Python\n’
>>> f.tell()
9
>>> f.readline()
‘2.Java\n’
>>> f.tell()
16
>>> f.close() # 使用完后关闭文件
12.3 文件对象增删改查
在shell中,我们要想对文件指定行插入内容、替换等情况,使用sed工具很容易就实现。在本章节讲的open()函数并没有直接类似与sed工具的方法,要想实现这样的操作,变通的处理能到达此效果,主要思路是先读取内容修改,再写会文件,以下举几个常用的情况 。
12.3.1 在第一行增加一行
例如:在开头添加一个test字符串
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open(‘test.txt’, ‘r’)
data = f.read()
data = “test\n” + data
f = open(‘test.txt’, ‘w’)
f.write(data)
f.flush()
f.close()
# python test.py
# cat test.txt
test
1.Python
2.Java
3.C++
4.Ruby
先将数据读出来,然后把要添加的test字符串拼接到原有的数据,然后在写入这个文件。
12.3.2 在指定行添加一行
例如:在第二行添加一个test字符串
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open(‘test.txt’, ‘r’)
data_list = f.readlines() # 经测试,此方法比下面迭代效率高
# data_list = []
# for line in f:
# data_list.append(line)
data_list.insert(1, ‘test\n’)
# data = ”.join(data)
f = open(‘test.txt’, ‘w’)
# f.write(data)
f.writelines(data_list)
f.flush()
f.close
# python test.py
# cat test.txt
1.Python
test
2.Java
3.C++
4.Ruby
先将数据以列表存储,就可以根据下标插入到指定位置,也就是哪一行了。再通过join把列表拼接成字符串,最后写到文件。
12.3.3 在匹配行前一行或后一行添加test字符串
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open(‘test.txt’, ‘r’)
data_list = f.readlines()
data_list.insert(2-1, ‘test\n’) # 在指定行减去一行就是上一行了,下一行插入同理
f = open(‘test.txt’, ‘w’)
f.writelines(data_list)
f.flush()
f.close
12.3.4 删除指定行
例如:删除第三行,与在指定行添加同理
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open(‘test.txt’, ‘r’)
data_list = f.readlines()
data_list.pop(2)
f = open(‘test.txt’, ‘w’)
f.writelines(data_list)
f.flush()
f.close
例如:只保留第一行至第三行
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open(‘test.txt’, ‘r’)
data_list = f.readlines()[0:2] # 列表切片
f = open(‘test.txt’, ‘w’)
f.write(data_list)
f.flush()
f.close
12.3.5 删除匹配行
例如:删除匹配Py字符的行
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open(‘test.txt’, ‘r’)
data = f.readlines()
# data_list = []
# for line in data:
# if line.find(‘Py’) == -1: # 如果当前行不包含Py字符,会返回-1,否则返回下标
# data_list.append(line)
data_list = [line for line in data if line.find(‘Py’) == -1]
f = open(‘test.txt’, ‘w’)
f.writelines(data_list)
f.flush()
f.close
12.3.6 全局替换字符串
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open(‘test.txt’, ‘r’)
data = f.read()
data.replace(‘old string’, ‘new string’)
f = open(‘test.txt’, ‘w’)
f.write(data)
f.flush()
f.close
12.3.7 在指定行替换字符串
例如:将C++改为C#
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open(‘test.txt’, ‘r’)
data = f.readlines()
data_list = []
for line in data:
if data.index(line) == 2:
data_list.append(line.replace(‘++’, ‘#’))
else:
data_list.append(line)
f = open(‘test.txt’, ‘w’)
f.writelines(data_list)
f.flush()
f.close
12.3.8 处理大文件
在读取上G文件时,直接读取所有内容会导致内存占用过多,内存爆掉。要想提高处理效率,有以下两种方法:
方法1:open()打开文件返回的对象本身就是可迭代的,利用for循环迭代可提高处理性能
>>> f = open(‘test.txt’)
>>> for line in f:
… print line # 每行后面会有一个换行符\n,所以会打印出来换行符,可以使用line.strip(‘\n’)去除
…
1.Python
2.Java
3.C++
4.Ruby
方法2:每次只读取固定字节
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open(‘test.txt’)
while True:
data = f.read(1024) # 每次只读取1024字节
if not data: break
12.3.9 下载文件
方法1:
import urllib
url = “http://nginx.org/download/nginx-1.10.1.tar.gz”
urllib.urlretrieve(url, “nginx-1.10.1.tar.gz”)
方法2:
import urllib2
url = “http://nginx.org/download/nginx-1.10.1.tar.gz”
f = urllib2.urlopen(url).read()
with open(“nginx-1.10.1.tar.gz”, “wb”) as data:
data.write(f)
12.4 fileinput
fileinput模块是Python内建模块,用于遍历文件,可对多文件操作。
方法 |
描述 |
fileinput.input([files[, inplace[, backup[, mode[, openhook]]]]]) |
files:文件路径,多文件这样写[‘1.txt,’2.txt”] inplace:是否将标准输出写到原文件,默认是0,不写 backup:备份文件扩展名,比如.bak mode:读写模式,默认r,只读 openhook: |
fileinput.isfirstline() |
检查当前行是否是文件的第一行 |
fileinput.lineno() |
返回当前已经读取行的数量 |
fileinput.fileno() |
返回当前文件数量 |
fileinput.filelineno() |
返回当前读取行的行号 |
fileinput.filename() |
返回当前文件名 |
12.4.1 遍历文件内容
#!/usr/bin/python
# -*- coding: utf-8 -*-
import fileinput
for line in fileinput.input(‘test.txt’):
print line
# python test.py
1.Python
2.Java
3.C++
4.Ruby
12.4.2 返回当前读取行的行号
#!/usr/bin/python
# -*- coding: utf-8 -*-
import fileinput
for line in fileinput.input(‘test.txt’):
print fileinput.filelineno()
print line, # 逗号忽略换行符
# python test.py
1
1.Python
2
2.Java
3
3.C++
4
4.Ruby
12.4.3 全局替换字符,修改原文件
#!/usr/bin/python
# -*- coding: utf-8 -*-
import fileinput
for line in fileinput.input(‘test.txt’, backup=’.bak’, inplace=1):
line = line.replace(‘++’,’#’)
print line,
先把要操作的文件备份一个以.bak的后缀文件,inplace=1是将标准输出写到原文件,也就是这个脚本如果没有标准输出,就会以空数据写到原文件。
12.4.4 对多文件操作
#!/usr/bin/python
# -*- coding: utf-8 -*-
import fileinput
for line in fileinput.input([‘test.txt’, ‘test2.txt’]):
print line,
12.4.5 实时读取文件新增内容,类似tail -f
#!/usr/bin/python
# -*- coding: utf-8 -*-
with open(‘access.log’) as f:
f.seek(0,2) # 每次打开文件都将文件指针移动到末尾
while True:
line = f.readline()
if line:
print line,
这个死循环会一直执行下面的操作。很消耗性能。
我们可以加个休眠,每秒读取一次:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import time
with open(‘access.log’) as f:
f.seek(0,2)
while True:
line = f.readline()
if line:
print line,
else:
time.sleep(1)
12.5 shutil
shutil模块是Python内建模块,用于文件或目录拷贝,归档。
方法 |
描述 |
shutil.copyfile(src, dst) |
复制文件 |
shutil.copytree(src, dst) |
复制文件或目录 |
shutil.move(src, dst) |
移动文件或目录 |
shutil.rmtree(path,ignore_errors=False, onerror=None) |
递归删除目录。os.rmdir()不能删除有文件的目录,就可以用这个了 |
shutil.make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, dry_run=0, owner=None, group=None, logger=None) |
Python2.7以后才有这个方法。 功能是创建zip或tar归档文件。 base_name:要创建归档文件名 format:归档文件格式,有zip、tar、bztar、gztar root_dir:要压缩的目录 base_dir:? 用法:shutil.make_archive(‘wp’,’zip’,’/root/wordpress’) |
12.6 with语句
在处理一些事务时,可能会出现异常和后续的清理工作,比如读取失败,关闭文件等。这就用到了异常处理语句try…except,如下:
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open(‘test.txt’)
try:
data = f.read()
finally:
f.close()
Python对于这种情况提供了一种更简单的处理方式,with语句。处理一个文件时,先获取一个文件句柄,再从文件中读取数据,最后关闭文件句柄。如下:
#!/usr/bin/python
# -*- coding: utf-8 -*-
with open(‘test.txt’) as f:
data = f.read()
可见这种方式显得更简约,一些异常、清理工作都交给with处理了。
第十三章 Python数据库编程
本章节讲解Python操作数据库,完成简单的增删改查工作,以MySQL数据库为例。
Python的MySQL数据库操作模块叫MySQLdb,需要额外的安装下。
通过pip工具安装:pip install MySQLdb
MySQLdb模块,我们主要就用到连接数据库的方法MySQLdb.Connect(),连接上数据库后,再使用一些方法做相应的操作。
MySQLdb.Connect(parameters…)方法提供了以下一些常用的参数:
参数 |
描述 |
host |
数据库地址 |
user |
数据库用户名, |
passwd |
数据库密码,默认为空 |
db |
数据库库名,没有默认库 |
port |
数据库端口,默认3306 |
connect_timeout |
连接超时时间,秒为单位 |
use_unicode |
结果以unicode字符串返回 |
charset |
插入数据库编码 |
连接对象返回的connect()函数:
commit() |
提交事务。对支持事务的数据库和表,如果提交修改操作,不适用这个方法,则不会写到数据库中 |
rollback() |
事务回滚。对支持事务的数据库和表,如果执行此方法,则回滚当前事务。在没有commit()前提下。 |
cursor([cursorclass]) |
创建一个游标对象。所有的sql语句的执行都要在游标对象下进行。MySQL本身不支持游标,MySQLdb模块对其游标进行了仿真。 |
游标对象也提供了几种方法:
close() |
关闭游标 |
execute(sql) |
执行sql语句 |
excutemany(sql) |
执行多条sql语句 |
fetchone() |
从执行结果中取第一条记录 |
fetchmany(n) |
从执行结果中取n条记录 |
fetchall() |
从执行结果中取所有记录 |
scroll(self, value, mode=’relative’) |
游标滚动 |
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ群:323779636(Shell/Python运维开发群) 13.1 数据库增删改查
13.1.1 在test库创建一张user表,并添加一条记录
>>> conn = MySQLdb.Connect(host=’192.168.1.244′,user=’root’,passwd=’QHyCTajI’,db=’test’,charset=’utf8′)
>>> cursor = conn.cursor()
>>> sql = “create table user(id int,name varchar(30),password varchar(30))”
>>> cursor.execute(sql) # 返回的数字是影响的行数
0L
>>> sql = “insert into user(id,name,password) values(‘1′,’xiaoming’,’123456′)”
>>> cursor.execute(sql)
1L
>>> conn.commit() # 提交事务,写入到数据库
>>> cursor.execute(‘show tables’) # 查看创建的表
1L
>>> cursor.fetchall() # 返回上一个游标执行的所有结果,默认是以元组形式返回
((u’user’,),)
>>> cursor.execute(‘select * from user’)
1L
>>> cursor.fetchall()
((1L, u’xiaoming’, u’123456′),)
13.1.2 插入多条数据
>>> sql = ‘insert into user(id,name,password) values(%s,%s,%s)’
>>> args = [(‘2′,’zhangsan’,’123456′), (‘3′,’lisi’,’123456′),(‘4′,’wangwu’,’123456′)]
>>> cursor.executemany(sql, args)
3L
>>> conn.commit()
>>> sql = ‘select * from user’
>>> cursor.execute(sql)
4L
>>> cursor.fetchall()
((1L, u’xiaoming’, u’123456′), (2L, u’zhangsan’, u’123456′), (3L, u’lisi’, u’123456′), (4L, u’wangwu’, u’123456′))
args变量是一个包含多元组的列表,每个元组对应着每条记录。当查询多条记录时,使用此方法,可有效提高插入效率。
13.1.3 删除用户名xiaoming的记录
>>> sql = ‘delete from user where name=”xiaoming”‘
>>> cursor.execute(sql)
1L
>>> conn.commit()
>>> sql = ‘select * from user’
>>> cursor.execute(sql)
3L
>>> cursor.fetchall()
((2L, u’zhangsan’, u’123456′), (3L, u’lisi’, u’123456′), (4L, u’wangwu’, u’123456′))
13.1.4 查询记录
>>> sql = ‘select * from user’
>>> cursor.execute(sql)
3L
>>> cursor.fetchone() # 获取第一条记录
(2L, u’zhangsan’, u’123456′)
>>> sql = ‘select * from user’
>>> cursor.execute(sql)
3L
>>> cursor.fetchmany(2) # 获取两条记录
((2L, u’zhangsan’, u’123456′), (3L, u’lisi’, u’123456′))
13.1.4 以字典形式返回结果
默认显示是元组形式,要想返回字典形式,使得更易处理,就用到cursor([cursorclass])中的cusorclass参数。
传入MySQLdb.cursors.DictCursor类:
>>> cursor = conn.cursor(MySQLdb.cursors.DictCursor)
>>> sql = ‘select * from user’
>>> cursor.execute(sql)
3L
>>> cursor.fetchall()
({‘password’: u’123456′, ‘id’: 2L, ‘name’: u’zhangsan’}, {‘password’: u’123456′, ‘id’: 3L, ‘name’: u’lisi’}, {‘password’: u’123456′, ‘id’: 4L, ‘name’: u’wangwu’})
13.2 遍历查询结果
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import MySQLdb
try:
conn = MySQLdb.Connect(host=’127.0.0.1′, port=3306, user=’root’, passwd=’123456′, connect_timeout=3, charset=’utf8′)
cursor = conn.cursor()
sql = “select * from user”
cursor.execute(sql)
for i in cursor.fetchall():
print i
except Exception, e:
print (“Connection Error: ” + str(e))
finally:
conn.close()
# python test.py
(2L, u’zhangsan’, u’123456′)
(3L, u’lisi’, u’123456′)
(4L, u’wangwu’, u’123456′)
使用for循环遍历查询结果,并增加了异常处理。
第十四章 Python发送邮件(常见四种邮件内容)
在写脚本时,放到后台运行,想知道执行情况,会通过邮件、SMS(短信)、飞信、微信等方式通知管理员,用的最多的是邮件。在linux下,Shell脚本发送邮件告警是件很简单的事,有现成的邮件服务软件或者调用运营商邮箱服务器。
对于Python来说,需要编写脚本调用邮件服务器来发送邮件,使用的协议是SMTP。接收邮件,使用的协议是POP3和IMAP。我想有必要说明下 ,POP3和IMAP的区别:POP3在客户端邮箱中所做的操作不会反馈到邮箱服务器,比如删除一封邮件,邮箱服务器并不会删除。IMAP则会反馈到邮箱服务器,会做相应的操作。
Python分别提供了收发邮件的库,smtplib、poplib和imaplib。
本章主要讲解如果使用smtplib库实现发送各种形式的邮件内容。在smtplib库中,主要主要用smtplib.SMTP()类,用于连接SMTP服务器,发送邮件。
这个类有几个常用的方法:
方法 |
描述 |
SMTP.set_debuglevel(level) |
设置输出debug调试信息,默认不输出 |
SMTP.docmd(cmd[, argstring]) |
发送一个命令到SMTP服务器 |
SMTP.connect([host[, port]]) |
连接到指定的SMTP服务器 |
SMTP.helo([hostname]) |
使用helo指令向SMTP服务器确认你的身份 |
SMTP.ehlo(hostname) |
使用ehlo指令像ESMTP(SMTP扩展)确认你的身份 |
SMTP.ehlo_or_helo_if_needed() |
如果在以前的会话连接中没有提供ehlo或者helo指令,这个方法会调用ehlo()或helo() |
SMTP.has_extn(name) |
判断指定名称是否在SMTP服务器上 |
SMTP.verify(address) |
判断邮件地址是否在SMTP服务器上 |
SMTP.starttls([keyfile[, certfile]]) |
使SMTP连接运行在TLS模式,所有的SMTP指令都会被加密 |
SMTP.login(user, password) |
登录SMTP服务器 |
SMTP.sendmail(from_addr, to_addrs, msg, mail_options=[], rcpt_options=[]) |
发送邮件 from_addr:邮件发件人 to_addrs:邮件收件人 msg:发送消息 |
SMTP.quit() |
关闭SMTP会话 |
SMTP.close() |
关闭SMTP服务器连接 |
看下官方给的示例:
>>> import smtplib
>>> s=smtplib.SMTP(“localhost”)
>>> tolist=[“one@one.org”,”two@two.org”,”three@three.org”,”four@four.org”]
>>> msg = ”’\
… From: Me@my.org
… Subject: testin’…
…
… This is a test ”’
>>> s.sendmail(“me@my.org”,tolist,msg)
{ “three@three.org” : ( 550 ,”User unknown” ) }
>>> s.quit()
我们根据示例给自己发一个邮件测试下:
我这里测试使用本地的SMTP服务器,也就是要装一个支持SMTP协议的服务,比如sendmail、postfix等。CentOS安装sendmail:yum install sendmail
>>> import smtplib
>>> s = smtplib.SMTP(“localhost”)
>>> tolist = [“xxx@qq.com”, “xxx@163.com”]
>>> msg = ”’\
… From: Me@my.org
… Subject: test
… This is a test ”’
>>> s.sendmail(“me@my.org”, tolist, msg)
{}
进入腾讯和网易收件人邮箱,就能看到刚发的测试邮件,一般都被邮箱服务器过滤成垃圾邮件,所以收件箱没有,你要去垃圾箱看看。
可以看到,多个收件人可以放到一个列表中进行群发。msg对象里From表示发件人,Subject是邮件标题,换行后输入的是邮件内容。
上面是使用本地SMTP服务器发送的邮件,测试下用163服务器发送邮件看看效果:
>>> import smtplib
>>> s = smtplib.SMTP(“smtp.163.com”)
>>> s.login(“baojingtongzhi@163.com”, “xxx”)
(235, ‘Authentication successful’)
>>> tolist = [“xxx@qq.com”, “xxx@163.com”]
>>> msg = ”’\
… From: baojingtongzhi@163.com
… Subject: test
… This is a test ”’
>>> s.sendmail(“baojingtongzhi@163.com”, tolist, msg)
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
File “/usr/lib64/python2.6/smtplib.py”, line 725, in sendmail
raise SMTPDataError(code, resp)
smtplib.SMTPDataError: (554, ‘DT:SPM 163 smtp10,DsCowAAXIdDIJAtYkZiTAA–.65425S2 1477125592,please see http://mail.163.com/help/help_spam_16.htm?ip=119.57.73.67&hostid=smtp10&time=1477125592’)
访问给出的163网址,SMTP554错误是: “554 DT:SUM 信封发件人和信头发件人不匹配;”
大概已经明白啥意思,看上面再使用本地SMTP服务器时候,收件人位置是“undisclosed-recipients”,看这样163的SMTP服务器不给我们服务的原因就是这里收件人没指定。
重新修改下msg对象,添加上收件人:
>>> msg = ”’\
… From: baojingtongzhi@163.com
… To: 962510244@qq.com ,zhenliang369@163.com
… Subject: test
…
… This is a test ”’
>>> s.sendmail(“baojingtongzhi@163.com”, tolist, msg)
{}
好了,可以正常发送邮件了。msg这个格式是SMTP规定的,一定要遵守。
14.1 Python发送邮件并抄送
#!/usr/bin/python
# -*- coding: utf-8 -*-
import smtplib
def sendMail(body):
smtp_server = ‘smtp.163.com’
from_mail = ‘baojingtongzhi@163.com’
mail_pass = ‘xxx’
to_mail = [‘962510244@qq.com’, ‘zhenliang369@163.com’]
cc_mail = [‘lizhenliang@xxx.com’]
from_name = ‘monitor’
subject = u’监控’.encode(‘gbk’) # 以gbk编码发送,一般邮件客户端都能识别
# msg = ”’\
# From: %s <%s>
# To: %s
# Subject: %s
# %s”’ %(from_name, from_mail, to_mail_str, subject, body) # 这种方式必须将邮件头信息靠左,也就是每行开头不能用空格,否则报SMTP 554
mail = [
“From: %s <%s>” % (from_name, from_mail),
“To: %s” % ‘,’.join(to_mail), # 转成字符串,以逗号分隔元素
“Subject: %s” % subject,
“Cc: %s” % ‘,’.join(cc_mail),
“”,
body
]
msg = ‘\n’.join(mail) # 这种方式先将头信息放到列表中,然后用join拼接,并以换行符分隔元素,结果就是和上面注释一样了
try:
s = smtplib.SMTP()
s.connect(smtp_server, ’25’)
s.login(from_mail, mail_pass)
s.sendmail(from_mail, to_mail+cc_mail, msg)
s.quit()
except smtplib.SMTPException as e:
print “Error: %s” %e
if __name__ == “__main__”:
sendMail(“This is a test!”)
s.sendmail(from_mail, to_mail+cc_mail, msg) 在这里注意下,收件人和抄送人为什么放一起发送呢?其实无论是收件人还是抄送人,它们收到的邮件都是一样的,SMTP都是认为收件人这样一封一封的发出。所以实际上并没有抄送这个概念,只是在邮件头加了抄送人的信息罢了!另外,如果不需要抄送人,直接把上面cc的信息去掉即可。
14.2 Python发送邮件带附件
由于SMTP.sendmail()方法不支持添加附件,所以可以使用email模块来满足需求。email模块是一个构造邮件和解析邮件的模块。
先看下如何用email库构造一个简单的邮件:
message = Message()
message[‘Subject’] = ‘邮件主题’
message[‘From’] = from_mail
message[‘To’] = to_mail
message[‘Cc’] = cc_mail
message.set_payload(‘邮件内容’)
基本的格式就是这样的!
继续回到主题,发送邮件带附件:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
from email import encoders
from email.mime.base import MIMEBase
from email.utils import parseaddr, formataddr
# 格式化邮件地址
def formatAddr(s):
name, addr = parseaddr(s)
return formataddr((Header(name, ‘utf-8’).encode(), addr))
def sendMail(body, attachment):
smtp_server = ‘smtp.163.com’
from_mail = ‘baojingtongzhi@163.com’
mail_pass = ‘xxx’
to_mail = [‘962510244@qq.com’, ‘zhenliang369@163.com’]
# 构造一个MIMEMultipart对象代表邮件本身
msg = MIMEMultipart()
# Header对中文进行转码
msg[‘From’] = formatAddr(‘管理员 <%s>’ % from_mail).encode()
msg[‘To’] = ‘,’.join(to_mail)
msg[‘Subject’] = Header(‘监控’, ‘utf-8’).encode()
# plain代表纯文本
msg.attach(MIMEText(body, ‘plain’, ‘utf-8’))
# 二进制方式模式文件
with open(attachment, ‘rb’) as f:
# MIMEBase表示附件的对象
mime = MIMEBase(‘text’, ‘txt’, filename=attachment)
# filename是显示附件名字
mime.add_header(‘Content-Disposition’, ‘attachment’, filename=attachment)
# 获取附件内容
mime.set_payload(f.read())
encoders.encode_base64(mime)
# 作为附件添加到邮件
msg.attach(mime)
try:
s = smtplib.SMTP()
s.connect(smtp_server, “25”)
s.login(from_mail, mail_pass)
s.sendmail(from_mail, to_mail, msg.as_string()) # as_string()把MIMEText对象变成str
s.quit()
except smtplib.SMTPException as e:
print “Error: %s” % e
if __name__ == “__main__”:
sendMail(‘附件是测试数据, 请查收!’, ‘test.txt’)