Python学习教程(五)——语句之条件与循环

来源:互联网 发布:access2000数据库修复 编辑:程序博客网 时间:2024/06/03 23:26

1. print 和 import的深入学习

  在深入学习之前,我们要先说明一下,在Python3.0中print不再是语句,而是变为了函数,功能基本不变。另外对于很多应用程序来说,使用logging模块来记录日志比print语句更为合适,关于logging的使用方法,我们会在后面学习到。

1.1. 使用逗号输出

>>> print 'Age:'+ 28Traceback (most recent call last):  File "<stdin>", line 1, in <module>TypeError: cannot concatenate 'str' and 'int' objects>>>>>> print 'Age:',28Age: 28

  看一下上面的代码,当我们试图把字符串和数字拼接输出时,程序报错了,因为不能将字符串和数字相加。那么我们可以使用下面的逗号的方式进行拼接输出,并且每个参数之间多了一个空格符。
  print语句的参数并不能像我们预期的那样构成一个元组。

>>> 1,2,3(1, 2, 3)>>> print 1,2,31 2 3>>> print (1,2,3)(1, 2, 3)

我们看到当用print和逗号打印时,并没有像直接输入1,2,3那样输出一个元组,而是拼接为了一个字符串。

  同样我们还可以使用变量的方式:

>>> username = 'yangliehui,'>>> age = 28>>> print username,ageyangliehui, 28

  如果我们是在脚本中而不是在交互式python会话中,还可以看到下面的用法:

print 'yangliehui,',print 28yangliehui, 28

如果在语句末尾加上逗号,那么接下来的语句会与前面的语句打在一行。

1.2. 模块的导入

  从模块中导入函数的语句通常有以下几种写法:
  1. import somemodule
  或者
  2. from somemodule import somefunction
  或者
  3. from somemodule import somefunction, anotherfunction, yetanotherfunction
  或者
  4. from somemodule import *
  
  
  只有确定自己要从给定的模块中导入所有的方法时,才使用语句1和4。但是这导致一个问题,当我们用这种方式导入了两个不同的模块,而这两个不同的模块都有一个相同的方法名时,我们如何使用呢?假如module1模块中有一个open方法,module2模块中也有一个open方法,当我们在使用时应该:

module1.open(...)module2.open(...)

使用时用模块名来进行区分

  当然我们还有其他的方法,比如可以给整个模块加一个别名或给模块的方法加一个别名:

>>> import math as foobar>>> foobar.sqrt(4)2.0>>> #####################...>>> from math import sqrt as sq>>> sq(4)2.0

2. 赋值语句的使用技巧

2.1. 序列解包

  多个赋值操作可以同时进行:

>>> x,y,z = 1,2,3>>> 11>>> y2>>> z3

  还可交换两个变量,这一点很有用:

>>> x,y,z = 1,2,3>>> x,y = y,x>>> print x,y,z2 1 3

  事实上,上面做的事情叫做序列解包(sequence unpacking)递归解包,即将多个值得序列解开,然后放到变量的序列中,可以等价于下面的代码:

>>> values = 1,2,3>>> values(1, 2, 3)>>> x,y,z = values>>> print x,y,z1 2 3

在上面的例子中,不仅元组可以实现上面的功能,其他序列以及可迭代对象都可以。所解包的序列中的元素数量必须和放置在赋值符号=左边的变量数量完全一致,否则就会发生异常,请自行验证。

2.2. 链式赋值

  将同一个值赋给多个变量:
  x = y = somefunction()
  和下面的语句是等价的:
  y = somefunction()
  x = y

注意上面的语句并不一定等价于:
x = somefunction()
y = somefunction()

2.3. 增量赋值

  将表达式运算符放置在运算符=的左边的方式,叫做增量赋值,比如:

>>> x = 1>>> x += 1>>> x2

3. 语句块

3.1. 语句块的缩排

  语句块并非是一种语句,它是可以被执行的一组语句,在代码前放置空格来缩进语句即可创建语句块。

