离线下载
PDF版 ePub版

极客学院团队出品 · 更新于 2018-11-28 11:00:42

简单语句

简单语句可以在一个逻辑行内表示,一些简单语句可以写在一行由分号分隔,简单语句的句法如下:

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

表达式语句

表达式语句用于计算和写一个值(多用在交互方式下), 或者(通常)调用过程(一个返回没有意义的结果的函数; 在Python中,过程返回None)。允许其它表达式语句的使用方法,有时也用的到。表达式语句的句法如下:

expression_stmt ::=  expression_list

一个表达式语句要对该表达式列表(可能只是一个表达式)求值.

在交互模式下,如果该值不是空,就使用内建函数repr()并自己将结果字符串写入一行标准输出,(除非结果为空,导致过程调用不会引起任何输出。)

赋值语句

使用赋值把名字绑定(重新绑定)到值,以及修改可变对象的属性或者项目:

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

(请参阅 attributerefsubscription和slicing的基本语法定义部分。)

一个赋值语句要对该表达式列表求值(请记住这可以是一个表达式或者一个逗号分隔的列表,后者导出一个元组),然后从左到右地将对象结果依次赋给目的列表里的每个对象。

根据目标(列表)的形式,赋值被递归地定义。当目标是一个可变对象的一部分时(一个属性引用,subscription或者slicing),可变的对象必须最终执行赋值,并确定其有效性,如果在赋值不可接受的情况下,可以引发出一个异常。被各种类型所遵循的规则和抛出的异常给出针对对象类型的定义(见标准的类型层次结构部分)。

分配对象到目标列表,括号或方括号内为可选,递归地定义如下.

  • 如果目标列表是单个目标,该对象就赋予该目标。
  • 如果目标序列是一组用逗号分隔的目标:该对象必须是一个其子项个数与目标列表中的目标个数 一样多的迭代对象,且其子项,从左到右地逐个赋予相应目标。
    • 如果目标列表包含一个前缀带星号的目标,被成为“starred”目标:这个对象必须是一个序列在目标列表中,有尽量多的子项目,不能少于一个子项目,序列从第一项被赋值,在带星的目标之前,从左到右逐个分配到目标列表。序列最后一项被赋值,则赋值到星号之后的目标。列表中剩余的项目最后被赋值给带星的目标(列表可以是空的)。
    • 否则:这个对象必须是一个在目标列表中有相同数目子项的序列,并且子项从左到右地赋予相应目标。

一个对象向单个目标递归地赋值定义如下。

  • 如果该目标是一个标志符(名字):

    • 如果该名字不出现于当前代码块的global或者nolocal语句当中:该名字就约束到当前本地命名空间的对象上。
    • 否则:该名字分别约束到全局名字空间或者nolocal确定的外部名字空间。

如果名字已经被约束了它就被重新约束。这可能导致早先约束到该名字的对象的引用计数降为 零,导致释放该对象的分配空间并调用其析构器,如果它有一个的话。

  • 如果目标是一个用括号或者方括号括起来的目标序列:该对象必须是具有和目标序列中目标个数同样数目的迭代类型,且其子项从左到右地赋值给相应目标。

  • 如果目标是一个属性引用:引用中的主元表达式被求值。它应该给出一个带可赋值属性的对象; 如果不是这种情况,就会抛出TypeError异常。然后那个对象就被要求将被赋值的对象赋值给给定 的属性;如果它无法执行该赋值,就会抛出一个例外(通常,但不是必须AttributeError异常)。

注意:如果对象是类的实例并且属性引用的赋值操作发生在两端,RHS 表达式,a.x既能访问一个实例属性,也能(如果没有实例属性存在)访问一个类的属性。这个LHS目标a.x通常设定一个实例属性,如果有必要就创建。因此,这两个a.x的发生不需要涉及同样的属性,如果RHS表达式涉及到类的属性,这个LHS会创建一个新的实例属性作为赋值的目标。

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

这个描述并不一定适用于描述符属性,像属性创建使用property()

  • 如果目标是一个下标:引用中的主元表达式被求值。这应给出或者一个可变有序对象(比如,一个列表)或者一个映射对象(比如,一个字典)。接着,下标表达式被求值。

如果基础对象是可变有序对象(例如列表),下标必须给出一个整数。如果是负数,序列的长度就被加上。最后的值必须是一个小于该序列长度的非负整数,然后该序列就被请求将被赋对象赋值给 它带那个指标的项。如果指标超出范围,就会抛出IndexError异常(给一个用下标引用的有序对象赋 值不会给列表增添新项)

