除了剛才介紹的while語句之外,Python也從其他語言借鏡了其他流程控制語句,並做了相應改變。 Python中流程控制語句的詳細介紹
if
Statements或許最廣為人知的語句就是if
語句了。例如:
x = int(input("Please enter an integer: "))if x < 0: x = 0print('Negative changed to zero')elif x == 0:print('Zero')elif x == 1:print('Single')else:print('More')
可以存在零個或多個elif
子句,而且else
子句是可選的。關鍵字elif
是else if
的間歇形式,可以避免過度的縮排。 if ... elif ... elif ...
序列是其他語言中switch
或case
語句的替代品。
for
StatementsPython中的for
語句與C或Pacsal中的for
語句有微小差異。 Python沒有採用對等差數列數字迭代的方式(在Pascal中)實現循環,也沒有採用如C語言一樣賦予用於定義迭代步數和停止條件的方式實現循環,Python的for
語句在任意序列(列表或字串)的項目上迭代,以項目在序列中的出現順序迭代。例如(沒有別的意思):
# Measure some strings:words = ['cat', 'window', 'defenestrate']for w in words:print(w, len(w))
如果需要在迴圈內修改正在遍歷的序列(例如複製選取項目),建議先對序列複製。遍歷序列時並沒有隱式地建立備份。切片語句是的複製序列特別方便:
for w in words[:]: # Loop over a slice copy of the entire list.if len(w) > 6: words.insert(0, w) words
使用for w in words:O
,實例會嘗試建立一個無限列表,不斷地插入字串defenestrate
。
range()
Function如果確實需要對數字的序列做迭代,built-in函數range()
可以派上用場。這個函數產生算術數列:
>>> for i in range(5): ... print(i) ...01234
end
參數不會被包含到產生的序列中;range(10)
產生10個值,序列項的合法索引長度是10。可以讓range在另一個數字上開始,或指定一個不同的增量(甚至是負數;有時增量稱作步長'step'):
range(5, 10) 5 through 9range(0, 10, 3) 0, 3, 6, 9range(-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 Mary1 had2 a3 little4 lamb
然而,在大多數這樣的情況下,使用enumerate()
函數很方便,詳細參考Looping Techniques。
直接印出range()的結果會很奇怪:
>>> print(range(10))range(0, 10)
#在許多情形中range()
傳回的物件就像一個列表一樣,但是事實上它並不是。當迭代這個物件時,它會傳回目標序列中的連續項,但是為了節約空間,並沒有真的創建一個列表。
這種物件稱為iterable
(可迭代的),即,若一些函數或結構期待從某種東西中獲得連續項,直到結束,那麼iterable
物件可以滿足這種需求。 for
語句就是這樣的迭代程式。 list()
函數是另外一個,它使用可迭代物件建立清單:
>>> list(range(5)) [0, 1, 2, 3, 4]
接下來會介紹更多返回可迭代物件和使用可迭代對象作為參數的函數。
break
and continue
Statements, and else
Clauses on Loops像C語言一樣,break
語句跳出for
或while
的最內層迴圈。
循環語句可以有else
子句;當for
循環遍歷完畢循環序列或while
迴圈的迴圈條件變成 False
時,else
子句會執行。但是當迴圈被break
語句終止時,else
子句不會執行。以下尋找質數的實例可以證明這一點:
>>> 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 number3 is a prime number4 equals 2 * 25 is a prime number6 equals 2 * 37 is a prime number8 equals 2 * 49 equals 3 * 3
(程式碼是正確的。靠近看:else
子句屬於for
循環,而不是if
語句。
子句相似,而與if
語句的else
子句不同:當沒有例外發生時,try
語句的else
子句會執行,在迴圈中,沒有break
發生時,else
子句會執行。想要了解更多關於try
語句和異常的信息,請參閱 Handling Exceptions。 continue
語句,也是從C語言借鏡而來,用來直接跳到迴圈的下一個迭代:
>>> 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 2Found a number 3Found an even number 4Found a number 5Found an even number 6Found a number 7Found an even number 8Found a number 9
4.5 pass
Statements
pass
語句。例如:>>> while True: ... pass # Busy-wait for keyboard interrupt (Ctrl+C)...
<p><code>pass
语句可以用来创建最小类:>>> class MyEmptyClass: ... pass...
pass
被默默地忽视:>>> def initlog(*args): ... pass # Remember to implement this!...
以下是打印任意边界斐波那契额数列的函数:
>>> 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)0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
关键字def
引入函数定义,必须在后面跟上函数名字和形参列表。构成函数体的语句另起一行,必须缩进。
函数体的第一条语句可以选择性使用字符串作为函数的文档字符串,或者docstring
。(更多关于文档字符串参见 Documentation Strings) 有许多工具使用文档字符串自动生成在线或者打印文档,或者允许使用者交互地浏览代码;在代码中写文档字符串是一个好的实践,因此需要将其作为习惯。
函数的执行引入了函数局部变量使用的新符号表。更准确的说,所有在函数中赋值变量都将值存储在局部符号表中;变量引用首先在局部符号表中查找,然后是封闭函数的局部符号表,然后是全局符号表,最后是built-in符号表。因此,在函数中全局变量可以被引用,但是不能直接赋予新值(除非使用global
语句声明)。
但函数被调用时,实参也被引入被调用函数的局部符号表中。因此,参数按照按值调用的方式传递(这里的值总是对象引用,而不是对象的值。)[1] 当函数调用其他函数时,为调用会产生新的局部符号表。
函数定义在当前符号表中引入函数名。函数名引用的值拥有一个被解释器识别为“用户自定义函数”的类型。这个值也可以赋值给其他名字,然后使用这个名字来调用函数。这种方式是函数重命名机制:
>>> fib<function fib at 10042ed0>>>> f = fib>>> f(100)0 1 1 2 3 5 8 13 21 34 55 89
拥有其他语言经验的程序员,可能会认为fib
不是一个方法而是一个过程,因为它没有返回值。事实上,没有返回语句的函数确实返回了值,而且是相当烦人的值,这个值被称为None
(built-in名字)。如果None
值是唯一要写的值,那么写的时候通常会被解释器忽视。使用print()
可以看到打印的None
值:
>>> fib(0)>>> print(fib(0))None
想要定义返回斐波那契数列数字而不是打印数字的函数非常简单:
>>> 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]
这个例子也示范了Python的一些新特性:
return
语句从函数中返回一个值。不带任何表达式的return
语句返回None
。函数结束时也会返回`None。
语句result.append(a)
调用列表对象result
的方法。方法是属于对象的函数,并且以obj.methodname
命名,这里obj
是一个对象(或许是返回对象的表达式),methodname
是定义在obj
所属类型中的函数名。不容的类型定义了不同的函数。不同类型中的函数可以拥有相同的名字而不会导致二义(使用类,自定义新的对象类型和函数是可行的,参见Classes
) 这个例子中的方法append()
定义在列表对象中;该方法在列表末尾添加新元素。这个例子中等同于result = result + [a]
,但是append()
方法更加高效。
定义函数可以使用许多参数,有三种形式,可以将其结合使用。
为一个或者更多参数指定默认值是最有用的。这种方式创建的函数,可以使用比定义所需更少的参数来调用。例如:
def ask_ok(prompt, retries=4, reminder='Please try again!'):while True: ok = input(prompt)if ok in ('y', 'ye', 'yes'):return Trueif ok in ('n', 'no', 'nop', 'nope'):return Falseretries = retries - 1if 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!')
这个例子也介绍了关键字in
,用来测试序列是否包含具体的值。
参数的默认值在函数定义时求值,因此:
i = 5def f(arg=i):print(arg) i = 6f()
会打印5。
重要提示: 参数默认值只被求值一次。当默认参数是诸如列表,字典或者其他大多数对象的可变对象时,会有很大不同。例如,以下函数累积后续调用传递的实参:
def f(a, L=[]): L.append(a)return Lprint(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
也可以使用如kwarg = value
形式的关键字参数调用函数。例如如下函数:
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 argumentparrot(voltage=1000) # 1 keyword argumentparrot(voltage=1000000, action='VOOOOOM') # 2 keyword argumentsparrot(action='VOOOOOM', voltage=1000000) # 2 keyword argumentsparrot('a million', 'bereft of life', 'jump') # 3 positional argumentsparrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword
但是下面的调用方式都是非法的:
parrot() # required argument missingparrot(voltage=5.0, 'dead') # non-keyword argument after a keyword argumentparrot(110, voltage=220) # duplicate value for the same argumentparrot(actor='John Cleese') # unknown keyword argument
函数调用中,关键字参数必须在位置参数之后。所有传递的关键字实参必须匹配函数接受的其中一个形参(例如,actor
对于函数parrot
来说就不是一个合法的关键字参数),其顺序不是重要的。也包括非可选形参(例如parrot(voltage = 1000)
也是合法的)。任意形参都不会接受两次实参值。以下示例就是由于这条限制而调用失败的:
>>> 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
形式时,这个参数接收字典(参见 Mapping Types-dict
),这个字典包含不能能与形参对应的关键字实参。关键字参数可以与形如*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
注意关键字参数打印的顺序保证是与其在函数调用时提供的顺序相同的。
最后,最不常用的是让函数可以使用任意数量的参数调用。这些参数会被包装在元组中(参见 Tuples and Sequences)。在可变数量形参之前,可以有零个或者更多普通参数:
def write_multiple_items(file, separator, *args):file.write(separator.join(args))
通常,可变参数是形参列表中的最后一个,因为可变参数接收了传递给函数的所有剩余实参。出现在*args
后面的任意任意形参都是keyword-only
参数(强制关键字参数),意味着他们只能作为关键字参数使用,而不能用作位置参数。
>>> def concat(*args, sep="/"): ... return sep.join(args) ...>>> concat("earth", "mars", "venus")'earth/mars/venus'>>> concat("earth", "mars", "venus", sep=".")'earth.mars.venus'
参数已经封装在列表或者元组中,但是当调用需要分离的位置参数的函数时,需要拆包元组或者列表。例如,built-in函数range()
需要分离的start
和stop
参数。如果它们不是分离的,可以在函数调用时使用*
操作符来从列表或者元组中拆包(译注:事实证明,字符串也是可以被拆包的):
>>> 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]
以同样的方式,使用**
操作符,字典可以传递关键字参数:
>>> 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 !
可以使用关键字lambda
创建小的匿名方法。这个函数返回其两个参数之和:lambda a, b: a + b
。Lambda方法可以在所有需要函数对象的地方使用。句法上,它们在被限制在一个单个表达式中。语法上,它们只是正常函数定义的语法糖而已。像内嵌函数定义一样,lambda方法也可以引用外部作用域的变量:
>>> def make_incrementor(n): ... return lambda x: x + n ...>>> f = make_incrementor(42)>>> f(0)42>>> f(1)43
上面的例子使用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')]
这里有一些关于文档字符串的内容和格式的约定。
第一行应该是对象用途短小而简明地总结。简洁起见,不应该详细描述对象的名字和类型,因为可以通过其他途径了解(除非名字恰好是描述方法操作的动词)。这一行应该以大写字母开始并以句号结束。
如果文档字符串有多行,第二行应该空出来,从视觉上把总结和其他的描述分开来。剩余行应是一个或者多个描述对象的调用约定及其副作用的段落。
Python分析程序不会去掉多行字符串中的缩排,因此如果必要的话,文档处理工具自己必须去掉缩排,这遵循以下约定:第一行字符串后面的第一个非空行决定整个文档字符串缩排的数量。(因为第一行紧挨着它的起始引号,因此表面上看不出其缩排,所以不能使用第一行作为标准)留白“相当于”是字符串的起始缩排。每一行都不应该有缩排,如果有缩排的话,所有的留白都应该清除掉。留白的长度应当等于扩展制表符的宽度(通常是8个空格)。
以下是多行文档字符串的示例:
>>> 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.
方法注解 是描述用户自定义方法使用的类型的元信息,是完全可选的(参见PEP 484获取更多信息)。
注解作为字典存储在方法属性__annotations__
中,并且对方法的其他部分没有任何影响。参数注解定义在参数名字后的冒号后面,紧跟计算注解值得表达式。返回注解使用->
定义,紧跟表达式,在参数列表和指示def
语句结束的冒号中间。下面的例子有一个位置参数,关键字参数以及返回值得注解:
>>> 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'
现在需要写更长更复杂的Python程序,是时候谈论编码风格了。大多数语言可以以不同的风格编写(更简洁的说是格式化),一些编码风格比其他的更加可读。让其他人更容易读懂你的代码一直都是很重要的,养成良好的编码风格很重要。
对于Python来说,PEP 8被作为风格规范指导,许多项目都在使用这种规范。这个规范促成了一种非常易读和养眼的代码风格。在某种程度上每一个Python开发者都应该阅读它,以下是从这个规范中提取的重要内容:
使用4个空格作为缩进,而不是tab
4个空格是小的缩进(允许更深的嵌套深度)和大缩进(更易读)之间的良好折中方案。Tabs会导致困扰,最好弃用
换行以保证每一行不会超过79个字符
这在小的显示器上非常有用,也可在打的显示器中分屏显示多个文件
使用空行分开方法和类,以及函数中的大代码块
如果可能,注释独占一行
使用文档字符串
在操作符的两端以及顿号后面使用空格,但是不要在括号内侧使用:a = f(1, 2) + g(3, 4)
统一命名类以及函数;约定使用CamelCase
(驼峰)命名类,使用lower_case_with_underscores
(小写带下划线)命名方法和函数。总是使用self
作为方法的第一个参数名(参见A first Look at Classes了解更多关于类和方法的genggd)
如果代码要在国际环境中使用,不要使用自己喜爱的编码方式。Python的默认编码UTF-8
或者甚至普通的ASCII
编码都能在任何情况下起作用。
同样的,不要在标识符中使用非ASCII
字符,除非是不同语种的人会阅读和维护代码。
[1] 事实上,使用引用传递会更好,因为如果传递的是可变对象,变调用者对对象做的改变对调用者可见(如在列表中插入新的项)
以上是Python中流程控制語句的詳細介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!