小段落


3. An Informal Introduction to Python

在底下的例子里,你可以很容易区别凡是需要输入的地方都会出现prompts (">>" 或 "... "),凡是输出的结果则没有。如果你想要跟着这个教学文件一起做的话,你就得打入所有在prompts之后的指令,凡是没有prompts出现的行就是直译器输出的结果。值得注意的是,secondary promt 之后如果什么东西都没有,表示这是一个空行(直接按ENTER的结果),也表示这是一个多行指令的结束。

在本文件中的大部分例子,都有加上注释,甚至是那些互动模式下的例子。注释(comment)在Python中是以 " #" 之后的东西都是注释(译:跟Perl一样)。注释可以自成一行,也可以跟在空白字元或是程式码的后面。但是,如果 " #" 是在字串常数(string literal)之中的话,就不代表注释的意义,而只是一个普通字元罢了。

底下是一些例子:

# this is the first comment
SPAM = 1 # and this is the second comment
# ... and now a third!
STRING = "# This is not a comment."


3.1 把Python当作计算机来用

现在我们来试一试一些简单的Python指令吧。请先启动Python的直译器并且等待primary prompt( " >>" )的出现。(应该不会很久的)


3.1.1 数字

直译器就好像一个计算机一样:你可以打入一个表示式(expression),然后直译器会把这个expression的执行结果秀出来。Expression的语法都很简单直接,一般的运算符号 +, -, * 以及 / 的用法就跟其他的程式语言(像是Pascal或C)一样。你也可以用括号 "( )" 来表示运算执行的先后次序。例子如下:

>>> 2+2
4
>>> # This is a comment
... 2+2
4
>>> 2+2 # and a comment on the same line as code
4
>>> (50-5*6)/4
5
>>> # Integer division returns the floor:
... 7/3
2
>>> 7/-3
-3

跟C语言一样,等于符号 ("=") 其实是表示设定某个值给一个变数的意思。虽然设定 ("=") 运算本身是有结果值的,但是直译器并不会输出其结果来。

>>> width = 20
>>> height = 5*9
>>> width * height
900

一个值是可以同时设给许多变数的:

>>> x = y = z = 0  # Zero x, y and z
>>> x
0
>>> y
0
>>> z
0

浮点数的运算在Python里面也是支援的,如果整数与浮点数(带小数点或e的数)进行运算的话,整数部分会先转换(convert)成浮点数再进行运算。

>>> 4 * 2.5 / 3.3
3.0303030303
>>> 7.0 / 2
3.5

甚至连复数的运算也支援喔,只需要把虚数部分加上 "j" 或是 " J"在其后就可以了。如果实部不为零的话,复数的写法就写成 "(real+ imagj)" 。或者,我们也可以用函数的方式来表示复数为 "complex(real , imag)" 的形式。

>>> 1j * 1J
(-1+0j)
>>> 1j * complex(0,1)
(-1+0j)
>>> 3+1j*3
(3+3j)
>>> (3+1j)*3
(9+3j)
>>> (1+2j)/(1+1j)
(1.5+0.5j)

复数的虚数部分及实数部分的值都是以浮点数(float point numbers)来表示的,如果 z 代表一个复数的话,你可以很轻易的用 z.real 以及 z.imag 得到一个复数的实数部分及虚数部分。

>>> a=1.5+0.5j
>>> a.real
1.5
>>> a.imag
0.5

复数没有办法直接用 (float(), int() 或是 long()) 转换成浮点数或是整数。事实上,复数没有直接对应的实数,你必须用 abs(z) 来得到 z 的magnitude(以浮点数表示),或是如上所述 用z.real 直接得到其实数部分。

>>> a=1.5+0.5j
>>> float(a)
Traceback (innermost last):
File "<stdin>", line 1, in ?
TypeError: can't convert complex to float; use e.g. abs(z)
>>> a.real
1.5
>>> abs(a)
1.58113883008

在互动模式之下,最后一个印出来的expression的值会储存在一个特殊变数 "_ " 之中。这表示,当你用Python的直译器来当作计算机用的时候,想要连续做运算其实是方便许多的。如下例:

>>> tax = 17.5 / 100
>>> price = 3.50
>>> price * tax
0.61249999999999993
>>> price + _
4.1124999999999998
>>> round(_, 2)
4.1100000000000003

