4.更多的流程控制工具
除去已经介绍过的while语句外,python也有其他语言中另外的分支控制语句。
4.1 if语句
或许被人们所熟知的语句中,if是最令人印象深刻的,例如:
>>> x = int(input("Please enter an integer: "))
Please enter an integer: 42
>>> if x < 0:
... x = 0
... print('Negative changed to zero')
... elif x == 0:
... print('Zero')
... elif x == 1:
... print('Single')
... else:
... print('More')
...
More
elif在if语句中可以有很多,也可以不用。else在if分支语句中是可选的。关键词elif是 else if的缩写,可以有效的避免过多的缩进,if..elif...else可以替换其他语言中的switch和case语句。
4.2 for语句
在python中,for语句可能和你之前了解过的C和P语言中的有点不一样。而不是迭代一个算术的运算(在P语言中),也不是让用户去定义迭代的步长和中止。python的迭代可以是任何有顺序的类型,(列表或者字符串)。例如:
>>> # Measure some strings:
... words = ['cat', 'window', 'defenestrate']
>>> for w in words:
... print(w, len(w))
...
cat 3
window 6
defenestrate 12
如果在迭代中你想修改序列,推荐是你先将这个进行拷贝。通过迭代一个序列其实并没有将它拷贝了一遍。通过切片就可是使这个实现起来更加简单:
>>> for w in words[:]: # Loop over a slice copy of the entire list.
... if len(w) > 6:
... words.insert(0, w)
...
>>> words
['defenestrate', 'cat', 'window', 'defenestrate']
在for w in words:中这个语句会尝试去创建一个无限的列表,插入defenestrate。
4.3 函数range()
如果你想迭代一个数字序列,内建函数range()可以做到这个。
>>> for i in range(5):
... print(i)
...
0
1
2
3
4
最后一个数字不会被生成,range(10)会产生10个数字,只会生成符合规定的10个数字,也可以修改开始的数字,也可以设置为特定的增量range(start,end,step):
range(5, 10)
5, 6, 7, 8, 9
range(0, 10, 3)
0, 3, 6, 9
range(-10, -100, -30)
-10, -40, -70
得到一个序列的索引和值,你可以将range()和len()函数结合使用:
>>> a = ['Mary', 'had', 'a', 'little', 'lamb']
>>> for i in range(len(a)):
... print(i, a[i])
...
0 Mary
1 had
2 a
3 little
4 lamb
但是在很多的时候,使用enumerate()函数比较方便
一件奇怪的事,如果你只打印range()
>>> print(range(10))
range(0, 10)
在很多时候通过使用range()函数返回的对象好像是一个列表。但是事实并不是这样的。其实是一个连续可以迭代的对象。当你一个一个的去迭代它。就像它不是一个列表一样。然而我们这样做的目的就是可以节省内存。
我们把这样的对象就叫做可迭代对象。像一个函数或者一个某种特定的结构一样。我们可以一直去通过连续的条目进行获取。直到它里边没有值了。我们之前叫这样的语句为迭代器。另一种方式可以使用list()函数。来通过可迭代对象来常见一个列表。
>>> list(range(5))
[0, 1, 2, 3, 4]
4.4 break语句和continue语句,在循环中的else字句。
break语句,像C一样,结束for和while最里边的循环。
循环语句一般都有else字句,当条件编程False或者当循环的列表到达末端时,下面就用一个找素数的示例来证明:
>>> for n in range(2, 10):
... for x in range(2, n):
... if n % x == 0:
... print(n, 'equals', x, '*', n//x)
... break
... else:
... # loop fell through without finding a factor
... print(n, 'is a prime number')
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3
在这里的else子句是属于for循环的,而不是if语句。
当我们使用循环的时候,else子句并不完全在if语句中出现,通常也会出现在try语句中,在try语句中,如果没有捕捉到错误的时候,就会执行else语句,在循环中的else子句,当没有break发生的时候。就会执行。
continue语句,借鉴的是C中的语法,continue是继续执行循环的下一步。
>>> for num in range(2, 10):
... if num % 2 == 0:
... print("Found an even number", num)
... continue
... print("Found a number", num)
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9
4.5 pass语句
pass语句的作用就是什么都不做,通常被用作自动执行,并且程序没有什么变化的时候。例如:
>>> while True:
... pass # Busy-wait for keyboard interrupt (Ctrl+C)
...
也可以创建一个最小的类:
>>> class MyEmptyClass:
... pass
...
另外一种用到pass的地方就是函数中的占位,当你在思考你要编写的代码的时候,可以用pass去代替其他语句。当执行到这的时候就会被忽略。
>>> def initlog(*args):
... pass # Remember to implement this!
...
4.6 自定义函数
我们可以自己写一个自定义的生成斐波那契数列边界的函数:
def fib(n): # write Fibonacci series up to n
"""Print a Fibonacci series up to n."""
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()
# Now call the function we just defined:
fib(2000)
关键词def 介绍了这个函数的定义,他的后边必须跟的是函数的名字,在名字后面加上括号,括号里边接受的事参数,后面的语句形成了函数体,而且必须是在新的一行中,开头必须缩进。
在函数中,一般情况下,函数的第一段话为文档字符串。就是用来说明这个函数的作用,以及描述。是帮助使用函数的人可以自动的获取函数的信息。在代码中使用文档是一个很好的习惯,你可以在你的代码中试试.
从其他语言的角度来讲,斐波那契数列是一个程序,而不是一个函数,因为他没有返回值,实际上,函数没有返回值或者没有返回的语句,也可以说他的返回值是None,如果只有一个值得话,None通常是被解释器禁止的,你可以用print()函数来检查一下。
>>> fib(0)
>>> print(fib(0))
None
写一个有返回值的斐波那契数列函数其实真的,用它来代替print函数。
>>> def fib2(n): # return Fibonacci series up to n
... """Return a list containing the Fibonacci series up to n."""
... result = []
... a, b = 0, 1
... while a < n:
... result.append(a) # see below
... a, b = b, a+b
... return result
...
>>> f100 = fib2(100) # call it
>>> f100 # write the result
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
这个示例,展示了朋友天虹的一些新的特性。
- return 可以返回函数的一个值,当没有返回值的时候可以返回一个None,在调用函数实现功能之后可以返回一个None。
- 语句result.append(a)调用list对象结果的方法。方法是一个“属于”对象的函数,名为obj.methodname,其中obj是某个对象(可能是表达式),methodname是由对象类型定义的方法的名称。不同类型定义不同的方法。不同类型的方法可以具有相同的名称而不会引起歧义。(可以使用类定义自己的对象类型和方法,请参阅类)示例中显示的方法append()是为列表对象定义的;它在列表的末尾添加了一个新元素。在此示例中,它等效于result = result + [a],但效率更高。
4.7详解自定义函数
定义的函数也可以接受参数,有三种形式,并且可以结合使用。
4.7.1默认参数值
最方便的是定义一到多个特定的参数值,定义的这个函数也可以被多次调用。
def ask_ok(prompt, retries=4, reminder='Please try again!'):
while True:
ok = input(prompt)
if ok in ('y', 'ye', 'yes'):
return True
if ok in ('n', 'no', 'nop', 'nope'):
return False
retries = retries - 1
if retries < 0:
raise ValueError('invalid user response')
print(reminder)
这个函数有三种调用方式:
- 传递一个参数ask_ok(‘Do you really want to quit?’)
- 传递两个可选参数ask_ok(‘OK to overwrite the file?’,2)
- 全部传参ask_ok(‘OK to overwrite the file?’,2,‘come on,only yes or no!’)
这个示例,介绍了关键字传参,不管是一个参数,还是多个参数都可以。
The default values are evaluated at the point of function definition in the defining scope, so that
i = 5
def f(arg=i):
print(arg)
i = 6
f()
将会输出5
重要提示: 这个默认的值被评估只有一次,这会产生不同的当默认的是一个可变的对象。就像一个列表、字典、以及一个实体的类。 例如, 以下函数累积在后续调用中传递给它的参数:
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
输出的结果是:
[1]
[1, 2]
[1, 2, 3]
如果你不想让默认值被调用,你可以这样写:
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
4.7.2 关键字参数
函数也可以通过关键字参数被调用。
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")
也可以通过一个参数(voltage)或者三个可选参数(state,action,type)这个函数可以被一下任意一种方式调用。
parrot(1000) # 1 positional argument
parrot(voltage=1000) # 1 keyword argument
parrot(voltage=1000000, action='VOOOOOM') # 2 keyword arguments
parrot(action='VOOOOOM', voltage=1000000) # 2 keyword arguments
parrot('a million', 'bereft of life', 'jump') # 3 positional arguments
parrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword
但是这样的调用确实规则不允许的。
parrot() # required argument missing
parrot(voltage=5.0, 'dead') # non-keyword argument after a keyword argument
parrot(110, voltage=220) # duplicate value for the same argument
parrot(actor='John Cleese') # unknown keyword argument
函数在调用的时候,关键词必须遵守位置传参调用,即所有传递的参数必须有被接受的位置参数。
>>> def function(a):
... pass
...
>>> function(0, a=0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: function() got multiple values for keyword argument 'a'
当正式的参数的为**name的时候,他表示着接受字典传参,当接受参数的事*name的时候,表示接受元祖传参,但是有个条件是:(*name必须在**name之前):
def cheeseshop(kind, *arguments, **keywords):
print("-- Do you have any", kind, "?")
print("-- I'm sorry, we're all out of", kind)
for arg in arguments:
print(arg)
print("-" * 40)
for kw in keywords:
print(kw, ":", keywords[kw])
调用的时候可以是这样:
cheeseshop("Limburger", "It's very runny, sir.",
"It's really very, VERY runny, sir.",
shopkeeper="Michael Palin",
client="John Cleese",
sketch="Cheese Shop Sketch")
输出是这样的:
-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
shopkeeper : Michael Palin
client : John Cleese
sketch : Cheese Shop Sketch
请注意,打印关键字参数的顺序保证与函数调用中提供它们的顺序相匹配。
4.7.3 任意的列表传参
最后, 最不常用的选项是指定可以使用任意数量的参数调用函数。 这些参数将被包含在一个元组中 (可以在元祖和序列中查看). 在这些任意的变量传递中, 没有参数或者多个参数都有可能发生.
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
通常的, 这些可变参数将在形式参数列表中排在最后, 因为它们传递给函数的所有剩余输入参数。任何正式参数通过*args等关键字进行传参。这就意味着只能够通过关键字传参,而不是位置传参。
>>> def concat(*args, sep="/"):
... return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'
4.7.4 参数拆解
当参数已经在列表或元组中但需要为需要单独位置参数的函数调用解包时,会发生相反的情况。例如,内置的range()函数需要单独的start和stop参数。如果它们不能单独使用,请使用* -operator编写函数调用以从列表或元组中解压缩参数:
>>> list(range(3, 6)) # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> list(range(*args)) # call with arguments unpacked from a list
[3, 4, 5]
在这样的样式中,字典可以被拆开成关键词,通过**-opreater
>>> def parrot(voltage, state='a stiff', action='voom'):
... print("-- This parrot wouldn't", action, end=' ')
... print("if you put", voltage, "volts through it.", end=' ')
... print("E's", state, "!")
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
4.7.5 匿名函数 lambda表达式
匿名函数可以通过关键词lambda来创建,这个函数一共需要两个参数,语句为:lambda a,b : a+b。lambda函数可以用在任何被调用的地方。这是一个自动的单独的语句。在语义上,lambda表达式就像真正的函数的语法糖一样,就像自定义函数的内嵌函数,lambda函数有自己的独立的命名空间。
>>> def make_incrementor(n):
... return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43
上面的示例是用lambda函数来返回一个函数,另一种用法可以在一个语句中使用lambda函数:
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
4.7.6文档字符串
这里有几点是关于文档字符串的内容和格式的介绍。
第一行的文档字符串一般都比较短,简明的总结了这个函数的用途,在这里不用去介绍函数对象的类型和名字,以及其他的信息,这一行的首字母应该大写,最后一句号结尾。
如果要写多行的文档字符串,第二行应该为空,在视觉上看起来和下面的介绍的内容应该有间隔,后边的文档字符串如果多的话可以尽可能用多几行的语句去描述。这样比较简明扼要 。
Python解析器不会解析Python中的多行字符串文字,因此,如果需要,处理文档的工具必须删除缩进。这是使用以下约定完成的. 字符串第一行之后的第一个非空行确定整个文档字符串的缩进量.然后从字符串的所有行的开头剥离与该缩进“等效”的空格。
这儿是多行文档字符串的示例:
>>> def my_function():
... """Do nothing, but document it.
...
... No, really, it doesn't do anything.
... """
... pass
...
>>> print(my_function.__doc__)
Do nothing, but document it.
No, really, it doesn't do anything.
4.7.7 函数的注释
函数的注释是完全可以选择一个注释信息。主要是表现在用户自己定义的函数。
>>> def f(ham: str, eggs: str = 'eggs') -> str:
... print("Annotations:", f.__annotations__)
... print("Arguments:", ham, eggs)
... return ham + ' and ' + eggs
...
>>> f('spam')
Annotations: {'ham': <class 'str'>, 'return': <class 'str'>, 'eggs': <class 'str'>}
Arguments: spam eggs
'spam and eggs'
4.8 编码风格
现在,你可以写出尽可能长,更加复杂的代码,是时候告诉你关于编码的风格了。很多种语言都有自己的编码风格。有的语言就比其他的语言的可读性好,让别人比较容易的读你的代码总是好的。选一个好的编码风格可以惊人的提高编码效率。
对于python而言,PEP 8 编码风格出现,有很多的项目开始遵循它,它提升了代码的可读性和人们读起来之后比较舒服。每一个python的开发者都应该去了解他,这里有几点比较重要的给你列出来了:
- 使用4个空格,而不是table键
- 一行最多些79个字符
- 用空行去分隔类和类方法
- 尽量在一行中,给代码做注释
- 使用文档字符串
- 在运算符的前后和逗号的后边使用空格。
- 在命名上要有规则,在使用类的时候用驼峰标记法,在类内的函数或者方法的是偶使用小写字母加下划线的方式。在类内要使用self参数。
- 不要使用小众的编码方式,因为python遵循的国际编码规则,UTF-8和ASCII在工作中经常会用到。
- 另外,不用使用非ASCII编码的符。
网友评论