python基础教程之Simple statements

python基础教程之Simple statements


7. Simple statements

A simple statement is comprised within a single logical line. 几个简单语句可以用分号分隔出现在单一的一行中。简单语句的语法是:

simple_stmt ::=  expression_stmt
                 | assert_stmt
                 | assignment_stmt
                 | augmented_assignment_stmt
                 | pass_stmt
                 | del_stmt
                 | return_stmt
                 | yield_stmt
                 | raise_stmt
                 | break_stmt
                 | continue_stmt
                 | import_stmt
                 | global_stmt
                 | nonlocal_stmt

7.1. Expression statements

Expression statements are used (mostly interactively) to compute and write a value, or (usually) to call a procedure (a function that returns no meaningful result; in Python, procedures return the value None). 其它表达式语句的使用也是允许的,但是很少有意义。表达式语句的语法是:

expression_stmt ::=  expression_list

表达式语句计算表达式列表的值(也可能是一个单一的表达式)。

In interactive mode, if the value is not None, it is converted to a string using the built-in repr() function and the resulting string is written to standard output on a line by itself (except if the result is None, so that procedure calls do not cause any output.)


7.2. Assignment statements

赋值语句用于(重新)绑定名称到具体的值以及修改可变对象的属性或元素:

assignment_stmt ::=  (target_list "=")+ (expression_list | yield_expression)
target_list     ::=  target ("," target)* [","]
target          ::=  identifier
                     | "(" target_list ")"
                     | "[" target_list "]"
                     | attributeref
                     | subscription
                     | slicing
                     | "*" target

(See section Primaries for the syntax definitions for attributeref, subscription, and slicing.)

赋值语句计算expression_list(记住,它可以是一个单一的表达式也可以是一个逗号分隔的序列,后者生成一个元组)并且从左到右把单一的结果对象赋值给target_list的每一个元素。

赋值是递归定义的,取决于目标(序列)的形式。当目标是可变对象的一部分时(属性引用,下标或者切片),最终必须由该可变对象做赋值操作并决定其合法性,如果赋值不可接受可以抛出一个异常。各种类型遵守的规则以及抛出的异常根据对象类型的定义给出(参见标准类型的层次一节)。

Assignment of an object to a target list, optionally enclosed in parentheses or square brackets, is recursively defined as follows.

  • 如果对象列表是单一的目标:对象赋值给该目标。
  • If the target list is a comma-separated list of targets: The object must be an
    iterable with the same number of items as there are targets in the target list,
    and the items are assigned, from left to right, to the corresponding targets.
    • If the target list contains one target prefixed with an asterisk, called a “starred” target: The object must be a sequence with at least as many items as there are targets in the target list, minus one. The first items of the sequence are assigned, from left to right, to the targets before the starred target. The final items of the sequence are assigned to the targets after the starred target. A list of the remaining items in the sequence is then assigned to the starred target (the list can be empty).
    • Else: The object must be a sequence with the same number of items as there are targets in the target list, and the items are assigned, from left to right, to the corresponding targets.

