print 和 import 的更多信息
对于很多应用程序来说,使用 logging 模块日志比 print 语句更合适.
使用逗号输出
打印多个表达式时,使用逗号将他们隔开就好:
>>> print 'Age:', 32
Age: 32
注意: print 的参数并不能像我们预期那样构成一个元组:
>>> 1, 2, 3
(1, 2, 3)
>>> print 1, 3, 4
1 3 4
>>> print (1, 3, 4)
(1, 3, 4)
>>> name = 'Gumby'
>>> salutation = 'Mr'
>>> greeting = 'Hello'
>>> print greeting, salutation, name
Hello Mr Gumby
>>> print greeting + ',', salutation, name
Hello, Mr Gumby
如果在结尾处加上逗号,那么接下来的语句会与前一条语句在同一行打印,例如:
print 'hello.',
print 'world!'
这只在脚本中有作用,交互式 Python 会话中则没有效果.
把某件事作为另一件事导入
从模块导入函数时,可以使用:
import somemodule
from somemodule import somefunction
from somemodule import somefunction, anotherfunction, yetanotherfunction
from somemodule import *
只有确定自己想要从给定的模块导入所有功能时,才使用最后一个版本.如果两个模块都有 open 函数,可以像下面这样使用函数:
module1.open(...)
module2.open(...)
也可以在语句末尾增加一个 as 子句,在该子句后给出名字,或为整个模块提供别名:
>>> import math as foobar
>>> foobar.sqrt(4)
2.0
也可以为函数提供别名:
>>> from math import sqrt as foobar
>>> foobar(4)
2.0
对于 open 函数,可以像下面这样使用:
from module1 import open as open1
from module2 import open as open2
注意:有些模块,例如 os.path 是分层次安排的(一个模块在另一个模块的内部).有关模块结构的更多信息,请参见第十章关于包的部分.
赋值魔法
序列解包
多个赋值操作可以同时进行:
>>> x, y, z = 1, 3, 4
>>> print x, y, z
1 3 4
用它交换两个(或多个)变量也是没问题的:
>>> x, y = y, x
>>> print x, y
3 1
事实上,这里所做的事情叫做序列解包或可选代解包——.将多个值的序列解开,然后放到变量的序列中。更形象一点:
>>> values = 1, 2, 3
>>> values
(1, 2, 3)
>>> x, y, z = values
>>> x
1
>>> y
2
>>> z
3
当函数或者方法返回元组(或者其他序列或可迭代对象)时,这个特性尤其有用.假设需要获取(和删除)字典中任意的键-值对,可以使用 popitem 方法,这个方法将键-值作为元组返回.那么这个元组就可以直接赋值到两个变量中:
>>> scoundrel = {'name': 'Robin', 'girlfriend': 'Marion'}
>>> key, value = scoundrel.popitem()
>>> key
'girlfriend'
>>> value
'Marion'
它允许函数返回一个以上的值并且打包成元组,然后通过一个赋值语句很容易进行访问.所解包的序列中的元素数量必须和放置在赋值符号 = 左边的变量数量完全一致,否则 Python 会在赋值时引发异常:
>>> x, y, z = 1, 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: need more than 2 values to unpack
>>> x, y, z = 1, 2, 3, 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack
注意:Python 3.0 中有另外一个解包的特性:可以像在函数的参数列表中一样使用星号运算符(参见第六章).
>>> a, b, *rest = 1, 2, 3, 4
>>> a
1
>>> b
2
>>> rest
[3, 4]
>>>
>>> *rest, a, b = 1, 2, 3, 4
>>> a
3
>>> b
4
>>> rest
[1, 2]
>>> *rest, a, b = 1, 2
>>> rest
[]
当使用星号的变量放置在第一个位置时,这样它就总会包含一个列表.右侧的赋值语句可以是可迭代对象.
链式赋值
链式赋值是将同一个值赋给多个变量的捷径.和并行赋值不同的是,这里只处理一个值:
x = y = somefunction()
or
x = somefunction()
y = x
增量赋值
增量赋值对于"+, -, *, /, %"等标准运算符都试用:
>>> x = 2
>>> x += 1
>>> x
3
>>> x *= 2
>>> x
6
>>> x * = 2
File "<stdin>", line 1
x * = 2
^
SyntaxError: invalid syntax
对于其他数据类型也适用(只要二元运算符本身适用于这些数据类型即可):
>>> fnord = 'foo'
>>> fnord += 'bar'
>>> fnord
'foobar'
>>> fnord *= 2
>>> fnord
'foobarfoobar'
语句块:缩排的乐趣
在代码前放置空格来缩进语句即可创建语句块.块的每行缩进应该是相同的量.在 Python 中,冒号(:)用来标识语句块的开始,块中每一个语句都是缩进的(缩进量相同).
条件和条件语句
这就是布尔变量的作用
真值(也叫做布尔值).下面的值在作为布尔表达式的时候,会被解释器看作假(false):
False None 0 "" () [] {}
换句话说,也就是标准值 False 和 None 、所有类型的数字 0(包括浮点型、长整型和其他类型)、空序列(比如空字符串、元组和列表)以及空的字典都为假.其他的一切都被解释为真,包括特殊值 True.
事实上,True 和 False 只不过是 1 和 0 的一种"华丽"的说法而已——看起来不同但作用相同.
>>> True
True
>>> true
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'true' is not defined
>>> False
False
>>> True == 1
True
>>> False == 0
True
>>> True + False + 32
33
布尔值 True 和 False 属于布尔类型,bool 函数可以用来(和 list, str 以及 tuple 一样)转换其他值.
>>> bool('I think')
True
>>> bool(42)
True
>>> bool('')
False
>>> bool(0)
False
因为所有值都可以用作布尔值,所以几乎不需要对它们进行显式转换(Python 会自动转换这些值).
注意:尽管 [] 和 “” 都是假值,但它们本身却不相等.
>>> bool([])
False
>>> bool("")
False
>>> [] == ""
False
条件执行和 if 语句
>>> name = raw_input('what is your name: ')
what is your name: wang Gumby
>>> if name.endswith('Gumby'):
... print 'Hello, Mr.Gumby'
...
Hello, Mr.Gumby
else 和 elif子句
>>> num = input('Enter a number: ')
Enter a number: 21
>>> if num > 0:
... print 'the number is positive'
... elif num < 0:
... print 'the number is negative'
... else:
... print 'the number is zero'
...
the number is positive
更复杂的条件
比较运算符
运算符"==, <, >, >=, <=, !=, is, is not, in, not in"都是比较运算符,'x is y'表示 x 和 y 是同一个对象,'x in y'表示 x 是 y 容器(例如,序列)的成员.
只有 x 和 y 是相同或者近似类型的对象时,比较才有意义(例如,两个整型数或者一个整型数和一个浮点型数进行比较).
Python 中比较运算和赋值运算一样是可以连接的——几个运算符可以连在一起使用,比如:0<age<100.
>>> age = 10
>>> 0<age<100
True
提示:比较对象的时候可以使用第二章中介绍的内建的 cmp 函数.
相等运算符
>>> "foo" == "foo"
True
>>> "foo" == "bar"
False
is: 同一性运算符
>>> x = y = [1, 2, 3]
>>> z = [1, 2, 3]
>>> x == y
True
>>> x ==z
True
>>> x is y
True
>>> x is z
False
变量 x 和 y 都被绑定到同一个列表上,而变量 z 被绑定在另外一个具有相同数值和顺序的列表上.它们的值可能相等,但是却不是同一个对象.
总结一下:使用'=='运算符来判定两个对象是否相等,使用 is 判定两者是否等同(同一个对象).
字符串和序列比较
字符串可以按照字母顺序排列进行比较:
>>> "alpha" < "beta"
True
>>> ord('a')
97
>>> chr(97)
'a'
>>> 'FnOrD'.lower() == 'fNord'.lower()
True
>>> 'FnOrD'.upper() == 'fNord'.upper()
True
其他的序列也可以用同样的方式进行比较,不过比较的不是字符而是元素的其他类型.
>>> [1, 2] < [2, 1]
True
>>> [2, [1, 4]] < [2, [1, 5]]
True
布尔运算符
布尔运算符:and, or, not.在 Python 中会使用短路逻辑.
>>> number = 3
>>> if number <= 10 and number >= 1:
... print 'Great'
... else:
... print 'Wrong'
...
Great
>>>
>>> number = 3
>>> if 1 <= number <= 10:
... print 'Great'
... else:
... print 'Wrong'
...
Great
name = raw_input('Please enter your name: ') or '<unknown>'
>>> 1 if 2 else 3
1
>>> 1 if False else 3
3
>>> 1 if True else 3
1
循环
while 循环
x = 1
while x <= 100:
print x
x += 1
name = ''
while not name: # while not name or name.isspace(): while not name.strip():
name = raw_input('Please input your name: ')
print 'Hello. %s!' % name
for 循环
要为一个集合(序列和其他可迭代对象)的每个元素都执行一个代码块,可以使用 for 循环.
numbers = [0, 1, 2, 3, 4, 5]
for num in numbers:
print num
因为迭代(循环)某范围的数字是很常见的,所以有个内建的范围函数供使用:
>>> range(0, 10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(2, 10)
[2, 3, 4, 5, 6, 7, 8, 9]
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
range 函数的工作方式类似于分片.它包含下限但不包含上限,即:前闭后开.如果希望下限为 0,可以只提供上限.
提示:如果能使用 for 循环,就尽量不用 while 循环.
xrange 函数的循环行为类似于 range 函数,区别在于 range 函数一次创建整个序列,而 xrange 一次只创建一个数.当需要迭代一个巨大的序列时 xrange 会更高效,不过一般情况下不需要过多关注它.
>>> xrange(10)
xrange(10)
循环遍历字典
一个简单的 for 语句就能循环字典的所有键,就像处理序列一样:
>>> d = {'x': 1, 'y': 2, 'z': 3}
>>> for key in d:
... print key, 'corresponds to', d[key]
...
y corresponds to 2
x corresponds to 1
z corresponds to 3
>>> d.keys()
['y', 'x', 'z']
>>> d.values()
[2, 1, 3]
d.items 方法会将键-值对作为元组返回,for 循环的一大好处就是可以循环中使用序列解包:
>>> for key, value in d.items():
... print key, 'corresponds to', value
...
y corresponds to 2
x corresponds to 1
z corresponds to 3
注意:字典元素的顺序通常是没有定义的.迭代的时候,字典中的键和值都能保证被处理,但是处理顺序不确定.如果顺序很重要的话,可以将键值保存在单独的列表中,在迭代前进行排序.
一些迭代工具
并行迭代
>>> names = ['anne', 'beth', 'george', 'damon']
>>> ages = [12, 45, 32, 102]
>>> for i in range(len(names)):
... print names[i], 'is', ages[i], 'years old'
...
anne is 12 years old
beth is 45 years old
george is 32 years old
damon is 102 years old
内建的 zip 函数可以把两个序列"压缩"在一起,然后返回一个元组的列表:
>>> zip(names, ages)
[('anne', 12), ('beth', 45), ('george', 32), ('damon', 102)]
>>>
>>> for name, age in zip(names, ages):
... print name, 'is', age, 'years old'
...
anne is 12 years old
beth is 45 years old
george is 32 years old
damon is 102 years old
zip 函数也可以作用于任意多的序列.并且可以应付不等长的序列,当最短的序列"用完"的时候就会停止:
>>> zip(range(5), xrange(10000000))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
在上面的代码中,xrange 只计算前 5 个数,如果替换为 range,则要计算所有的值,这需要花费很长时间.
编号迭代
有些时候想要迭代序列中的对象,同时还要获取当前对象的索引.例如,在一个字符串列表中替换所有包含'xxx'的子字符串.实现方法有很多:
# 方法1
for string in strings:
if 'xxx' in string:
index = strings.index(string)
strings[index] = '[censored]'
# 方法2
index = 0
for string in strings:
if 'xxx' in string:
strings[index] = '[censored]'
index += 1
另一种方法是使用内建的 enumerate 函数:
for index, string in enumerate(strings):
if 'xxx' in string:
strings[index] = '[censored]'
翻转和排序迭代
reversed 和 sorted: 它们同列表的 reverse 和 sort (sorted 和 sort 使用同样的参数)方法类似,单作用于任何序列或可迭代对象上,不是原地修改对象,而是返回翻转或排序后的版本:
>>> sorted([4, 3, 6, 8, 3])
[3, 3, 4, 6, 8]
>>> sorted('Hello, world!')
[' ', '!', ',', 'H', 'd', 'e', 'l', 'l', 'l', 'o', 'o', 'r', 'w']
>>> list(reversed('Hello, world!'))
['!', 'd', 'l', 'r', 'o', 'w', ' ', ',', 'o', 'l', 'l', 'e', 'H']
>>> ''.join(reversed('Hello, world!'))
'!dlrow ,olleH'
>>> reversed('Hello, world!')
<reversed object at 0x7f6729129910>
注意,虽然 sorted 方法返回列表,reversed 方法却返回一个更加不可思议的可迭代对象.我们可以在 for 循环以及 join 方法中使用,而不会有任何问题.不过不能直接对它使用索引、分片以及调用 list 方法,如果希望进行上述处理,那么可以使用 list 类型转换返回的对象.
跳出循环
break:结束(跳出)循环.
continue:它会结束当前的迭代,"跳"到下一轮循环的开始.即:"跳过剩余的循环体,但是不结束循环".
循环中的 else 子句
在循环中增加一个 else 子句——它仅在没有调用 break 时执行.
>>> from math import sqrt
>>> for n in range(99, 8, -1):
... root = sqrt(n)
... if root == int(root):
... print n
... break
... else:
... print "Did not find it!"
...
81
>>> from math import sqrt
>>> for n in range(99, 81, -1):
... root = sqrt(n)
... if root == int(root):
... print n
... break
... else:
... print "Did not find it!"
...
Did not find it!
列表推导式——轻量级循环
列表推导式是利用其他列表创建新的列表的一种方法.它的工作方式类似于 for 循环:
>>> [x * x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> [x * x for x in range(10) if x % 3 == 0]
[0, 9, 36, 81]
>>> girls = ['alice', 'bernice', 'clarice']
>>> boys = ['charis', 'arnold', 'bob']
>>> [b + '+' + g for b in boys for g in girls if b[0] == g[0]]
['charis+clarice', 'arnold+alice', 'bob+bernice']
更优方案:
>>> girls = ['alice', 'bernice', 'clarice']
>>> boys = ['chris', 'arnold', 'bob']
>>> letterGirls = {}
>>> for girl in girls:
... letterGirls.setdefault(girl[0], []).append(girl)
...
>>> print [b + '+' + g for b in boys for g in letterGirls[b[0]]]
['chris+clarice', 'arnold+alice', 'bob+bernice']
>>>
三人行
走马观花的看下另外 3 个语句:pass,del 和 exec
什么都没发生
有些时候,程序什么事情都不用做.它可以在代码中做占位符使用.
if name == 'Ralph':
pass
使用 del 删除
>>> scoundrel = {'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
>>> robin = scoundrel
>>> scoundrel
{'last name': 'of Locksley', 'first name': 'Robin', 'age': 42}
>>> robin
{'last name': 'of Locksley', 'first name': 'Robin', 'age': 42}
>>> scoundrel = None
>>> robin
{'last name': 'of Locksley', 'first name': 'Robin', 'age': 42}
>>> robin = None
当 scoundrel 和 robin 都设为 None 时,字典就'漂'在内存里面了,不能获取和使用它,所以 Python 解释器使用垃圾收集删除了那个字典.
另一种方法是使用 del 语句,它不仅会移除一个对象的引用,也会移除那个名字本身.
>>> x = 1
>>> del x
>>> x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> x = ["hello", "world"]
>>> y = x
>>> y[1] = "python"
>>> x
['hello', 'python']
>>>
>>> del x
>>> y
['hello', 'python']
>>> x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>>
del 删除的只是名称,而不是列表本身(值).事实上,在 Python 中是没有办法删除值的.
使用 exec 和 eval 执行和求值字符串
执行存储在字符串中的 Python 代码
exec
执行一个字符串语句:
>>> exec "print 'hello world'"
hello world
最好给它提供一个命名空间(字典):
>>> from math import sqrt
>>> exec 'sqrt = 1'
>>> sqrt(4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
>>> sqrt
1
可以通过增加 in
>>> from math import sqrt
>>> scope = {}
>>> exec 'sqrt = 1' in scope
>>> sqrt(4)
2.0
>>> sqrt
<built-in function sqrt>
>>> scope['sqrt']
1
注意:如果要将 scope 打印出来的话,会单刀其中包含很多东西,因为内建的 builtins 字典自动包含所有的内建函数和值:
>>> len(scope)
2
>>> scope.keys()
['__builtins__', 'sqrt']
eval
用于"求值",是类似于 exec 的内建函数.exec 语句会执行一系列 Python 语句,而 eval 会计算 Python 表达式(以字符串的形式书写),并返回结果值,(exec 语句并不返回任何对象,因为它本身就是语句).
>>> eval(raw_input("Enter an arithmetic expression: "))
Enter an arithmetic expression: 2 * 3 + 6
12
注意:表达式 eval(raw_input(…)) 事实上等同于 input(…).在 Python 3.0 中,raw_input 被重命名为 input.
可以给 eval 提供两个命名空间,一个全局的一个局部的.全局的必须是字典,局部的可以是任何形式的映射.
给 exec 或者 eval 语句提供命名空间时,还可以在真正使用命名空间前放置一些值进去:
>>> scope = {}
>>> scope['x'] = 2
>>> scope['y'] = 3
>>> eval('x * y', scope)
6
同理,exec 或者 eval 调用的作用域也能在另外一个上面使用:
>>> scope = {}
>>> exec 'x = 2' in scope
>>> eval('x * x', scope)
4