6. 简单语句

简单语句包含在单一的一个逻辑行中。几个简单语句可以用分号分隔出现在单一的一行中。简单语句的语法是:

  1. simple_stmt ::= expression_stmt
  2. | assert_stmt
  3. | assignment_stmt
  4. | augmented_assignment_stmt
  5. | pass_stmt
  6. | del_stmt
  7. | print_stmt
  8. | return_stmt
  9. | yield_stmt
  10. | raise_stmt
  11. | break_stmt
  12. | continue_stmt
  13. | import_stmt
  14. | global_stmt
  15. | exec_stmt

6.1. 表达式语句

表达式语句用于(主要用于交互式地)计算和写入一个值,或者(通常)调用一个过程(返回结果没有意义的函数;在Python中,过程返回None值)。其它表达式语句的使用也是允许的,但是很少有意义。表达式语句的语法是:

  1. expression_stmt ::= expression_list

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

在交互模式下,如果值不是None,它将被内建的repr()函数转换为一个字符串,产生的字符串被写到标准输出(参见print语句) 的一行上。(生成None 的表达式不会被输出,因此过程调用不会带来任何输出。)

6.2. 赋值语句

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

  1. assignment_stmt ::= (target_list "=")+ (expression_list | yield_expression)
  2. target_list ::= target ("," target)* [","]
  3. target ::= identifier
  4. | "(" target_list ")"
  5. | "[" target_list "]"
  6. | attributeref
  7. | subscription
  8. | slicing

(最后三种符号的定义参见初级操作一节。)

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

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

赋值一个对象给一个目标序列按如下方式递归定义:

  • 如果对象列表是单一的目标:对象赋值给该目标。
  • 如果目标序列是逗号分隔的序列:对象必须是可迭代的且元素个数与目标序列中目标个数相同,然后元素从左向右赋值给对应的目标。
    赋值一个对象给一个单一的目标按如下方式递归定义。

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

  • 如果名称没有出现在当前代码块的global语句中:名称绑定到当前局部命名空间中的对象。

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

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

  • 如果目标是属性引用:计算引用中的初级表达式。它产生的对象应该具有一个可以赋值的属性;如果情况不是这样,则抛出TypeError异常。然后要求该对象将被赋值的对象赋值给给定的属性;如果不能做此操作,它会抛出一个异常(通常是AttributeError,但不一定)。

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

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

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

  • 如果目标是下标操作符:计算引用中的初级表达式。它应该生成一个可变的序列对象(例如列表)或者映射对象(例如字典)。然后,计算下标表达式。
    如果primary是一个可变的序列对象(例如一个列表),则下标必须产生一个普通的整数。如果它是负数,将会加上序列的长度。结果值必须是一个小于序列长度的非负整数,然后将要求序列赋值该对象给具有那个索引的元素。如果索引超出范围,则引发IndexError异常(给序列下标赋值不能添加新的元素到序列中)。

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

  • 如果目标是一个切片:计算引用中的初级表达式。它应该产生一个可变序列对象(例如列表)。被赋值的对象应该是相同类型的序列对象。下一步,如果存在,则计算下边界和上边界表达式;默认是零和序列的长度。边界计算的值应该是(小)整数。如果任意一个边界为复数,则会给它加上序列的长度。结果求得的边界在零和序列的长度之间,包括边界在内。最后,要求序列对象用赋值的序列元素替换切片。切片的长度可能不同于赋值的序列的长度,因此如果对象允许则改变目标序列的长度。
    CPython实现细节:在目前的实现中,目标的语法和表达式的语法相同,不合法的语法将在代码生成阶段被排除,导致不够详细的错误信息。

警告:虽然赋值的定义暗示左侧和右侧之间的交叉赋值是‘安全的’(例如,a,b=b,a交换两个变量),但是赋值目标集的内部有交叉则是不安全的!例如,下面的程序将打印[0,2]:

  1. x = [0, 1]
  2. i = 0
  3. i, x[i] = 1, 2
  4. print x

