条件、循环、其它语句
print和import
随着更加深入地学习python,可能会出现这种感觉:有些自以为已经掌握的知识点,还隐藏着一些让人惊讶的特性。
使用逗号输出
打印多个表达式,只要将这些表达式用逗号隔开即可:
>>> print "age:",28
age: 28
参数之间都插入了一个空格符。
如果在结尾加上逗号,那么接下来的语句会与前一条语句在同一行打印:
print "Hello,",
print "World!"
结果:
Hello, World!
把某件事作为另一件事导入
从模块导入函数的时候,可以使用:
import somemodule
或
from somemodule import somefunction
或
from sommodule 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
赋值魔法
序列解包
多个赋值操作可以同时进行:
>>> x,y,z=1,23,456
>>> print x,y,z
1 23 456
用它交换两个(或多个)变量:
>>> x,y=y,x
>>> print x,y
23 1
>>> x,y,z=y,z,x
>>> print x,y,z
1 456 23
事实上,这里所做的事情叫做序列解包或可迭代解包——将多个值的序列解开,然后放到变量的序列中。
>>> values=1,2,3
>>> values
(1, 2, 3)
>>> x,y,z=values
>>> print x,y,z
1 2 3
当函数或者方法返回元组(或者其他序列或可迭代对象)时,这个特性尤其有用。假设需要获取(和删除)字典中的任意的键-值对,可以使用popitem方法,这个方法将键值对作为元组返回,那么这个元组可以直接赋值到两个变量中:
>>> people={'name':'signjing','age':'28','weight':'72'}
>>> x,y=people.popitem()
>>> print x,y
age 28
>>> x,y=people.popitem()
>>> print x,y
name signjing
>>> x,y=people.popitem()
>>> print x,y
weight 72
它允许函数返回一个以上的值并且打包成元组,然后通过一个赋值语句很容易进行访问。所解包的序列中的元素数量必须和放置在赋值符号=左边的变量数量完全一致,否则python会在赋值时引发异常:
>>> x,y=1,3,4
Traceback (most recent call last):
File "<pyshell#27>", line 1, in <module>
x,y=1,3,4
ValueError: too many values to unpack
>>> x,y,z=2,9
Traceback (most recent call last):
File "<pyshell#28>", line 1, in <module>
x,y,z=2,9
ValueError: need more than 2 values to unpack
链式赋值
链式赋值是将同一个值赋给多个变量的捷径。
x=y=somefunction()
和下面语句的效果是一样的:
y=somefunction()
x=y
注意:上面的语句和下面的语句不一定等价:
x=somefunction()
y=somefunction()
增量赋值
将表达式运算符放置在赋值运算符=的左边,这种写法叫做增量赋值,对于*、/、%等标准运算符都适用:
>>> x=5
>>> x+=3
>>> x*=2
>>> x/=4
>>> x
4
对于其他数据类型也适用(只要二元运算符本身适用于这些数据类型即可):
>>> s='too'
>>> s+='many'
>>> s*=2
>>> s
'toomanytoomany'
语句块
语句块是在条件为真(条件语句)时执行或执行多次的一组语句。在代码前放置空行来缩进语句即可创建语句块。
块中的每行都需要缩进相同的量。
很多语言使用特殊单词或者字符(比如begin或{)来表示一个语句块的开始,用另外的单词或字符(如end或})表示语句块的结束。
在python中,冒号(:)用来标识语句块的开始,块中的每一个语句都是缩进的(缩进量相同)。当回退到和已经闭合的块一样的缩进量时,就表示当前块已经结束了。
条件和条件语句
布尔变量
下面的值在作为布尔表达式时候,会被解释看做假(False):
False None 0 “” () [] {}
换句话说,也就是标准值False和None、所有类型的数字0(包括浮点数、长整型和其它类型)、空序列(比如空字符串、元组和列表)以及空的字典都为假。其它的一切都解释为真,包括特殊值True。
标准的真值为True和False。事实上,True和False只不过是1和0的“华丽”说法而已。
>>> True
True
>>> False
False
>>> True==0
False
>>> True==1
True
>>> False==0
True
>>> False==1
False
>>> False+True+23
24
布尔值True和False属于布尔类型,bool函数可以用来(和list、str以及tuple一样)转换其它值。
>>> bool('I think ,therefore I am')
True
>>> bool(42)
True
>>> bool(0)
False
>>> bool('')
False
>>> bool(False)
False
>>> bool(())
False
>>> bool({})
False
>>> bool([])
False
因为所有值都可以用作布尔值,所以几乎不需要对它们进行显式转换(可以说python会自动转换这些值)。
注意:尽管[]和””都是假植(也就是说bool([])==bool(“”)==False),它们本身并不相等。对于其他不同类型的假值对象也是如此(例如,()!=False)。
条件执行和if语句
if语句可以实现条件执行,如果条件判定为真,则后面的语句块执行,如果条件为假,语句块就不会被执行。
name=raw_input("Please input a name: ")
if name.endswith("Bob"):
print "Hello,Bob!"
如果用户输入了以Bob结尾的名字,则打印出问候语。
else语句
else子句(之所以叫子句,是因为它不是独立的语句,而只能作为if语句的一部分)可以增加一种选择。
name=raw_input("Please input a name: ")
if name.endswith("signjing"):
print "Hello,signjing!"
else:
print "Hello,someone!"
如果第1个语句块没有被执行,则转入第2个语句块。
elif语句
如果需要检查多个条件,就可以使用elif,它是“else if”的简写,也是if和else子句的联合使用——也就是具有条件的else子句。
num=input("Please input a num: ")
if num>0:
print "num > 0"
elif num<0:
print "num < 0"
else:
print "num = 0"
嵌套代码块
if语句里可以嵌套使用if语句:
num = input("Please input a num: ")
if num > 0:
if num >5:
print "num > 5"
elif num > 3:
print "num >3 and num <= 5"
else:
print "num <= 3 and num > 0"
elif num < 0:
if num < -10:
print "num < -10"
elif num < -4:
print "num < -4 and num >= -10"
else:
print "num >= -4 and num < 0"
else:
print "num = 0"
更复杂的条件
比较运算符
表达式 描述
x==y x等于y
x<y x小于y
x>y x大于y
x>=y x大于等于y
x<=y x小于等于y
x!=y x不等于y
x is y x和y是同一个对象
x is not y x和y是不同的对象
x in y x是y容器的成员
x not in y x不是y容器的成员
比较不兼容类型
理论上,对于相对大小的任意两个对象x和y都是可以使用比较运算符比较的,并且都会得到一个布尔值结果。但是只有在x和y是相同或者近似类型的对象时,比较才有意义。
在python中比较运算和赋值运算一样是可以连接的——几个运算符可以连在一起使用:
>>> a=5
>>> 0<a<8
True
>>> 0<a<3
False
>>> 0<a>3
True
相等运算符
想要知道两个东西是否相等,应该使用相等运算符,即两个等号==:
>>> "abc"=="abc"
True
>>> "abc"=="Abc"
False
单个相等运算符是赋值运算符,是用来改变值的,不能用来比较。
is:同一性运算符
这个运算符比较有趣,它看起来和==一样,但事实上却不同:
>>> x=y=[1,2,3]
>>> z=[1,2,3]
>>> x==y
True
>>> x is y
True
>>> x == z
True
>>> x is z
False
x和z相等却不等同,因为is运算符是判定同一性而不是相等性。
x和y被绑定到同一个列表上,而变量z被绑定在另外一个具有相同数值和顺序的列表上。它们的值可能相等,但却不是一个对象。
in:成员资格运算符
之前说明过了,此处略;
字符串和序列比较
字符串可以按照字母顺序排列进行比较。
>>> "a" < "b"
True
>>> "abc" < "ab"
False
注意:实际的顺序可能会因为使用不同的locale而和上面的例子有所不同。
其他的序列也可以以相同的方式进行比较,不过比较的不是字符而是元素的其它类型:
>>> [1,2,4]>[1,2,3]
True
>>> [1,2,[2,'a']]<[1,2,[3,4]]
True
布尔运算符
and运算符,连接两个布尔值,且在两者都为真时返回真,否则返回假。
or运算符,连接两个布尔值,且在两者都为假时返回假,否则返回真。
not运算符,连接一个布尔值,当连接的布尔值为真时返回假,为假时返回真。
短路逻辑
x and y需要两个变量都为真时才为真,如果x为假,则x and y立即返回false,而不需计算y的值。同理,x or y只要有一个为真则为真,如果x为真,则x or y直接返回True,而不需计算y的值。这样,避免了无用地执行代码,可以作为一种技巧使用。
断言
if语句有个非常有用的近亲,其工作方式多少有点像下面这样(伪代码):
if not condition:
crash program
这样做是因为与其让程序在晚些时候崩溃,不如在错误条件出现时直接让它崩溃。一般来说,你可以要求某些条件必须为真。语句中使用的关键字为assert。
>>> age=10
>>> assert 0<age<102
>>> age=-2
>>> assert 0<age<102
Traceback (most recent call last):
File "<pyshell#39>", line 1, in <module>
assert 0<age<102
AssertionError
如果需要确保程序中的某个条件一定为真才能让程序正常工作的话,assert语句就有用了,可以在程序中置入检查点:
条件后可以添加字符串(用逗号把条件和字符串隔开),用来解释断言:
>>> assert 0<age<102, 'make age right'
Traceback (most recent call last):
File "<pyshell#42>", line 1, in <module>
assert 0<age<102, 'make age right'
AssertionError: make age right
循环
while循环
用来在任何条件为真的情况下重复执行一个代码块。
i=5
while i>0:
print i
i-=1
结果:
5
4
3
2
1
for循环
list1=list('signjing')
for l in list1:
print l
结果:
s
i
g
n
j
i
n
g
numbers=[1,2,3,4]
for n in numbers:
print n
结果:
1
2
3
4
迭代(循环的另一种说法)某个范围的数字是很常见的,有个内建的函数可供使用:
for n in range(1,5):
print n
结果:
1
2
3
4
range函数的工作方式类似于分片,包含下限,不包含上限,如果希望下限为0,则可以只提供上限。
提示:
如果能使用for循环,就尽量不使用while循环。
循环遍历字典元素
一个简单的for语句就能循环字典的所有键,就像处理序列一样:
d={'x':1,'y':2,'z':3}
for key in d:
print key,d[key]
结果:
y 2
x 1
z 3
for循环的一大好处就是可以在循环中使用序列解包:
d={'x':1,'y':2,'z':3}
for key,value in d.items():
print key,value
结果:
y 2
x 1
z 3
一些迭代工具
在python中迭代序列(或其它可迭代对象)时,一些函数非常有用。有些函数位于itertools模块,还有些python的内建函数也十分有用。
并行迭代
程序可以同时迭代两个序列:
names=['anne','beth','damon']
ages=[13,19,21]
for i in range(len(names)):
print names[i],'is',ages[i],'years old.'
结果:
anne is 13 years old.
beth is 19 years old.
damon is 21 years old.
内建的zip函数可以用来进行并行迭代,把两个序列“压缩”在一起,然后返回一个元组的列表:
names=['anne','beth','damon']
ages=[13,19,21]
for name,age in zip(names,ages):
print name,"is",age,"years old."
结果:
anne is 13 years old.
beth is 19 years old.
damon is 21 years old.
zip函数可以作用于任意多的序列。关于它很重要的一点是zip可以应付不等长的序列:当最短的序列“用完”的时候就会停止:
>>> zip(range(5),xrange(100))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
>>> zip(range(5),xrange(2))
[(0, 0), (1, 1)]
编号迭代
有些时候想要迭代序列中的对象,同时还要获取当前对象的索引。
暂略;
翻转和排序迭代
reversed和sorted,同列表的reverse和sort方法类似,但作用于任何序列或可迭代对象上,不是原地修改对象,而是返回翻转或排序后的版本:
>>> sorted([4,3,6,8,3])
[3, 3, 4, 6, 8]
>>> sorted('hello,world!')
['!', ',', 'd', 'e', 'h', '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'
跳出循环
break
结束循环可以使用break语句。
for i in range(5):
print i
if i > 2:
break
print "--"
结果:
0
--
1
--
2
--
3
continue
continue语句用的要少很多,它让当前的迭代结束,“跳”到下一轮循环的开始。基本意思是:跳过剩余的循环体,但不结束循环。当循环体很大而且很复杂时,会很有用。
for i in range(5):
print i
if i > 2:
continue
print "--"
结果:
0
--
1
--
2
--
3
4
while True/break习语
while True实现了一个永远不会自己停止的循环,但在循环体内部的if语句中加入条件,在条件满足时调用break语句。这样,就可以在循环内部任何地方而不是只在开头终止循环。
尽管应该小心在代码中频繁使用break语句,但这个特殊的技术用得非常普遍。
循环中的else子句
当在循环体内部使用break语句时,通常是因为找到了某物或某事发生了,在跳出时做一些事情是简单的,但有些时候想要在没有跳出之前做些事情。可以在循环中增加一个else语句——仅在没有调用break时执行。
for i in range(5):
print i
if i > 6:
break
print "--"
else:
print "I'm here"
结果:
0
--
1
--
2
--
3
--
4
--
I'm here
for和while循环中都可以使用continue、break语句和else子句。
列表推导式——轻量级循环
列表推导式是利用其它列表创建新列表的一种方法。工作方式类似于for循环。
>>> [x*x for x in range(1,6)]
[1, 4, 9, 16, 25]
可以增加判断条件:
>>> [x*x for x in range(1,6) if x%2==0 ]
[4, 16]
可以增加更多的for语句:
>>> [x*y for x in range(1,3) for y in (2,5)]
[2, 5, 4, 10]
也可以和if语句联合使用:
>>> [x*y for x in range(1,11) for y in range(1,9) if x%3==0 if y%4==0 ]
[12, 24, 24, 48, 36, 72]
注意:for循环部分和if语句部分之间都没有标点符号。
三人行
什么都没发生
有些时候,程序什么事情都不用做:
>>> pass
>>>
name=raw_input("Please input a name: ")
if name == 'signjing':
print "I'm signjing"
elif name == 'sj':
pass
else:
print "unknown"
执行:
Please input a name: signjing
结果:
I'm signjing
执行:
Please input a name: sj
如果将pass行注释:
if name == 'signjing':
print "I'm signjing"
elif name == 'sj':
#pass
else:
print "unknown"
执行报错:
File "pass.py", line 6
else:
^
IndentationError: expected an indented block
使用del删除
一般来说,python解释器(以其无穷的智慧)自动删除那些不再使用的对象。另一种方法是使用del语句,它不仅会移除一个对象的引用,也会移除那个名字本身。
>>> x=['hello','world']
>>> y=x
>>> y[1]='python'
>>> x
['hello', 'python']
>>> del x
>>> y
['hello', 'python']
事实上,python中是没有办法删除值的,因为值不再使用时,python解释器会负责内存的回收。
使用exec和eval执行和求值字符串
有些时候会动态地创造python代码,然后将其作为语句执行或作为表达式计算,这近似于“黑暗魔法”——在此之前,一定要慎之又慎,仔细考虑:
exec
执行一个字符串的语句是exec:
>>> exec "print 'hello'"
hello
但使用简单形式的exec语句绝不是好事。很多时候可以给它提供命名空间——可以放变量的地方。
>>> from math import sqrt
>>> exec "sqrt = 1"
>>> sqrt(4)
Traceback (most recent call last):
File "<pyshell#70>", line 1, in <module>
sqrt(4)
TypeError: 'int' object is not callable
可以通过in <scope>来实现,其中<scope>就是起到放置代码字符串命名空间作用的字典。
>>> from math import sqrt
>>> scope={}
>>> exec "sqrt = 1" in scope
>>> sqrt(4)
2.0
>>> scope['sqrt']
1
可见,潜在的破坏性代码并不会覆盖sqrt函数,原来的函数能正常工作,而通过exec赋值的变量sqrt只在它的作用域内有效。
注意,如果需要将scope打印出来的话,能看到其中包括很多东西,因为内建的__builtins__字典自动包含所有的内建函数和值:
>>> len(scope)
2
>>> scope.keys()
['__builtins__', 'sqrt']
eval
eval(用于“求值”)是类似于exec的内建函数。
exec语句会执行一系列的python语句,而eval会计算python表达式,并返回结果值。
exec语句并不返回任何对象,因为它本身就是语句。
>>> eval(raw_input("Enter an arithmetic expression: "))
Enter an arithmetic expression: 2*6+8
20