4个空格的缩进量,如果是tab键的话,需要设置为4个空格。

  在python中,冒号(:)用来标识语句块的开始,块中的每一个语句都是按相同的缩进量缩进的,当回退到和已经闭合的块一样的缩进量时,就表示当前块已经结束了。

4. 条件和条件语句

4.1. 布尔变量

  表示“真.假”的值成为布尔值。
  下面的值在作为布尔表达式时,都会被解释器看做假(false):
  False None 0 "" () [] {}
  除此之外其他的一切都被解释为真,包括True。
  其实True和False等价于1和0:

>>> True == 1True>>> False == 0True>>> 1 + True2>>> 1 + False1

  bool函数可以用来转换其他值为布尔类型的值:

>>> bool('yangliehui')True>>> bool(42)True>>> bool(0)False>>> bool("")False>>> bool([])False

虽然”“和[]都是返回的假值,但是它们本身并不相等。同样的,() != False。

4.2. 条件执行和if语句

name = raw_input("What's your name ?")if name.endswith('hui'):    print 'Hello',name

  上面的代码就是if语句,可以实现条件执行。

4.3. else子句

name = raw_input("What's your name ?")if name.endswith('hui'):    print 'Hello',nameelse :    print 'Hello !'

  如果if下面的语句块不满足条件,就会执行else里面的语句块。

4.4. elif子句

  用于检查多个条件时使用,elif是else if的缩写,它是具有条件的else子句,代码如下:

num = input('Enter a number')if num > 0:    print 'The number is positive'elif num < 0:    print 'The number is negative'else:    print 'The number is zero'

4.5. 代码块的嵌套

  以if条件语句为例,代码的嵌套指的是if里面还可以继续嵌套if语句,这里不再举代码示例,但是要注意虽然语法结构上允许代码块的嵌套,但是在实际开发过程中,类似条件语句的深层嵌套是不推荐使用的,一段代码如果超过了三层嵌套会导致代码阅读性较差。

4.6. 条件运算符

4.6.1. 比较运算符

  Python中常用的比较运算符总结在如下表中:

表达式 描述 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 不等于 y x is y x 和 y是同一个对象 x is not y x和y是不同的对象 x in y x是y容器(例如:序列)的成员 x not in y x不是y容器(例如:序列)的成员

  对于上面表中运算符不难理解,基本看到就能知道要表达什么意思,但要注意is的用法,is是对比是否是同一个对象的,它于==符号不同,比如:

>>> x = [1,2,3]>>> y = [1,2,3]>>> x == yTrue>>> x is yFalse

这是因为==符号是比较值的,显然x与y的值相等,返回True。而is对比的是对象,或者说对比的是物理地址。

  另外,比较符号两边的值尽量是兼容的类型,不兼容的类型比较毫无意义而且得到的结果也不可靠,比如:

>>> 'abc' < 3False

几个运算符可以连在一起使用,比如0<age<100

4.6.2. 序列的比较

  字符串可以按照字母顺序排列进行比较:

>>> "alpha" < "beta"True

注意:实际的顺序可能会因为使用不同的本地化设置(locale)而和上面的例子有所不同。

  其他的序列也可以用同样的方式进行比较,不过比较的不是字符而是其他类型的元素,比如:

>>> [1,2] < [2,1]True>>> [1,[1,4]] < [1,[1,5]]True

4.6.3. 布尔运算符

  布尔类型的运算符有三种:and、or、not,使用这三个运算符就可以随意结合布尔值,比如:

if ((cash > price) or customer_has_good_cerdit) and not out_of_stock :    give_goods()