6.2.1. 增强的赋值语句

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

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

(最后三个符号的语法定义参见初级操作一节。)

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

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

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

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

6.3. assert 语句

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

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

其简单形式,assertexpression,等同于

  1. if __debug__:
  2. if not expression: raise AssertionError

其扩展形式,assertexpression1,expression2,等同于

  1. if __debug__:
  2. if not expression1: raise AssertionError(expression2)

这些等价的语句假定debugAssertionError引用的是同名的内建变量。在当前的实现中,内建的变量debug在正常情况下为为True,在要求优化时(命令行选项 -o)为False。在编译时刻,当要求优化时,目前的代码生成器不会为断言语句生成任何代码。注:不必把失败的表达式的源代码包含进错误信息;它将作为栈回溯的一部分显示出来。

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

6.4. pass 语句

  1. pass_stmt ::= "pass"

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

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

6.5. del 语句

  1. del_stmt ::= "del" target_list

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

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

删除一个名称将从局部或全局命名空间中删除该名称的绑定,取决于名称是否出现在相同代码块的global语句中。如果名称没有绑定,将抛出一个NameError 异常。

如果名称作为自由变量出现在嵌套的代码块中,从局部命名空间中删除它是非法的。

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

6.6. print 语句

  1. print_stmt ::= "print" ([expression ("," expression)* [","]]
  2. | ">>" expression [("," expression)+ [","]])

print依次计算每一个表达式并将求得的对象写入标准输出(参见下文)。如果对象不是字符串,那么首先使用字符串转换规则将它转换成字符串。然后输出(求得的或者原始的)字符串。在(转换和)输出每个对象之前会输出一个空格,除非输出系统认为它位于一行的开始。这些情况包括(1)还没有字符写入到标准输出,(2)写入到标准输出的最后一个字符是除''以外的空白字符,或者(3)最后向标准输出写入的操作不是print语句。(在某些情况下由于这个原因向标准输出写入一个空白字符串可能是有用的。)

注意

行为像文件对象但是不是内建的文件对象的对象通常不会恰当地模拟文件对象的这方面行为,所以最好不要依赖这个行为。

在结尾会写入一个'\n'字符,除非print语句以逗号结束。如果语句只包含关键字print,这将是唯一的行为。

标准输出定义为内建模块sys中名为stdout的对象。如果不存在该对象,或者它没有write()方法,将抛出一个RuntimeError异常。

print同样有一种扩展的形式,由上面描述的语法的第二部分定义。这种形式有时被称为“print chevron。”在这种形式中,>>之后的第一个表达式必须是一个“类文件”对象,具体点就是具有上面提到的write()方法的对象。通过这种扩展形式,随后的表达式被输入到该文件对象。如果第一个表达式求值为None,那么使用sys.stdout作为输出的文件。

6.7. return 语句

  1. return_stmt ::= "return" [expression_list]

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

如果存在expression_list,则计算它,否则使用None替换。

return离开当前的函数调用时以expression_list(或None)作为返回值。

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

在生成器函数中,return语句不允许包含expression_list。在这种情况下,空的return表明生成器已经完成并将导致StopIteration异常抛出。

6.8. yield 语句

  1. yield_stmt ::= yield_expression

yield语句只在定义生成器函数是使用,且只在生成器函数的函数体中使用。在函数定义中使用yield语句就足以创建一个生成器函数而不是普通函数。

当调用生成器函数时,它返回一个称为生成器迭代器的迭代器,或者通常就叫做生成器。生成器函数体的执行通过重复调用生成器的next()方法直到它抛出一个异常。

当执行一个yield语句时,生成器的状态将冻结起来并且将expression_list的值返回给next()的调用者。“冻结”的意思是所有局部的状态都会被保存起来,包括当前局部变量的绑定、指令指针、内部的计算栈:保存足够的信息以使得下次调用next()时,函数可以准确地继续,就像yield语句只是另外一个外部调用。