对于使用者来说, "_" 这个变数是一个唯读的变数。你没有办法设定一个值给它,当你这样做的时候,事实上你是重新创造一个同名的变数,但是跟之前系统内建的 "_" 这个变数是一点关系也没有的了。


3.1.2 字串

除了数字之外, Python也有能力处理字串(string)。字串在Python中有很多种表达方式,它可以放在双括号””之中,也可以放在单括号’’里面:

>>> 'spam eggs'
'spam eggs'
>>> 'doesn\'t'
"doesn't"
>>> "doesn't"
"doesn't"
>>> '"Yes," he said.'
'"Yes," he said.'
>>> "\"Yes,\" he said."
'"Yes," he said.'
>>> '"Isn\'t," she said.'
'"Isn\'t," she said.'

字串常数(string literals)是可以跨越多行的,其表示方法有很多。如果要换行的话可以用”\”符号来表示之。如下例:

hello = "This is a rather long string containing\n\
several lines of text just as you would do in C.\n\
Note that whitespace at the beginning of the line is\
significant.\n"
print hello

这个例子会印出以下的结果:

This is a rather long string containing
several lines of text just as you would do in C.
Note that whitespace at the beginning of the line is significant.

你也可以用成对的三个单引号( """ ) 或双引号 ( ''' ) 来表示字串。在此情况下你所打入的ENTER就会直接被解读为换行符号而不需要再用\n了。

print """
Usage: thingy [OPTIONS]
-h Display this usage message
-H hostname Hostname to connect to
"""

这个例子会印出以下的结果:

Usage: thingy [OPTIONS] 
-h Display this usage message
-H hostname Hostname to connect to

如果你打入的expression是字串的运算,运算的结果的同样的会由直译器显示出来,而且显示的方式跟你直接打入字串常数(string literals)是一样的:会在引号之中,所有有escape character “\”表示的字元都会依样的显示出来。如果字串本身包含有单引号,整个字串就会用双引号括起来,要不然就会只用单引号来把整个字串括起来。(如果你使用 print 这个叙述(statement)来印出字串的话,萤幕的输出就不会有引号出现,而且字串中的escape character (\”表示的特殊字元) 都会显示出其所代表的意义来。)

字串可以用 + 这个运算元来相加 (连接起来),或是用 * 这个运算元来重复之。请看例子:

>>> word = 'Help' + 'A'
>>> word
'HelpA'
>>> '<' + word*5 + '>'
'<HelpAHelpAHelpAHelpAHelpA>'

如果你把两个字串常数放在一起,它们自动就会相加起来。所以,上面的例子的第一行也可以写作 "word = 'Help' 'A'" 。不过这个方法只适用于两个字串常数的相加,其他情况就不适合了。请看例子:

>>> import string
>>> 'str' 'ing' # <- This is ok
'string'
>>> string.strip('str') + 'ing' # <- This is ok
'string'
>>> string.strip('str') 'ing' # <- This is invalid
File "<stdin>", line 1
string.strip('str') 'ing'
^
SyntaxError: invalid syntax

如同在C语言一样,字串是有标记(subscript(index))的,第一个字元的标记(subscript(index))就是0。在Python中没有另外一个字元character资料型态,一个字元就是一个长度为 1的字串。就像是在Icon语言一样,字串是可以用其subscript(index)来切出( slice notation )其中的一部份的,其语法为 ""。

>>> word[4]
'A'
>>> word[0:2]
'He'
>>> word[2:4]
'lp'

与C不同的是,Python的字串是不可改变的(immutable),如果你想要改变其中的一个字元或是一个部份(slice),你会得到一个错误的信息:

>>> word[0] = 'x'
Traceback (innermost last):
File "<stdin>", line 1, in ?
TypeError: object doesn't support item assignment
>>> word[:-1] = 'Splat'
Traceback (innermost last):
File "<stdin>", line 1, in ?
TypeError: object doesn't support slice assignment

但是你可以任意使用一个字串的一个字元或是一个部份(slice)来创造出另一个字串,这是完全可行的:

>>> 'x' + word[1:]
'xelpA'
>>> 'Splat' + word[-1:]
'SplatA'

当你用字串切片(string slice)的语法时,可以使用其预定(default)的subscript(index)值,这是很方便的。第一个subscript(index)的预设值是0,第二个subscript(index)的预设值则是这个字串的整体长度。

>>> word[:2]    # The first two characters
'He'
>>> word[2:] # All but the first two characters
'lpA'

所以, s[:i] + s[i:] 会恰好等于 s 。你可以想一想为什么:

>>> word[:2] + word[2:]
'HelpA'
>>> word[:3] + word[3:]
'HelpA'

如果你用一些奇怪的index来切割字串,Python直译器也都处理的很好:如果第二个index太大的话就自动代换为字串的长度,如果第二个index比第一个index还要小的话就自动传回 一个空字串。

>>> word[1:100]
'elpA'
>>> word[10:]
''
>>> word[2:1]
''

字串的index甚至可以是负数,若是负数的话,就必须从字串的尾巴开始算起。如下例:

>>> word[-1]     # The last character
'A'
>>> word[-2] # The last-but-one character
'p'
>>> word[-2:] # The last two characters
'pA'
>>> word[:-2] # All but the last two characters
'Hel'

但是 -0 事实上是等于 0 ,所以不会从尾巴开始算起。

>>> word[-0]     # (since -0 equals 0)
'H'

如果负数index超过字串的范围的话,就自动只会到最大可能的范围,但是如果不是切割一部份的话就会造成错误的情形:

>>> word[-100:]
'HelpA'
>>> word[-10] # error
Traceback (innermost last):
File "<stdin>", line 1
IndexError: string index out of range

最好避免错误的方法是把index看成是指向字元及字元间位置的指标,字串的最开头是0,字串的结尾处就是字串的长度。如下图所示:

 +---+---+---+---+---+ 
| H | e | l | p | A |
+---+---+---+---+---+
0 1 2 3 4 5
-5 -4 -3 -2 -1

上图的数字部分第一行代表的是正数的index,由0到字串的长度,第二行代表的是负数的index。字串的切片(slice)很容易就可以看出来,就是两个index之间的所有字元组合成的字串啰。

对于正数的index来说,如果两个index都在范围之内,字串的切片(slice)的长度就正好是其两个index相减的结果。举例来说 word[1:3] 的长度就正好是 2。

Python内建的 len() 函式可以帮助我们得到字串的长度值。

>>> s = 'supercalifragilisticexpialidocious'
>>> len(s)
34


3.1.3 Unicode 字串

从Python 2.0 开始Python支援一种新的储存文字资料的资料型态:Unicode物件(object)。使用这个物件你可以储存并控制Unicode的资料(详见 http://www.unicode.org [off-site link] ) ,并且这个物件跟已经存在的字串(string)物件是完全可以互相整合,并且在需要时可以互相转换的。

使用Unicode的好处是可以处理各种不同国家语言的字元。在Unicode之前,在一个code page里只有256个字元可以使用在script中。这个限制的结果常常造成软体国际化(internationalizetion,通常写作 "i18n" -- "i" + 18个字元 + "n")时候的困扰。Unicode的出现定义一个所有script都可以用的code page,如此就解决了这个问题。

在Python中要创造一个Unicode字串就跟创造一个普通字串一样容易:

>>> u'Hello World !'
u'Hello World !'

在引号之前小写的 "u" 代表这个字串是一个Unicode字串。如果你要用到特殊字元,你可能要使用Python的Unicode特殊字元编码( Unicode-Escape encoding)。底下的范例示范如何使用之:

>>> u'Hello\\u0020World !'
u'Hello World !'

上面的
u0020
表示在这个位置要插入一个由十六位元0x0020所代表的Unicode字元 (就是空白字元啦)。

其他的字元也是一样的会被解读为其对应的Unicode字元。由于Unicode对应中的前256 个Unicode字元正好就是大部分欧美国家使用的Latin-1 编码字元,所以其转换是更加的容易。

对于专家们来说,有一个字串的原始模式(raw mode)可以使用。你必须再加上一个小写 'r' 来使Python 使用这一个原始的Unicode特殊字元编码( Raw-Unicode-Escape encoding)。只有当
uXXXX
之中的小写 'r' 有奇数的'\'时才会用到这一个编码的。

>>> ur'Hello\u0020World !'
u'Hello World !'
>>> ur'Hello\\u0020World !'
u'Hello\\\\u0020World !'

这个原始模式(raw mode)通常用在当你的字串里面有一大堆的反斜线 '\' 时 ,例如regular expressions(正规表示)时就常用到。

除了这些标准的编码之外, Python还提供了一整套的方法让你可以从以知的编码中创造出Unicode字串来。

Python内建的 unicode() p() 函式可以让你使用所有的已注册的Unicode解码/编码系统(codecs (COders and DECoders))。 这个 codes 可以与大部分的系统互相转换,包括 Latin-1, ASCII , UTF-8 以及 UTF-16 等等。上面所提到的最后两种系统是可变长度的编码系统,可以来储存8位元及16位元的Unicode字元。Python预设使用UTF-8为预设编码系统。当你印出Unicode或是将Unicode写入档案时都会使用到。

>>> u"äöü"
u'\344\366\374'
>>> str(u"äöü")
'\303\244\303\266\303\274'

如果你要使用一个特别的编码系统,但是要印出对应的Unicode码时,你可以使用 unicode() 函式,加上这个编码系统的名称当作第二个参数。

>>> unicode('\303\244\303\266\303\274','UTF-8')
u'\344\366\374'

如果要把Unicode字串转换为一般的字串编码时,可以使用Unicode物件的 encode() 方法(method)。

>>> u"äöü".encode('UTF-8')
'\303\244\303\266\303\274'


3.1.4 列(List)

(译:硬要翻译list实在太不方便,我直接用原文啰)

Python能够了解一些较为 复杂 的资料型态,这些资料型态大多是用来处理一群的其他资料值。最方便使用的要算是 list 了,一个list可以写成一串由逗号分开的值(东西),然后用角括号括起来便成。放在list里的东西不需要是同一个资料型态

>>> a = ['spam', 'eggs', 100, 1234]
>>> a
['spam', 'eggs', 100, 1234]

跟字串的index用法相同,list的index也由0开始,同样你可以用index来切割lists、组合两个list等等:

>>> a[0]
'spam'
>>> a[3]
1234
>>> a[-2]
100
>>> a[1:-1]
['eggs', 100]
>>> a[:2] + ['bacon', 2*2]
['spam', 'eggs', 'bacon', 4]
>>> 3*a[:3] + ['Boe!']
['spam', 'eggs', 100, 'spam', 'eggs', 100, 'spam', 'eggs', 100, 'Boe!']

与字串不相同的是,字串的个别字元是不可变动的( immutable ),但是list的个别成员是可以自由改变的。

>>> a
['spam', 'eggs', 100, 1234]
>>> a[2] = a[2] + 23
>>> a
['spam', 'eggs', 123, 1234]

你也可以设定一个值或是一个list给一个list的切割部分(slice),但是这样的结果会改变整个list的长度:

>>> # Replace some items:
... a[0:2] = [1, 12]
>>> a
[1, 12, 123, 1234]
>>> # Remove some:
... a[0:2] = []
>>> a
[123, 1234]
>>> # Insert some:
... a[1:1] = ['bletch', 'xyzzy']
>>> a
[123, 'bletch', 'xyzzy', 1234]
>>> a[:0] = a # Insert (a copy of) itself at the beginning
>>> a
[123, 'bletch', 'xyzzy', 1234, 123, 'bletch', 'xyzzy', 1234]

内建的 len() 函式仍然可用在list上面:

>>> len(a)
8

一个list也可以是另一个list的成员(这叫作巢状list, nested list),参考下例:

>>> q = [2, 3]
>>> p = [1, q, 4]
>>> len(p)
3
>>> p[1]
[2, 3]
>>> p[1][0]
2
>>> p[1].append('xtra') # See section 5.1
>>> p
[1, [2, 3, 'xtra'], 4]
>>> q
[2, 3, 'xtra']

注意前一个例子, p[1] 以及 q 事实上指得是同一个物件。我们在之后还会再讨论物件的语法( object semantics )。


3.2 迈向程式设计的第一步

当然Python能做比二加二更有用更复杂的事,例如说,我们可以写一个程式来印出费氏数列( the Fibonacci series )来:

>>> # Fibonacci series:
... # the sum of two elements defines the next
... a, b = 0, 1
>>> while b < 10:
... print b
... a, b = b, a+b
...
1
1
2
3
5
8

这个范例告诉了我们很多新的事情:


请看 关于此文件… 里面有关如何给我们建议的说明。