短路逻辑和条件表达式
  什么是短路逻辑呢?我们以and运算符为例,and的运算符的特点是,必须所有的子条件都返回True时整个条件运算值才为True,比如:if condition1 and condition2时,要想执行if下的代码块,则要求condition1返回True**并且**condition2也返回True。那么短路逻辑就是,当condition1返回了False时,那么condition2就已经没有再运行的必要了,因为condition2不管返回True还是False都改变不了整个if后面的条件为False的最终结果。所以短路逻辑就是要避免这种不必要的操作。或者说任何的子条件组合起来的条件运算,一旦能明确整个条件的布尔值后就停止后面不必要的运算就称为短路逻辑。
  这种设置有什么用处呢?它避免了执行不必要的代码。另外还可以有下面的用法:

>>> name = raw_input('Please enter your name:') or '<unknown>'Please enter your name:>>> name'<unknown>'

当我没有输入任何字符的时候,就把or后面的默认值赋给了name变量。

4.6.4. 断言

  断言好似if语句的另一种写法,有点像下面的一段伪代码(伪代码就是表达意思,并不能执行):

if not condition :    crash grogram # 程序终止

上面的伪代码,if not就等价于断言(关键字:assert),翻译成日常用语就是:“我断定condition条件一定是成立,如果不成立就终止程序吧!”。我们来看一下真正的断言应该怎么写:

>>> age = 28>>> assert 0 < age < 100>>>>>> age = -1>>> assert 0 < age < 100Traceback (most recent call last):  File "<stdin>", line 1, in <module>AssertionError

上面的代码我们用了两次断言,第二次终止了程序,上面的代码很好理解,第一次断言既是:“程序断定age参数一定是在0到100区间的数字”,而age=28,说明程序断定的结果正确,所以程序继续往下走。第二次断言时age=-1,说明程序断定的结果不对,程序终止了。在写程序时可以灵活运用,比如可以在程序中设置一个检查点,与其让程序晚些报错,不如在检查点处直接让他报错终止。

>>> age = -1>>> assert 0 < age < 100, 'The age must be realistic'Traceback (most recent call last):  File "<stdin>", line 1, in <module>AssertionError: The age must be realistic

断言后面可以加上说明,来解释断言,如上面的代码。

5. 循环

5.1. while循环

  while循环语句的结构如下:

while condition :    # 可执行代码块

判断condition条件是否为True,如果为True则执行内部的可执行代码块,然后继续判断condition条件,如果为True继续执行内部的可执行代码块,直到condition条件为False为止。

x = 1while x < 100 :    print x    x += 1

上面的代码会从1一直打印到99。因为累加到100的时候while后面的条件不满足条件,终止了循环。

5.2. for循环

  在while循环中,我们知道它可以在任何条件为真的情况下重复执行一个代码块。但是当我们要为一个集合的每个元素都执行一个代码块,就用到了我们这里提到的for循环。

numbers = [0,1,2,3,4,5,6,7,8,9]for number in numbers:    print number

  因为循环某一范围的数字很常见,因此Python提供了一个内建函数来完成这项任务:

>>> range(0,10)[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

包含下限但是不包含上限

如果要循环[0,9]之间的数字:

for number in range(0,10):    print number

我们发现这比使用while循环要简洁


  xrange函数的循环行为类似于range函数,区别在于range函数一次创建一个序列,而xrange一次只创建一个数。当需要迭代一个巨大的序列时xrange会更高效。Python3.0中,range函数会被转换为xrange风格的函数。

5.3. 遍历字典元素

d = {"a":"1","b":"2","c":"3"}for key in d:    print key , "corresponds to" , d[key]

  for循环字典的一大好处是可以在循环中使用序列解包:

d = {"a":"1","b":"2","c":"3"}for key , value in d.items():    print  key , "corresponds to" , value

注意,循环时能保证字典中所有的项都能被循环处理,但是不能以何种顺序,因为字典元素的顺序通常是没有定义的。

5.3. 常用迭代工具

5.3.1. 并行迭代

  程序同时迭代两个序列,比如有下面两个列表,想要打印名字对应的年龄,可以想下面这样做:

names = ['yangliehui','zhaonan','zhangsan','lisi']ages = [28,30,12,38]for i in range(len(names)):    print names[i],'is',ages[i],'years old'

  上面是我们用for循环来实现的两个序列的同时遍历,而内建的zip函数也可以进行并行遍历,它使用更方便,可以把两个序列“压缩”在一起,然后返回一个元组的列表:

>>> names = ['yangliehui','zhaonan','zhangsan','lisi']>>> ages = [28,30,12,38]>>> print zip(names,ages)[('yangliehui', 28), ('zhaonan', 30), ('zhangsan', 12), ('lisi', 38)]

  上面的代码返回了一个元组列表,下面可以在循环中解包元组:

names = ['yangliehui','zhaonan','zhangsan','lisi']ages = [28,30,12,38]for name,age in zip(names,ages):    print name , 'is' , age , 'years old.'

zip函数可以作用于任意多的序列,并且可以处理不等长的序列,当最短的序列“用完”的时候就会停止。

5.3.2. 按索引迭代

  有时候需要迭代访问序列中的对象,比如,迭代访问序列中的字符串,为了能获得索引-值对,我们可以使用Python中的内建函数enumerate函数:

for index,string in enumerate(strings):    if 'xxx' in string:        strings[index] = '[censored]'

这个函数可以在提供索引的地方迭代索引-值对。

5.3.3. 翻转和排序迭代

  让我们来看看另外的两个参数:reversedsorted。他们和列表的reverse和sort(和sorted使用同样的参数)方法类似,但这两个方法不是原地修改对象,而是返回一个副本。

>>> numbers = [4,5,2,1,0,9]>>> snumbers = sorted(numbers)>>> snumbers[0, 1, 2, 4, 5, 9]>>> numbers[4, 5, 2, 1, 0, 9]>>> list(reversed(numbers)) # 因为reversed返回的是一个迭代器,所以需要用到list[9, 0, 1, 2, 5, 4]

5.4. 跳出循环

  正常情况下,循环会一直执行到条件为False时,或者序列元素用完时。但是有时我们可能会提前中断一个循环,因此就需要控制跳出循环。

5.4.1 break

  假如我们要寻找100以内最大的平方数,那么程序可以从99往下迭代到1,当遍历求取的平方根为整数的时候,终止循环,就找到了这个最大平方数。

import mathfor n in range(99,0,-1):    root = math.sqrt(n)    if root == int(root):        print n        break

上面的代码中我们又用到了range函数,-1表示步长反向迭代,同样的迭代的结果包含第一个参数,但是不包含第二个参数。

5.4.2. continue

  和break不同的是,他表示跳出的当前的迭代,后面的迭代代码块不再执行,然后跳到下一轮的迭代的开始。所以continue并不是要中断循环,而是要中断本次正在执行的循环。比如:

for x in seq:    if condition1 :         continue    do_something()

  上面的代码当满足condition1条件时,则本次不会再执行do_something()方法,而是重新进行下一次循环。

5.4.3. while True/break

  假如我们要实现一个能不断输入字符串的程序,当不输入任何字符的时候终止,那么我们的程序应该这么写:

word = 'dummy'while word :    word = raw_input("Please enter a word :")    print "The Word was" + word

  但是,为了能够让程序第一次走进循环体内,我们需要给Word赋一个初始值,虽然这个值没有什么意义,我们把它称为“哑值(未使用的值)”。这样做显然不太完美,那我们应该怎么做呢?可以使用while True/break语句:

while True:    word = raw_input('Please enter a word :')    if not word:        break    print 'The word was ' + word

5.4.4. 循环中的else子句

  在循环体中,当设置了满足某一条件后就break时,我们希望后的代码能都知道在循环体内是否执行了break。用程序实现如下:

broke_out = Falsefor x in seq     do_something()    if condition(x):        broke_out = True        break    do_something_else()if not broke_out:    print "I didn't find it !"

  比上面更简单的方式使在循环中增加一个else子句,它仅在没有调用break时执行。

import mathfor n in range(99,81,-1):    root = math.sqrt(n)    if root == int(root):        print n        breakelse :     print "Didn't find it !"

6. 列表推导式——轻量级循环

  列表推导式是利用其它列表创建新列表的一种方法。它的工作方法类似于for循环:

>>> [x*x for x in range(10)][0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

  如果只想打印出那些能被3整除的的数呢?这个语句可以增加一个if部分添加到列表推导式中:

>>> [x*x for x in range(10) if x % 3 == 0][0, 9, 36, 81]

  也可以增加更多的for语句部分:

>>> [(x,y) for x in range(3) for y in range(3)][(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

  也可以和if子句联合使用,得到首字母相同的组合:

>>> ss = ['a','b','c']>>> ns = ['a1','b2','c3']>>> [s+'+'+n for s in ss for n in ns if s[0] == n[0]]['a+a1', 'b+b2', 'c+c3']

7. pass、del和exec语句的使用

7.1. pass语句

  当需要一行占位符的时候,就可以使用pass语句,它本身什么也不做。

>>> pass>>>

  看一下它的使用场景:

if name == 'Ralph Auldus Melish':    print 'Welcome!'elif name = 'Enid':elif name = 'Bill Gates':    print 'Access Denied'

  我们看到上面的代码,elif name = 'Enid'时没有做任何操作,但是在python中空代码块是非法的,因此我们可以在那个位置。

7.2. 使用del删除

  先来看一段代码:

>>> x = ["Hello","World"]>>> y = x>>> y[1] = "Python">>> x['Hello', 'Python']>>> del x>>> y['Hello', 'Python']>>> xTraceback (most recent call last):  File "<stdin>", line 1, in <module>NameError: name 'x' is not defined

在上面的代码中,我们先定义了一个x并赋了一个列表值,然后把x赋值给y,显然x和y指向了同一个地址,当把y[1]的值修改后,x[1]也会变化(因为不管是x还是y,都是一个引用变量,指向的是同一块物理地址)。但是当我们del了x后,我们会认为这时在打印y的时候会找不到值,但实际情况是我们看到了y依然正常,而x已经报错了。这说明del语句并没有删除到值,而仅仅是删除了引用(变量)。事实上,在Python中是没有办法删除值的,只要删除了引用,当没有任何一个引用在指向这个物理地址力的值的时候,Python解释器会负责自动清理这块内存,这就是Python的“垃圾回收”。

7.3. 使用exec和eval执行和求值字符串

  有时候我们可能需要动态的创造Python代码,然后将其作为语句执行或作为表达式计算,但这种方法我们要谨慎使用。

警告:当我们掌握了如何执行存储在字符串中的Python代码后,就会写出存在严重潜在安全漏洞的代码。如果程序员将用户提供的一段内容字符串作为代码执行,程序可能会失去对代码执行的控制,造成安全风险。

7.3.1. exec

>>> exec "print 'Hello World!'"Hello World!

  但是上面的代码绝对不是一段安全的代码,假如exec后面的字符串是由有户提交过来的,那么就有可能执行不该执行的语句,比如:

>>> 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后,sqrt就不再是函数了,所有在调用sqrt函数的时候报错了。所以为了安全起见,可以增加一个字典,起到命名空间的作用。

>>> from math import sqrt>>> scope = {}>>> exec 'sqrt = 1' in scope>>> sqrt(4)2.0

7.3.2. eval

  类似于exec的内建函数,exec语句会执行一系列Python语句,而eval会计算Python表达式(以字符串的形式书写),并且返回结果值。(exec语句并不返回任何对象,因为它本身就是语句)

>>> eval(raw_input("Enter :"))Enter :3+47

  和exec类似,它也可以使用命名空间,比如:

>>> scope = {}>>> scope['x'] = 2>>> scope['y'] = 3>>> eval('x*y',scope)6>>>>>> scope = {}>>> exec 'x = 2' in scope>>> eval('x*x',scope)4

实际上exec语句和eval并不常用,况且一旦用不好还会有安全隐患。

阅读全文
0 0
原创粉丝点击