从Python 2.5版开始,yield语句允许在出现在tryfinally结构的try子句中。如果生成器在终结(引用数达到零或者被当作垃圾回收)之前没有恢复,将调用生成器迭代器的close()方法, 这允许任何挂起的finally子句可以执行。

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

注意

在Python 2.2中,yield语句只有当generators功能启用了时才允许。future导入语句用来启用该功能:

  1. from __future__ import generators

另请参阅

PEP 0255 - 简单的生成器添加生成器和yield语句到Python中的提议。PEP 0342 - 通过增强的生成器实现协程该提议提出,除了其它的生成器的增强之外,允许yield出现在tryfinally代码块的内部。

6.9. raise 语句

  1. raise_stmt ::= "raise" [expression ["," expression ["," expression]]]

如果没有表达式,raise 重新抛出当前作用域中最后一个激活的异常。如果当前作用域中没有活着的异常,则抛出TypeError异常以表示这是一个错误(如果在IDLE中运行,则会抛出Queue.Empty异常)。

否则,raise计算后面的表达式以得到三个对象,使用None作为省略的表达式的值。前面的两个对象用于决定异常的类型

如果第一个对象是一个实例,那么异常的类型是实例的类,实例本身是值,第二个对象必须是None。

如果第一个对象是一个类,那么它将成为异常的类型。第二个对象用于决定异常的值:如果它是类的实例,那么该实例将成为异常的值。如果第二个对象是一个元组,它用于类构造函数的参数列表;如果它是None,则使用一个空的参数列表,如果是其它任何对象则被当做构造函数的一个单一的参数。通过调用构造函数创建的实例将用作该异常的值。

如果存在第三个对象且不为None,那么它必须是一个回溯对象(参见标准类型的层次一节), 且它将替换当前异常发生的位置。如果存在第三个对象且值不是回溯对象或者None,将会抛出TypeError 异常。具有三个表达式形式的raise用于在except子句中显式地重新抛出异常,但是如果重新抛出的异常是当前作用域中最近激活的异常则应该优先使用不带表达式的raise

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

6.10. break 语句

  1. break_stmt ::= "break"

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

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

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

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

6.11. continue 语句

  1. continue_stmt ::= "continue"

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

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

6.12. import 语句

  1. import_stmt ::= "import" module ["as" name] ( "," module ["as" name] )*
  2. | "from" relative_module "import" identifier ["as" name]
  3. ( "," identifier ["as" name] )*
  4. | "from" relative_module "import" "(" identifier ["as" name]
  5. ( "," identifier ["as" name] )* [","] ")"
  6. | "from" module "import" "*"
  7. module ::= (identifier ".")* identifier
  8. relative_module ::= "."* module | "."+
  9. name ::= identifier

import语句分两步执行:(1)找到模块,如果必要则进行初始化;(2)定义(import语句所在作用域的)局部命名空间中的名称。该语句有两种形式,区别在于有没有使用from关键字。第一种形式(没有from)针对序列中的每个标识符重复执行这些步骤。具有from的形式将先执行一次步骤(1),然后重复执行步骤(2)。

为了理解步骤(1)如何发生,你必须首先理解Python如何处理模块的分层命名。为了帮助组织模块并提供一套命名的层级,Python有一个包的概念。包可以包含其它包和模块,但是模块不可以包含其它模块或者包。从文件系统的角度,包是目录而模块是文件。原始的包的说明仍然可以阅读,尽管自从该文档的编写以来小的细节已经发生了变化。

一旦知道模块的名字(除非特别指出,“模块”这个词兼指包和模块),模块或者包的搜索就可以开始。首先检查的地方是sys.modules,这里是之前已经导入的所有模块的缓存。如果找到该模块,那么将在导入的步骤(2)使用它。

