python基础教程之简单语句
6. 简单语句¶
简单语句包含在单一的一个逻辑行中。几个简单语句可以用分号分隔出现在单一的一行中。简单语句的语法是:
simple_stmt ::= expression_stmt | assert_stmt | assignment_stmt | augmented_assignment_stmt | pass_stmt | del_stmt | print_stmt | return_stmt | yield_stmt | raise_stmt | break_stmt | continue_stmt | import_stmt | global_stmt | exec_stmt
6.1. 表达式语句¶
表达式语句用于(主要用于交互式地)计算和写入一个值,或者(通常)调用一个过程(返回结果没有意义的函数;在Python中,过程返回None值)。其它表达式语句的使用也是允许的,但是很少有意义。表达式语句的语法是:
expression_stmt ::= expression_list
表达式语句计算表达式列表的值(也可能是一个单一的表达式)。
在交互模式下,如果值不是None,它将被内建的repr()函数转换为一个字符串,产生的字符串被写到标准输出(参见print语句) 的一行上。(生成None 的表达式不会被输出,因此过程调用不会带来任何输出。)
6.2. 赋值语句¶
赋值语句用于(重新)绑定名称到具体的值以及修改可变对象的属性或元素:
assignment_stmt ::= (target_list "=")+ (expression_list | yield_expression) target_list ::= target ("," target)* [","] target ::= identifier | "(" target_list ")" | "[" target_list "]" | attributeref | subscription | slicing
(最后三种符号的定义参见初级操作一节。)
赋值语句计算expression_list(记住,它可以是一个单一的表达式也可以是一个逗号分隔的序列,后者生成一个元组)并且从左到右把单一的结果对象赋值给target_list的每一个元素。
赋值是递归定义的,取决于目标(序列)的形式。当目标是可变对象的一部分时(属性引用,下标或者切片),最终必须由该可变对象做赋值操作并决定其合法性,如果赋值不可接受可以抛出一个异常。各种类型遵守的规则以及抛出的异常根据对象类型的定义给出(参见标准类型的层次一节)。
赋值一个对象给一个目标序列按如下方式递归定义:
- 如果对象列表是单一的目标:对象赋值给该目标。
- 如果目标序列是逗号分隔的序列:对象必须是可迭代的且元素个数与目标序列中目标个数相同,然后元素从左向右赋值给对应的目标。
赋值一个对象给一个单一的目标按如下方式递归定义。
-
如果目标是一个标识符(名称):
- 如果名称没有出现在当前代码块的global语句中:名称绑定到当前局部命名空间中的对象。
- 否则:名称绑定到当前全局命名空间的对象。
如果名称已经绑定,那么它将重新绑定。这可能导致之前绑定到该名称的对象的引用计数变为零,引起该对象被释放并调用它的析构函数(如果有的话)。
-
如果目标是一个包含在圆括号或者方括号中的目标序列:对象必须是可迭代的且元素个数与目标序列中目标个数相同,然后元素从左向右赋值给对应的目标。
-
如果目标是属性引用:计算引用中的初级表达式。它产生的对象应该具有一个可以赋值的属性;如果情况不是这样,则抛出TypeError异常。然后要求该对象将被赋值的对象赋值给给定的属性;如果不能做此操作,它会抛出一个异常(通常是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()创建的属性。
-
如果目标是下标操作符:计算引用中的初级表达式。它应该生成一个可变的序列对象(例如列表)或者映射对象(例如字典)。然后,计算下标表达式。
如果primary是一个可变的序列对象(例如一个列表),则下标必须产生一个普通的整数。如果它是负数,将会加上序列的长度。结果值必须是一个小于序列长度的非负整数,然后将要求序列赋值该对象给具有那个索引的元素。如果索引超出范围,则引发IndexError异常(给序列下标赋值不能添加新的元素到序列中)。
如果primary 是一个映射对象(例如一个字典),下标必须具有和映射的关键字类型兼容的类型,然后要求映射创建一个键/值对将下标映射到赋值的对象。这既可以替换一个具有相同键值得已存在键/值对,也可以插入一个新的键/值对(如果不存在相同的键)。
-
如果目标是一个切片:计算引用中的初级表达式。它应该产生一个可变序列对象(例如列表)。被赋值的对象应该是相同类型的序列对象。下一步,如果存在,则计算下边界和上边界表达式;默认是零和序列的长度。边界计算的值应该是(小)整数。如果任意一个边界为复数,则会给它加上序列的长度。结果求得的边界在零和序列的长度之间,包括边界在内。最后,要求序列对象用赋值的序列元素替换切片。切片的长度可能不同于赋值的序列的长度,因此如果对象允许则改变目标序列的长度。
CPython实现细节: 在目前的实现中,目标的语法和表达式的语法相同,不合法的语法将在代码生成阶段被排除,导致不够详细的错误信息。
警告:虽然赋值的定义暗示左侧和右侧之间的交叉赋值是‘安全的’(例如,a, b = b, a交换两个变量),但是赋值目标集的内部有交叉则是不安全的!例如,下面的程序将打印[0, 2]:
x = [0, 1]
i = 0
i, x[i] = 1, 2
print x
6.2.1. 增强的赋值语句¶
增强的赋值是将二元操作和赋值语句组合成一个单一的语句。
augmented_assignment_stmt ::= augtarget augop (expression_list | yield_expression) augtarget ::= identifier | attributeref | subscription | slicing augop ::= "+=" | "-=" | "*=" | "/=" | "//=" | "%=" | "**=" | ">>=" | "<<=" | "&=" | "^=" | "|="
(最后三个符号的语法定义参见初级操作一节。)
增强的赋值将先求值target(和普通的赋值语句不同,它不可以是一个可拆分的对象)和expression_list,然后完成针对两个操作数的二元操作,最后将结果赋值给初始的target。target只计算一次。
像x += 1这样增强的赋值表达式可以重写成x = x + 1以达到类似但不完全等同的效果。在增强版本中,x只计算一次。还有,如果可能,真实的操作是原地的,意思是不创建一个新的对象并赋值给target,而是直接修改旧的对象。
除了不可以在一个语句中赋值给元组和多个目标,增强的赋值语句完成的赋值和普通的赋值以相同的方式处理。类似地,除了可能出现的原地行为,增强的赋值完成的二元操作和普通的二元操作相同。
如果target是属性引用,关于类和实例属性的注意事项同样适用于正常的赋值。
6.3. assert 语句
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)
这些等价的语句假定__debug__和AssertionError引用的是同名的内建变量。在当前的实现中,内建的变量__debug__在正常情况下为为True,在要求优化时(命令行选项 -o)为False。 在编译时刻,当要求优化时,目前的代码生成器不会为断言语句生成任何代码。注:不必把失败的表达式的源代码包含进错误信息;它将作为栈回溯的一部分显示出来。
给__debug__赋值是非法的。内建变量的值在解释器启动的时候就已决定。
6.4. pass 语句
pass_stmt ::= "pass"
pass是一个空操作 — 执行它的时候,什么都没有发生。它的用处是当语法上要求有一条语句但是不需要执行任何代码的时候作为占位符,例如:
def f(arg): pass # a function that does nothing (yet)
class C: pass # a class with no methods (yet)
6.5. del 语句
del_stmt ::= "del" target_list
删除是递归定义的,和赋值的定义方式非常相似。这里就不详细讲述完整的细节,只给出一些注意事项。
删除目标将从左向右递归删除每一个目标。
删除一个名称将从局部或全局命名空间中删除该名称的绑定,取决于名称是否出现在相同代码块的global语句中。如果名称没有绑定,将抛出一个NameError 异常。
如果名称作为自由变量出现在嵌套的代码块中,从局部命名空间中删除它是非法的。
属性引用、下标和切片的删除将传递给原始的对象;切片的删除在一般情况下等同于赋予一个右边类型的空切片(但即使这点也是由切片的对象决定)。
6.6. print 语句
print_stmt ::= "print" ([expression ("," expression)* [","]] | ">>" expression [("," expression)+ [","]])
print依次计算每一个表达式并将求得的对象写入标准输出(参见下文)。如果对象不是字符串,那么首先使用字符串转换规则将它转换成字符串。然后输出(求得的或者原始的)字符串。在(转换和)输出每个对象之前会输出一个空格,除非输出系统认为它位于一行的开始。这些情况包括(1)还没有字符写入到标准输出,(2)写入到标准输出的最后一个字符是除' '以外的空白字符,或者(3)最后向标准输出写入的操作不是print语句。(在某些情况下由于这个原因向标准输出写入一个空白字符串可能是有用的。)
注意
行为像文件对象但是不是内建的文件对象的对象通常不会恰当地模拟文件对象的这方面行为,所以最好不要依赖这个行为。
在结尾会写入一个'\n'字符,除非print语句以逗号结束。如果语句只包含关键字print,这将是唯一的行为。
标准输出定义为内建模块sys中名为stdout的对象。 如果不存在该对象,或者它没有write()方法,将抛出一个RuntimeError异常。
print同样有一种扩展的形式,由上面描述的语法的第二部分定义。这种形式有时被称为“print chevron。” 在这种形式中,>>之后的第一个表达式必须是一个“类文件”对象,具体点就是具有上面提到的write()方法的对象。通过这种扩展形式,随后的表达式被输入到该文件对象。如果第一个表达式求值为None,那么使用sys.stdout作为输出的文件。
6.7. return 语句
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 语句
yield_stmt ::= yield_expression
yield语句只在定义生成器函数时使用,且只在生成器函数的函数体中使用。在函数定义中使用yield语句就足以创建一个生成器函数而不是普通函数。
当调用生成器函数时,它返回一个称为生成器迭代器的迭代器,或者通常就叫做生成器。生成器函数体的执行通过重复调用生成器的next()方法直到它抛出一个异常。
当执行一个yield语句时,生成器的状态将冻结起来并且将expression_list的值返回给next()的调用者。“冻结”的意思是所有局部的状态都会被保存起来,包括当前局部变量的绑定、指令指针、内部的计算栈:保存足够的信息以使得下次调用next()时,函数可以准确地继续,就像yield语句只是另外一个外部调用。
从Python 2.5版开始,yield语句允许在出现在try … finally结构的try子句中。如果生成器在终结(引用数达到零或者被当作垃圾回收)之前没有恢复,将调用生成器迭代器的close()方法, 这允许任何挂起的finally子句可以执行。
注意
在Python 2.2中,yield语句只有当generators功能启用了时才允许。__future__导入语句用来启用该功能:
from __future__ import generators
6.9. raise 语句
raise_stmt ::= "raise" [expression ["," expression ["," expression]]]
如果没有表达式,raise 重新抛出当前作用域中最后一个激活的异常。如果当前作用域中没有活动的异常,则抛出TypeError异常以表示这是一个错误(如果在IDLE中运行,则会抛出Queue.Empty异常)。
否则,raise计算后面的表达式以得到三个对象,使用None作为省略的表达式的值。前面的两个对象用于决定异常的类型和值。
如果第一个对象是一个实例,那么异常的类型是实例的类,实例本身是值,第二个对象必须是None。
如果第一个对象是一个类,那么它将成为异常的类型。第二个对象用于决定异常的值:如果它是类的实例,那么该实例将成为异常的值。如果第二个对象是一个元组,它用于类构造函数的参数列表;如果它是None,则使用一个空的参数列表,如果是其它任何对象则被当做构造函数的一个单一的参数。通过调用构造函数创建的实例将用作该异常的值。
如果存在第三个对象且不为None,那么它必须是一个回溯对象(参见标准类型的层次一节), 且它将替换当前异常发生的位置。如果存在第三个对象且值不是回溯对象或者None,将会抛出TypeError 异常。具有三个表达式形式的raise用于在except子句中显式地重新抛出异常,但是如果重新抛出的异常是当前作用域中最近激活的异常则应该优先使用不带表达式的raise。
6.10. break 语句
break_stmt ::= "break"
break在语法上只可以出现在for或者while循环中,但不能嵌套在这些循环内的函数和类定义中。
它终止最相近的循环,如果循环有else子句将跳过。
6.11. continue 语句
continue_stmt ::= "continue"
continue在语法上只可以出现在for或while循环中,但不能嵌套在这些循环内的函数定义、类定义和finally子句中。它继续最内层循环的下一轮。
6.12. 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语句分两步执行:(1)找到模块,如果必要则进行初始化;(2)定义(import语句所在作用域的)局部命名空间中的名称。该语句有两种形式,区别在于有没有使用from关键字。第一种形式(没有from)针对序列中的每个标识符重复执行这些步骤。具有from的形式将先执行一次步骤(1),然后重复执行步骤(2)。
为了理解步骤(1)如何发生,你必须首先理解Python如何处理模块的分层命名。为了帮助组织模块并提供一套命名的层级,Python有一个包的概念。包可以包含其它包和模块,但是模块不可以包含其它模块或者包。从文件系统的角度,包是目录而模块是文件。原始的包的说明仍然可以阅读,尽管自从该文档的编写以来小的细节已经发生了变化。
一旦知道模块的名字(除非特别指出,“模块”这个词兼指包和模块),模块或者包的搜索就可以开始。首先检查的地方是sys.modules,这里是之前已经导入的所有模块的缓存。如果找到该模块,那么将在导入的步骤(2)使用它。
如果在缓存中没有找到该模块,则搜索sys.meta_path(sys.meta_path的说明可以在PEP 302中找到)。该对象是finder对象的一个列表,通过以模块的名称调用它们的find_module()方法可以知道如何加载模块。如果模块正好包含在某个包中(由名称中存在的点号表示),那么父包中的__path__属性将作为find_module() 第二个参数给出(正在导入的模块的名字中,最后一个点号之前的所有内容)。 如果某个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 find_module(), __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之后指定的模块或包中使用前导的点号,你可以指定相对当前包层级向上移动的高度而不用指明准确的名称。一个前导的点号表示正在进行导入的模块所存在的包。两个点号表示向上一级。三个点表示向上两级,等等。所以如果你从pkg包的一个模块中执行from . import mod,那么你导入的将是pkg.mod。如果你从pkg.subpkg1的内部执行from ..subpkg2 import mod,你将导入pkg.subpkg2.mod。相对导入的说明包含在PEP 328中。
还提供importlib.import_module()以支持应用程序动态决定需要加载哪些模块。
6.12.1. Future 语句¶
future语句是一个针对编译器的指令,它指示某个特定的模块应该使用在未来版本的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 2.6识别的特性有unicode_literals、print_function、absolute_import、division、generators、nested_scopes和 with_statement。generators, with_statement, nested_scopes在Python 2.6和更高的版本中是重复的,因为它们始终是被启用的。
future语句的识别和特殊处理在编译的时刻:核心语义的变化通常通过生成不同的代码实现。甚至有可能新的功能引入新的不兼容语法(例如一个新的保留字),在这种情况下编译器可能需要以不同的方式解析模块。这些决定不可能推迟到运行时刻。
对于任何给定的版本,编译器知道哪些特性的名称已经定义了,如果future语句包含一个不认识的特性它将抛出编译时刻错误。
运行时刻的语义和任何import 语句是一样的:有一个标准的模块__future__(在后面讲述),它将在future语句执行的时候以正常的方式被导入。
运行时刻的有趣语义取决于future 语句启用的特定功能。
注意下面的语句没有什么特别的:
import __future__ [as name]
它不是future 语句;它是一个普通的没有特殊语义和语法限制的import 语句。
在包含future语句的模块M中,exec语句编译的代码或者内建函数compile()和execfile()的调用默认将使用与future语句关联的新的语法或语义。从Python 2.2开始,这种行为可以通过compile()可选的参数可以控制 — 详细信息参见该函数的文档。
在交互式解释器中敲入的future 语句将对解释器剩下的会话生效。如果解释器以-i 选项启动,然后传入一个脚本的名称给它执行,且脚本包含一个future 语句,它将在脚本执行之后的交互式会话中生效。
另请参阅
- PEP 236 – 回到__future__
- __future__机制的原始提议。
6.13. global 语句
global_stmt ::= "global" identifier ("," identifier)*
global语句是针对当前整个代码块的声明。它的意思是列举出来的标识符要解释为全局的。不用global给全局变量赋值是不可能实现的,尽管自由变量可以引用全局变量而不用声明为全局的。
在相同的代码块中,global语句中列出的名称不可以在global语句之前使用。
global语句列出的名称不可定义为形式参数或者定义在for循环的控制目标、类定义、函数定义或import语句中。
CPython实现细节: 目前的实现没有强制实行后两个限制,但是程序不应该滥用这个自由,因为未来的实现可能强制实行或者默默地改变程序的含义。
程序员提醒:global是针对解析器的指令。它只应用于与global语句同一时刻解析的代码。特别地,exec语句中包含的global语句不会影响包含exec语句的代码块,包含exec语句的代码中的global语句不影响exec语句中包含的代码。eval()、execfile()和compile()函数具有同样的机制。
6.14. exec 语句
exec_stmt ::= "exec" or_expr ["in" expression ["," expression]]
该语句支持动态执行Python 代码。第一个表达式应该是Unicode 字符串,Latin-1 编码的字符串,文件对象,代码对象,或者元组。如果它是一个字符串,该字符串将被当做Python 语句组解析,然后执行(除非发生语法错误)。[1] 如果它是一个打开的文件,将解析该文件直到EOF并执行。如果它是一个代码对象,将简单地执行它。对于元组的解释,参见下文。对于所有的情况,都期望执行的代码和文件输入一样有效(参见文件输入一节)。注意即使在传递给exec语句的代码中,return和yield语句也不可以在函数定义之外使用。
在所有情况下,如果可选的部分被省略,代码将在当前的作用域中执行。如果in 之后给出第一个表达式,它应该是一个字典,全局和局部变量都将使用它。如果给出两个表达式,它们将分别用于全局和局部变量。如果给出,局部变量可以是任意一个映射对象。记住在模块级别,全局变量和局部变量是同一个字典。如果给出两个不同的对象作为全局变量 和 局部变量,代码的执行将像是在类定义中一样。
第一个表达式也可以是一个长度为2或者3的元组。在这种情况下,可选的部分必须被省略。exec(expr, globals) 形式等同于exec expr in globals, 而exec(expr, globals, locals) 等同于exec expr in globals, locals。exec 的元组形式提供了与Python 3的兼容性,在Python 3中exec 是一个函数而不是语句。
版本2.4中的变化:正式要求局部变量 为一个字典。
作为一种副作用,除了由执行的代码设置的变量名称,某种实现可能在给出的字典中插入额外的关键字。例如,当前的实现可能以键__builtins__添加一个指向内建模块__builtin__ 的引用(!)。
给程序员的提示:内建函数eval()支持动态计算表达式。内建函数 globals()和locals()分别返回当前的全局变量和局部变量字典,可传递给exec使用。
脚注
[1] | 注意解析器只接受Unix风格的行结束惯例。如果你正在阅读一个文件中的代码,请确保使用统一的新行模式以转换Windows或Mac风格的换行。 |