如果基础对象是一个映射对象(比如字典),下标的类型必须和映射的键类型兼容,接着该映射就被 要求创建一个把下标映射到被赋对象的键/数据对。这(操作)要不用新键值取代已存在的具有相同 键的键/值对,要不插入一个新的键/值对(如果不存在相同的键)

用户自定义的对象, ____setitem__ __()方法被调用以适当的参数。

  • 如果目标是一片断:引用中的主元表达式被求值。这应给出一个可变有序对象(比如列表)。被赋值 对象应该是同一类型的有序对象。下一步,在它们所出现的范围内,对上下限表达式求值;缺省值是零和序列长度。限值应求值为整数。如果任一限是负的,就加上序列的长度。结果限值被修整至零和序列长度之间(含零和序列长度)。最后,有序对象被要求用被赋有序对象的子项替换该片断。片断的长度可能和被赋序列的长度不同,那就改变目标序列的长度,如果该对象允许的话。

CPython实现细节:在当前实现中,目标对象的语法采取同一表达式,同时无效的语法在代码生成阶段内被拒绝,导致更少的详细的错误消息输出.

虽然赋值的定义隐含着左手边和右手边之间的重叠是“同时的”(比如,a, b = b, a交换两个变量),在所赋值变量间的重叠却是不安全的!在集合中出现从左到右分配变量间的重叠是不安全的,有时候值是混乱的,例如,下面的程序打印出[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.

增量的赋值语句

增量赋值就是在单条语句内合并一个二元运算和一个赋值语句。

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

(见最后三项符号基础语法定义)

一条增量赋值语句对目标(和一般的赋值语句不同,它不能是展开的对象)和表达式列表求值,执行特定于两个操作数的赋值类型的二元运算,并将结果赋值给原先的目标。目标仅求值一次。

一条赋值语句,比如x+=1, 可以重写为x=x+1,效果是类似的,但并不完全一样。在增量版本中,x仅求值一次。而且,只要可能,实际的操作是就地进行的,意思是并非创建一个新对象然后将其赋值给目标,而是修改老的对象。

不像普通的赋值语句,增量赋值先计算左边再计算右边。例如,a[i] += f (x) 首先查看a[i],然后它计算 f(x) 并执行加法操作,最后,把结果写入a[i]

除了在一条语句中赋值给元组和多个对象的情况,增量赋值语句所完成的赋值用与普通赋值同样的方式处理。类似地,除了可能的就地方式,由增量赋值执行的二元运算和普通的二元运算也是一样的。

For targets which are attribute references, the same caveat about class and instance attributes applies as for regular assignments.

assert 语句

assert语句是一个在程序中插入调试断言的常用方法:

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

简单形式的, ”assert expression”, 等价于:

    if __debug__:
       if not expression: raise AssertionError

扩展形式的, ”assert expression”, 等价于:

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

这些等价式假定了存在__debug__AssertionError, 而不是具有相同名字的相应内建变量.在当前实现, 内建变量 __debug__ 在普通情况下为True, 在要求优化的情况下为False(命令行选项-O) 在编译要求优化时, 当前的代码生成器不产生任何断言语句的代码.注意在错误信息包括源代码的作法是多余的; 因为它们会作为跟踪回溯对象的一部分显示。

__debug__赋值是非法的,解释器是在启动时读取内建变量的值的。

pass 语句

    pass_stmt ::=  "pass"

pass是一个空操作 - 当执行它,什么也不会做。它在句法上要求有一个语句时但不需要写代码时用到,例如:

    def f(arg): pass   # a function that does nothing (yet)
    class C: pass      # a class with no methods (yet)

del 语句

del_stmt ::=  "del" target_list

与赋值的定义方法类似,删除也是递归的。下面是一些说明:

一个目标表的递归删除操作会从左到右地删除其中的每个对象地.

删除名字就是在本地命名空间和全局名字空间删除掉该名字的绑定(必须存在), 从哪个名字空间删除取决于该名字是否出现在其代码块的global语句中。如果名字没有绑定,会抛出NameError异常。

对于属性引用, 下标和片断的删除会作用到相关的主元对象, 对片断的删除一般等价于对该片断赋予相应 类型的空片断(但这也受被截为片断的对象的限制).

3.2版本变化:之前如果嵌套数据块中有空变量,在本地命名空间中删除名字是不合法的。

return 语句

return_stmt ::=  "return" [expression_list]

return在句法上仅可以出现在嵌套的函数定义中, 不能出现在嵌套的类定义中.

如果给出了表达式表, 就计算其值, 否则就用None代替.

return的作用是离开当前函数调用, 并以表达式表的值(或None)为返回值.

return放在具有finally子句的try语句中, finally子句中的语句会在函数真正退出之前执行一次.

在生成器函数中, return语句意味着生成器执行完成,并且将会引发一个StopIteration异常。返回值(如果有的话)是作为StopIteration构造方法的参数,并且会变成StopIteration.value属性。

yield 语句

yield_stmt ::=  yield_expression

yield语句在语义上等同于yield表达式。yield语法被用来省略括号,否则需要使用相同的yield表达式语句。例如,yield语句

    yield <expr>
    yield from <expr>

等同于yield表达式语句

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

yield表达式和语句只有当在定义生成器函数时被使用。只使用在生成器函数体中。在函数定义时使用yield,足够导致定义一个创建生成器函数而不是普通函数。

yield详细的语法,参考yield表达式部分。

raise 语句

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

如果不给出表达式,raise重新引发当前范围内上次引发的异常。如果当前范围没有可用的异常,会抛出RuntimeError异常,暗示出现错误。

否则,raise使用第一个表达式作为异常对象求值。 对异常对象的第一个表达式求值。他必须是一个子类或者一个BaseException的实例。如果是一个类,当实例化一个无参数类被需要的时候,异常实例将被获取,

异常的类型是异常实例类,是实例本身。

traceback对象通常被自动创建,当异常发生并且被附加到它的 traceback 属性上。你可以创建异常并设置你自己的traceback,在第一步使用异常方法with _traceback()(这个方法返回相同的异常实例,traceback设置到他的参数中),就像:

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

from子句用于异常链接:如果给出,则第二个表达式必须是另一个异常类或实例,然后将附加到的 __cause__ 属性 (它是可写)引发的异常。如果不处理引发的异常,则将打印两个例外情况:

    >>>>>>>>> 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

一个类似的机制隐式工作如果里面的异常处理程序引发的异常或 finally 子句: 那么前一个异常附加作为新异常 __context__ 属性:

    >>>>>>>>> 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语句章节。

break 语句

    break_stmt ::=  "break"

break在句法上只能出现在forwhile循环中, 但不能出现在循环中的函数定义或类定义中。

它中断最内层的循环, 跳过其可选的else语句(如果有的话)。

如果for循环被break中断, 它的循环控制对象还保持当前值。

break放在具有finally子句的try语句中, finally子句中的语句会在循环真正退出之前执行一次。

continue 语句

    continue_stmt ::=  "continue"

continue在句法上只能出现在forwhile循环中, 但不能出现在循环中的函数定义或类定义,或在循环内部的finally子句。 它重新开 始最内层的循环。

continue通行控制离开一个包含finally子句的try语句,finally子句执行之前真的开始下一个循环周期。

import 语句

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

基本的import语句(不是from子句)执行下面两个步骤:

  1. 查找module,如果需要加载并且初始化它。
  2. 定义一个或多个名称在本地命名空间,在import语句出现的范围内。

当语句包含多个子句(由逗号分隔)分别对每个子句实施两个步骤,就像子句被分隔成个体的import语句。

详细的第一步,查找并加载模块更详细的描述在import system部分,它同样描述可以被导入的模块和各种类型的包,以及所有的钩子可用于自定义导入系统。请注意在此步骤中的失败可能表明该模块无法找到,或者模块初始化错误,包含可执行模块的代码错误。

如果请求的模块检测到成功,它将提供局部空间中的三种方式中的一种:

  • 如果模块名后跟as,那么这个名字后as会直接绑定到导入的模块。

  • 如果没有指定其他名字,并且被导入的是顶层模块,模块名被绑定在本地的命名空间作为引用导入模块。

  • 如果导入不是顶层模块,那么包含该模块的顶层包名被绑定到本地命名空间,作为一个顶级包的引用。导入的模块,必须可以使用完全限定名称访问而不是直接访问。

from方式的使用过程稍微复杂:

  1. 查找from子句中指定的模块,如果需要加载并初始化它。
  2. 对于每一个import子句的指定标识符:
    1. 检查import的模块具有该属性的名称
    2. 如果没有,尝试根据名称导入子模块,然后在导入的模块中再次检查该属性。
  3. 如果属性没有找到,抛出ImportError异常。
  4. 否则,在本地命名空间中存储这个引用值,如果存在使用as子句里的名称,否则使用属性名

Examples: 例子:

    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

如果标识符列表由星号('*')取代,该模块中定义的所有公共名称,都在import语句所在的本地命名空间中被约束。

一个模块所定义的“公共名称”通过检查该模块的命名空间中的名为 __all__ 的变量决定。如果(该变量)有定义,它必须是一个字符串的有序序列,这些字符串是由该模块定义或者导入的名字。在 __all__ 中给出的名字都被认为是公共的且要求其存在。如果 __all__ 没有定义,(该模块的)公共名字的集合就包含所有在该模块的名字空间中找到的,不以下划线(” _ ”)起首的所有名字。

导入的通配符形式 - from module import *- 只允许模块级别。试图在类或者函数中使用它会抛出SyntaxError

在指定你要导入的模块时,你并不一定要指定模块的绝对名字。当一个模块或包是在其他包之内时,在同一个顶层包下,我们有可能使用不需指明包名的相对加载。通过在from之后的模块或者包之前指定几个句号, 就可以在没有精确名字的情况下,指定在当前包层次中向上搜索多少层。一个句号代表包括导入操作所在模块的当前包。两个句号代表上一个包层次,三个句号代表再上一个包层次,依次类推。所以,如果你在 pkg 包中执行 from.import mod ,你就会导入pkg.mod。如果你从包 pkg.subpkg1 执行 from ..subpkg2 import mod ,你就会导入 pkg.subpkg2.mod。相对导入的规范参见PEP328

函数importlib.import_module()用于支持要确定哪些模块需要动态加载的应用程序。

future 语句

future语句是一个编译器指令,在指定的将来的Python版本应该可以使用语法或语义去编译一个特定的模块,到那时Python特性将成为标准。

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语句。

Python 3.0 版本公认的特性是absolute_import, division, generators, unicode_literals, print_function, nested_scopeswith_statement。他们都是多余的,因为他们总是在启用状态,同时只保持向后兼容的可编译性。

在编程的时候future语句被识别出来,并且被特殊地对待:更改核心构建的语义通常是通过生成不同的代码来实现的。甚至有可能一个新的功能会引入一个新的不兼容的语法(比如一个新的保留字),在这种情况下,编译器可能需要用区别对待的方式解析模块。此类决定不能被推迟到运行时。

对于任何发布的版本,编译器明确已经被定义的功能名称,如果future语句包含未知功能时,编译器会提示编译出错。

直接运行语义作为任何导入语句都是相同的:这里有一个标准的__future__模块,稍后会解释,当future语句被执行的时候,将以通用方式被引入。

令人关注的运行时语义取决于被future语句启用的特定功能。

请注意,没有什么特别的语句。

    import __future__ [as name]

那个不是future语句;这是一个普通的导入语句没有特殊的语义或语法的限制。

通过调用内置函数exec()compile()来编译的代码,此代码在M模块发起,并包含future语句,在默认情况下,会结合future语句采用新的语法和语义。这可以由可选参数来控制去compile()——有关详细信息,请参阅文档的功能。

在交互式解析器提示符下键入future语句,重启解析器器会话生效。如果解析器以-i选项开始,且是通过一个脚本名去执行,同时这个脚本包含future语句,它将作用在交互会话中,在脚本执行后开始。

参见:
PEP 236 - Back to the future
The original proposal for the future mechanism.

global 语法

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

global语句是对整个代码块都有作用的一个声明. 它指出其所列的标识符要解释为全局的.如果某名字在本地命名空间中没有定义, 就自动使用相应的全局名字. 没有global是不可能手动指定一个名字是全局的。

global中出现的名字不能在global 之前的代码中使用.

global中出现的名字不能作为形参, 不能作为for循环的控制对象, 不能在定义, 函数定义, import语句中出现.

CPython详细实现:当前实现不强制两个限制,但程序不应该滥用这种自由,作为将来的实现可能会强制他们或默默地改变程序的意义。

程序员注意: global是一个解析器的指示字. 它仅仅会对和global一起解析的代码有效,尤其是,一个global语句被包含在字符串或者代码对象提供内建的exec()函数,并不影响包含该函数调用的代码块,相同的机制也应用于eval()compile() 函数。

nonlocal 语句

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

nonlocal语句引发列出的标识符,适用于不包括global之外最近的封闭范围,前一个绑定变量。这是重要的因为绑定的默认行为是首先搜索本地命名空间。该语句允许封装的代码绑定除了global(模块)范围之外,局部作用域的变量。

nonlocal语句不同于那些在global语句中列出的名称,必须引用到预先存在的封闭范围中的绑定(不能明确的确定要在其中创建一个新绑定的范围)。

nonlocal语句中列出的名字,在本地范围必须不和已经存在的绑定冲突。

参见:
PEP 3104 - Access to Names in Outer Scopes
The specification for the nonlocal statement.

上一篇: 表达式 下一篇: 复合语句