本文翻译自Python3.7向导
if 语句
x = int(input("please enter an integer:"))
please enter an integer:52
if x < 0:
x = 0
print('Negative changed to zerp')
elif x == 0:
print('zero')
elif x == 1:
print('Single')
else:
print('More')
More
for 语句
Python中的for语句与C或Pascal中的for语句有所不同。 Python的for语句并不总是迭代数字的算术级数(比如在Pascal中),或者让用户能够定义迭代步骤和停止条件(如C),Python的for语句迭代任何序列的项目(列表或 一个字符串),按顺序出现在序列中。 例如:
words = ['cat', 'window', 'defenestrate']
for w in words:
print(w, len(w))
cat 3
window 6
defenestrate 12
如果您需要修改在循环中迭代的序列(例如复制选定项目),建议您先制作副本。 遍历一个序列并不会隐式地创建一个副本。 切片符号使这特别方便
for w in words[:]:
if len(w) > 6:
words.insert(0, w)
words
['defenestrate', 'cat', 'window', 'defenestrate']
range()函数
如果您确实需要遍历一系列数字,则内置函数范围()将派上用场。 它生成算术级数:
for i in range(5):
print(i)
0
1
2
3
4
给定的终点不是生成的序列的一部分; 范围(10)生成10个值,即长度为10的序列的项目的合法索引。可以让范围从另一个数字开始,或指定不同的增量(甚至是负数;有时称为“步长”):
range(5, 10)
range(5, 10)
range(0, 10, 2)
range(0, 10, 2)
range(-10, -100, -30)
range(-10, -100, -30)
要迭代序列的索引,可以按如下方式组合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()返回的对象的行为就好像它是一个列表,但事实上并非如此。 它是一个对象,它在您迭代时返回所需序列的连续项,但它并不真正生成列表,从而节省空间。
我们说这样一个对象是可迭代的,也就是说,适合作为函数和构造的目标,期望它们可以从中获得连续项,直到耗尽。 我们已经看到for语句就是这样一个迭代器。 函数list()是另一个; 它会根据迭代创建列表:
list(range(5))
[0, 1, 2, 3, 4]
break 和 continue 语句
break语句,跳出最内层的for和while循环,和c一样。
循环语句可能有一个else从句(clause); 当循环通过用尽列表(用for)或条件变为false(用while)时循环终止时执行,但当循环由break语句终止时执行。 这由以下循环来举例说明,该循环搜索素数:
for n in range(2, 10):
for x in range(2, n):
if n % x == 0:
print(n, 'equals', x, 'x', n//x)
break
else:
print(n, 'is a prime number')
3 is a prime number
4 equals 2 x 2
5 is a prime number
5 is a prime number
5 is a prime number
6 equals 2 x 3
7 is a prime number
7 is a prime number
7 is a prime number
7 is a prime number
7 is a prime number
8 equals 2 x 4
9 is a prime number
9 equals 3 x 3
与循环一起使用时,else子句与try语句一块相比它与if语句更常用:try语句的else子句在没有发生异常时运行,并且在没有发生中断时运行循环的else子句。 有关try语句和异常的更多信息,请参阅处理异常。
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 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
pass 语句
pass语句什么都不做,当语句需要语法时可以使用它,但程序不需要任何操作。
while True:
pass #busy-wait for keyboard interrupt
它通常用来创建迷你类
class MyEmptyClass:
pass
另一个地方pass可以用于当您处理新代码时作为函数或条件体的占位符(place-holder),使您可以继续思考更抽象的级别。
def initlog(*args):
pass #remember to implement it
定义函数
我们可以创建一个函数,它能够写任意(arbitrary)边界的菲波那切数列
def fib(n):
"""Print a Fibonacci series up to n"""
a, b = 0, 1
while a < n:
print(a, end = ' ')
a, b = b, a+b
print()
fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
关键字def引入了一个函数定义。它必须跟随函数名称和形式参数的括号括起来的列表。构成函数主体的语句从下一行开始,并且必须缩进。
函数体的第一个语句可以可选地是一个字符串文字;此字符串文字是函数的文档字符串或docstring。 (关于文档字符串的更多信息可以在文档字符串部分找到。)有一些工具可以使用文档自动生成在线或打印文档,或让用户交互式浏览代码;在您编写的代码中包含文档字符串是一种很好的做法,所以养成它的习惯。
**函数的执行引入了一个用于函数局部变量的新符号表。更确切地说,函数中的所有变量赋值都将值存储在本地符号表中;而变量引用首先在本地符号表中查找,然后在封闭函数的本地符号表中,然后在全局符号表中,最后在内置名称表中查找。
因此,全局变量不能直接在函数内赋值(assigned a value)(除非在全局语句中命名),尽管它们可能被引用。**
函数调用的实际参数(参数)在被调用函数的本地符号表中引入时被调用;因此,参数通过值调用传递(call by value)(其中值始终是对象引用,而不是对象的值)。 [1]当函数调用另一个函数时,为该调用创建一个新的本地符号表。
函数定义在当前符号表中引入函数名称。函数名称的值具有由解释器识别为用户定义函数的类型。这个值可以分配给另一个名字,然后也可以作为一个函数使用。这是一个通用的重命名机制(a general renaming mechanism)
fib
<function __main__.fib(n)>
f = fib
f(100)
0 1 1 2 3 5 8 13 21 34 55 89
来自其他语言,你可能会反对说fib不是一个函数,而是一个过程,因为它不返回一个值。 事实上,即使没有return语句的函数也会返回一个值,尽管这是一个相当枯燥的值。 这个值被称为无(这是一个内置的名称)。 写入值None通常会被解释器抑制,如果它是唯一写入的值。 你可以看到它,如果你真的想使用print():
fib(0)
print(fib(0))
None
编写一个的函数返回斐波那契数列,而不是打印它,是很简单的
def fib2(n):
"""Return a list containing the Fibonacci series up to n."""""
result = []
a, b = 0, 1
while a < n:
result.append(a)
a, b = b, a+b
return result
f100 = fib2(100)
f100
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
关于函数定义的更多信息
默认参数值
最有用的形式是为一个或多个参数指定默认值。 这创建了一个函数,它可以用比它定义允许的参数更少的参数来调用。 例如:
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(’你真的想退出吗?’)
给出一个可选的参数:ask_ok(’确定覆盖文件?’,2)
或者甚至给出所有参数:ask_ok(’确定覆盖文件?’,2,’来吧,只有是或不是!’)
ask_ok('sdf')
sdfsdf
Please try again!
这个例子还介绍了in关键字。 这测试一个序列是否包含某个值。
重要警告:默认值只计算一次。 当默认值是可变对象(如列表,字典或大多数类的实例)时,这会有所不同。 例如,以下函数会累积在后续调用中传递给它的参数:
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
print(f(1))
print(f(2))
print(f(3))
[1]
[2]
[3]
关键字参数(keyword arguments)
函数也可以通过使用形如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)和三个可选参数(状态,动作和类型)。 该功能可以通过以下任何方式调用:
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
-- This parrot wouldn't voom if you put 1000 volts through it
-- Lovely plumage, the Norwegian Blue
--It's a stiff !
-- This parrot wouldn't voom if you put 1000 volts through it
-- Lovely plumage, the Norwegian Blue
--It's a stiff !
-- This parrot wouldn't VOOOOOM if you put 1000000 volts through it
-- Lovely plumage, the Norwegian Blue
--It's a stiff !
-- This parrot wouldn't VOOOOOM if you put 1000000 volts through it
-- Lovely plumage, the Norwegian Blue
--It's a stiff !
-- This parrot wouldn't jump if you put a million volts through it
-- Lovely plumage, the Norwegian Blue
--It's bereft of life !
-- This parrot wouldn't voom if you put a thousand volts through it
-- Lovely plumage, the Norwegian Blue
--It's pushing up the daisies !
但下面的调用无效:
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
File "<ipython-input-7-2ac707ad11c1>", line 2
parrot(voltage=5.0, 'dead') # non-keyword argument after a keyword argument
^
SyntaxError: non-keyword arg after keyword arg
在函数调用中,关键字参数必须跟在位置参数后面(position arguments),所有传递的关键字参数必须与函数接受的参数之一相匹配(例如,参与者不是鹦鹉函数的有效参数),它们的顺序并不重要。这也包括非可选参数(例如parrot(voltage = 1000)也是有效的)。没有参数可以多次接受一个值
def function(a):
pass
function(0, a=0)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-8-c27f71aecf56> in <module>()
1 def function(a):
2 pass
----> 3 function(0, a=0)
TypeError: function() got multiple values for argument 'a'
当最后的形式参数(formal parameter)以**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.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch
请注意,打印关键字参数的顺序与它们在函数调用中提供的顺序相匹配。
任意参数列表
最后,最不经常使用的选项是指定可以用任意数量的参数调用一个函数。 这些参数将包含在一个元组中(请参阅元组和序列)。 在可变数量的参数之前,可能会出现零个或多个正常参数
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
通常,这些可变参数(variadic)在形式参数列表中排在最后,因为它们会获取传递给该函数的所有剩余输入参数。 *args参数后面出现的任何形式参数都是“仅关键字”参数,这意味着它们只能用作关键字而不是位置参数。
def concat(*args, sep = '/'):
return sep.join(args)
concat("earth", "mars", "venus")
'earth/mars/venus'
concat('earth', 'mars', 'venus', sep = ',')
'earth,mars,venus'
解压参数列表
当参数已经在列表或元组中时,会出现相反的情况,但需要对需要单独位置参数的函数调用进行解压缩。 例如,内置的range()函数需要单独的启动和停止参数。 如果它们不能单独使用,请使用* -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]
同样,字典也可以通过**来解压传递
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关键字创建小的匿名函数。 这个函数返回它的两个参数的总和:lambda a,b:a + b。 Lambda函数可用于需要函数对象的任何地方。 它们在语法上受限于单个表达式。 在语义上,它们只是正常函数定义的语法糖(syntactic sugar)。 与嵌套函数定义一样,lambda函数可以引用来自包含范围的变量:
def make_incrementor(n):
return lambda x: x + n
f = make_incrementor(42)
f(0)
42
f(2)
44
上面的例子使用一个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解析器不会从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.
功能注释function annotations
功能注释完全是有关用户定义函数使用的类型的可选元数据信息(有关更多信息,请参阅PEP 3107和PEP 484)。
注释以字典的形式存储在函数的annotations属性中,并且不影响函数的其他部分。 参数注释由参数名称后面的冒号(colon)定义,后跟表达式评估注释的值。 返回注释由参数列表和表示def语句结束的冒号之间的literal - >,后跟一个表达式定义。 以下示例具有位置参数,关键字参数和注释的返回值:
def f(ham: str, eggs: str = 'eggs') -> str:
print("Annotations:", f.__annotations__)
print("Arguments:", ham, eggs)
return ham + ' and ' + eggs
f('spam')
Annotations: {'return': <class 'str'>, 'eggs': <class 'str'>, 'ham': <class 'str'>}
Arguments: spam eggs
'spam and eggs'
代码风格
现在您要编写更长,更复杂的Python,现在是讨论编码风格的好时机。 大多数语言可以用不同的风格编写(或更简洁,格式化); 有些比其他更可读。 让别人轻松阅读你的代码总是一个好主意,采用一种很好的编码风格对此非常有帮助。
对于Python而言,PEP 8已经成为大多数项目遵循的风格指南; 它促进了非常可读和令人喜欢的编码风格。 每个Python开发者都应该在某个时候阅读它; 这里是为你提取的最重要的点:
- 使用4个空格来缩进,不要用tab。4个空格是在小的缩进(可以有更大的嵌套深度)和大的缩进(易读)之间的权衡。Tabs会引入混淆,不要使用
- 确保每行不超过79个字符,否则就要换行。这有助于用户使用小型显示器,并可以在较大的显示器上并排显示多个代码文件。
- 使用空行分开函数和类以及函数内部大的代码块
- 如果可能,将注释和代码放在一行
- 使用文本字符串docstrings
- 在操作符两侧及逗号commas后面使用空格,但不要在括号内侧使用,例如
f(1, 2)
- 使你的类和函数命名风格一致,惯例是使用CameCase风格命名类,使用lower——case——with——underscores命名函数和方法
保证让self作为第一个方法参数 - 如果你的代码用在国际环境,不要使用奇怪的编码。Python默认的UTF-8或者甚至ASCII可以工作在任何条件下
- 同样,不要在标识符中使用非ASCII字符,如果只有轻微的机会人们说不同的语言会读取或维护代码。