赋值一个对象给一个单一的目标按如下方式递归定义。

  • 如果目标是一个标识符(名称):

    • If the name does not occur in a global or nonlocal statement in the current code block: the name is bound to the object in the current local namespace.
    • Otherwise: the name is bound to the object in the global namespace or the outer namespace determined by nonlocal, respectively.

    如果名称已经绑定,那么它将重新绑定。这可能导致之前绑定到该名称的对象的引用计数变为零,引起该对象被释放并调用它的析构函数(如果有的话)。

  • 如果目标是一个包含在圆括号或者方括号中的目标序列:对象必须是可迭代的且元素个数与目标序列中目标个数相同,然后元素从左向右赋值给对应的目标。

  • 如果目标是属性引用:计算引用中的初级表达式。它产生的对象应该具有一个可以赋值的属性;if this is not the case, TypeError is raised. 然后要求该对象将被赋值的对象赋值给给定的属性;if it cannot perform the assignment, it raises an exception (usually but not necessarily AttributeError).

    注意:如果对象是类的实例且属性引用出现在赋值运算符的两侧,那么右侧的表达式a.x既可以访问实例属性(如果不存在实例属性)也可以访问类属性。左侧的目标将a.x永远设置成实例的属性,如果必要将创建它。因此,a.x的两次出现不是一定会引用同一个属性:如果右侧表达式引用的是一个类属性,左侧的表达式将创建一个新的实例属性作为赋值的目标。

    class Cls:
        x = 3             # class variable
    inst = Cls()
    inst.x = inst.x + 1   # writes inst.x as 4 leaving Cls.x as 3
    

    这里的描述不一定适用描述器属性,例如property()创建的属性。

  • 如果目标是下标操作符:计算引用中的初级表达式。它应该生成一个可变的序列对象(例如列表)或者映射对象(例如字典)。然后,计算下标表达式。

    If the primary is a mutable sequence object (such as a list), the subscript must yield an integer. 如果它是负数,将会加上序列的长度。结果值必须是一个小于序列长度的非负整数,然后将要求序列赋值该对象给具有那个索引的元素。If the index is out of range, IndexError is raised (assignment to a subscripted sequence cannot add new items to a list).

    如果primary 是一个映射对象(例如一个字典),下标必须具有和映射的关键字类型兼容的类型,然后要求映射创建一个键/值对将下标映射到赋值的对象。这既可以替换一个具有相同键值得已存在键/值对,也可以插入一个新的键/值对(如果不存在相同的键)。

    For user-defined objects, the __setitem__() method is called with appropriate arguments.

  • 如果目标是一个切片:计算引用中的初级表达式。它应该产生一个可变序列对象(例如列表)。被赋值的对象应该是相同类型的序列对象。下一步,如果存在,则计算下边界和上边界表达式;默认是零和序列的长度。The bounds should evaluate to integers. 如果任意一个边界为复数,则会给它加上序列的长度。结果求得的边界在零和序列的长度之间,包括边界在内。最后,要求序列对象用赋值的序列元素替换切片。The length of the slice may be different from the length of the assigned sequence, thus changing the length of the target sequence, if the target sequence allows it.

CPython实现细节: 在目前的实现中,目标的语法和表达式的语法相同,不合法的语法将在代码生成阶段被排除,导致不够详细的错误信息。

Although the definition of assignment implies that overlaps between the left-hand side and the right-hand side are ‘simultanenous’ (for example a, b = b, a swaps two variables), overlaps within the collection of assigned-to variables occur left-to-right, sometimes resulting in confusion. 例如,下面的程序将打印[0, 2]

x = [0, 1]
i = 0
i, x[i] = 1, 2         # i is updated, then x[i] is updated
print(x)

请参阅

PEP 3132 – Extended Iterable Unpacking
The specification for the *target feature.

7.2.1. Augmented assignment statements

增强的赋值是将二元操作和赋值语句组合成一个单一的语句。

augmented_assignment_stmt ::=  augtarget augop (expression_list | yield_expression)
augtarget                 ::=  identifier | attributeref | subscription | slicing
augop                     ::=  "+=" | "-=" | "*=" | "/=" | "//=" | "%=" | "**="
                               | ">>=" | "<<=" | "&=" | "^=" | "|="

(See section Primaries for the syntax definitions of the last three symbols.)

增强的赋值将先求值target(和普通的赋值语句不同,它不可以是一个可拆分的对象)和expression_list,然后完成针对两个操作数的二元操作,最后将结果赋值给初始的target。target只计算一次。

x += 1这样增强的赋值表达式可以重写成x = x + 1以达到类似但不完全等同的效果。在增强版本中,x只计算一次。还有,如果可能,真实的操作是原地的,意思是不创建一个新的对象并赋值给target,而是直接修改旧的对象。

Unlike normal assignments, augmented assignments evaluate the left-hand side before evaluating the right-hand side. For example, a[i] += f(x) first looks-up a[i], then it evaluates f(x) and performs the addition, and lastly, it writes the result back to a[i].

除了不可以在一个语句中赋值给元组和多个目标,增强的赋值语句完成的赋值和普通的赋值以相同的方式处理。类似地,除了可能出现的原地行为,增强的赋值完成的二元操作和普通的二元操作相同。

如果target是属性引用,关于类和实例属性的注意事项同样适用于正常的赋值。


7.3. The assert statement

Assert语句是插入调试断言到程序中的一种便捷方法:

assert_stmt ::=  "assert" expression ["," expression]

其简单形式,assert expression,等同于

if __debug__:
   if not expression: raise AssertionError

其扩展形式,assert expression1, expression2,等同于

