Python 学习小记:Python Tutorial
Python Tutorial 『资料来源 python.cn』http://i18n.linux.net.cn/others/python2.3tut/tut//**** *Noted by:metaphy *2005-8-5*****/1.数值Python 支持复数,如 2+3j>>> a=1.5+0.5j>>> a.real1.5>>> a.imag0.5可以连续赋值>>> x = y =
Python Tutorial 『资料来源 python.cn』
http://i18n.linux.net.cn/others/python2.3tut/tut/
/****
*Noted by:metaphy
*2005-8-5
*****/
1.数值
<1.1>
Python 支持复数,如 2+3j
>>> a=1.5+0.5j
>>> a.real
1.5
>>> a.imag
0.5
<1.2>
可以连续赋值
>>> x = y = z = 0
>>> x,y,z = 0,1,2
2.字符串
Python 变量定义大小写敏感!
<2.1>
>>> print '"Come on!" she said.'
"Come on!" she said.
>>> words = 'Help yourself'
>>> words[2:]
'lp yourself'
>>> words[:2] + words[2:]
'Help yourself'
>>> words[:3] + words[3:]
'Help yourself'
索引可以是负数,计数从右边开始,例如:
>>> words[-1]
'f'
字符串的长度
>>>len(words)
索引的方式只能用来读取,不可以用来改变字符串
>>>words[0] = 't' #error!
此外,字符串可以用一对三重引号”””或'''来标识。三重引号中的字符串在行尾不需要换行标记,所有的格式都会包括在字符串中。
<2.2>
从Python2.0开始,程序员们可以使用一种新的数据类型来存储文本数据:Unicode 对象。它可以用于存储多种Unicode数据(请参阅 http://www.unicode.org/ ),并且,通过必要时的自动转换,它可以与现有的字符串对象良好的结合。
针对现代和旧式的文本中所有的字符提供了一个序列。以前,字符只能使用256个序号,文本通常通过绑定代码页来与字符映射。这很容易导致混乱,特别是软件的国际化( internationalization“i18n”)。
Python 中定义一个 Unicode 字符串和定义一个普通字符串一样简单:
>>> u'Hello World !'
u'Hello World !'
3.链表 Lists
<3.1>
Python已经有了几个复合数据类型,用于组织其它的值。最通用的是链表,它写为中括之间用逗号分隔的一列值(子项),链表的子项不一定是同一类型的值。
>>> a = ['spam', 'eggs', 100, 1234]
>>> a
['spam', 'eggs', 100, 1234]
像字符串一样,链表也以零开始,可以被切片,联接,等等
与不变的字符串不同,链表可以改变每个独立元素的值
>>> lista = ['hello',6,'eggs']
>>> 3*lista[:2]
['hello', 6, 'hello', 6, 'hello', 6]
>>> lista[0]= 'h' # 改变元素值
>>> lista
['h', 6, 'eggs']
<3.2>
链表可嵌套
>>> p =[-10,-11]
>>> q = [1,2,p]
>>> len(q)
3
>>> q[2][0]
-10
4.流程控制(注意:此处写法和C家族语言的不太一样,并且python没有switch语句)
<4.1>
if 语句
x = int (raw_input("Please enter an integer: "))
if x<0 :
print 'negative'
elif x==0 :
print 'zero'
elif x==1 :
print 'single'
else :
print 'more'
for 语句
Python中的for语句和C或Pascal中的略有不同。通常的循环可能会依据一个等差数值步进过程(如Pascal)或由用户来定义迭代步骤和中止条件(如 C ),Python 的 for 语句依据任意序列(链表或字符串)中的子项,按它们在序列中的顺序来进行迭代。例如(没有暗指):
>>> # Measure some strings:
... a = ['cat', 'window', 'defenestrate']
>>> for x in a:
... print x, len(x)
...
cat 3
window 6
defenestrate 12
break 语句和 C 中的类似,用于跳出最近的一级 for 或 while 循环。 continue 语句是从 C 中借鉴来的,它表示循环继续执行下一次迭代。
pass 语句什么也不做。它用于那些语法上必须要有什么语句,但程序什么也不做的场合,例如:
>>> while True:
... pass # Busy-wait for keyboard interrupt
...
<4.2>函数定义
关键字 def 引入了一个函数定义。在其后必须跟有函数名和包括形式参数的圆括号。函数体语句从下一行开始,必须是缩进的。函数体的第一行可以是一个字符串值,这个字符串是该函数的文档字符串(documentation string),也可称作 docstring
5. 数据结构
<5.1>链表类型有很多方法,这里是链表类型的所有方法:
append( x)
把一个元素添加到链表的结尾,相当于 a[len(a):] = [x]
extend( L)
通过添加指定链表的所有元素来扩充链表,相当于 a[len(a):] = L。
insert( i, x)
在指定位置插入一个元素。第一个参数是准备插入到其前面的那个元素的索引,例如a.insert(0, x) 会插入到整个链表之前,而a.insert(len(a), x) 相当于 a.append(x)。
remove( x)
删除链表中值为x的第一个元素。如果没有这样的元素,就会返回一个错误。
pop( [i])
从链表的指定位置删除元素,并将其返回。如果没有指定索引,a.pop()返回最后一个元素。元素随即从链表中被删除。(方法中i两边的方括号表示这个参数是可选的,而不是要求你输入一对方括号,你会经常在Python 库参考手册中遇到这样的标记。)
index( x)
返回链表中第一个值为x的元素的索引。如果没有匹配的元素就会返回一个错误。
count( x)
返回x在链表中出现的次数。
sort( )
对链表中的元素进行适当的排序。
reverse( )
倒排链表中的元素。
//后进先出
用append() and pop() 方法可以将链表模拟堆栈来用
//先进先出
用append() and pop(0) 方法可以将链表模拟堆栈来用
<5.2>
对于链表来讲,有三个内置函数非常有用:filter(), map(), 和 reduce()。
a)
"filter(function, sequence)"返回一个序列(sequence),包括了给定序列中所有调用function(item)后返回值为true的元素。(如果可能的话,会返回相同的类型)。例如,以下程序可以计算部分素数:
>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> filter(f, range(2, 25))
[5, 7, 11, 13, 17, 19, 23]
b)
"map(function, sequence)" 为每一个元素依次调用function(item)并将返回值组成一个链表返回。例如,以下程序计算立方:
>>> def cube(x): return x*x*x
...
>>> map(cube, range(1, 11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
c)
"reduce(func, sequence)" 返回一个单值,它是这样构造的:首先以序列的前两个元素调用函数,再以返回值和第三个参数调用,依次执行下去。例如,以下程序计算1到10的整数之和:
>>> def add(x,y): return x+y
...
>>> reduce(add, range(1, 11))
55
<5.3>链表推导式
链表推导式提供了一个创建链表的简单途径,无需使用map(), filter() 以及 lambda。返回链表的定义通常要比创建这些链表更清晰。每一个链表推导式包括在一个for 语句之后的表达式,零或多个 for或 if 语句。返回值是由 for 或 if子句之后的表达式得到的元素组成的链表。如果想要得到一个元组,必须要加上括号。
<5.4>删除语句
有一个方法可从链表中删除指定索引的元素:del 语句。这个方法也可以从链表中删除切片(之前我们是把一个空链表赋给切片)。例如:
>>> a = [-1, 1, 66.6, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.6, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.6, 1234.5]
del 也可以用于删除整个变量:
>>> del a
<5.5>元组(Tuples)和序列(Sequences )
元组在输出时总是有括号的,以便于正确表达嵌套结构。在输入时可能有或没有括号都可以,不过经常括号都是必须的(如果元组是一个更大的表达式的一部分)。
>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> # Tuples may be nested:
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
一个特殊的问题是构造包含零个或一个元素的元组:为了适应这种情况,语法上有一些额外的改变。一对空的括号可以创建空元组;要创建一个单元素元组可以在值后面跟一个逗号(在括号中放入一个单值是不够的)。丑陋,但是有效。例如:
>>> empty = ()
>>> singleton = 'hello', # <-- note trailing comma
>>> len(empty)
0
>>> len(singleton)
1
>>> singleton
('hello',)
<5.6> Dictionaries 字典
另一个非常有用的Python内建数据类型是字典。字典在某些语言中可能称为“联合内存”(``associative memories'')或“联合数组”(``associative arrays'')。序列是以连续的整数为索引,与此不同的是,字典以关键字为索引,关键字可以是任意不可变类型,通常用字符串或数值。如果元组中只包含字符串和数字,它可以做为关键字,如果它直接或间接的包含了可变对象,就不能当做关键字。不能用链表做关键字,因为链表可以用它们的append() 和 extend()方法,或者用切片、或者通过检索变量来即时改变。
理解字典的最佳方式是把它看做无序的关键字:值对(key:valuepairs)集合,关键字必须是互不相同的(在同一个字典之内)。一对大括号创建一个空的字典:{}。初始化链表时,在大括号内放置一组逗号分隔的关键字:值对,这也是字典输出的方式。
字典的主要操作是依据关键字来存储和析取值。也可以用del来删除关键字:值对。如果你用一个已经存在的关键字存储值,以前为该关键字分配的值就会被遗忘。试图析取从一个不存在的关键字中读取值会导致错误。
在字典中循环时,关键字和对应的值可以使用 iteritems()方法同时解读出来。
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.iteritems():
... print k, v
...
gallahad the pure
robin the brave
<5.7>深入条件控制 More on Conditions
in 和 not in 比较操作符审核值是否在一个区间之内。操作符 is is not 和比较两个对象是否相同;这只和诸如链表这样的可变对象有关。所有的比较操作符具有相同的优先级,低于所有的数值操作。
比较操作可以传递。例如 a < b == c 审核是否 a 小于 b 并 b 等于c。
6. 模块
可以将一些公共的东西写在一个文件里,叫做一个模块。模块的名就是文件名去了后缀(.py)
fib.py
#Fibonacci numbers module
def fib(n):
a,b = 0,1
while b<n:
print b
a,b = b, a+b
def fiblist(n):
a,b,result = 0,1,[]
while b<n:
result.append(b)
a,b = b, a+b
return result
而在另一个文件里,可以用import fib 导入
test.py
import fib
list = fib.fiblist(200)
print list
<6.1> 深入模块
模块可以像函数定义一样包含执行语句。这些语句通常用于初始化模块。它们只在模块第一次导入时执行一次。
import语句的一个变体直接从被导入的模块中导入命名到本模块的语义表中。如:
form fib import fiblist
<6.2> 模块搜索路径
导入一个叫spam的模块时,解释器先在当前目录中搜索名为spam.py的文件,然后在环境变量PYTHONPATH的目录列表中搜索,然后是环境变量PATH中的路径列表。如果PYTHONPATH没有设置,或者文件没有找到,接下来搜索安装目录,在UNIX中,通常是 .:/usr/local/lib/python
实际上,解释器由sys.path变量指定的路径目录搜索模块,该变量初始化时默认包含了输入脚本(或者当前目录),PATHPATH和安装目录。这样就允许Python程序了解如何修改或替换模块搜索目录。需要注意的是由于这些目录中包含有搜索路径中运行的脚本,所以这些脚本不应该和标准模块重名,否则在导入模块时Python会尝试把这些脚本当作模块来加载。这通常会引发一个错误。
<6.3> “编译”Python文件
对于引用了大量标准模块的短程序,有一个提高启动速度有重要方法,如果在spam.py目录下存在一个名为spam.pyc的文件,它会被视为spam模块的预“编译”(byte-compiled,二进制编译)版本。用于创建spam.pyc的这一版spam.py的修改时间记录在spam.pyc文件中,如果两者不匹配,.pyc文件就被忽略。
通常你不需要为创建spam.pyc文件做任何工作。一旦spam.py成功编译,就会试图编译对应版本的spam.pyc。如果有任何原因导致写入不成功,返回的spam.pyc文件就会视为无效,随后即被忽略。spam.pyc文件的内容是平台独立的,所以Python模块目录可以在不同架构的机器之间共享。
部分高级技巧:
以-O参数调用Python解释器时,会生成优化代码并保存在.pyo文件中。通用的优化器没有太多帮助;它只是删除了断言(assert)语句。使用-O参数,所有的代码都会被优化;.pyc文件被忽略,.py文件被编译为优化代码。
向Python解释器传递两个 -O 参数(-OO)会执行完全优化的二进制优化编译,这偶尔会生成错误的程序。当前,压缩的.pyo文件只是从二进制代码中删除了__doc__字符串。因为某些程序依赖于这些变量的可用性,你应该只在确定无误的场合使用这一选项。
来自.pyc文件或.pyo文件中的程序不会比来自.py文件的运行更快;.pyc或.pyo文件只是在它们加载的时候更快一些。
通过脚本名在命令行运行脚本时,不会为该脚本向创建.pyc或.pyo文件的二进制代码。当然,把脚本的主要代码移进一个模块里,然后用一个小的解构脚本导入这个模块,就可以提高脚本的启动速度。也可以直接在命令行中指定一个.pyc或乾.pyo文件。
对于同一个模块(这里指例程spam--译者),可以只有spam.pyc文件(或者spam.pyo,在使用 -O 参数时)而没有spam.py文件。这样可以打包发布比较于逆向工程的Python代码库。
compileall模块 可以为指定目录中的所有模块创建.pyc文件(或者使用-O参数创建.pyo文件)。
<6.4> dir() 函数
内置函数dir()用于按模块名搜索模块定义,它返回一个字符串类型的存储列表:
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__', '__stderr__', '__st
din__', '__stdout__', '_getframe', 'api_version', 'argv', 'builtin_module_names'
, 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dllhand
le', 'exc_clear', 'exc_info', 'exc_type', 'excepthook', 'exec_prefix', 'executab
le', 'exit', 'getcheckinterval', 'getdefaultencoding', 'getfilesystemencoding',
'getrecursionlimit', 'getrefcount', 'getwindowsversion', 'hexversion', 'maxint',
'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cach
e', 'platform', 'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setprofile', 'setre
cursionlimit', 'settrace', 'stderr', 'stdin', 'stdout', 'version', 'version_info
', 'warnoptions', 'winver']
<6.5> 包
包通常是使用用“圆点模块名”的结构化模块命名空间。必须要在目录地下放一个__init__.py 才能被视作一个包。
7.输入和输出
<7.1> str() & repr() 可以将任何值转换为字符串
>>> str(0.1)
'0.1'
>>> repr(0.1)
'0.10000000000000001'
而将字符串转换为数字可以这样:
>>> str1 = '123'
>>> int(str1)
123
>>> str2='3.14159'
>>> float(str2)
3.1415899999999999
python的输出和c一样,可以哟昂%控制
>>> for x in range(1,11):
... print '%2d %3d %4d' % (x, x*x, x*x*x)
...
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000
<7.2> 文件操作
#print "[file read&write samples]"
f = open('data.t','r')
lines = f.readlines()
f.close()
f = open('data.t','w')
f.write(str(int(lines[0])+1))
f.close()
默认为当前目录,同样,可以用'd://data.t' 来确保d:盘上的文件
8.错误和异常
<8.1>
try:
#...
except XError:
#...
except YError:
#...
except ZError:
#...
(感觉异常处理在编程语言中都是大同小异,和Java 一样,except 可以有多个)
try...except语句可以带有一个else子句,该子句只能出现在所有except子句之后。当try语句没有抛出异常时,需要执行一些代码,可以使用这个子句。例如:
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except IOError:
print 'cannot open', arg
else:
print arg, 'has', len(f.readlines()), 'lines'
f.close()
<8.2> 抛出异常
发生了特定的异常时,程序员可以用 raise 语句强制抛出异常。例如:
如果你决定抛出一个异常而不处理它,raise 语句可以让你很简单的重新抛出该异常。
>>> try:
... raise NameError, 'HiThere'
... except NameError:
... print 'An exception flew by!'
... raise #抛到上一层的try 了
...
An exception flew by!
Traceback (most recent call last):
File "<stdin>", line 2, in ?
NameError: HiThere
<8.3> 定义清理行为
try 语句还有另一个可选的子句,目的在于定义在任何情况下都一定要执行的功能。例如:
>>> try:
... raise KeyboardInterrupt
... finally:
... print 'Goodbye, world!'
...
Goodbye, world!
Traceback (most recent call last):
File "<stdin>", line 2, in ?
KeyboardInterrupt
不管try子句中有没有发生异常, finally 子句都一定会被执行。如果发生异常,在finally子句执行完后它会被重新抛出。 try 子句经由 break 或 return 退出也一样会执行finally 子句。
在finally 子句中的代码用于释放外部资源(例如文件或网络连接),不管这些资源是否已经成功利用。
9. 类
Python在尽可能不增加新的语法和语义的情况下加入了类机制。这种机制是C++和Python's Modula-3的混合。Python中的类没有在用户和定义之间建立一个绝对的屏障,而是依赖于用户自觉的不去“破坏定义”。然而,类机制最重要的功能都完整的保留下来。类继承机制允许多继承,派生类可以覆盖(override)基类中的任何方法,方法中可以调用基类中的同名方法。对象可以包含任意数量的私有成员。
(Python 和C++ 的血缘关系比和Java 更近)
我要提醒读者,这里有一个面向对象方面的术语陷阱,在Python中“对象”这个词不一定指类实例。Python中并非所有的类型都是类:例如整型、链表这些内置数据类型就不是,甚至某些像文件这样的外部类型也不是,这一点类似于C++和Modula-3,而不像Smalltalk。然而,所有的Python类型在语义上都有一点相同之处:描述它们的最贴切词语是“对象”。
对象是被特化的,多个名字(在多个作用域中)可以绑定同一个对象。这相当于其它语言中的别名。通常对Python的第一印象中会忽略这一点,使用那些不可变的基本类型(数值、字符串、元组)时也可以很放心的忽视它。然而,在Python代码调用字典、链表之类可变对象,以及大多数涉及程序外部实体(文件、窗体等等)的类型时,这一语义就会有影响。这通用有助于优化程序,因为别名的行为在某些方面类似于指针。例如,很容易传递一个对象,因为在行为上只是传递了一个指针。如果函数修改了一个通过参数传递的对象,调用者可以接收到变化--在Pascal中这需要两个不同的参数传递机制。
<9.1>Python 的作用域和命名空间
命名空间是从命名到对象的映射。当前命名空间主要是通过Python字典实现的,不过通常不关心具体的实现方式(除非出于性能考虑),以后也有可能会改变其实现方式。以下有一些命名空间的例子:内置命名(像 abs() 这样的函数,以及内置异常名)集,模块中的全局命名,函数调用中的局部命名。某种意义上讲对象的属性集也是一个命名空间。关于命名空间需要了解的一件很重要的事就是不同命名空间中的命名没有任何联系,例如两个不同的模块可能都会定义一个名为“maximize”的函数而不会发生混淆--用户必须以模块名为前缀来引用它们。
顺便提一句,我称Python中任何一个“.”之后的命名为属性--例如,表达式z.real中的real是对象z的一个属性。严格来讲,从模块中引用命名是引用属性:表达式 modname.funcname中,modname 是一个模块对象, funcname 是它的一个属性。因此,模块的属性和模块中的全局命名有直接的映射关系:它们共享同一命名空间! 9.1
属性可以是只读过或写的。后一种情况下,可以对属性赋值。你可以这样作:“modname.the_answer = 42”。可写的属性也可以用del语句删除。例如:“del modname.the_answer”会从 modname对象中删除the_answer 属性。
(这一点可以很好的被利用)
<9.2>类定义
class MyClass:
"A simple example class" #类的文档字符串
i = 12345
def f(self):
return 'hello world'
class Bag:
def __init__(self):
self.data = []
def add(self, x):
self.data.append(x)
def addtwice(self, x):
self.add(x)
self.add(x)
<9.3> 继承
Python 支持多继承:
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>
<9.4> 迭代器
现在你可能注意到大多数容器对象都可以用 for 遍历:
for element in [1, 2, 3]:
print element
for element in (1, 2, 3):
print element
for key in {'one':1, 'two':2}:
print key
for char in "123":
print char
for line in open("myfile.txt"):
print line
这种形式的访问清晰、简洁、方便。这种迭代器的用法在Python中普遍而且统一。在后台,for 语句在容器对象中调用 iter() 。 该函数返回一个定义了 next() 方法的迭代器对象,它在容器中逐一访问元素。没有后续的元素时,next() 抛出一个 StopIteration 异常通知 for 语句循环结束。
了解了迭代器协议的后台机制,就可以很容易的给自己的类添加迭代器行为。定义一个 __iter__() 方法,使其返回一个带有 next() 方法的对象。如果这个类已经定义了 next(),那么 __iter__() 只需要返回self:
>>> class Reverse:
"Iterator for looping over a sequence backwards"
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def next(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
>>> for char in Reverse('spam'):
print char
m
a
p
s
<9.5> 发生器
发生器是创建迭代器的简单而强大的工具。它们写起来就像是正则函数,需要返回数据的时候使用yield 语句。每次 next() 被调用时,生成器回复它脱离的位置(它记忆语句最后一次执行的位置和所有的数据值)。以下示例演示了发生器可以很简单的创建出来:
>>> def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
>>> for char in reverse('golf'):
print char
f
l
o
g
前一节中描述了基于类的迭代器,它能作的每一件事发生器也能作到。因为自动创建了 __iter__() 和 next() 方法,发生器显得如此简洁。
除了创建和保存程序状态的自动方法,当发生器终结时,还会自动抛出 StopIteration异常。综上所述,这些功能使得编写一个正则函数成为创建迭代器的最简单方法。
2005-8-5
更多推荐
所有评论(0)