Python是一种解释性的、面向对象的、具有动态语义的高级程序设计语言。它内建高级数据结构,配以动态类型和动态捆绑,使其在快速应用开发中非常有利,就像脚本或粘合语言一样将已存在的构件连接在一起。Python的简单性和句法的易学性使其代码具有优秀的可读牲,因此维护程序的成本得以大大降低。Python具有模块和包的概念,以支持程序的模块化和代码重用。在主流平台上,Python的解释器和大量标准库都可以免费地以源代码形式或可执行文件形式获得,并且可以自由发布。
更新日期
更新内容
2015-04-09
第一版发布(PythonV3.4.3)
介绍
本参考手册描述了Python编程语言,不能替代入门教程。
虽然我尝试尽可能的精确,除了语法和词法分析之外,仍然选择使用英语而不是形式化的规范的来描述。这应该使文档更具可读性,但可能存在歧义。因此,如果你来自火星,仅从这个文档试图重新实现Python,您可能需要猜一些东西,事实上你可能会最终实施一门不同的语言。另一方面,如果您正在使用Python,想了解某些细节精确的语言规则,你应该可以在这里找到他们。如果你想看到一个更正式的语言定义,也许你自己搞了,或者发明一个克隆机:-)。
添加太多的实现细节到语言的参考文档中是危险的事情—实现可能会改变,并且同一语言的不同实现可能以不同方式工作。另一方面,CPython是广泛使用的一个Python实现(虽然替代实现继续得到支持),这里有些细节还是被提及了,特别是某种实现增加了限制时,因此你可以在本文档中找到“实现注意”的标记。
虽然这是迄今为止最受欢迎的Python实现,不同的Python爱好者对一些替代的实现有特别感兴趣。
已知实现包括:
CPython这是Python的原始和被维护最多的实现,用c语言编写,新的语言特性通常首先出现在这里。
每种实现在某种程度上的变化,从本手册记录的语言,或者介绍具体信息以外的标准Python文档。请参考详细的实现文档,以确定在使用的实现中还有什么是你想了解的。
在描述词法和句法分析时候,我们使用不甚严格的BNF,通常是以下的定义方式:
第一行说明name为lc_letter后跟随零个以上(包括零个)lc_letter或下划线的序列。lc_letter是”a”至”z”中任意一个字符.(实际上,这个”name”的定义贯穿于本文档的整个词法和语法规则中)
每个规则以一个名字(为所定义的规则的名字)和一个冒号"::="为开始.竖线(|)用于分隔可选项.这是记法中结合性最弱的符号.星号(*)意味着前一项的零次或多次的重复;同样,加号(+)意味着一次或多次的重复.在方括号([])中的内容意味着它可以出现零次或一次(也就是说它是可选的).星号"*"和加号"+"与前面的项尽可能地紧密的结合,小括号用于分组.字符串的字面值用引号括住.空白字符仅仅在分隔语言符号(token)时有用.通常规则被包含在一行之中,有很多可选项的规则可能会被格式化成多行的形式,后续行都以一个竖线开始。
在词法定义中(如上例),有两个习惯比较常用:以三个句点分隔的一对串字面值意味着在给定(包括)的ASCII字符范围内任选一个字符。在尖括号(<...>)中的短语给出了非正式的说明,例如,这用在了需要说明”控制字符”记法的时候。
即使在句法和词法定义中使用的记法几乎相同,但它们之间在含义上还是有着的很大不同:词法定义是在输入源的一个个字符上进行操作,而句法定义是在由词法分析所生成的语言符号流上进行操作。在下节(”词法分析”)中使用的BNF都是词法定义,以后的章节是句法定义。
词法分析
一个Python程序是由一个解析器读取的。解析器的输入是一个由词法分析器生成的符号流。本章介绍了词法分析器如何将文件分解为符号。
一个Python程序会被分成多个逻辑行。
物理行是一个以行尾序列结尾的字符序列。在源文件中,任何平台下标准的行终止序列都可以使用-在Unix平台上使用ASCIILF(换行)符,在Windows平台上使用ASCII字符序列CRLF(回车加换行),在老版本的Macintosh平台上使用ASCIICR(回车)符。无论使用什么平台,所有这些形式都可以使用。
使用嵌入式Python开发时,需要使用标准C语言约定的换行符将源代码传递给PythonAPI(字符\n表示ASCIILF,即行终止符)。
注释以(#)字符开始(且此时该符号不是当前字符串的一部分),以该物理行的结尾为结束。如果没有调用隐式行连接,那么注释即意味着逻辑行的结尾。注释会被语法分析所忽视,它们不会被作为程序代码处理。
#-*-coding:
该表达式也可以被GNUEmacs所识别,BramMoolenaar’sVIM识别的表达式形式为
#vim:fileencoding=
两个或者多个物理行可以使用反斜线(\)合并为一个逻辑行,例如:当一个物理行以反斜线结束,且该反斜线不是字符串值或注释的一部分时,它就与下面的物理行合并构成一个逻辑行,并将反斜线及行结束符删除。例如:
if1900 以反斜线结尾的行不能在其后加注释,反斜线不能延续注释行。除了字符串文本,反斜线也不能延续语言符号(也就是说,其他不是字符串文本的语言符号不可以使用反斜线横跨多个物理行)。在字符串文本外的其他地方出现反斜线都是非法的。 圆括号、方括号或大括号中的表达式可以跨多个物理行,而无需使用反斜杠。例如: month_names=['Januari','Februari','Maart',#Thesearethe'April','Mei','Juni',#Dutchnames'Juli','Augustus','September',#forthemonths'Oktober','November','December']#oftheyear 可以在隐式行连接的末尾添加注释。接续行的缩进可以不考虑。并且允许出现空的接续行。在隐式的接续行中是不存在NEWLINE符号的。隐式的行连接在三重引用串(后述)中也是合法的,在那种情况下不能添加注释。 一个只包含空格符、制表符、换页符和可能是注释的逻辑行可以忽略(也就是说没有NEWLINE符号产生)。在交互式输入语句时,空行的处理可能不同,其依赖于输入-计算-输出循环的实现方式。在标准的交互实现中,一个纯粹的空逻辑行(即不包含任何东西,甚至是空白和注释)可以结束多行语句。 位于逻辑行开始前的空白(空格和制表符)用于计算行的缩进层次,该层次可用于语句的分组。 制表符被(从左到右)替换为一至八个空格,这样直到包括替换部分的总字符数是八的倍数(这与Unix中使用的规则是相同的)。第一个非空字符前的空格总数决定了行的缩进。不能使用反斜线在多个物理行之间对缩进进行拆分;第一个反斜线之前的空白字符用于检测缩进。 跨平台兼容性注意:由于非UNIX平台上文本编辑器的特性,在单个源文件中混合使用空格符和制表符的缩进是不明智的。还需要注意的是,不同的平台可能会显式的限制最大缩进级别。 虽然在行首可能会出现换页符;但它在以上的缩进计算中会被忽略。出现在其他位置的换页符的作用是不确定的(比如,它可能将空格数重置为0)。 连续行的缩进层次用于生成INDENT及DEDENT语言符号,在此过程中使用了堆栈,如下所述。 文件的第一行未被读取之前,一个0被压入栈中;它以后也不会被弹出来。被压入栈中的数字都从栈底向栈顶增长,在每个逻辑行的开头处,将行的缩进层次与栈顶元素比较,如果相等则什么都不做。如果大于栈顶元素,则将其压入栈中并生成一个INDENT语言符号。如果小于栈顶元素,那么其应该是堆栈中已经存在的数字中的一个,堆栈中所有大于它的数都将被弹出,并且每个弹出的数字都会生成一个DEDENT语言符号。在文件的结尾,每个仍留在栈中且大于0的元素都会生成一个DEDENT语言符号。 这里有一个正确(尽管有点乱)缩进格式的Python代码的例子: defperm(l):#Computethelistofallpermutationsofliflen(l)<=1:return[l]r=[]foriinrange(len(l)):s=l[:i]+l[i+1:]p=perm(s)forxinp:r.append(l[i:i+1]+x)returnr 下面的例子展示了多种缩进错误: defperm(l):#error:firstlineindentedforiinrange(len(l)):#error:notindenteds=l[:i]+l[i+1:]p=perm(l[:i]+l[i+1:])#error:unexpectedindentforxinp:r.append(l[i:i+1]+x)returnr#error:inconsistentdedent (实际上,前三种错误都是由解析器检测出来的,只有最后一个错误是由词法分析器发现的——returnr的缩进层次与堆栈中弹出的数字不匹配。) 除非位于逻辑行的行首或者字符串当中,空格符、制表符以及换页符都可以用于分割语言符号。只有当两个符号串接在一起可能会被解释为不同的符号时,才会在两个符号之间增加空格(例如:ab是一个符号,但ab却是两个符号)。 除了NEWLINE,INDENT和DEDENT,还有以下几类语言符号:标识符、关键字、文本、运算符及分隔符。空格符不是语言符号(除了断行符,之前讨论过),但是可以用来分隔语言符号。当解释某个语言符号存在歧义时,该语言符号被看作是由一个尽可能长的字符串组成的合法符号(从左至右)。 标识符(也被称为名字)是由以下词法定义描述的。 在ASCII范围内(U+0001..U+007F),合法的标识符字符与Python2.X版本中是一致的:大写及小写字母的A到Z,下划线_以及数字从0到9(第一个字符除外)。 标识符的长度是没有限制的。 上面提到的Unicode分类代码分别代表: 解析器会将所有标识符转换为标准形式的NFKC,标识符的比较是基于NFKC的。 下面列出的标识符被用作保留字,或者叫做该语言的关键字,这些保留字不能作为普通标识符使用。这些关键字必须严格像下面一样拼写: FalseclassfinallyisreturnNonecontinueforlambdatryTruedeffromnonlocalwhileanddelglobalnotwithaselififoryieldassertelseimportpassbreakexceptinraise 不同类型的标识符(关键字除外)都有其特殊含义。这些类型由前导和尾随的下划线字符模式确定: __*类私有名称。在一个类定义的上下文中使用此类别中的名字时,为避免基类及派生类中“私有”属性之间的命名冲突,会被重写为一种乱序形式。 文本是某些内建类型常量的表示方法。 字符串文本由以下词法定义描述: 简单来说:两种类型的字符串文本都可以用成对的单引号(')或双引号(")括起来。它们也可以用成对的三个单引号或双引号括起来(这些通常统称为三重引号的字符串)。反斜线字符(\)称为转义字符,如果不转义则其可能会产生歧义,例如换行符、反斜线本身或者引号。 Python3.3中可能会再次在字符串文本前增加u字符前缀,用以简化2.X及3.X版本代码的维护工作。 字符串文本及字节文本都可以(可选)加上字符前缀r或R;这样的字符串被称为原始字符串,将反斜线视为原义字符。这样会导致在字符串文本中,原始串中的转义字符‘\U’及‘\u’不会被特殊处理。考虑到Python2.X版本中的原始unicode类型的特性与3.X版本差距较大,因此在Python3.X版本中不再支持‘ur’语法。 3.3版本新特性:原始字节文本的前缀‘rb’与‘br’意义相同。 在三重引用串中,允许出现未转义的新行和引用字符(并被保留),除非三个连续的引用字符串中断了该串。(引用字符是用于引用字符串的字符,如,’或”。) 如果给出一个‘r’或‘R’,那么其含义就像标准C中的规则类似的解释,转义序列如下: 转义序列 意义 注意事项 \newline 反斜线且忽略换行 \\ 反斜线(\) \’ 单引号(’) \" 双引号(”) \a ASCIIBell(BEL) \b ASCII退格(BS) \f ASCII换页符(FF) \n ASCII换行符(LF) \r ASCII回车符(CR) \t ASCII水平制表符(TAB) \v ASCII垂直制表符(VT) \ooo 八进制值为ooo的字符 (1,3) \xhh 十六进制值为hh的字符 (2,3) 字符串文本中的转义序列规则如下: \N{name} Unicode数据库中以name命名的字符 (4) \uxxxx 16位16进制字符值:xxxx (5) \Uxxxxxxxx 32位16进制字符值:xxxxxxxx (6) 注意: 不像标准C,所有不能被识别的转义序列都保留在串中且不做改变,例如,反斜线会保留在结果中。(这个行为在调试过程中非常有用:如果输入了一个错误的转义序列,在输出结果中更容易识别出错误。)此外,至关重要的是要注意转义字符只能在字符串文本中起作用,在字节文本类别中无法被识别。 即使在原始文本中,可以使用反斜线将引号转义,但是反斜线本身会在结果中保留;比如r"\""是一个由两个字符组成的合法字符串:一个反斜线与一个双引号;但r"\"却是一个非法字符串(即原始的字符串也不能以奇数个反斜杠结尾)。具体而言,一个原始的文本不能以单个反斜杠结尾(由于反斜线会将跟在其后的引号转义)。另外需要注意的是,如果一个反斜线跟在换行符后,反斜线与换行符会被当做文本的两个字符,而不是一个连续行。 多个相邻的字符串文本或字节文本(由空白分隔),允许使用不同的引用习惯,并且其含义与连接在一起时是一样的。因此,"hello"‘world’与"helloworld"是等价的。这个特性可以用来减少反斜线的使用数量,可以很方便的将一个长字符串分隔在多行上,甚至可以在字符串的某一部分添加注释,例如: re.compile("[A-Za-z_]"#letterorunderscore"[A-Za-z0-9_]*"#letter,digitorunderscore) 需要注意的是,这个特性是定义在句法层次上的,但是是在编译时实现的。在运行时连接串必须使用‘+’运算符。并且不同的引用字符可以混用,甚至可以将原始串与三重引用串混合使用。 总共有三种类型数字文本:整型、浮点型以及虚数型。不存在复数文本(复数可以由一个实数加一个虚数的形式给出)。 注意,数字文本不包含符号(正负号);像-1实际上是一个组合了一元运算符'-'和数字1的表达式。 整型文本描述的词法定义如下: 除了可以存储在可用内存中外,对整型文本的长度是没有限制的。 注意,不允许在非零的小数前加零。这是为了消除与Python3.0之前版本中使用的C风格八进制文本的歧义。 整型文本的例子如下: 721474836470o1770b1001101113792281625142643375935439503360o3770x100000000792281625142643375935439503360xdeadbeef 浮点型文本描述的词法定义如下: 注意,浮点型文本的整数部分及指数部分都使用十进制表示。比如,077e010是合法字符,并且与77e10等价。浮点型文本允许的范围是依赖于实现的。一些浮点型文本的例子如下: 3.1410..0011e1003.14e-100e0 注意,数字文本不包含符号(正负号);像-1实际上是由一元运算符-和文本1组成。 虚数文本描述的词法定义如下: 虚数是一个实部为零的复数,复数代表一对有着相同取值范围限制的浮点数对。为了创建一个实部非零的复数,可以对它增加一个浮点数,例如(3+4j)。下面是一些关于虚数文本的例子: 3.14j10.j10j.001j1e100j3.14e-10j 下面列出的符号为操作符: +-***///%<<>>&|^~<><=>===!= 以下符号用作语法上的分隔符: ()[]{},:.;@=->+=-=*=/=//=%=&=|=^=>>=<<=**= 句号也可以在浮点型及虚数文本中出现,一个连续三个句号的序列具有特殊含义,代表了一个片段中的省略部分。该列表的后半部分,即参数化赋值运算符,在词法上是作为分隔符处理,但也执行运算。 下面列出的ASCII字符作为其他符号的一部分具有特殊含义,或者对于词法分析器具有重要意义。 '"#\ 下面列出的ASCII字符没有在Python中使用。当它们出现在字符串文本及注释之外时就认为是非法的。 $` 数据模型 对象是Python的抽象数据。在一个Python程序中,所有数据都通过对象或对象之间的关系表示。(在某种意义上讲,和冯·诺依曼的“存储程序计算机”的模型一致,代码也是由对象所表示的。) CPython的实现细节:对于CPython,id(x)是x存储的内存地址。 某些对象的值是可以改变的。那些值可以改变的对象被认为是可变的;那些值不可以改变的对象一旦创建就被称为不可变的。(属于不可变对象的容器在包括有可变对象的引用时,当可变对象改变了时,其实是可变的,但仍被看作是可变对象,因为它所包含的对相机集合不可变的,所以不可变对象和值不可变是不完全一样的,它更加微妙。)一个对象的可变性是由它的类型来确定的;例如,数值,字符串和元组是不可变的,而字典和列表是可变的。 对象不用显式地释放他们;然而,当这些对象变的不能访问时,它们可能当做垃圾被收集。一种处理方式是延迟垃圾收集或者完全忽略——这是回收机制如何实现的质量问题,只要求尚能访问的对象不被回收。 有些对象包含了其它对象的引用;它们叫做容器。容器的例子有元组,列表和字典。引用作为容器值的一部分。大多数情况下,当我们谈及一个容器的值时,我们只是涉及了这个值,而不是所包含的对象;但是,当我们谈及容器对象的可变性的时候,就只是涉及被直接包含的对象的标识了。因此,如果一个不可变对象(如元组)包含了一个可变对象,那么只要这个可变对象的值变了则它的值也就改变了。 类型对对象的大多数行为都有影响。甚至在某种程度上对对象的标识也有重要的影响:对于不可变对象,计算新值的运算符可能实际上返回的是一个已经存在的具有相同类型和值的对象的引用,对于可变对象,只是不允许的。例如,在a=1;b=1之后,a和b有可能指向一个具有值为1的对象,这依赖于实现,但c=[];d=[]之后,c和d可以保证是保存了两个不同的,独立的,新创建的空列表。(注意c=d=[]是把相同的对象赋予了c和d。) 以下是Python内建类型的一个列表,扩展模块(用C语言,Java,或者其他语言写成,取决于实现方式)可以定义其他类型。以后版本的Python可能会在这个类型层次中增加其他类型(例如:有理数,高效率存储的整数数组,等等),即使通过标准库将会提供这些类型。 有些类型描述中包括了一个列出“特殊属性”的段落。他们提供了一些访问实现的方法而不是作为一般目的使用。这些定义可能会在未来改变。 无None这个类型具有单一值,也只有一个对象有这个值,这个对象可以通过内建名字None访问它在许多场合不是无值,例如它在那些没有返回值的函数中返回,其值为真值。 未实现NotImplemented这个类型具有单一值,也只有一个对象有这个值,这个对象可以通过内建名字NotImplemented访问。如果数值方法和许多比较方法的操作数未提供其实现,他们就可能返回这个值。(解释器会试图扩展成其它操作,或者其它退化的擦做,它依赖于运算符)他的真值为真。 省略Ellipsis这个类型具有单一只,也只有一个对象有这个值。这个对象可以通过文字...或者内建名字Ellipsis访问。其真值为真。 Python对整数,浮点数和复数区别对待: 有2种整数类型: 整数表示法的这个规则是为了使对负数操作时尽量有实际意义。 有序类型也支持片断:a[i:j]可以表示满足i<=k 有些有序类型用第三个“步骤”参数来支持“扩展片断”:a[i:j:k]选择所有项的索引x,其中x=i+n*k,n>=0并且i<=x 有序类型按照可变性分为两类: 不可变类型Immutablesequences一个不可变对象一旦建立其值不可能再改变。(如果这个对象包括其它对象的引用,这个被引用的对象可以是可变对象并且其值可以变化;但为该不可变对象所直接引用的对象集合是不能变的。) 以下类型是不可变类型: 元组Tuples元组对象的项可以是做任意的Python对象。具有两个或多个项的元组是用逗号分隔开表达式列表,只有一项的列表(独元)其项可以后缀一个逗号使其成为满足要求元组要求的表达式(一个表达式本身不能创建一个元组,因为圆括号本身用于表达式的分组)。一个空元组可以以一对空圆括号表示。 目前只有一种可变类型。 列表Lists列表对象的项可以是任意类型的Python对象。列表对象是在方括号之间的用逗号分开的表达式表。(注意,形成长度为0或者1的链表不要求特别的写法)。 对于集元素,相同的不变性属性作为申请字典关键字。注意,数字类型遵从数字比较的正常规则:如果两个数字比较后相等(比如1和1.0),那么会只有一个数字包含在集合里。 目前有两种原始集类型: 当前只有一个内置的映射类型: 字典Dictionaries本类型表达了几乎是任意类型对象都可作索引的有限对象集合。不可接受的键值仅仅是那些包括有列表和字典的值,或那些是通过值比较而不是通过对象标识符比较的类型的值。其原因是字典的高效实现要求键的哈希值保持不变。用于键值的数值型在比较时使用通常的规则:如果两值相等(如:1和0),那么它们可以互换地索引相同的字典项。 特殊属性: 属性 - __doc__ 是函数的文档串,如果无效就是None,不可以被子类继承 可写 __name__ 是函数名称 __qualname__ 是函数的限定名称,在版本3.3.中的新属性 __module__ 定义在函数模块里的定义,如果无效就是None __defaults__ 一个元组包含默认参数值,这些参数是默认的,如果没有默认参数值就是None, __code__ 一个编译后的代码对象, __globals__ 是一个引用,指向保存了函数的全局变量的字典——如果在函数所在模块中定义了全局变量的话, 只读 __dict__ 包含了支持任意函数属性的命名空间, __closure__ 要么是None,要么是包括有函数自由变量绑定的单元的元组, __annotations__ 是一个包含注释的字典参数,字典的关键字是参数名字,并且如果有提供的话,返回注释return, __kewdefaults__ 是一个只包含默认关键字的字典, 大部分的“可写”属性需要检查分配的值的类型。 函数对象也支持获取和设置任意属性,它可以被使用,例如,元数据功能。常规属性一样用于获取和设置这些属性。注意,当前的实现只支持函数属性对用户定义的函数,功能属性内置函数可能在未来才会支持。 关于函数的定义可以由它的代码对象得到;见下面关于内部对象的描述。 实例方法Instancemethods 实例方法合并了一个类,一个类实例和任何可调用对象(一般是用户自定义函数)。 方法也支持访问(不是设置)的其实际所调用的函数的任意属性。 如果类的属性是一个用户定义函数对象或类方法对象,那么获得类属性的用户可以创建自定义方法对象(也可能通过这个类的一个属性)。 当用户自定义方法对象采用一个用户自定义函数对象来创建时,这个对象来自于它的一个实例类,它的__self__属性是个实例,并且该方法称为是捆绑的。新方法的__func__属性是原始函数对象。 当用户自定义方法对象采用另外一个类或实例的方法对象创建时,其特性与函数对象创建时相同的,不同之处是这个__func__新类的属性不是原始函数对象,而是它的__func__属性。 当调用实例方法对象时,实际调用的是函数(__func__),同时在参数列表前插入类实例(__self__)。比如:当类C包含了一个函数定义f(),并且x是C的一个实例,调用x.f(1)相当于调用C.f(x,1)。 当一个实例方法对象时由一个类方法对象衍生而来时,存储在__self__的“类实例”实质上将会是类本身,所以不论调用x.f(1)或者C.f(1)相当于调用f(C,1),其中f是实际函数。 注意,由函数对象转变到实例方法对象,每次都会检索实例中的属性。在某些情况下,卓有优化是成效的优化是将属性分配给一个局部变量并调用它。同时应注意,这种转变只会发生在在用户自定义函数上,检索其他的可调用对象(以及不可调用对象)不需要这种转变。同样重要的是要注意,具有类实例属性的用户自定义函数不能转换成绑定方法;当且仅当发生在函数是一个类的属性时。 内建方法Built-inmethods这实际上是内建函数的一个不同的包装而已,此时传递给C函数一个对象作为隐含的参数。例如,内建方法alist.append()假定alist是一个列表对象,此时特殊只读属性__self__指定为对象alist。 属性的赋值更新模块的空间名字,例如,m.x=1等价于m.__dict__["x"]`。 特殊只读属性:__dict__是字典形式的模块名字空间。 Python实现细节:由于CPython清理模块字典的方式,当模块超出范围时,即使字典还在引用,字典模块也将被消除。为避免这种情况发生,在直接使用字典时应该备份字典或将其保留在模块附近。 类属性的赋值会更新类的字典,而不是基类的字典。 一个类对象可以创建(见上),这样会产生一个类实例(下述)。 类实例Classinstances 内部类型Internaltypes少部分由解释器内部使用的类型,开放给了用户。它们的定义可能会在未来的解释器版本中改变,但都会在这儿提及。 特殊属性:co_name给出了函数名;co_argument是位置参数的数目(包括有默认值的参数);co_nlocals是函数中局部变量的数目。co_varnames是一个包括局部变量名的元组(从参数名开始);co_callvars是一个元组,包括由嵌套函数所引用局部变量名;co_freevals包括了既不是局部变量也不是全局变量的;co_code包括了编译过的字节码指令序列的字符串;co_consts包括字节码中使用的字面值的元组;co_names包括字节码中使用的名字的元组;co_filename包括着编译过的字节码文件名;co_firstlineno是函数首行行号;co_lnotab是一个字符串,是字节码偏移到行号的映射(详见解释器代码);co_stacksize是所需要的堆栈尺寸(包括局部变量);co_flags是一个整数,其解释成为许多解释器的标志。 以下标志位由co_flags定义:如果函数使用*argument语法来接受任意数目的位置参数,就置位0x04;如果函数使用*keywords语法来接受任意数量的关键字参数,就置位0x08;如果函数是个生成器,就置位0x20。 在co_flages中的其他为预留为内部使用。 如果一个代码对象描述一个函数,则co_consts的第一项是该函数的文档串,如果未定义,它就是无None。 堆栈结构对象Frameobjects堆栈结构对象描述了可执行结构,它们会在跟踪回溯对象中出现(下述)。 特殊只读属性:f_back指出前一个堆栈结构(向着调用者的方向),如果位于堆栈的底部它就是None;f_code指出本结构中能执行的代码对象。f_locals是用于查找局部变量的字典;f_globals用于全局变量;f_builtin用于内建名字;f_lasti给出精确的指令(是一个代码对象的字符串的索引)。 特殊可写属性:f_trace如果非空,就是从每个源代码行的开始处调用的函数(用于调试器);f_lineno给出行号——写这个函数从内部跟踪的行(只限于最底层堆栈)。调试器可以通过编写f_lineno实现跳转命令(即设置下一条语句)。 堆栈结构对象支持一种方法: 堆栈结构清除frame.clear()这种方法清除所有局部变量的引用。同时,这个结构堆栈属于一个发生器,那么会终结这个发生器。这有助有终止堆栈结构对象循环(比如,供以后使用异常捕获和回溯存储)。 新版本3.4。 特殊只读属性:start是下界,step是上界,step是步进值,如果在片段中忽略了它,就是None。这些属性可以是任意类型的。 片断对象支持一种方法: 片断索引slice.indices(self,length)这个方法接受一个整数参数length并计算片断信息,切片对象用于描述length项序列。它返回一个元组包含为三个整数,分别代表开始索引,停止索引启动和偏度的步进或步长,处理缺失或界外指数的方式与普通片断一致。处理缺失或界外索引的方式与普通片断一致。 当实现一个模拟任何内建类型的类时,重要的地方在于模拟的程度只要使对象模拟时有效就行了。例如,某些有序类型的对象可能在单独提取某引起值时有效,但在使用片断时是没有意义的。(这样的一个例子是在W3C的文档对象模型中的NodeList接口。) 典型的实现方式是创建一个类的新实例,通过使用具有适当参数的super(currentclass,cls).__new__(cls[,...])调用超级类的__new__,然后根据需要,在返回之前修改新创建的实例。 如果__new__返回一个cls实例,那么新实例的方法__init__()将会像``__init__(self[,...])被调用,这时候,新实例和剩余的参数被传递给__new__()相同。 如果__new__不返回一个cls实例,那么就不会调用这个新实例的__init__()。 __new__的主要目的是允许子类不可变类型(如整数,字符或元组)定制实例创建。也通常覆盖在自定义原类中来创建自定义类。 object.__init__(self[,...])实例在创建(通过__new__创建)之后才会被调用,但之前返回给调用者。构造函数的参数是指传递表达式。如果一个基本类具有方法__init__()或派生类的方法__init__(),必须显式的调用它,以确保实例部分的基本类初始化;比如:BaseClass.__init__(self,[args...])。 因为__new__和__init__同时作用来构建对象(__new__创建它,__init__来定制),没有non-'None'值可能通过__init__来返回,这样做会导致在运行时上报Typeerror。 object.__del__(self)在实例被删掉时被调用,也叫作析构器。如果其基类也具有del()方法,继承类应该在其del()显式地调用它,以保证适当地删掉它的父类部分。注意,在del()通过创建新的引用来推迟删除操作是允许的,但这不是推荐的做法。它然后在最后这个引用删除时被调用。不能保证在解释器退出时,仍存在的对象一定会调用del()方法。 告警:因为调用del_()方法的不确定性,在它执行时的异常会被忽略,而只是在sys.stderr打印警告信息。另外,当某模块被删除,相应的del_()方法调用时(例如,程序退出时),有些为del_()方法所引用的全局名字可能已经删除了。由于这个原因,del_()方法应该对其外部要求保持到最小。Python1.5可以保证以单下划线开始的全局名字在其它全局名字被删除之前从该模块中被删除;如果没有其它对存在的全局名字的引用,这会对确定那些已导入的模块在调用del_()之后有那些还是有效的时是有所帮助的。 本函数典型地用法是用于调试,所以这个串表述成详尽并无歧义是十分重要的。 它的返回值必须是一个字符串对象。 object.__lt__(self,other)object.__le__(self,other)object.__eq__(self,other)object.__ne__(self,other)object.__gt__(self,other)object.__ge__(self,other)它们称为“厚比较”方法集,具体的方法名和相应的运算符的对应关系如下:x 厚比较方法的参数并不是非要有。 可以定义以下方法用于定制类实例属性的访问的含义(用于赋值,或删除x.name)。 object.__setattr__(self,name,value)在属性将被赋值时调用。这是作为正常机制的代替使用的(就是地实例字典中存储值)。name是实例值,vaule是要赋的值。 下列方法仅适用于一个包含方法类的实例(所谓的描述符类)出现在一个所有者类中(描述符必须在主类的字典中或者在其一个父类的类字典中)。下述的例子中,“属性”指的是其名字所有者类__dict__属性的关键属性。 object.__set__(self,instance,value)对所有者类的实例,将实例属性设一个新值及值时调用。 object.__delete__(self,instance)对所有者类的实例,将实例属性删除时调用。 属性访问的默认行为是从一个对象的字典中获取、设置、或者删除属性。比如,a.x是个起始为a.__dict__['x']的查询链,然后是type(a).__dict__['x'],紧接着通过type(a)基本类型,不包括元类。 不论如何,如果查询值是一个对象定义的描述符方法,那么Python覆盖默认行为,用调用描述符方法替代。这个在链中发生的优先级取决于定义何种描述方法和如何调用它们。 描述符调用的起点是个“绑定”,a.x。参数如何装配取决于a: 直接调用最简单最常见的调用是用户代码直接调用一个描述符方法:x.__get()__(a)。 实例绑定如果绑定到一个类实例,a.x转化为调用:type(a).__dict__['x'].__get__(a,type(a))。 类绑定如果绑定到一个类,A.x转化为调用:A.__dict__['x'].__get__(None,A)。 默认情况下,类的实例都有一本字典来存储属性,这对对象浪费空间有很少的实例变量。当创建大量实例时,空间消耗会变的很严重。 在类定义行,通过关键参数metaclass可以自定义创建类,或者从一个已存在的包含参数的类中继承。在下面的例子中,MyClass和MySubclass都是Meta的实例。 classMeta(type):passclassMyClass(metaclass=Meta):passclassMySubclass(MyClass):pass 任何其他类定义中指定的关键字参数传递到所有元类操作描述如下。 当执行一个类定义时,会发生如下步骤: 适当元类的定义如下: 从显式指向的元类(如果有的话)和详细说明的基本元类(如type(cls))中选择最派生元类。最派生元类是所有候选元类的子类中的一个。如果没有元类符合标准,类定义将会抛出TypeError。 一旦确定了适当的元类,那么类空间名也会准备好。如果元类具有__prepare__属性,它被调用为namespace=metaclass.__prepare__(name,bases,**kwds)(如果有来自类定义的额外关键参数)。 也可以参考: PEP3115-在Python3000中的元类介绍了__prepare__命名空间挂钩 然而,即使类定义发生在函数里时,在类里定义方法仍不能明确在类范围内定义的名字。类变量必须通过实例或类方法的第一个参数,无法访问所有的静态方法。 一旦通过执行类主体填充了类命名空间,通过调用metaclass(name,bases,namespace,**kwds)创建类对象(这里传递的关键字相同于传递到__prepare__)。 在创建类对象后,它是传递给在类中定义(如果有)的解释器,以及随之而来的在本地命名空间定义类的对象绑定。 PEP3135-新超级类描述了__class__隐式封闭引用 classOrderedClass(type):@classmethoddef__prepare__(metacls,name,bases,**kwds):returncollections.OrderedDict()def__new__(cls,name,bases,namespace,**kwds):result=type.__new__(cls,name,bases,dict(namespace))result.members=tuple(namespace)returnresultclassA(metaclass=OrderedClass):defone(self):passdeftwo(self):passdefthree(self):passdeffour(self):pass->>>A.members(`__module__`,`one`,`two`,`three`,`four`) class.__instancecheck__(self,instance)如果考虑实例应该是类的(直接或间接)实例,返回真。如果定义了它,调用实现isinstance(instance,class)。 class.__subclasscheck__(self,subclass)如果考虑子类应该是类的(直接或间接)子类,返回真。如果定义了它,调用实现issubclass(subclass,class)。 注意,在一个类的类型(元类)上查询这些方法,在真实类里,它们不能定义成类方法。这是符合实例上调用特殊方法查找的,也只有在这种情况下,实例本身就是一个类。 object.__call__(self[,args...]) 当实例像一个函数使用时调用本方法。如果定义了这个方法,那么x(arg1,arg2,...)是x.call(arg1,arg2,...)的缩写形式。 新的在版本3.4. **注意:**用以下三种方法完全完成切断,调用像 a[1:2]=b 翻译成 a[slice(1,2,None)]=b 等等。缺失的部分项用None来填充。 注意:for循环可以通过对由于对无效索引值而抛出的IndexError异常进行捕获来对访问有序类型的结尾做适当地检测。 object.__iter__(self)要求使用包容器的子迭代时,这个方法被调用。本方法应该返回一个可以迭代包容器所有对象的迭代子对象。对于映射,应该在键的基础上进行迭代, object.__contains__(self,item)使用成员测试运算符时调用。如果item在self中,返回true;否则返回false。对于映射对象,比较应该在键上进行,不应该是键值对。 以下方法用于模拟数值类型。其中,对于有些种类数值所不支持的操作对应的方法未定义(如,对非整数值的位运算)。 object.__add__(self,other)object.__sub__(self,other)object.__mul__(self,other)object.__truediv__(self,other)object.__floordiv__(self,other)object.__mod__(self,other)object.__divmod__(self,other)object.__pow__(self,other[,modulo])object.__lshift__(self,other)object.__rshift__(self,other)object.__and__(self,other)object.__xor__(self,other)object.__or__(self,other) 如果这些方法其中之一不支持提供的参数运算,它应该返回NotImplementd。 object.__radd__(self,other)object.__rsub__(self,other)object.__rmul__(self,other)object.__rtruediv__(self,other)object.__rfloordiv__(self,other)object.__rmod__(self,other)object.__rdivmod__(self,other)object.__rpow__(self,other)object.__rlshift__(self,other)object.__rrshift__(self,other)object.__rand__(self,other)object.__rxor__(self,other)object.__ror__(self,other) 注意:如果右操作数的类型是左操作数的类型的子类,子类提供了反映操作方法,这个方法将被称为前左操作数的non-reflected方法,这个动作允许子类覆盖他们的父类操作。 object.__iadd__(self,other)object.__isub__(self,other)object.__imul__(self,other)object.__itruediv__(self,other)object.__ifloordiv__(self,other)object.__imod__(self,other)object.__ipow__(self,other[,modulo])object.__ilshift__(self,other)object.__irshift__(self,other)object.__iand__(self,other)object.__ixor__(self,other)object.__ior__(self,other) object.__neg__(self)object.__pos__(self)object.__abs__(self)object.__invert__(self) object.__complex__(self)object.__int__(self)object.__float__(self)object.__round__(self[,n]) 上下文管理器的典型应用包括保存和回复各种全局状态,锁定和释放资源,关闭打开文件等。 如果提供的是个异常,并且方法想要停止异常(如,防止它被传播),它应该返回一个真值。否则,异常处理会退出这个方法 另请参考: PEP0343-with语句Python中with语句的规范,背景,以及用例。 对于自定义类,如果在对象类型上而不是字典对象的实例上定义它,特殊方法的隐式调用只能保证正常地工作。这就是为什么下面代码会抛出个异常: >>>classC:...pass...>>>c=C()>>>c.__len__=lambda:5>>>len(c)Traceback(mostrecentcalllast):File" >>>1.__hash__()==hash(1)True>>>int.__hash__()==hash(int)Traceback(mostrecentcalllast):File" 试图错误地以这种方式调用一个类的非绑定方法有时被称为“元类混乱”,而且避免了避开实例查找特殊方法: >>>type(1).__hash__(1)==hash(1)True>>>type(int).__hash__(int)==hash(int)True >>>classMeta(type):...def__getattribute__(*args):...print("Metaclassgetattributeinvoked")...returntype.__getattribute__(*args)...>>>classC(object,metaclass=Meta):...def__len__(self):...return10...def__getattribute__(*args):...print("Classgetattributeinvoked")...returnobject.__getattribute__(*args)...>>>c=C()>>>c.__len__()#通过实例隐式查找Classgetattributeinvoked10>>>type(c).__len__(c)#通过类型隐式查找Metaclassgetattributeinvoked10>>>len(c)#隐式查找10 执行模型 名字Names参考对象,名字绑定操作介绍了名字,程序代码中每一个名字的产生都参考从建立在包含使用的最里面功能块名字bingding。 代码块在运行结构框架内执行,一个框架包含一些管理信息(用来调试),决定执行完代码块后在哪继续执行和怎么执行。 一个范围定义代码块内名字的可视性,如果在一个快中定义了局部变量,它的范围就包含那个代码块,如果定义出现在一个函数块里,那么范围就扩展到任何一个包含这个定义的代码块,除非代码块引进一个不同的绑定名字。类代码块中定义名字范围仅限于类代码块;它不能扩展到方法代码块——包括理解式和生成表达式,因为它们使用一个函数实现范围。这意味着下面执行将会失败: classA:a=42b=list(a+iforiinrange(10)) 在代码块中使用名字时,使用最近封闭范围解决。所有这样的设置可视范围代码块的集合叫做块的环境。 每次分配或导入语句发生在代码块内,它通过类或者函数定义或者在模块级别定义(顶端是代码块)。 Python语句中有几种情况是在非法使用时结合最近的包含自由变量的范围。 如果在一个封闭范围中引用一个变量,删除这个名字是非法的。在编译时将产生一个错误报告。 Python解释器在它检测到一个运行时错误时抛出一个异常(比如除以零)。某个Python程序也可以通过raise语句显式地抛出异常。异常处理器可以用try...except语句指定。try...finally语句指定清理代码块,但是它不处理异常,只是无论先前代码中是否产生异常都会得到执行。 Python使用所谓的“中断”错误处理模型:一个异常处理器能在外层找出错误发生和继续执行的位置。但是它不能修复错误和重试错误的操作(除非重新从头进入该段出错的代码)。 注意:异常消息不是Python标准处理借口API的一部分,它的内容可能会伴随Python版本升级改变,而不带有任何告警,不应依赖于在多个版本的编译器下运行代码。 导入系统 你可以将包当成一个文件系统的目录,将模块当成目录中的文件,但不能太随便地做这样的类比,因为包和模块不需要来自文件系统。为了本文件的目的,我们将使用目录和文件的这个方便的类比。类似文件系统目录,包被分级组织起来,而且包本身也可以包含子包,常规模块也是如此。 重要的是要牢记所有的包都是模块,但不是所有的模块都是包。或者换一种说法,包仅是一种特殊的模块。具体地说,任何包含__path__属性的模块被认为是包。 举个例子,下面这个文件系统布局定义了一个有三个子包的顶层parent包: parent/__init__.pyone/__init__.pytwo/__init__.pythree/__init__.py 导入parent.one将隐式执行parent/__init__.py和parent/one/__init__.py。随后导入parent.two或者parent.three将分别执行parent/two/__init__.py和parent/three/__init__.py。 没有带有命名空间包的parent/__init__.py文件。实际上,导入搜索中可能有多种parent目录存在,而每一个目录都被不同的部分提供。因此,parent/one在物理上可能不是紧挨着parent/two边。在这种情况下,无论在什么时候导入该父包或它的子包时,Python将为高级别的父包创建一个命名空间包。 导入机制是可扩展的,因此新的查找器能够被添加来扩展模块搜索的范围和广度。 导入机制被设计为可扩展的;其基础的运行机制是导入钩子程序。存在两种导入钩子程序的型态:元钩子程序和导入路径钩子程序。 一个单一的导入要求可以多次遍历元路径。举例说明,假设还没有缓存任何所涉的模块,在每个元路径查找器(mpf)上调用mpf.find_spec("foo",None,None),导入foo.bar.baz将首先执行一个高阶导入。导入foo后,调用mpf.find_spec("foo.bar",foo.__path__,None),通过再一次遍历元路径,foo.bar将被导入。一旦完成导入foo.bar,最后的遍历将调用mpf.find_spec("foo.bar.baz",foo.bar.__path__,None)。 一些元路径查找器仅支持高阶导入。当除None以外的的任何东西作为第二参数通过时,这些导入器将一直返回None。 当找到一个模块分支时,在加载模块时导入机将使用它(及它包含的加载器)。下面是导入加载部分时所发生的近似值: module=Noneifspec.loaderisnotNoneandhasattr(spec.loader,'create_module'):module=spec.loader.create_module(spec)ifmoduleisNone:module=ModuleType(spec.name)#Theimport-relatedmoduleattributesgetsethere:_init_module_attrs(spec,module)ifspec.loaderisNone:ifspec.submodule_search_locationsisnotNone:#namespacepackagesys.modules[spec.name]=moduleelse:#unsupportedraiseImportErrorelifnothasattr(spec.loader,'exec_module'):module=spec.loader.load_module(spec.name)#Set__loader__and__package__ifmissing.else:sys.modules[spec.name]=moduletry:spec.loader.exec_module(module)exceptBaseException:try:delsys.modules[spec.name]exceptKeyError:passraisereturnsys.modules[spec.name] 注意以下细节: 3.4版本中的更新:加载器的create_module()方法。 为了与存在的加载器相兼容,导入机制将使用加载器的load_module方法(如果存在,并且加载器也不实现exec_module()的话)。然而,load_module()已不被推崇,加载器应替代实现exec_module()。 除了运行模块之外,load_module()方法必须实现以上所述的所有的公式化加载功能。经过一些补充说明,所有相同的约束条件都可应用。 在导入时使用一个分支允许语句在导入系统组件间传输,例如,在创建模块分支的查找器和运行它的加载器之间传输。最重要的是,它允许导入机制执行加载的公式化操作,而如果没有模块分支,加载器承担那项任务。 3.4版本的更新. 在加载器运行模块前,根据模块分支,导入机制在加载中在每个模块上填入下列属性。 __name__属性必须设置为模块的全限定名。该名称用来唯一识别导入系统的模块。 __loader__ __spec____spec__属性必须设置为导入模块时所使用的模块分支。这主要用作自查以及主要用在加载中。准确设置__spec__同样应用于解释器启动中初始化的模块。有一个例外是__main__,在一些例子中在__spec__设置为None。 3.4版本的更新: __path__ 假如模块是个包(不管常规包还是命名空间包),该模块对象的__path__属性必须设置。值必须是迭代的,但如果__path__没有更深的意义,值也可以是空的。如果__path__不是空的,迭代结束时它必须产生字符串。更多有关__path__语义学的细节将在下面给出。无包模块不应有__path__属性。 __file____cached__ __file__是可选的。如果设置,该属性的值必须是一个字符串。如果没有语义学意义(例如从数据库中加载的一个模块),导入系统可以选择不设__file__。 没有设置__file__时,也可以设置__cached__。然而,那种情况相当非典型。最终,加载器是利用__file__和/或__cached__的使用者。所以假如一个加载器能够从一个缓存模块中加载而不能从一个文件中加载的话,适用这个非典型的情况可能是合适的。 module.__path__ 根据定义,如果一个模块有一个__path__属性,无论它的值是什么,它就是一个包。 通过默认,所有模块都有一个可用的表示,然而,根据上述所设的各属性,在模块分支中,你可以更明确地控制各模块对象的表示。 假如模块存在一个分支(__spec__),导入机制将试图从它那生成一个表示。如果失败或不存在分支,导入系统将使用模块上任何可用信息制作一个默认的表示。它将尝试使用module.__name__,module.__file__和module.__loader__输入到该表示中,并默认缺失的任何信息。 以下是所使用的确切的规则: 基于路径的查找器本身不知道如何导入东西。相反,它遍历单一路径入口,将每个入口与知道如何处理这种特定路径的路径入口查找器结合起来。 路径入口不需要受制于文件系统位置。他们可以引用URLs,数据库查询,或者其他能够用一个字符串指定的位置。 相比之下,在某种意义上,路径入口查找器是实现基于路径查找器的一个详述,并且事实上,如果将路径基础查找器从sys.meta_path中移除,没有任何路径入口查找器语义将被调用。 早期的路径入口查找器可能实现这两个不被推崇的函数之一,而代替find_spec()。为了向后兼容,这些方法仍被赞誉。但是如果在路径入口查找器上实现find_spec(),那么遗留的方法将被忽略。 假如find_loader()返回一个不是None的加载器的值,该部分忽略,并且从基于路径查找器返回加载器,并通过路径入口结束搜索。 为了向后兼容其他导入协议的实现程序,许多路径入口查找器同时支持与元路径查找器支持的相同的、传统的find_module()方法。然而路径入口查找器的find_module()方法从来没有用一个path参数调用(他们预计会记录路径钩子程序的初始调用的准确路径信息)。 由于不允许路径入口查找器扩展命名空间包,因此路径入口查找器上的find_module()方法不被推荐使用。如果find_loader()和find_module()同时存在于一个路径入口查找器,导入系统将一直优先调用find_loader()而不是find_module()。 如仅改变导入语句的行为而不影响访问导入系统的其他APIs是可以接受的话,那么代替内建__import__()函数可能就足够了。也可以在模块级上采用这个技术仅在该模块中改变导入语句行为。 __main__ __main__.__spec__ 也要注意,即使当__main__对应一个可导入模块并且对应设置__main__.__spec__,但仍认为他们是远程模块。这是因为if__name__=="__main__"保护的区块:检查仅在模块用作密集__main__命名空间时执行,且不是在常规的导入程序中。 XXX做一个图表真的很有帮助。 XXXrunpy,pkgutil等人在图书馆手册都可以获得在上方的“SeeAlso”连接,其指向新的导入系统部分。 XXX增加有关__main__初始化不同方式的更多解释? 脚注 表达式 本章描述了Python中表达式的组成元素的含义。 句法注意:在本章和之后的章节中,描述句法时使用与词法分析时不同的扩展BNF记法。当某个句法规则(可能是可选的)具有如下形式: name::=othername 并且未给出特定语义时,name的这种形式的意义就与othername相同。 当用以下短语“数值型参数转换为通用类型”描述数值型操作数时,参数使用第三章结尾处的强制规则进行强制转换。如果两个参数都属于标准数值型,就使用以下的强制规则: 对于某些运算符有特殊的规则(例如,作为运算符‘%’左侧参数的字符)。扩展必须制定转换规则。 原子是表达式最基本的组成单位,最简单的原子是标识符或者字面值。以圆括号、方括号或大括号括住的符号在句法上也看成是原子。原子的句法如下: 私有名字变换:在类定义中,以两个或多个下划线开始,并且尾部不是以两个或多个下划线结束的标识符,它被看作是类的私有名字。在产生它的代码之前,私有名字被变换成更长的形式。这种变换是在将去掉前导下划线的类名插入到名字前,再在类名前插入一个下划线。例如,在类Ham中定义的标识符__spam,会被变换成__Ham_spam。本变换是不依赖于使用该标识符处代码的句法上的上下文的。如果变换后的结果过长(超过255个字符),就会执行该Python实现定义的截短名字的操作。如果某类的名字仅仅由下划线组成,这种变换是不会发生的。 Python支持字符串、字节和各种数值型的字面值: 所有字面值都属于不可变的数据类型,因此对象的标识比起它们的值来说显得次要一些。多次使用相同值的字面值(在程序代码中以相同形式出现或者以不同的形式出现)可能获得的是相同的对象或具有相同值的不同对象。 一个括号表达式是位于一对小括号内可选的表达式表。 表达式表生成什么类型的值括号表达式也就生成什么类型的值:如果表达式表中包括了至少一个逗号,它就生成一个元组;否则,就生成那个组成表达式表的唯一的表达式。 一个空的表达式表会生成一个空的元组对象。因为元组是不可变的,因此这里适用字符串所用的规则(即两个具有空表达式的元组可能是同一个对象也可能是不同的对象)。 请注意元组不是依靠小括号成定义的,而是使用逗号。其中空元组是个例外,此时要求有小括号——在表达式中允许没有小括号的“空”可能会引起歧义,并容易造成难以查觉的笔误。 为了构建一个列表、一个集合或者一个字典,Python提供了一种特殊的语法叫“表示”,每一个都有两种方式: 推导式的一般语法元素是: 请注意,推导式是在一个单独的范围内执行的,所以目标列表的名字分配不能泄漏到封闭的范围内。 一个列表用一对方括号括住的表达式序列(可能为空)表示: 使用一个列表会生成一个新的列表对。它的值由表达式表或由推导式给出。当给出一个逗号分隔的表达式表时,从左到右地对每个元素求值然后按顺序放进列表对象中。如果给出的是推导式,列表是由从推导式得出的元素组合而成。 一个集合是用花括号来表示的,它和字典的区别是缺少分隔键和值的冒号。 集合的表示将产生一个新的集合对象,内容是由一个表达式序列或者一个推导式来制定。当提供一个由逗号分隔的表达式列表时,将从左到由计算它的元素并把它们加入到集合对象中。当提供一个推导式时,集合将由从推导式中得出的元素组成。 一个空的集合不能由{}构建,这种形式将构建一个空的字典。 一个字典用一对大括号括住的键/数据对的序列(可能为空)表示: 使用一个字典会生成一个新的字典对象。 键/数据对按在字典中定义的从左到右的顺序求值:每个键对象作为键嵌入到字典中存储相应的数据。这意味着你可以在键/数据列表中多次指定相同的键,键值是最后一次指定的值。 字典推导式,与列表和集合推导式相反,需要两个由冒号分开的表达式,并且其后跟一般的“for”和“if”子句。当推导式在运行时,产生的键和值元素将按照它们生成的顺序插入到新的字典中去。 生成器表达式是由圆括号括起来的紧凑的生成器符号。 一个生成器表达式产生一个新的生成器对象。它是被括在圆括号中而不是方括号或者花括号中,除此之外,它的语法和推导式一样。 所有这些特性让生成器函数和协同程序非常像,它们可以多次输出,有多于一个的程序执行入口点,而且它们的执行过程可以被挂起。唯一的区别是当输出一次执行结果后,需要继续执行时,生成器函数无法控制,只有当生成器被调用时才可以被控制。 3.3版本修改的地方:添加yieldfrom 当yield表达式是赋值表达式右边唯一的表达式时,圆括号可以被忽略。 这一部分将描述生成器迭代器的方法,该方法可以控制生成器程序的执行。 generator.__next__() generator.send(value) generator.throw(type[,value[,traceback]]) generator.close() 此处用一个简单的例子来说明生成器和生成器函数的执行。 >>>defecho(value=None):...print("Executionstartswhen'next()'iscalledforthefirsttime.")...try:...whileTrue:...try:...value=(yieldvalue)...exceptExceptionase:...value=e...finally:...print("Don'tforgettocleanupwhen'close()'iscalled.")...>>>generator=echo(1)>>>print(next(generator))Executionstartswhen'next()'iscalledforthefirsttime.1>>>print(next(generator))None>>>print(generator.send(2))2>>>generator.throw(TypeError,"spam")TypeError('spam',)>>>generator.close()Don'tforgettocleanupwhen'close()'iscalled. 基元指和语言本身中接合最紧密的若干操作。它们的语法如下: 一个属性引用是由一个主元(primary)后跟一个句号和一个名字构成: 一个下标选择一个有序类型对象(字符串,元祖或列表)或映射(字典)对象的一项: 如果主元是一个映射,则对表达式表求值的结果必须是映射中的一个键,然后此下标操作在主元映射中选择与该键所对应的值。(如果表达式表只有一项,那么它就是一个元组)。 如果主元是一个有序类型,表达式(列表)的计算结果应该是一个整数或片段(见下方讨论部分)。 字符串的元素是字符,字符不是单独的数据类型而仅仅是只有一个字符长的字符串。 一个片断选择某个有序类型对象(如字符串、元组、列表)一段范围之内的项。片断可以作为表达式使用,或者是赋值和del语句的目标。下面是片断的句法: 在这里形式句法的说明中有点含糊:任何看起来像表达式表的语法构件也能看作是片断表,所以任何下标都可以解释为片断。但这样要比更复杂的句法要合适,该定义是没有歧义的,在这种情况下(在片断表中没有包括适当的片断或者省略写法)优先将其解释为下标,而不是片断。 在关键字参数表后面可以出现一个逗号,但它在语义上是没有任何作用的。 如果在函数调用中使用了*exprsiones句法,那么exprsiones的结果必须是有序类型的。这个有序类型对象的元素被当作附加的位置参数处理;如果存在有位置参数x1,...,xN,并且*exprsiones的计算结果为y1,...,yM,那么它与具有M+N个参数x1,...xN,y1,...,yM的调用等效。 由此可以得到一个推论:尽管*exprsiones句法出现在任何关键字参数之后,但它在处理关键字参数之前计算。(如果有的话,**exprsiones也是如此,参见下述),所以: >>>deff(a,b):...print(a,b)...>>>f(b=1,*(2,))21>>>f(a=1,*(2,))Traceback(mostrecentcalllast):File" 一同使用关键字语法和*expression的情况十分罕见,所以实际上这种混乱是不会发生的。 使用*identifier”或**identifier句法的形参不能作为位置参数或关键字参数名使用。 一个元组如果没有引发异常,通常会返回一些值,可能为None。怎样计算这个值依赖于可调用对象的类型。 如果它是— 类对象:返回该类的一个新实例。 类实例的方法:调用对应的用户自定义函数,其参数个数比普通的函数调用多一:该实例成为方法的第一个参数。 类实例:类实例必须定义方法call();效果与调用该方法相同。 幂运算符比在操作数左边的一元运算符有更高的优先级;但比右面的一元运算符要低。句法为: 因此,在一串没有括号的由幂运算符和一元运算符组成的序列,会从左到右面求值(没有强制改变求值顺序):-1**2结果为-1。 对于整数操作数,其结果的类型与操作数相同除非第二个参数为负数;在这种情况下,所有的参数被转换为float进而结果也是float。例如,10\*\*2返回100,但10\*\*-2返回0.01。 所有一元算术运算符(和位运算符)有相同的优先级: 一元运算符-(减)对其数值型操作数取负。 一元运算符+(加)不改变其数值型操作数。 一元运算符(取反)对其普通整数或长整数参数求逆(比特级)。x的比特级求逆运算定义为-(x+1)。它仅仅用于整数型的操作数。 二元算术运算符的优先级符合我们的正常习惯。注意其中有些运算符也可以应用于非数值型操作数。除了幂运算符,它们只分两个优先级:一个是乘法类运算,一个是加法类运算。 *(乘)运算符计算其操作数的积,其两个参数必须是数值型的,或一个是整数另一个是有序类型。第一种情况下,数值参数被转换成通用类型然后计算积。后一种情况下,重复连接有序类型对象。一个负连接因子产生一个有空的有序类型对象。 +(加)运算符计算参数的和。参数必须都是数值型的,或都是相同有序类型的对象。对于前一种情况,它们先转换成通用类型,然后相加。后一种情况下,所有有序类型对象被连接起来。 -(减)计算参数的差,数值型的参数首先转换成通用类型。 移位运算符的优先级比算术运算符低。 这些运算符接受整数作为参数。它们将第一个参数向左或向右移动第二个参数指出的位数。 右移n位可以定义为除以pow(2,n),左移n位可以定义为乘以pow(2,n); 三个二元位运算符具有各不相同的优先级: &运算符进行比特级的AND(与)运算,参数必须是整数。 运算符进行比特级的XOR(异或)运算,参数必须是整数。 |运算符进行比特级的OR(同或)运算,参数必须是整数。 不像C语言,在Python中所有比较运算符具有相同的优先级,比所有算术运算符、移位运算符和位运算符的优先级都低,并且,表达式a 比较运算返回逻辑值:True意味着结果为真,False意味着结果为假。 比较运算符可以任意的连接,例如,x 形式上,如果a,b,c,...,y,z为表达式,op1,op2,...,opN为比较运算符,则aop1bop2c...yopNz等价于aop1bandbop2cand...yopNz,但是每个表达式最多只求值一次。 注意aop1bop2c并没有隐式地规定a和c之间的比较运算种类,所以x 相同类型的对象的比较法则依赖于该类型: 不同类型的对象的比较取决于该类型是否对比较算法提供了明确的支持。大多数数字类型可以互相比较。当不支持交叉式的比较时,方法会返回NotImplemented。 in运算符和notin运算符用于测试集合成员。如果x是集合s的成员,那么xins的结果为真,否则为假。xnotins的结果与上相反。所有的内置序列和集合类型和和字典一样都支持in测试,测试字典中是否含有给定的键。对于容器类型,如列表、元组、可变集合、不可变集合、字典,或双向队列,表达式xiny等价于any(xiseorx==eforeiny)。 对于字符串和字节类型,当且仅当X是y的子字符串时xiny为真,等价于y.find(X)!=1。空字符串被认为是任意一个字符串的子字符串,所以""in"abc"返回True。 如果运算符not的参数为False,它返回True,否则返回False。 表达式xandy首先计算x;如果x为假,就返回它的值;否则,计算y的值,并返回其结果。 表达式xory首先计算x;如果x为真,就返回它的值;否则,计算y的值,并返回其结果。 条件表达式(有时也称为“三元算子”)在所有的Python操作中具有最低优先级。 表达式xifCelsey,首先计算条件C,而不是条件x,如果C为真,则计算x并返回x的值;如果C为假,则计算y并返回y的值。 lambda表达式(也称作lambda形式)被用于创建类型函数。表达式lambdaarguments:expression生成一个行为与下面定义的函数一致的函数对象: def 一个表达式表是一个包括至少一个逗号的元组,它的长是表中表达式的个数。其中表达式从左到右按顺序计算。 最后的逗号仅仅在创建单元素元组(又称为”独元”)时才需要;在其它情况下,它是可选的。一个没有后缀逗号的表达式不会创建元组,但仍会计算该表达式的值。(可以使用一对空括号()创建一个空元组)。 Python从左到右计算表达式,注意当计算一个赋值表达式时,先计算右边再计算左边。 下边的内容,表达式将以他们后缀的顺序来计算 expr1,expr2,expr3,expr`(expr1,expr2,expr3,expr4`{expr1:expr2,expr3:expr4}expr1+expr2*(expr3-expr4)expr1(expr2,expr3,*expr4,**expr5)expr3,expr4=expr1,expr2 下表总结了Python中运算符的优先级,从低优先级(弱捆绑)到高优先级(强捆绑),在同一格子中的运算符具有相同的优先级。如果没有特殊的句法指定,运算符是二元的。一格子内的运算符都从左至右结合(幂运算符是个例外,它从右至左结合)。 运算符 描述 lambda lambda表达式 if-else 条件表达式 or 布尔OR and 布尔AND notx 布尔NOT in,notin,is,isnot,<,<=,>,>=,!=,== 成员测试,标志测试,比较 | 比特级OR ^ 比特级XOR & 比特级AND <<,>> 移位 +,- 加减 *,/,//,% +x,-x,~x 正运算,负运算,比特级not ** x[index],x[index:index],x(arguments...),x.attribute 下标,片段,函数调用,属性 (expressions...),[expressions...],{key:value...},{expressions...} 表达式分组或元祖,列表,字典,集合 [2]如果X的值约等于y的整数倍,由于舍入原因,x//y的值很可能比x-x%y//y的值大。在这种情况下,为了保持divmod(x,y)[0]*y+x%y的值和x的值非常接近,Python将返回后者的值。 [5]运算符%也被用来格式化字符串;与“取余”操作的优先级相同。 [6]与算术运算符或位运算符相比,运算符**右边的数结合的并没有特别紧密,即2**-1的值为0.5。 简单语句 简单语句可以在一个逻辑行内表示,一些简单语句可以写在一行由分号分隔,简单语句的句法如下: 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 一个表达式语句要对该表达式列表(可能只是一个表达式)求值. 使用赋值把名字绑定(重新绑定)到值,以及修改可变对象的属性或者项目: assignment_stmt::=(target_list"=")+(expression_list|yield_expression)target_list::=target(","target)*[","]target::=identifier|"("target_list")"|"["target_list"]"|attributeref|subscription|slicing|"*"target 一个赋值语句要对该表达式列表求值(请记住这可以是一个表达式或者一个逗号分隔的列表,后者导出一个元组),然后从左到右地将对象结果依次赋给目的列表里的每个对象。 分配对象到目标列表,括号或方括号内为可选,递归地定义如下. 一个对象向单个目标递归地赋值定义如下。 如果名字已经被约束了它就被重新约束。这可能导致早先约束到该名字的对象的引用计数降为零,导致释放该对象的分配空间并调用其析构器,如果它有一个的话。 注意:如果对象是类的实例并且属性引用的赋值操作发生在两端,RHS表达式,a.x既能访问一个实例属性,也能(如果没有实例属性存在)访问一个类的属性。这个LHS目标a.x通常设定一个实例属性,如果有必要就创建。因此,这两个a.x的发生不需要涉及同样的属性,如果RHS表达式涉及到类的属性,这个LHS会创建一个新的实例属性作为赋值的目标。 classCls:x=3#classvariableinst=Cls()inst.x=inst.x+1#writesinst.xas4leavingCls.xas3 如果基础对象是一个映射对象(比如字典),下标的类型必须和映射的键类型兼容,接着该映射就被要求创建一个把下标映射到被赋对象的键/数据对。这(操作)要不用新键值取代已存在的具有相同键的键/值对,要不插入一个新的键/值对(如果不存在相同的键) CPython实现细节:在当前实现中,目标对象的语法采取同一表达式,同时无效的语法在代码生成阶段内被拒绝,导致更少的详细的错误消息输出. 虽然赋值的定义隐含着左手边和右手边之间的重叠是“同时的”(比如,a,b=b,a交换两个变量),在所赋值变量间的重叠却是不安全的!在集合中出现从左到右分配变量间的重叠是不安全的,有时候值是混乱的,例如,下面的程序打印出[0,2]: x=[0,1]i=0i,x[i]=1,2#iisupdated,thenx[i]isupdatedprint(x) 增量赋值就是在单条语句内合并一个二元运算和一个赋值语句。 augmented_assignment_stmt::=augtargetaugop(expression_list|yield_expression)augtarget::=identifier|attributeref|subscription|slicingaugop::="+="|"-="|"*="|"/="|"//="|"%="|"**="|">>="|"<<="|"&="|"^="|"|=" 一条增量赋值语句对目标(和一般的赋值语句不同,它不能是展开的对象)和表达式列表求值,执行特定于两个操作数的赋值类型的二元运算,并将结果赋值给原先的目标。目标仅求值一次。 一条赋值语句,比如x+=1,可以重写为x=x+1,效果是类似的,但并不完全一样。在增量版本中,x仅求值一次。而且,只要可能,实际的操作是就地进行的,意思是并非创建一个新对象然后将其赋值给目标,而是修改老的对象。 不像普通的赋值语句,增量赋值先计算左边再计算右边。例如,a[i]+=f(x)首先查看a[i],然后它计算f(x)并执行加法操作,最后,把结果写入a[i]。 除了在一条语句中赋值给元组和多个对象的情况,增量赋值语句所完成的赋值用与普通赋值同样的方式处理。类似地,除了可能的就地方式,由增量赋值执行的二元运算和普通的二元运算也是一样的。 assert语句是一个在程序中插入调试断言的常用方法: 简单形式的,”assertexpression”,等价于: if__debug__:ifnotexpression:raiseAssertionError 扩展形式的,”assertexpression”,等价于: if__debug__:ifnotexpression1:raiseAssertionError(expression2) pass_stmt::="pass" deff(arg):pass#afunctionthatdoesnothing(yet)classC:pass#aclasswithnomethods(yet) del_stmt::="del"target_list 与赋值的定义方法类似,删除也是递归的。下面是一些说明: 一个目标表的递归删除操作会从左到右地删除其中的每个对象地. 3.2版本变化:之前如果嵌套数据块中有空变量,在本地命名空间中删除名字是不合法的。 return_stmt::="return"[expression_list] 如果给出了表达式表,就计算其值,否则就用None代替. yield_stmt::=yield_expression yield 等同于yield表达式语句 (yield raise_stmt::="raise"[expression["from"expression]] 异常的类型是异常实例类,值是实例本身。 traceback对象通常被自动创建,当异常发生并且被附加到它的__traceback__属性上。你可以创建异常并设置你自己的traceback,在第一步使用异常方法with_traceback()(这个方法返回相同的异常实例,traceback设置到他的参数中),就像: raiseException("foooccurred").with_traceback(tracebackobj) from子句用于异常链接:如果给出,则第二个表达式必须是另一个异常类或实例,然后将附加到的__cause__属性(它是可写)引发的异常。如果不处理引发的异常,则将打印两个例外情况: >>>>>>>>>try:...print(1/0)...exceptExceptionasexc:...raiseRuntimeError("Somethingbadhappened")fromexc...Traceback(mostrecentcalllast):File" >>>>>>>>>try:...print(1/0)...except:...raiseRuntimeError("Somethingbadhappened")...Traceback(mostrecentcalllast):File" break_stmt::="break" continue_stmt::="continue" 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".")*identifierrelative_module::="."*module|"."+name::=identifier 当语句包含多个子句(由逗号分隔)分别对每个子句实施两个步骤,就像子句被分隔成个体的import语句。 如果请求的模块检测到成功,它将提供局部空间中的三种方式中的一种: Examples:例子: importfoo#fooimportedandboundlocallyimportfoo.bar.baz#foo.bar.bazimported,fooboundlocallyimportfoo.bar.bazasfbb#foo.bar.bazimportedandboundasfbbfromfoo.barimportbaz#foo.bar.bazimportedandboundasbazfromfooimportattr#fooimportedandfoo.attrboundasattr 一个模块所定义的“公共名称”通过检查该模块的命名空间中的名为__all__的变量决定。如果(该变量)有定义,它必须是一个字符串的有序序列,这些字符串是由该模块定义或者导入的名字。在__all__中给出的名字都被认为是公共的且要求其存在。如果__all__没有定义,(该模块的)公共名字的集合就包含所有在该模块的名字空间中找到的,不以下划线(”_”)起首的所有名字。 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::=identifiername::=identifier future语句必须出现在模块的顶部。唯一能出现在future前面的语句是: Python3.0版本公认的特性是absolute_import,division,generators,unicode_literals,print_function,nested_scopes和with_statement。他们都是多余的,因为他们总是在启用状态,同时只保持向后兼容的可编译性。 在编程的时候future语句被识别出来,并且被特殊地对待:更改核心构建的语义通常是通过生成不同的代码来实现的。甚至有可能一个新的功能会引入一个新的不兼容的语法(比如一个新的保留字),在这种情况下,编译器可能需要用区别对待的方式解析模块。此类决定不能被推迟到运行时。 对于任何发布的版本,编译器明确已经被定义的功能名称,如果future语句包含未知功能时,编译器会提示编译出错。 请注意,没有什么特别的语句。 import__future__[asname] 那个不是future语句;这是一个普通的导入语句没有特殊的语义或语法的限制。 在交互式解析器提示符下键入future语句,重启解析器器会话生效。如果解析器以-i选项开始,且是通过一个脚本名去执行,同时这个脚本包含future语句,它将作用在交互会话中,在脚本执行后开始。 global_stmt::="global"identifier(","identifier)* CPython详细实现:当前实现不强制两个限制,但程序不应该滥用这种自由,作为将来的实现可能会强制他们或默默地改变程序的意义。 nonlocal_stmt::="nonlocal"identifier(","identifier)* 复合语句 复合语句中包含了其他语句(语句组);它以某种方式影响或控制着其他语句的执行。一般来讲,复合语句会跨越多个行,然而一个完整的复合语句也可以简化在一行中。 iftest1:iftest2:print(x) ifx 总结: compound_stmt::=if_stmt|while_stmt|for_stmt|try_stmt|with_stmt|funcdef|classdefsuite::=stmt_listNEWLINE|NEWLINEINDENTstatement+DEDENTstatement::=stmt_listNEWLINE|compound_stmtstmt_list::=simple_stmt(";"simple_stmt)*[";"] 为了能够叙述清楚,以下章节中的每个子句的语法规则格式都被分行说明。 if_stmt::="if"expression":"suite("elif"expression":"suite)*["else"":"suite] while_stmt::="while"expression":"suite["else"":"suite] for_stmt::="for"target_list"in"expression_list":"suite["else"":"suite] for循环语句序列可以对循环控制对象列表中的变量赋值。这将覆盖所有以前分配给那些变量的值,包括for循环中的语句序列中的变量: foriinrange(10):print(i)i=5#thiswillnotaffectthefor-loop#becauseiwillbeoverwrittenwiththenext#indexintherange 注意:如果序列对象在循环过程中被修改(只有可变类型会出现这种情况,例如列表),这里有一些需要注意的地方。有一个内部计数器用于跟踪下一轮循环使用的元素,并且每迭代一次便增加一次。当这个计数器的值达到了序列的长度时循环终止。这就意味着如果从语句序列中删除当前(或前一个元素)元素,下一个元素会被跳过而不被执行(因为当前索引值的元素已经处理过了)。另一方面,如果在当前元素前插入一个元素,下一轮循环时当前元素会被再次重复处理。这会导致难以察觉的错误,但可以通过使用含有整个有序类型对象的片段而生成的临时拷贝避免这个问题,例如: forxina[:]:ifx<0:a.remove(x) try_stmt::=try1_stmt|try2_stmttry1_stmt::="try"":"suite("except"[expression["as"identifier]]":"suite)+["else"":"suite]["finally"":"suite]try2_stmt::="try"":"suite"finally"":"suite 当使用astarget关键词给异常赋值时,该target会在except子句结束后被销毁。例如: exceptEasN:foo 可以解释为: exceptEasN:try:foofinally:delN 这就意味着必须给异常指派一个不同的名称,以保证在except子句结束后可以索引到它。这些异常最后会被清除,因为这些异常与堆栈信息会绑定在一起,他们与栈帧形成了循环引用。直到下次垃圾回收发生前,所有这些栈中的局部变量都是存活的。 >>>deff():...try:...1/0...finally:...return42...>>>f()42 >>>deffoo():...try:...return'try'...finally:...return'finally'...>>>foo()'finally' with_stmt::="with"with_item(","with_item)*":"suitewith_item::=expression["as"target] 5、执行语句序列。 withA()asa,B()asb:suite 等价于 withA()asa:withB()asb:suite 3.1版本中新特性:支持多上下文表达式。 funcdef::=[decorators]"def"funcname"("[parameter_list]")"["->"expression]":"suitedecorators::=decorator+decorator::="@"dotted_name["("[parameter_list[","]]")"]NEWLINEdotted_name::=identifier("."identifier)*parameter_list::=(defparameter",")*|"*"[parameter](","defparameter)*[",""**"parameter]|"**"parameter|defparameter[","])parameter::=identifier[":"expression]defparameter::=parameter["="expression]funcname::=identifier 函数定义是一个可执行语句。它在当前局部命名空间中将函数名称与函数对象(函数的可执行代码的组合)捆绑在一起。该函数对象包括着一个全局命名空间的引用,以便在调用时使用。 @f1(arg)@f2deffunc():pass deffunc():passfunc=f1(arg)(f2(func)) 默认参数值在函数定义被执行时从左至右计算。这意味着在函数定义时,该表达式仅被执行一次,且在每次调用时都使用相同的“预计算”值。这在理解默认参数值是一个像列表或者字典这样的可变对象时尤其值得注意。如果函数修改了这个对象(例如给一个列表中追加了一个元素),默认值也随之被修改。这显然不是我们期望的。一个避免这个问题的方法就是使用None作为默认值,并在函数体中做显式测试,例如: defwhats_on_the_telly(penguin=None):ifpenguinisNone:penguin=[]penguin.append("propertyofthezoo")returnpenguin 参数列表可能包含“参数名称后紧跟:expression”形式的注解。所有参数都有注解,甚至*identifier或**identifier形式的参数也有注解。函数参数列表后可能包含->expression形式的“return”注解。这些注解可以是任意合法的Python表达式,并且会在函数定义执行时计算这些表达式的值。注解的计算顺序可能会与其在源代码中出现的书序不同。注解的出现不会改变一个函数的语义。函数对象的__annotations__属性中的参数名称可以作为字典的key,其对应的value为注解的值。 classdef::=[decorators]"class"classname[inheritance]":"suiteinheritance::="("[parameter_list]")"classname::=identifier classFoo:pass classFoo(object):pass 类中也可以加修饰符:就像修饰函数一样, @f1(arg)@f2classFoo:pass classFoo:passFoo=f1(arg)(f2(Foo)) 类修饰符表达式的值计算方式与函数修饰符相同。其结果必须是一个类对象,并将该类对象与一个类名绑定。 顶层组件 Python解释器的可以有几种输入源:从标准输入或程序参数传入的脚本,交互方式下的输入,从模块源文件,等等。本章给出在这些情况下使用的语法。 针对于文件输入来说的完整的Python程序语法,在下一节给出描述。 在Unix上,一个完整程序可以以三种形式传给解释器:使用-c字符串命令行选项,以一个文件作为命令行的第一个参数,或作为标准输入。如果文件或标准输入是一个tty(终端)设备,解释器进行交互模式;否则,它把文件作为一个完整程序来运行。 所有从非交互文件的输入读取具有相同的形式: 这个语法用于以下的情况: 交互模式输入使用以下语法进行解析: 请注意一个(顶层)复合语句后面在交互模式下必须跟着一个空行;需要用它来帮助解释器检测输入的结束。 eval()用于表达式的输入。它将忽略前面的空格。Eval()的字符串参数必须具有以下形式: 完整的语法规范 这是一个完整的Python语法,它由解析器生成器读取,并用于解析Python源文件。