if __debug__:
   if not expression1: raise AssertionError(expression2)

These equivalences assume that __debug__ and AssertionError refer to the built-in variables with those names. In the current implementation, the built-in variable __debug__ is True under normal circumstances, False when optimization is requested (command line option -O). 在编译时刻,当要求优化时,目前的代码生成器不会为断言语句生成任何代码。注:不必把失败的表达式的源代码包含进错误信息;它将作为栈回溯的一部分显示出来。

__debug__赋值是非法的。内建变量的值在解释器启动的时候就已决定。


7.4. The pass statement

pass_stmt ::=  "pass"

pass是一个空操作 — 执行它的时候,什么都没有发生。它的用处是当语法上要求有一条语句但是不需要执行任何代码的时候作为占位符,例如:

def f(arg): pass    # a function that does nothing (yet)

class C: pass       # a class with no methods (yet)

7.5. The del statement

del_stmt ::=  "del" target_list

删除是递归定义的,和赋值的定义方式非常相似。这里就不详细讲述完整的细节,只给出一些注意事项。

删除目标将从左向右递归删除每一个目标。

删除一个名称将从局部或全局命名空间中删除该名称的绑定,取决于名称是否出现在相同代码块的global语句中。If the name is unbound, a NameError exception will be raised.

属性引用、下标和切片的删除将传递给原始的对象;切片的删除在一般情况下等同于赋予一个右边类型的空切片(但即使这点也是由切片的对象决定)。

Changed in version 3.2: Previously it was illegal to delete a name from the local namespace if it occurs as a free variable in a nested block.


7.6. The return statement

return_stmt ::=  "return" [expression_list]

return在语法上只可以出现在函数定义中,不可以出现在类定义中。

If an expression list is present, it is evaluated, else None is substituted.

return leaves the current function call with the expression list (or None) as return value.

return将控制传出带有finally子句的try语句时,在真正离开函数之前会执行finally子句。

In a generator function, the return statement indicates that the generator is done and will cause StopIteration to be raised. The returned value (if any) is used as an argument to construct StopIteration and becomes the StopIteration.value attribute.


7.7. The yield statement

yield_stmt ::=  yield_expression

A yield statement is semantically equivalent to a yield expression. The yield statement can be used to omit the parentheses that would otherwise be required in the equivalent yield expression statement. For example, the yield statements

yield <expr>
yield from <expr>

are equivalent to the yield expression statements

(yield <expr>)
(yield from <expr>)

Yield expressions and statements are only used when defining a generator function, and are only used in the body of the generator function. Using yield in a function definition is sufficient to cause that definition to create a generator function instead of a normal function.

yield语义的完整细节,参考Yield表达式一节。


7.8. The raise statement

raise_stmt ::=  "raise" [expression ["from" expression]]

如果没有表达式,raise 重新抛出当前作用域中最后一个激活的异常。If no exception is active in the current scope, a RuntimeError exception is raised indicating that this is an error.

Otherwise, raise evaluates the first expression as the exception object. It must be either a subclass or an instance of BaseException. If it is a class, the exception instance will be obtained when needed by instantiating the class with no arguments.

The type of the exception is the exception instance’s class, the value is the instance itself.

A traceback object is normally created automatically when an exception is raised and attached to it as the __traceback__ attribute, which is writable. You can create an exception and set your own traceback in one step using the with_traceback() exception method (which returns the same exception instance, with its traceback set to its argument), like so:

raise Exception("foo occurred").with_traceback(tracebackobj)

The from clause is used for exception chaining: if given, the second expression must be another exception class or instance, which will then be attached to the raised exception as the __cause__ attribute (which is writable). If the raised exception is not handled, both exceptions will be printed:

>>> try:
...     print(1 / 0)
... except Exception as exc:
...     raise RuntimeError("Something bad happened") from exc
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

A similar mechanism works implicitly if an exception is raised inside an exception handler or a finally clause: the previous exception is then attached as the new exception’s __context__ attribute:

>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Something bad happened")
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

关于异常的更多信息可以在异常一节中找到,如何处理异常的信息在try语句一节。


7.9. The break statement

break_stmt ::=  "break"

break在语法上只可以出现在for或者while循环中,但不能嵌套在这些循环内的函数和类定义中。

它终止最相近的循环,如果循环有else子句将跳过。