如果在缓存中没有找到该模块,则搜索sys.meta_pathsys.meta_path的说明可以在PEP 302中找到)。该对象是finder对象的一个列表,通过以模块的名称调用它们的findmodule()方法可以知道如何加载模块。如果模块正好包含在某个包中(由名称中存在的点号表示),那么父包中的_path属性将作为findmodule() 第二个参数给出(正在导入的模块的名字中,最后一个点号之前的所有内容)。如果某个finder能够找到该模块,它将返回一个[_loader](#)(后面讨论)或者None。

If none of the finders on sys.meta_path are able to find the module then some implicitly defined finders are queried.Implementations of Python vary in what implicit meta path finders are defined.The one they all do define, though, is one that handles sys.path_hooks, sys.path_importer_cache, and sys.path.

The implicit finder searches for the requested module in the “paths” specified in one of two places (“paths” do not have to be file system paths).If the module being imported is supposed to be contained within a package then the second argument passed to findmodule(), _path on the parent package, is used as the source of paths.If the module is not contained in a package then sys.path is used as the source of paths.

Once the source of paths is chosen it is iterated over to find a finder that can handle that path.The dict at sys.path_importer_cache caches finders for paths and is checked for a finder.If the path does not have a finder cached then sys.path_hooks is searched by calling each object in the list with a single argument of the path, returning a finder or raises ImportError.If a finder is returned then it is cached in sys.path_importer_cache and then used for that path entry.If no finder can be found but the path exists then a value of None is stored in sys.path_importer_cache to signify that an implicit, file-based finder that handles modules stored as individual files should be used for that path.If the path does not exist then a finder which always returns None is placed in the cache for the path.

If no finder can find the module then ImportError is raised.Otherwise some finder returned a loader whose load_module() method is called with the name of the module to load (see PEP 302 for the original definition of loaders).A loader has several responsibilities to perform on a module it loads.First, if the module already exists in sys.modules (a possibility if the loader is called outside of the import machinery) then it is to use that module for initialization and not a new module.But if the module does not exist in sys.modules then it is to be added to that dict before initialization begins.If an error occurs during loading of the module and it was added to sys.modules it is to be removed from the dict.If an error occurs but the module was already in sys.modules it is left in the dict.

The loader must set several attributes on the module.name is to be set to the name of the module.file is to be the “path” to the file unless the module is built-in (and thus listed in sys.builtin_module_names) in which case the attribute is not set.If what is being imported is a package then path is to be set to a list of paths to be searched when looking for modules and packages contained within the package being imported.package is optional but should be set to the name of package that contains the module or package (the empty string is used for module not contained in a package).loader is also optional but should be set to the loader object that is loading the module.

If an error occurs during loading then the loader raises ImportError if some other exception is not already being propagated.Otherwise the loader returns the module that was loaded and initialized.

当步骤(1)结束时没有抛出异常,步骤(2)就可以开始。

import语句的第一种形式将局部命名空间中的模块名绑定到模块对象,然后如果有下一个标识符则继续导入。如果模块后面带有as,则as后面的名称将用于模块的局部名称。

from形式不绑定模块的名称:它遍历标识符序列,在步骤(1)中找到的模块中逐一查找它们,然后绑定局部命名空间中的名称到找到的对象。与第一种形式的import类似,可以通过“as localname”提供另外一个名称。如果找不到名称,则引发ImportError。如果用星号('*')替换标识符序列,那么模块中定义的所有公开的名称都将被绑定到import语句所在的局部命名空间。

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

带有from 形式只可以出现在模块作用域中。如果通配符形式的导入— import — 在函数中使用并且函数包含或者是一个带有自由变量的嵌套代码块,编译器将抛出SyntaxError

在指出你要导入的模块时,你不必指明模块的绝对路径名。当一个模块或者包包含在另外一个包中时,可以在同一个等级的包中使用相对导入而不需要提及包的名字。通过在from之后指定的模块或包中使用前导的点号,你可以指定相对当前包层级向上

原文: https://wizardforcel.gitbooks.io/python-doc-27-34/content/Text/3.6.html