15. 解决问题——编写一个Python脚本
问题:为所有的重要文件创建备份的程序
设计中应该明确:
1. 需要备份的文件盒目录由一个列表指定。
2. 本分应该保存在主备份目录中。
3. 文件备份成为一个zip文件。
4. zip存档的名称是当前的日期和时间。
解决方案:
方案一:
#!/usr/bin/python
#Filename: backup_ver1.py
import os;
import time;
#1. 指定要备份的目录和列表
# 如果要使用Linux,使用source = ['home/backup', r'home/swaroop/bin'];或者类似这样的
source = [r'G:\backup', r'G:\backup备份.txt'];
# 2. 备份必须存储在主备份目录
target_dir = r'G:\backup';
# 3. 文件备份为rar文件
# 4. rar归档文件的名称是当前的日期和时间
target = target_dir + time.strftime('%Y%m%d%H%M%S') + '.rar';
# 5. 使用rar命令将文件放入rar归档文件中
rar_command = "rar a '%s' '%s' " %(target, ''.join(source));
# Run the backup
if os.system(rar_command) == 0:
print 'Successful backup to', target;
else:
print 'Backup FAILED';
注解:
获得日期与时间,使用time.strtime()函数获得。
%Y会被无世纪的年份代替,%m会被01到12之间十进制月份代替,一次类推。
使用的压缩命令是winrar a f:\backup\backup f:\backuping\201104111705.rar
字符串的join方法把source列表 转换为一个字符串
使用os.system函数运行命令,利用这个函数就行是在系统中运行命令一样,即在shell中运行命令,如果成功,则返回0, 否则返回错误号。
注:在Python中'\'是转义字符,如果要在字符串中使用'\',则使用'\\'。
版本二:
采用一种更加好的文件名机制,使用时间作为文件名,使用当前的日期作为目录名称。这样使得你的备份会以等级结构存储,更加容易管理。并且可以缩短文件的名称长度。
#!/usr/bin/python
# -*- coding: cp936 -*-
#Filename: backup_ver2.py
import os;
import time;
# 要进行备份的目录和文件放入source中
source = ['\\backup', '\\backup备份.txt'];
sourcename = ['backup', 'backup备份.txt'];
# 备份必须存储在主目录中
target_dir = 'f:\\backup\\';
source_dir = 'f:\\backuping';
# 文件备份为rar文件
# 当前的日期是主目录下的子目录
today = target_dir + time.strftime('%Y%m%d');
# 当前时间是rar归档文件的名字
now = time.strftime('%H%M%S');
# 如果没有存在,则创建子目录
if not os.path.exists(today):
os.mkdir(today); # 创建目录
print 'Successfully created directory', today;
#备份名称
i = 0;
while i < len(source):
target = today + os.sep + now + sourcename[i] + '.rar';
print target;
rar_command = "winrar a %s %s" %(target, source_dir + source[i]);
if os.system(rar_command) == 0:
print 'Susccessful backup to', target;
else:
print 'Failed to backup file';
i = i + 1;
else:
print 'Backup all file successfully!';
修改的主要部分:使用os.exists函数检验在主备份目录中是否有以当前日期作为名称的目录,如果没有使用os.mkdir函数创建。
其中的os.sep变量 - 它会根据操作系统给出的目录分隔符,即在Linux下是'\',在windows下是'\\',而在mac下是':'。使用os.sep而非直接使用字符,会使我们的程序具有移植性。
版本三:
#!/usr/bin/python
# -*- coding: cp936 -*-
#Filename: backup_ver3.py
import os;
import time;
# 要进行备份的目录和文件放入source中
source = ['\\backup', '\\backup备份.txt'];
sourcename = ['backup', 'backup备份.txt'];
# 备份必须存储在主目录中
target_dir = 'f:\\backup\\';
source_dir = 'f:\\backuping';
# 文件备份为rar文件
# 当前的日期是主目录下的子目录
today = target_dir + time.strftime('%Y%m%d');
# 当前时间是rar归档文件的名字
now = time.strftime('%H%M%S');
# 为文件名加入注释
comment = raw_input('Enter a coment -->');
if len(comment) == 0:
comment = "";
else:
comment = comment.replace(' ', '_');
print comment;
# 如果没有存在,则创建子目录
if not os.path.exists(today):
os.mkdir(today); # 创建目录
print 'Successfully created directory', today;
#文件名称
i = 0;
while i < len(source):
target = today + os.sep + now + sourcename[i] + comment + '.rar';
print target;
rar_command = "winrar a %s %s" %(target, source_dir + source[i]);
if os.system(rar_command) == 0:
print 'Susccessful backup to', target;
else:
print 'Failed to backup file';
i = i + 1;
else:
print 'Backup file successful!';
raw_input函数得到用户的输入,然后通过len函数找到输入的长度,以检验是否确实输入了东西。
将输入的注释中的空格换为_以作为文件名称。
进一步进行改进:
* 可以从命令行将要备份的文件传递给程序,使用list类的extend方法将他们加入到source列表中去。
* 最理想的归档的方法是使用zipfile和tarfile,这两个函数是Python标准库中的方法,使用这两个函数,就不需要使用os.system这个函数了,因为这个函数很容易引发错误。
16. 面向对象编程
对象可以好似用普通的 属于 对象的变量存储数据。属于一个对象或者是类的变量称为域,对象也可以使用属于类的函数来具有功能。域与方法合称为类的属性。
域有两种类型—— 属于每个实例/类的对象或者是属于类本身,他们分别称为实例变量和类变量。
类的方法与普通的函数只有一个区别——它必须有一个额外的第一个参数名称,但是调用的时候不需要调用者为这个参数赋值,Python会自动提供这个值。这个特别的变量指对象本身,按照惯例它的名称是self
注:虽然可以给这个参数任何名称,但是建议使用self这个名称。使用标准名称,可以让读者迅速识别,一些IDE也有一些支持。
self原理:假如你有一个MyClass类,以及这个类的一个实例MyObject,当调用MyObject.method(arg1, arg2)的时候,这会由Python自动转化为MyClass.method(MyObject, arg1, arg2)。
1. 简单类的例子
一个最简单的类的例子:
#!/usr/bin/python
#Filename: simplestclass.py
class Person:
pass # An empty block
p = Person(); #一定注意缩进,在Python中缩进就相当于括号的作用
print p;
2. 类的方法
类/对象可以拥有像函数一样的方法,这些方法与函数的区别只是一个额外的self变量。
一个例子:
#!/usr/bin/python
#Filename: method.py
class Person:
def sayHi(self):
print 'Hello, how are you?';
p = Person();
p.sayHi(); # Also can be written as Person().sayHi();
本例中的self参数必须写入sayHi函数中,但是在执行时不需要给self赋值,系统会自动给它赋值。
Python的类中,很多方法的名字有特殊的重要意义。
__init__ 方法
该方法在类的一个对象被创建时,马上运行。这个方法用来对你的对象做一些希望的初始化的工作。
注:改函数名称的开头和结尾都是双下划线。
#!/usr/bin/python
# Filename: class_init.py
class Person:
def __init__(self, name):
self.name = name;
def sayHi( self):
print 'Hello, my name is ', self.name;
p = Person('Andy');
p.sayHi();
本例中给Person类加了一个域 self.name。
其实__init__() 函数就是类的初始化函数,
3. 类的数据
类的数据其实就是与对象的命名空间绑定的普通变量,这些名称在这些类与对象的前提下有效。
有两种类型的域: 类的变量和对象的变量,它们根据是类还是对象 拥有这个变量而区分。
类变量:由一个类的所有对象(实例)共享使用。只有一个类变量的拷贝,当某个对象对类的变量做了改动,
这个改动会反应到所有其他的实例上。
对象的变量:由类的每个对象/实例拥有。因此每个对象有自己对这个域的一个拷贝,即它们不是共享的。
在同一个类的不同的实例中,虽然对象的变量有相同的名称,但是相互不相关。
#!/usr/bin/python
#Filename: objvar.py
class Person:
'''Represents a person.''';
population = 0;
def __init__(self, name):
self.name = name;
print '(Initializing %s)' %self.name;
# when this person is created, he/she adds to the population
Person.population += 1;
def __del__(self):
'''I am dying.'''
print '%s say bye.' %self.name;
Person.population -= 1;
if Person.population == 0:
print 'I am the last one.';
else:
print 'There are still %d people left.' %Person.population;
def sayHi(self):
'''Greeting by the person.
Really, that's all it does.'''
print 'Hi, my name is %s.' %self.name;
def howMany(self):
'''Prints the current population.''' #文档字符串
if Person.population == 1:
print 'I am the only person here.';
else:
print 'We have %d persons here.' %Person.population;
Andy = Person('Andy');
Andy.sayHi();
Andy.howMany();
kalam = Person('Abdul Kalam');
kalam.sayHi();
kalam.howMany();
Andy.sayHi();
Andy.howMany();
del Andy;
del kalam;
population属于Person类,它是一个类的变量,name变量属于对象(使用self赋值),它是对象的变量。
只能使用self变量来访问同一个对象的变量和方法,被称为属性访问。
本程序中可以看到docstring对类和方法同样有用。可以在运行时使用
Person.__doc__和Person.sayHi.__doc__来分别访问类与方法的文档字符串。
如同__init__()方法,__del__()方法也是一个特殊的方法,它是在对象销毁的时候被调用。
对象销毁,即对象不再被使用,它所占用的内存将返回给系统。
在代码中可以调用del obj 来显示销毁对象,否则该对象的销毁由系统来做。
注:数据成员以双下划线为前缀,例如__privatevar,Python的名称管理体系会有效地把它作为私有变量。
惯例:某个变量只想在类或对象中使用,就应该以单下划线前缀,其他的名称都作为公共的,可以被其他的
类/对象使用。(与双下划线前缀不同)。
4. 继承
面向对象的编程带来的主要好处之一是代码的重用,实现这一机制可以通过 继承实现。
继承可以理解为类之间的类型和子类型的关系。
为了记录师生的信息,如果没有继承,则需要写两个类,每一个类都要将一些变量重新写一遍。
一个比较好的方法是创建一个共同的类称为 SchoolMember,然后让教师和学生的类 继承这个共同的类
它们都是这个类的子类型,然后为这些子类型添加专有的属性。
一个子类型在任何需要父类型的场合可以被替代成父类型,即对象可以被视作父类的实例,这种现象成为多态
那就就有 父类/超类 子类/导出类 区分。
#!/usr/bin/python
#Filename: inherit.py
class SchoolMember:
'''Represents any school member.'''
def __init__(self, name, age):
self.name = name;
self.age = age;
print '(Initialized SchoolMember: %s)'%self.name;
def tell(self):
'''Tell my details.'''
print 'Name: %s Age:%s' %(self.name, self.age);
class Teacher(SchoolMember):
'''Represents a teacher.'''
def __init__(self, name, age, salary):
SchoolMember.__init__(self, name, age);
self.salary = salary;
print 'Initialized Teacher: %s'%self.name;
def tell(self):
SchoolMember.tell(self);
print 'Salary: %d' %self.salary;
class Student(SchoolMember):
'''Represents a student.'''
def __init__(self, name, age, marks):
SchoolMember.__init__(self, name, age);
self.marks = marks;
print 'Initialized Teacher: %s'%self.name;
def tell(self):
SchoolMember.tell(self);
print 'Marks: %d' %self.marks;
t = Teacher('Mrs. Shrividya', 40, 30000);
s = Student('Swaroop', 22, 75);
print ;
members = [t, s];
for member in members:
member.tell();
实现继承class Teacher(SchoolMember): ,基类的__init__()方法要使用self专门调用
这一点很重要:Python不会自动调用基类的构造函数,需要我们自己专门调用。
在基类构造方法前加基类名称,使用self和其他的参数进行调用。
在子类名后的元组中列举了不止一个类名,那么则是多继承。
17. 输入/输出
程序与用户交互,从用户得到输入,然后打印结果。分别使用raw_input和print方法实现。
还可以使用str(字符串)类进行输出,可以参考help(str)
文件
创建一个file类的对象打开一个文件,分别用file类的read、readline或write方法来读写文件。
文件操作完毕,调用close方法,关闭文件。
#!/usr/bin/python
#Filename: using_file.py
poem='''\
Programming is fun
When the work is done
if you wanna make your work also fun:
use Python!
''';
f = file('poem.txt', 'w'); # open for 'w'riting
f.write(poem); # write text to file
f.close(); # close the file
f = file('poem.txt');
# if no mode is specified, 'r'ead mode is assumed by default
while True:
line = f.readline();
if len(line) == 0: # Zero length indicates EOF
break;
print line, # Notice comma to avoid automatic newline added by Python
f.close(); # close the file
通过指定希望打开的文件和模式来创建一个file类的实例,
模式可以为读模式('r')、写模式('w')、追加模式('a'),更多的模式可以参考help(file)文档了解。
文件使用完毕后,一定调用close方法关闭文件。
不指定文件模式,则是按照读模式打开。使用readline方法读取一行,这一行包含行尾的换行符。
返回值为空字符串时,表明到达了文件的结尾。
在print语句上使用逗号可以消除自动换行。
持久地存储对象,Python提供了一个标准的模块,pickle,使用它可以在文件中存储任何Python对象,之后可以把它从文件中完整读取出来。
还有一个类似的模块,成为cPickle,它是使用C语言写的,比pickle模块要快很多。
#!/usr/bin/python
#Filename: pickling.py
import cPickle as p;
#import pickle as p
shoplistfile = 'shoplist.data';
shoplist = ['apple', 'mango', 'carrot'];
f = file(shoplistfile, 'w');
p.dump(shoplist, f); # dump the object to a file
f.close();
del shoplist;
f = file(shoplistfile);
storedlist = p.load(f);
print storedlist;
import .. as 语法,一种便利的使用方法,以便于可以使用更短的模块名称。他还以简单地通过一行就切换到另外一个模块中(cPickle或者pickle)。
调用存储器模块的dump函数将对象存储到打开的文件中。通过load函数返回来取得对象。
18. 异常
程序出现某些异常的状况时候,异常就发生了。最常见的是如果要读取文件时候,文件不存在,或者运行时不小心删除了,再访问就会出现异常。
错误:如果有错误,源码是无法执行的,并且会打印出错误的地方。
try ... except
>>> S = raw_input('Enter something-->');
Enter something-->
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
S = raw_input('Enter something-->');
EOFError: EOF when reading a line
EOFError Python引发了一个成为EOFError的错误,这个错误基本上是意味着它发现了一个不期望的文件尾。
(这个文件尾是由Ctrl-d引起的)
异常处理:
使用try ... except语句来处理异常。通常将语句放在try-块中,而把我们的错误处理语句放在except-块
例子:
#!/usr/bin/python
#Filename : try_except.py
import sys;
try:
s = raw_input('Enter something -->');
except EOFError: # catch EOFError
print '\nWhy did you do an EOF on me?';
sys.exit(); # exit the program
except: # Catch any error
print '\n Some error/exception occurrd.';
# here, we are not exiting the program
print 'Done';
except从句可以专门处理单一的错误或异常,或者一组包括在圆括号内的错误/异常。如果没有给出错误或
异常的名称,它会处理所有的错误和异常。对于每一个try从句,至少有一个相关联的except从句。
如果某个错误或异常没有被处理,默认的Python处理器就会被调用。它会终止程序,并且打印一个消息。
也可以使用try...catch块关联一个else从句。当没有异常的时候,else从句将被执行。
引发异常:
使用raise语句引发异常,还需要指明错误/异常的名称或伴随异常触发的异常对象。引发的错误或异常
应该分别从一个Error或Exception类直接或间接导出类。
#!/usr/bin/python
#Filename: raising.py
class ShortInputException(Exception):
'''A user-defined exception class.'''
def __init__(self, length, atleast):
Exception.__init__(self);
self.length = length;
self.atleast = atleast;
try:
s = raw_input('Enter something-->');
if len(s) < 3:
raise ShortInputException(len(s), 3);
#Other work can continue as usual here
except EOFError:
print '\nWhy did you do an EOF on me?';
except ShortInputException, x:
print 'ShortinputExcetion: The input was of length %d, was excepting at least %d' %(x.length, x.atleast);
else:
print 'No exception was raised.';
此处创建了一个自己的异常类型,可以使用预定义的异常/错误。新的异常类型ShortInputException,两个域
length是给定的输入的长度,atleast则是程序期望的最小的长度。
使用finally
#!/usr/bin/python
#Filename: finally.py
import time
try:
f = file('poem.txt');
while True:
line = f.readline();
if len(line) == 0:
break;
time.sleep(2);
print line;
finally:
f.close();
print 'Clean up... closed the file';
使用time.sleep(2)故意让程序休眠2秒,这样可以有时间按下ctrl-c。
按下ctrl+c后,程序中断,出发KeyboardInterrupt异常被触发,程序退出,但是退出之前,finally从句依然要被执行。
19. Python的标准库
sys模块
sys.argv的用法
#!/usr/bin/python
#Filename: cat.py
import sys
def readfile(filename):
'''Print a file to standard output'''
f = file(filename);
while True:
line = f.readline();
if len(line) == 0:
break;
print line;
f.close(); # don't close the file before finished.
#script start from here
if len(sys.argv) < 2:
print 'No action specified.'
sys.exit();
if sys.argv[1].startswith('--'):
option = sys.argv[1][2:];
#fetch sys.argv[1],but without the first two characters
if option == 'version':
print 'Version 1.2'
elif option == 'help':
print '''\
This program prints files to standard output.
Any number of files can be specified.
Options include:
--version: Prints the version number
--help:Display this help'''
else:
print 'Unknow option';
sys.exit();
else:
for filename in sys.argv[1:]:
readfile(filename);
sys.argv列表中至少有一个项目,sys.argv[0]中保存的是当前运行的程序的名称。
对指定的参数,是--version 或 --help将打印相应的一些信息
使用sys.exit()方法退出程序
更多sys内容:
sys.version字符串提供的是安装的Python的版本信息。sys.version_info元组则提供一个更简单的版本信息。如下:
>>> import sys;
>>> sys.version
'2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)]'
>>> sys.version_info
sys.version_info(major=2, minor=7, micro=2, releaselevel='final', serial=0)
sys的标准输入输出项目:
sys.stdin sys.stdout sys.stderr
对应于标准输入输出,与标准错误流
OS模块
os.sep 取代操作系统的特定的路径分隔符。
os.name 字符串显示正在使用的平台,不如Window 为nt Linux/unix为 posix。
os.getcwd()获得当前工作目录,即当前Python脚本的工作的目录路径
os.getenv() 和 os.putenv():放别用来读取和设置环境变量
os.listdir():返回指定目录下的所有的文件和目录名
os.remove(): 删除一个文件
os.system(): 用来运行shell命令
os.linesep(): 给出当前平台使用的行终止符,例如windows使用'\r\n', Linux使用'\n'等
os.path.split() 返回一个路径的目录名和文件名
os.path.isfile() 和 os.path.isdir() : 分别判断是否是文件和目录
os.path.existe() 判断目录或文件是否存在。
20. 更多的Python的内容
特殊的方法:
__init__(self, ...): 这个方法在新建对象恰好要被返回使用之前调用
__del__(self): 恰好在对象被删除之前调用
__str__(self): 在对对象使用print语句或是使用str()的时候调用
__It__(self, other): 当使用小于 运算符(<)的时候调用。类似地,对于所有的运算符(+, > 等等)都有特殊方法
__getitem__(self, key): 在使用x[key]索引操作符的时候调用
__len__(self): 对对象使用内建的len()函数时调用
将元组和字典作为函数参数:
#!/usr/bin/python
# Filename: tupleAsPara.py
def powersum(power, *args):
total = 0;
for i in args:
total += pow(i, power);
return total;
print powersum(2, 3, 4);
print powersum(2, 10);
由于args变量前有*前缀,所有多余的函数参数都会作为一个元组存储在args中。
如果使用**前缀,多余的参数则会被作为一个字典的键值对,存入args。
exec和eval语句:
exec语句用来执行存储在字符串或文件中的python语句。
可以生成一个Python代码的字符串,然后使用exec语句执行这些语句。
例如:
exec 'print "hello world"';
eval语句用来计算存储在字符串中有效的Python表达式,例如:
eval('2*3');
assert语句:
assert语句用来声明某个条件是真的。如果条件为假,则会引发错误,AssertionError错误。