如果break终止了一个for循环,控制循环的目标保持当前的值。

break将控制传出带有finally子句的try语句时,在离开循环之前会执行finally子句。


7.10. The continue statement

continue_stmt ::=  "continue"

continue在语法上只可以出现在forwhile循环中,但不能嵌套在这些循环内的函数定义、类定义和finally子句中。它继续最内层循环的下一轮。

continue将控制传出带有finally子句的try语句时,在真正开始下一轮循环之前会执行finally子句。


7.11. The import statement

import_stmt     ::=  "import" module ["as" name] ( "," module ["as" name] )*
                     | "from" relative_module "import" identifier ["as" name]
                     ( "," identifier ["as" name] )*
                     | "from" relative_module "import" "(" identifier ["as" name]
                     ( "," identifier ["as" name] )* [","] ")"
                     | "from" module "import" "*"
module          ::=  (identifier ".")* identifier
relative_module ::=  "."* module | "."+
name            ::=  identifier

The basic import statement (no from clause) is executed in two steps:

  1. find a module, loading and initializing it if necessary
  2. define a name or names in the local namespace for the scope where the import statement occurs.

When the statement contains multiple clauses (separated by commas) the two steps are carried out separately for each clause, just as though the clauses had been separated out into individiual import statements.

The details of the first step, finding and loading modules are described in greater detail in the section on the import system, which also describes the various types of packages and modules that can be imported, as well as all the hooks that can be used to customize the import system. Note that failures in this step may indicate either that the module could not be located, or that an error occurred while initializing the module, which includes execution of the module’s code.

If the requested module is retrieved successfully, it will be made available in the local namespace in one of three ways:

  • If the module name is followed by as, then the name following as is bound directly to the imported module.
  • If no other name is specified, and the module being imported is a top level module, the module’s name is bound in the local namespace as a reference to the imported module
  • If the module being imported is not a top level module, then the name of the top level package that contains the module is bound in the local namespace as a reference to the top level package. The imported module must be accessed using its full qualified name rather than directly

The from form uses a slightly more complex process:

  1. find the module specified in the from clause, loading and initializing it if necessary;
  2. for each of the identifiers specified in the import clauses:
    1. check if the imported module has an attribute by that name
    2. if not, attempt to import a submodule with that name and then check the imported module again for that attribute
    3. if the attribute is not found, ImportError is raised.
    4. otherwise, a reference to that value is stored in the local namespace, using the name in the as clause if it is present, otherwise using the attribute name

例子:

import foo                 # foo imported and bound locally
import foo.bar.baz         # foo.bar.baz imported, foo bound locally
import foo.bar.baz as fbb  # foo.bar.baz imported and bound as fbb
from foo.bar import baz    # foo.bar.baz imported and bound as baz
from foo import attr       # foo imported and foo.attr bound as attr

If the list of identifiers is replaced by a star ('*'), all public names defined in the module are bound in the local namespace for the scope where the import statement occurs.

模块定义的公开的名称通过检查模块命名空间中一个名为__all__的变量决定;如果定义,它必须是一个字符串序列,它们是该模块定义或者导入的名称。__all__中给出的名称都被认为是公开的且要求必须存在。如果__all__没有定义,那么公开的名称集合包括模块命名空间中找到的所有不是以下划线字符 ('_')开始的名称。__all__应该包含全部的公开API。它的意图是避免意外地导出不是API的部分(例如模块内部导入和使用的库模块)。

The wild card form of import — from module import * — is only allowed at the module level. Attempting to use it in class or function definitions will raise a SyntaxError.

在指出你要导入的模块时,你不必指明模块的绝对路径名。当一个模块或者包包含在另外一个包中时,可以在同一个等级的包中使用相对导入而不需要提及包的名字。通过在from之后指定的模块或包中使用前导的点号,你可以指定相对当前包层级向上移动的高度而不用指明准确的名称。一个前导的点号表示正在进行导入的模块所存在的包。两个点号表示向上一级。三个点表示向上两级,等等。So if you execute from . import mod from a module in the pkg package then you will end up importing pkg.mod. If you execute from ..subpkg2 import mod from within pkg.subpkg1 you will import pkg.subpkg2.mod. The specification for relative imports is contained within PEP 328.

importlib.import_module() is provided to support applications that determine dynamically the modules to be loaded.


