3. Python的非正式介绍
在后面的例子中,区分输入和输出的方法是看是否有提示符(“>;>;>; ”和“.. ”):想要重复这些例子的话,你就要在提示符显示后输入所有的一切;没有以提示符开始的行,是解释器输出的信息。需要注意的是示例中的从属提示符用于多行命令的结束,它表示你需要输入一个空行。
本手册中的很多示例都包括注释,甚至有一些在交互提示符中折行。Python中的注释以符号“#”起始,一直到当前行的结尾。注释可能出现在一行的开始,也可能跟在空格或程序代码之后,但不会出现在字符串中,字符串中的#号只代表#号。
示例:
# 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命令。启动解释器然后等待主提示符“>;>;>; ”出现(这用不了太久)。
3.1.1 数值
解释器的行为就像是一个计算器。你可以向它输入一个表达式,它会返回结果。表达式的语法简明易懂:+,-,*,/和大多数语言中的用法一样(比如C或Pascal),括号用于分组。例如:
>;>;>; 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完全支持浮点数,不同类型的操作数混在一起时,操作符会把整型转化为浮点数。
>;>;>; 3 * 3.75 / 1.5
7.5
>;>;>; 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)
复数总是由实部和虚部两部分浮点数来表示。可能从 z.real 和 z.imag 得到复数z的实部和虚部。
>;>;>; a=1.5+0.5j
>;>;>; a.real
1.5
>;>;>; a.imag
0.5
用于向浮点数和整型转化的函数(float(), int() 和 long())不能对复数起作用--没有什么方法可以将复数转化为实数。可以使用abs(z)取得它的模,也可以通过z.real得到它的实部。
>;>;>; a=3.0+4.0j
>;>;>; float(a)
Traceback (most recent call last):
File "<stdin>;", line 1, in ?
TypeError: can't convert complex to float; use e.g. abs(z)
>;>;>; a.real
3.0
>;>;>; a.imag
4.0
>;>;>; abs(a) # sqrt(a.real**2 + a.imag**2)
5.0
>;>;>;
交互模式下,最近一次表达式输出保存在_变量中。这意味着把Python当做桌面计算器使用时,它可以更容易的进行连续计算,例如:
>;>;>; tax = 12.5 / 100
>;>;>; price = 100.50
>;>;>; price * tax
12.5625
>;>;>; price + _
113.0625
>;>;>; round(_, 2)
113.06
>;>;>;
这个变量对于用户来说是只读的。不要试图去给它赋值--由于Python的语法效果,你只会创建一个同名的局部变量覆盖它。
3.1.2 字符串
除了数值,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.'
字符串可以通过几种方式分行。可以在行加反斜杠做为继续符,这表示下一行是当前行的逻辑沿续。
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."
print hello
注意换行用 \n 来表示;反斜杠后面的新行标识(newline,缩写“n”)会转换为换行符,示例会按如下格式打印:
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.
然而,如果我们创建一个“raw”行,\n序列就不会转为换行,示例源码最后的反斜杠和换行符n都会做为字符串中的数据处理。如下所示:
hello = r"This is a rather long string containing\n\
several lines of text much as you would do in C."
print hello
会打印为:
This is a rather long string containing\n\
several lines of text much as you would do in C.
或者,字符串可以用一对三重引号”””或'''来标识。三重引号中的字符串在行尾不需要换行标记,所有的格式都会包括在字符串中。
print """
Usage: thingy [OPTIONS]
-h Display this usage message
-H hostname Hostname to connect to
"""
produces the following output:
Usage: thingy [OPTIONS]
-h Display this usage message
-H hostname Hostname to connect to
解释器打印出来的字符串与它们输入的形式完全相同:内部的引号,用反斜杠标识的引号和各种怪字符,都精确的显示出来。如果字符串中包含单引号,不包含双引号,可以用双引号引用它,反之可以用单引号。(后面介绍的print语句,可以可以用来写没有引号和反斜杠的字符串)。
字符串可以用+号联接(或者说粘合),也可以用*号循环。
>;>;>; 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, in ?
string.strip('str') 'ing'
^
SyntaxError: invalid syntax
字符串可以用下标(索引)查询;就像C一样,字符串的第一个字符下标是0。这里没有独立的字符类型,字符仅仅是大小为一的字符串。就像在Icon中那样,字符串的子串可以通过切片标志来表示:两个由冒号隔开的索引。
>;>;>; word[4]
'A'
>;>;>; word[0]
'He'
>;>;>; word[2]
'lp'
切片索引可以使用默认值;省略前一个索引表示0,省略后一个索引表示被切片的字符串的长度。
>;>;>; word[] # The first two characters
'He'
>;>;>; word[2:] # All but the first two characters
'lpA'
和C字符串不同,Python字符串不能改写。按字符串索引赋值会产生错误。
>;>;>; word[0] = 'x'
Traceback (most recent call last):
File "<stdin>;", line 1, in ?
TypeError: object doesn't support item assignment
>;>;>; word[] = 'Splat'
Traceback (most recent call last):
File "<stdin>;", line 1, in ?
TypeError: object doesn't support slice assignment
然而,可以通过简单有效的组合方式生成新的字符串:
>;>;>; 'x' + word[1:]
'xelpA'
>;>;>; 'Splat' + word[4]
'SplatA'
切片操作有一个很有用的不变性: s[] + s[i:] 等于s。
>;>;>; word[] + word[2:]
'HelpA'
>;>;>; word[] + word[3:]
'HelpA'
退化的切片索引被处理的很优美:过大的索引代替为字符串大小,下界比上界大的返回空字符串。
>;>;>; word[1]
'elpA'
>;>;>; word[10:]
''
>;>;>; word[2]
''
索引可以是负数,计数从右边开始,例如:
>;>;>; 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'
越界的负切片索引会被截断,不过不要尝试在前元素索引(非切片的)中这样做:
>;>;>; word[-100:]
'HelpA'
>;>;>; word[-10] # error
Traceback (most recent call last):
File "<stdin>;", line 1, in ?
IndexError: string index out of range
理解切片的最好方式是把索引视为两个字符之间的点,第一个字符的左边是0,字符串中第n个字符的右边是索引n,例如:
+---+---+---+---+---+
| H | e | l | p | A |
+---+---+---+---+---+
0 1 2 3 4 5
-5 -4 -3 -2 -1
第一行是字符串中给定的0到5各个索引的位置,第二行是对应的负索引。从i到j的切片由这两个标志之间的字符组成。
对于非负索引,切片长度就是两索引的差。例如,word[1]的长度是2。
内置函数 len() 返回字符串长度:
>;>;>; s = 'supercalifragilisticexpialidocious'
>;>;>; len(s)
34
3.1.3 Unicode 字符串
从Python2.0开始,程序员们可以使用一种新的数据类型来存储文本数据:Unicode 对象。它可以用于存储多种Unicode数据(请参阅 http://www.unicode.org/ ),并且,通过必要时的自动转换,它可以与现有的字符串对象良好的结合。
Unicode针对现代和旧式的文本中所有的字符提供了一个序列。以前,字符只能使用256个序号,文本通常通过绑定代码页来与字符映射。这很容易导致混乱,特别是软件的国际化(internationalization--通常写做“i18n”--“i”+18 characters +“n”)。Unicode通过为所有字符定义一个统一的代码页解决了这个问题。
Python中定义一个Unicode字符串和定义一个普通字符串一样简单:
>;>;>; u'Hello World !'
u'Hello World !'
引号前小写的“u”表示这里创建的是一个Unicode字符串。如果你想加入一个特殊字符,可以使用Python的 Unicode-Escape 编码。如下例所示:
>;>;>; u'Hello\u0020World !'
u'Hello World !'
被替换的 \u0020 标识表示在给定位置插入编码值为 0x0020 的 Unicode字符(空格符)。
其它字符也会被直接解释成对应的Unicode码。如果你有一个在西方国家常用的Latin-1编码字符串,你可以发现Unicode字符集的前256个字符与Lation-1的对应字符编码完全相同。
另外,有一种与普通字符串相同的行模式。想要使用Python的Raw-Unicode-Escape 编码,你需要在字符串的引号前加上 ur 前缀。如果在小写“u”前可能有不止一个反斜杠,它只会把那些单独的 \uXXXX 转化为Unicode字符。
>;>;>; ur'Hello\u0020World !'
u'Hello World !'
>;>;>; ur'Hello\\u0020World !'
u'Hello\\\\u0020World !'
行模式在你需要输入很多个反斜杠时很有用,可能会用于正规表达式。
作为这些编码标准的一部分,Python提供了一个完备的方法集用于从已知的编码集创建Unicode字符串。
内置函数unicode() 提供了访问(编码和解码)所有已注册的Unicode编码的方法。它能转换众所周知的 Latin-1, ASCII, UTF-8, 和 UTF-16。后面的两个可变长编码字符集用一个或多个byte存储Unicode字符。默认的字符集是 ASCII ,它只处理0到127的编码,拒绝其它的字符并返回一个错误。当一个Unicode字符串被打印、写入文件或通过str()转化时,它们被替换为默认的编码。
>;>;>; u"abc"
u'abc'
>;>;>; str(u"abc"
'abc'
>;>;>; u"&&ü"
u'\xe4\xf6\xfc'
>;>;>; str(u"&&ü"
Traceback (most recent call last):
File "<stdin>;", line 1, in ?
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(12
要把一个Unicode字符串用指定的字符集转化成8位字符串,可以使用Unicode对象提供的encode()方法,它有一个参数用以指定编码名称。编码名称小写。
>;>;>; u"&&ü".encode('utf-8')
'\xc3\xa4\xc3\xb6\xc3\xbc'
如果你有一个特定编码的字符串,想要把它转为Unicode字符集,,可以使用uncode()函数,它以编码名做为第二个参数。
>;>;>; unicode('\xc3\xa4\xc3\xb6\xc3\xbc', 'utf-8')
u'\xe4\xf6\xfc'
3.1.4 链表
Python 已经有了几个复合数据类型,用于组织其它的值。最通用的是链表,它写为中括之间用逗号分隔的一列值(子项),链表的子项不一定是同一类型的值。
>;>;>; a = ['spam', 'eggs', 100, 1234]
>;>;>; a
['spam', 'eggs', 100, 1234]
像字符串一样,链表也以零开始,可以被切片,联接,等等:
>;>;>; a[0]
'spam'
>;>;>; a[3]
1234
>;>;>; a[-2]
100
>;>;>; a[1:-1]
['eggs', 100]
>;>;>; a[] + ['bacon', 2*2]
['spam', 'eggs', 'bacon', 4]
>;>;>; 3*a[] + ['Boe!']
['spam', 'eggs', 100, 'spam', 'eggs', 100, 'spam', 'eggs', 100, 'Boe!']
与不变的字符串不同,链表可以改变每个独立元素的值:
>;>;>; a
['spam', 'eggs', 100, 1234]
>;>;>; a[2] = a[2] + 23
>;>;>; a
['spam', 'eggs', 123, 1234]
可以进行切片操作,甚至还可以改变链表的大小:
>;>;>; # Replace some items:
... a[0] = [1, 12]
>;>;>; a
[1, 12, 123, 1234]
>;>;>; # Remove some:
... a[0] = []
>;>;>; a
[123, 1234]
>;>;>; # Insert some:
... a[1] = ['bletch', 'xyzzy']
>;>;>; a
[123, 'bletch', 'xyzzy', 1234]
>;>;>; a[] = a # Insert (a copy of) itself at the beginning
>;>;>; a
[123, 'bletch', 'xyzzy', 1234, 123, 'bletch', 'xyzzy', 1234]
内置函数len()也同样可以用于链表:
>;>;>; len(a)
8
它也可以嵌套链表(在链表中创建其它链表),例如:
>;>;>; 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实际上指向同一个对象!我们在后面会讲到对象语法。
3.2 开始编程
当然,我们可以用Python做比2加2更复杂的事。例如,我们可以用以下的方法输出菲波那契(Fibonacci)序列的子序列:
>;>;>; # 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
示例中介绍了一些新功能:
第一行包括了复合参数:变量a和b同时被赋值为0和1。最后一行又一次使用了这种技术,证明了在赋值之前表达式右边先进行了运算。右边的表达式从左到右运算。
while循环运行在条件为真时执行。在Python中,类似于C任何非零值为真,零为假。 这个条件也可以用于字符串或链表,事实上于对任何序列类型,长度非零时为真,空序列为假。示例所用的是一个简单的比较。标准的比较运算符写法和C相同: < (小于),>; (大于),== (等于),<= (小于等于),>;=(大于等于)和!= (不等于)。
循环体是缩进的:缩进是Python对语句分组的方法。 Python仍没有提供一个智能编辑功能所以你要在每一个缩进行输入一个tab或(一个或多个)空格。 实际上你可能会准备更为复杂的文本编辑器来编写你的Python程序,大多数文本编辑器都提供了自动缩进功能。交互式的输入一个复杂语句时,需要用一个空行表示完成(因为解释器没办法猜出你什么时候输入最后一行)。需要注意的是每一行都要有相同的空字符来标识这是同一个语句块。
print语句打印给定表达式的值。它与你仅仅输入你需要的表达式(就像前面的计算器示例)不同,它可以同时输出多个表达式。字符串输出时没有引号,各项之间用一个空格分开,你可以很容易区分它们,如下所示:
>;>;>; i = 256*256
>;>;>; print 'The value of i is', i
The value of i is 65536
print语句末尾的逗号避免了输出中的换行:
>;>;>; a, b = 0, 1
>;>;>; while b < 1000:
... print b,
... a, b = b, a+b
...
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
需要注意的是,如果最后一行仍没有写完,解释器会在它打印下一个命令时插入一个新行。