开篇:
有两种方式构建软件设计:一种是把软件做得很简单以至于明显找不到缺陷,另一种是把它做得很复杂以至于找不到明显的缺陷。
——C. AR. Hoare
厚的人生中的成功需要的专注于坚持不懈多过天才和机会。
——C. W. Wendte
1. 简介
Python语言写的程序不需要编译成二进制代码,可以直接从源代码运行程序。计算机内部,Python解释器把源代码转成成为字节码的中间形式,然后再把它翻译成计算机使用的机器语言并运行。
2. 安装Python
略…
3. 最初的步骤
a. 简介
有两种使用Python方式运行你的程序——使用交互式的带提示符的解释器或使用源文件。
进入带提示符的解释器:
Window: 开始 -> 程序 ->Python 2.x –> Python
Linux: 命令行下输入 Python
退出带提示符的解释器:
Window: Ctrl-z[Enter]
Linux: Ctrl-d
使用源文件:
文件首行:#!/usr/bin/python
在shell或DOS提示符下,直接运行:python ***.py 例:python hello.py
*Linux特殊用法:修改文件属性,直接运行。
$ chmod a+x ***.py
$ ./***.py
只要知道程序的确切位置,就可以运行程序的了——但是如果希望程序能够从各个位置运行的话,要把你的程序保存在PATH环境变量中的目录之一。
我们能够用echo命令来显示PATH变量。也可以把你选择的目录添加到PATH变量中去——这可以通过运行PATH=$PATH:/home/username/userdir. 其中/home/username/userdir是我想要添加到PATH变量中的目录。
注意:Python是大小写敏感的,即print与Print是不一样的,确保在每一行的开始字符前没有空格或者制表符(/t)
提示:对于Python来说,程序、脚本或者软件都是指同一个东西。
b. 获得帮助
使用内建的help功能。help(str)——会显示str的帮助。按q退出帮助。
如果想要获取关于如print那样操作符的帮助,那么你需要正确的设置PYTHON DOCS环境变量。这可以在Linux/Unix中轻松地通过env命令完成。
$ env PYTHON DOCS=/usr/share/doc/python-docs-2.6.4/xxx
>>> help(‘print’)
此处特意在print上使用了引号,那样Python就可以理解我是希望获取关于print的帮助而不是想要它打印东西。
4. 基本概念
a. 字面意义上的常量
一个字面意义常量的例子是如同5、1.23、9.25e-3这样的数,或者如同This is a string这样的字符串。他们被称为字面意义上的,应为他们具备字面的意义——可以使用他们的值。
b. 数
在Python中有4种类型的数——整数、长整数、浮点数和复数
c. 字符串
字符串是字符的序列。字符串基本上就是一组单词。
单引号 (‘)
你可以用单引号指示字符串,就如同’Quote me on this’这样。所有的空白,制表符都照原样保留。
双引号 (“)
在双引号的字符串与单引号中的字符串的使用完全相同。
三引号 (‘’’或“”“)
利用三引号,你可以指示一个多行的字符串。你可以在三引号中自由的使用单引号和双引号。
转义符
用’/’来在单引号的字符串中进行转义,’What/’s your name ? ‘
另一种表示方法:“What’s your name ?” 即用双引号。双引号字符串中使用双引号本身的时候,以可以借助于转义符。另外,可以用转义符//来表示反斜杠本身。
值得注意的一件事是,在一个字符串中,行末的单独一个反斜杠表示字符串在下一行继续,而不是开始一个新的行。
自然字符串
如果你想要指示某些不需要如转义符那样的特别处理的字符串,那么你需要指定一个自然字符串。自然字符串通过给字符串加上前缀r或R来指定。例r”Newlines /n”。
Uncode字符串
Unicode是书写国际文本的标准方法。如果你想要用你的母语如阿拉伯语文本,那么你需要有一个支持Unicode的编译器。类似地,Python允许你处理文本——你只需要在字符串前加上前缀u或者U。例如,u“This is a Unicode string”
记住,在你处理文本文件的时候使用Unicode字符串,特别是当你知道这个文件含有非英语的语言写的文本。
字符串是不可变的
按字面意义级连字符串
一定要用自然字符串处理正则表达式。否则会需要使用很多的反斜杠。
变量
标识符的第一字符必须是字母表中的字母(大写或小写)或者一个下划线(_),标识符名称的其他部分可以由字母(大小写)、下划线(_)或数字(0-9)组成,标识符名称是对大小写敏感的。
数据类型
变量可以处理不同类型的值,称为数据类型。基本的类型是数和字符串
对象
Python把在程序中用到的任何东西都成为对象。
逻辑行与物理行
物理行是你在编写程序时所看见的。逻辑行是Python看见的单个语句。
默认地,Python希望每行都只是用一个语句,这样使得代码更加易读。
如果你想要在一个物理行中使用过于一个逻辑行,那么你需要使用分号(;)来特别地表明这种用法。分号表示一个逻辑行语句的结束。
例:i = 5 ; print i
逻辑行中使用了圆括号、方括号或波形括号的时候。这被称为暗示的行连接,这种情况下不需要使用反斜杠。
缩进
在逻辑行首的空白(空格和制表符)用来决定逻辑行的缩进层次,从而用来决定语句的分组。这意味着同一层次的语句必须有相同的缩进。每一组这样的语句称为一个块。
5. 运算符与表达式
只记录几个特殊的:
**: 幂
//: 取整除
not: 布尔“非”
and: 布尔”与”
or: 布尔“或”
6. 控制流
if
注意 if 语句在结尾处包含一个冒号——通过它告诉Python下面跟着一个语句块,elif 和 else 从句都必须在逻辑行结尾处有一个冒号,下面跟着一个相应的语句块。
while
注意 while 语句在结尾处包含一个冒号
for
for…in… 它在一序列的对象上递归逐一使用队列中的每个项目。它对于任何序列都适用。
break
continue
7. 函数
函数是重用的程序段。通过def关键字定义。
例:def function_name(arg1, arg2):
局部变量
所有变量的作用域是它们被定义的块,从它们的名称被定义的那点开始。
全局变量
global 用于定义全局变量。使用global语句可以清楚地表明变量是在外面的块定义的。
默认参数值
def function_name (arg1 = 100, arg2 = ‘username’):
重要:只有在形参表末尾的那些参数可以有默认参数值,即你不能在生命函数形参的时候,先声明有默认值的形参而后声明没有默认值的形参。这是因为赋给形参的值是根据位置而赋值的。
关键参数
可以通过命名来为这些参数赋值,我们使用名字而不是位置来给函数指定实参。有两个优势:不必担心参数的顺序;只给我们想要的那些参数赋值。
return
注意:没有返回值的return语句等价于return None。None是Python中的空类型。例:如果一个变量的值为None,则表示它没有值。
pass
pass 语句在Python中表示的一个空的语句块。
DocStrings
文档字符串,由于它帮助你的程序文档更加简单易懂。
文档字符串的管理是一个多行字符串,它的首行以大写字符开始,句号结尾。第二行是空行,从第三行开始是详细的描述。
可以使用__doc__(双下划线)调用函数的文档字符串属性。
8. 模块
import sys
它是如何工作的:
首先,利用import语句输入sys模块。sys模块包含了与Python解释器和它的环境有关的函数。
当Python执行import sys语句的时候,它在sys.path比啊粮中所列目录中找到了这个文件,这个模块的主块中的语句将被运行,然后这个模块将能够被使用。注意,初始化过程仅在我们第一次输入模块的时候进行。
字节编译的.pyc文件
.pyc 使你下次从别的程序输入这个模块时,会快很多。
from…import…
如果你想直接输入argv变量到你的程序中,那么可以from sys import argv语句,如果想输入所有sys模块使用的名字,那么from sys import *即可。
模块__name__
每个Python模块都有它的__name__,如果它是’__main__’,这说明这个模块被用做主块,我们可以进行相应的恰当操作。
创建自己的模块
和普通的Python程序一样编写。记住这个编写出来的模块应该被放置在我们import它的程序的同一个目录中,或者在sys.path所列的目录之一。
我们可以用点号来使用模块的成员。例子:import sys; print sys.path
dir()函数
使用内建的dir函数列出模块定义的标识符。标识符有函数、类和变量。
del 语句:在运行后被用来删除一个变量/名称。被删除的对象就好像从来没有存在过一样。
9. 数据结构
Python有三种内建的数据结构:列表、元组和字典。
列表
list 是处理一组有序项目的数据结构,即你可以在一个列表中存储一个序列的项目。每个项目之间用逗号分隔。列表中的项目应该包括在方括号中[]。一旦你创建个列表,你可以添加、删除或者搜索列表中的项目。
注意:在print语句的结尾使用一个逗号来消除每个print语句自动打印的换行符。
list.sort() 方法用来对列表排序。这个方法影响列表本身。
元组
元组和列表类似,但元组是不可变的,元组通过元口号中用逗号分隔的项目定义。可以通过一对方括号来指明某个项目的位置从而来访问元组中的项目,和列表用法一样。这被称为索引运算符。zoo[2]访问zoo中的第三个元素,使用zoo[2][2]来访问zoo元组的第三个项目的第三个项目(Python 从0开始)。
含有0个或1个项目的元组。一个空元组由一堆空的圆括号组成。含有单个元素的元素必须在第一个,也是唯一的一个项目后跟一个逗号,这样Python才能区分元组和表达式中一个带圆括号的对象,即singletuple=(2, )。
print语句可以使用跟着%符号的项目元组的字符串。可以定制某种特定的格式。%s,%d。元组必须按照相同的顺序来对应这些定制。
字典
字典包含键和值。键必须是唯一的。只能使用不可变的对象来作为字典的键,但是可以把不可变或可变的对象作为字典的值。d = {key1:value1, key2:value2},字典中的键/值对是没有顺序的。如果想要一个特定的顺序,那么应该在使用前自己对他们排序。字典是dict类的实例/对象。
可以使用索引操作符来寻址一个键并为它赋值。[]
可是使用del语句删除键/值对。del dict[‘wang’]
可以使用in操作符来检查一个键/值对是否存在,或是使用dict类的has_key
序列
列表、元组和字符串都是序列(没有字典)。序列的两个主要特点是索引操作符和切片操作符。索引操作符让我们可以从序列中抓取一个特定项目。切片操作符让我们获得序列的一个切片,即一部分序列。
索引可以是负数,表示是,位置是从序列尾开始计算的。
切片操作符[数字:数字],数是可选的,冒号是必须的。这样shoplist[1:3]返回从位置1开始,包括位置2,但是停止在位置3的一个序列切片,返回一个含有两个项目的切片。[:] 返回整个序列的拷贝。
序列的神奇之处在于你可以用相同的方法访问元组、列表和字符串。
引用
当创建一个对象并给他赋一个变量的时候,这个变量仅仅引用那个对象,而不是表示这个对象本身。也就是说,变量名指向你计算机中存储那个对象的内存。这被成为名称到对象的绑定。
如果想要赋值一个列表或者类似的序列或者其他复杂的对象,那么必须使用切片操作符来取得拷贝。如果只是想要使用另一个变量名,两个名称都引用同一个对象,那么如果不小心的话,可能会引来各种麻烦。
字符串的方法
startwith 用来测试字符串是否以给定的字符串开始。name.startswith(‘sun’)
in 操作符用来检验一个给定字符串是否为另一个字符串的一部分。’sun’ in name
find 用来找出给定字符串在另一个字符串中的位置,或者返回-1以表示找不到子字符串
join 用一个作为分隔符的字符串来连接序列项目,返回一个生成的字符串。
10. 解决问题——编写一个Python脚本
略…
11. 面向对象的编程
把数据和功能结合起来,用称为对象的东西包裹起来组织程序的方法,被称为面向对象的编程概念。
类和对象是面向对象编程的两个主要方面。类创建一个新类型,而对象是这个类的实例。属于一个对象或类的变量被称为域。属于类的函数被称为方法。域和方法可以合称为类的属性。
类使用class 关键字创建。类的域和方法被列在一个缩进块中。
self
类的方法与普通函数只有一个特殊区别——它们必须有一个额外的第一个参数名称,但是在调用这个方法的时候你不为这个参数赋值,Python会提供这个值。这个变量指对象本身,按照惯例它的名称是self。
例子:假如一个类: Myclass 和这个类的一个实例Myobject。当你调用这个对象的Myobject.method(arg1,arg2)的时候,Python会自动转为Myclass.method(arg2)
这也意味着如果你有一个不需要参数的方法,你还是得给这个方法定义一个self参数。
__init__方法
它在类的一个对象被建立时,立即运行。这个方法可以用来对你的对象做一些期望的初始化。
例子:
class Person:
def __init__(self, name):
self.name = name
def sayHi(self):
print 'Hello, my name is', self.name
p = Person('Swaroop')
p.sayHi()self.name 是类的域,name是传进来的变量。我们没有专门调用__init__方法,只是在创建一个类的新实例时,把参数传进来,从而传递给__init__方法。
类与对象的方法
类的变量:由一个类的所有对象(实例)共享使用。只有一个类变量的拷贝,所以当某个对象对类变量做了改动的是偶,这个改动会反映到所有其他的实例上。
对象的变量:由类的每个对象/实例拥有。因此每个对象有自己对这个域的一份拷贝,即他们不是共享的,在同一个类的不同实例中,虽然对象的变量有相同的名称,但是互不相关。
例子:
class Person:
'''Represents a person.'''
population = 0
def __init__(self, name):
'''Initializes the person's data.'''
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 says 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
swaroop = Person('Swaroop')
swaroop.sayHi()
swaroop.howMany()
kalam = Person('Abdul Kalam')
kalam.sayHi()
kalam.howMany()
swaroop.sayHi()
swaroop.howMany()这里population是类变量,name是对象变量(它使用self赋值)。
只能使用self变量来参考同一个对象的变量和方法。这被称作属性参考。
上面的程序中,还有docstring对于类和方法同样有用。我们可以在运行时使用__doc__和Person.sayHi.__doc__来分别访问类与方法的文档字符串。
还有一个特殊的方法__del__,它在对象消逝的时候被调用,即对象不再被使用,它所占用的内存将返回给系统。
Python中所有类成员(包括数据成员)都是公共的,所有方法都是有效的。只有一个列外:使用双下划线前缀的数据成员为被系统有效地当作私有变量,例子:__privatevar
惯例:如果某个变量只想在类或对象中使用,就应该以单下划线前缀。而其他名称都将作为公共的,可以被其他类/对象使用。(与双下划线前缀不同)
继承
集成完全可以理解成类之间的类型和子类型关系。
一个子类型在任何需要父类型的场合可以被替换成父类型,即对象可以被视作是父类的实例,这种现象被称为多态现象。
例子:
class Member:
pass
class Teacher(Member):
pass
为了使用集成,把基本类的名称作为一个元组跟在定义类时的类名称之后。然后主要到基本类的__init__方法专门使用self变量调用,这样我们可以初始化对象的基本类,Python不会自动调用基本类的constructor.
调用继承类的方法时,Python重视首先查找对应类型的方法,如果它不能在类中找到对应的方法,它开始到基类中逐个查找。基本类是在类定义的时候,在元组之中指明的。
如果在继承元组中列了一个以上的类,那么它就被称为多重继承。
12. 输入/输出
存储器
Python提供一个标准的模块,成为pickle。使用它你可以在一个文件中存储任何Python块,之后你又可以把它完整无缺地取出来。这被称为持久地存储对象。另外还有cPickle,功能与pickle完全相同,它是用C语言写的,速度快。
#!/usr/bin/python
# Filename: pickling.py
import cPickle as p
#import pickle as p
shoplistfile = 'shoplist.data'
# the name of the file where we will store the object
shoplist = ['apple', 'mango', 'carrot']
# Write to the file
f = file(shoplistfile, 'w')
p.dump(shoplist, f) # dump the object to a file
f.close()
del shoplist # remove the shoplist
# Read back from the storage
f = file(shoplistfile)
storedlist = p.load(f)
print storedlist我们使用了import…as 语法,这是一种便利的方法,以便于我们可以使用自己定义的名称,在这个例子中,它还能让我们能够通过简单地改变一行就切换到另一个模块(cPickle –> pickle)。
为了在文件里存储一个对象,首先以写模式打开一个file对象,然后调用存储器模块的dump函数,把对象存储到打开的文件中。这个过程叫存储。
接下来,我们使用pickle模块的load函数的返回来取回对象。这个过程称为取储存。
13. 异常
处理异常
我们可以使用try…excpet…语句来处理异常。把通常的语句放在try块中,而异常处理语句放在except块中。
#!/usr/bin/python
# Filename: try_except.py
import sys
try:
s = raw_input('Enter something --> ')
except EOFError:
print '/nWhy did you do an EOF on me?'
sys.exit() # exit the program
except:
print '/nSome error/exception occurred.'
# here, we are not exiting the program
print 'Done'我们把所有可能引发错误的语句放在try块中,然后在except从句/块中处理所有的错误。excpet从句可以专门处理单一的错误或异常,或者一组包括在圆括号内的错误/异常。如果给出错误或异常的名称,它会处理所有的错误和异常。对于每个try从句,至少都有一个相关联的except从句。
引发异常
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 'ShortInputException: The input was of length %d, /
was expecting at least %d' % (x.length, x.atleast)
else:
print 'No exception was raised.'我们创建了我们自己的异常类型,其实我们可以使用任何预定义的异常/错误。这个新的异常类型是ShortInputExcpetion类。它有两个域,在except从句中,我们提供了错误类和用来表示错误/异常对象的变量。在这个特别的excpet从句中,我们使用异常对象的域来打印一个恰当的消息。
try..finally
假如你在读一个文件的时候,希望在无论异常发生与否的情况下都关闭文件,该怎么做呢?这可以使用finally块来完成。注意,在一个try块下,你可以同时使用except从句和finally块。如果你要同时使用它们的话,需要把一个嵌入另外一个。
14. Python标准库
略…
15. 更多的Python内容
略…