7.11.1. Future statements

A future statement is a directive to the compiler that a particular module should be compiled using syntax or semantics that will be available in a specified future release of Python where the feature becomes standard.

future语句的意图是使得迁移到未来版本的Python变得容易,这些未来版本向语言中引入了不兼容的变化。它允许在该功能变成标准之前以每个模块为基础使用新的功能。

future_statement ::=  "from" "__future__" "import" feature ["as" name]
                      ("," feature ["as" name])*
                      | "from" "__future__" "import" "(" feature ["as" name]
                      ("," feature ["as" name])* [","] ")"
feature          ::=  identifier
name             ::=  identifier

future语句必须出现在靠近模块的顶部。出现在future语句之前的行只可以是:

  • 模块的文档字符串(如果有的话),
  • 注释,
  • 空白行,和
  • 其它future语句。

The features recognized by Python 3.0 are absolute_import, division, generators, unicode_literals, print_function, nested_scopes and with_statement. They are all redundant because they are always enabled, and only kept for backwards compatibility.

future语句的识别和特殊处理在编译的时刻:核心语义的变化通常通过生成不同的代码实现。甚至有可能新的功能引入新的不兼容语法(例如一个新的保留字),在这种情况下编译器可能需要以不同的方式解析模块。这些决定不可能推迟到运行时刻。

对于任何给定的版本,编译器知道哪些特性的名称已经定义了,如果future语句包含一个不认识的特性它将抛出编译时刻错误。

运行时刻的语义和任何import 语句是一样的:有一个标准的模块__future__(在后面讲述),它将在future语句执行的时候以正常的方式被导入。

运行时刻的有趣语义取决于future 语句启用的特定功能。

注意下面的语句没有什么特别的:

import __future__ [as name]

它不是future 语句;它是一个普通的没有特殊语义和语法限制的import 语句。

Code compiled by calls to the built-in functions exec() and compile() that occur in a module M containing a future statement will, by default, use the new syntax or semantics associated with the future statement. This can be controlled by optional arguments to compile() — see the documentation of that function for details.

在交互式解释器中敲入的future 语句将对解释器剩下的会话生效。如果解释器以-i 选项启动,然后传入一个脚本的名称给它执行,且脚本包含一个future 语句,它将在脚本执行之后的交互式会话中生效。

请参阅

PEP 236 – Back to the __future__
__future__机制的原始提议。

7.12. global 语句

global_stmt ::=  "global" identifier ("," identifier)*

global 是一个声明语句,该声明在当前整个代码块中有效。这意味着列在其后的所有标识符将被解析为全局有效的。尽管没有被声明为全局的自由变量也可以引用全局变量,但想要对全局变量赋值则必须先使用 global关键字进行声明。

在同一代码块中,列在 global 语句中的所有标识符不能在该 global 语句前出现。

列在global 语句中的标识符不能被定义成形参,不能出现在 for 循环控制的目标、 定义和函数定义,或者 import 语句中。

CPython 实现细节: 当前实现并未强制履行上面两条限制,但程序不应该滥用这种自由,因为未来的版本可能会强制履行它们或者不留痕迹的改变程序含义。

注意: global对解析器而言是一个指令。它仅仅会作用于与 global 语句一同解析的代码。特别的一点,作为参数应用于内建函数 exec() 的字符串或代码对象中如果包含 global 语句,该语句不会对 包含 exec()函数调用的代码块产生任何副作用,相似的,作为 exec() 参数的字符串中的代码也不会受包含该 exec() 的代码块中的 global 语句影响。eval()compile() 函数也是如此。


7.13. nonlocal 语句

nonlocal_stmt ::=  "nonlocal" identifier ("," identifier)*

nonlocal 语句导致列出的标识符 to refer to previously bound variables in the nearest enclosing scope excluding globals. This is important because the default behavior for binding is to search the local namespace first. The statement allows encapsulated code to rebind variables outside of the local scope besides the global (module) scope.

Names listed in a nonlocal statement, unlike those listed in a global statement, must refer to pre-existing bindings in an enclosing scope (the scope in which a new binding should be created cannot be determined unambiguously).

Names listed in a nonlocal statement must not collide with pre-existing bindings in the local scope.

请参阅

PEP 3104 – Access to Names in Outer Scopes
The specification for the nonlocal statement.

留下回复