笔记:Python 语法要点汇总

Python 是一门解释型语言,无需编译和链接

Python 解释器

交互模式

脚本模式


源码编码

默认情况下,Python 源文件是 UTF-8 编码
指定源文件的字符编码
# -*- coding:utf-8 -*-
注释,#注释 及"""注释"""

数据结构

标准数据类型

ython3 中有六个标准的数据类型:

  • Number(数字)
  • String(字符串)
  • List(列表)
  • Tuple(元组)
  • Set(集合)
  • Dictionary(字典)

Python3 的六个标准数据类型中:

  • 不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组);
  • 可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。

数字(Number)
Python3 支持 int、float、bool、complex(复数)
整数类型是 int,带有小数部分的数字类型是 float
使用 / 运算符返回浮点数使用 // 运算符返回整数
变量在使用前必须 “定义”(赋值),否则会出错
浮点数有完整的支持;整数和浮点数的混合计算中,整数会被转换为浮点数
交互模式中,最近一个表达式的值赋给变量 _
数字型变量对于用户是只读的,再次赋值只会创建独立的同名变量

字符串(String)
用单引号 ('...') 或双引号 ("...") 标识字符串,效果相同
\ 可以用来转义引号
在交互式解释器中,输出的字符串会用引号引起来,特殊字符会用反斜杠转义
在第一个引号前面加上一个 ,可获取原始字符串

字符串文本能够分成多行,使用三引号:"""...""" 或者 '''...''',
行尾换行符会被自动包含到字符串中,可以在行尾加上 \ 来避免这个行为

字符串可以由 + 操作符连接(粘到一起),可以由 * 表示重复
相邻的两个字符串文本自动连接在一起,只用于两个字符串文本,不能用于字符串表达式
Python没有单独的字符类型;一个字符就是一个简单的长度为1的字符串
字符串可以被截取(检索),索引值以 0 为开始值,-1 为从末尾的开始位置
注意 -0 实际上就是 0,所以它不会导致从右边开始计算
字符串支持切片
索引用于获得单个字符,切片 让你获得一个子字符串
注意,包含起始的字符,不包含末尾的字符。这使得 s[:i] + s[i:] 永远等于 s
切片的索引有非常有用的默认值;省略的第一个索引默认为零,省略的第二个索引默认为切片的字符串的大小

切片的工作方式:切片时的索引是在两个字符 之间
左边第一个字符的索引为 0,而长度为 n 的字符串其最后一个字符的右界索引为 n
 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
 0   1   2   3   4   5   6-6  -5  -4  -3  -2  -1
对于非负索引,如果上下都在边界内,切片长度就是两个索引之差
试图使用太大的索引会导致错误

一个过大的索引值(即下标值大于字符串实际长度)将被字符串实际长度所代替,
当上边界比下边界大时(即切片左值大于右值)就返回空字符串
Python字符串不可以被更改,它们是 不可变的 ;赋值给字符串索引的位置会导致错误

内置函数 len() 返回字符串长度

特殊字符例如 \n 在单引号('...')和双引号("...")中具有相同的含义。
两者唯一的区别是在单引号中,你不需要转义 " (但你必须转义 \' )

列表(List)
列表时括号之间的一列逗号分隔的值
列表的元素不必是同一类型
列表可以被索引和切片,所有的切片操作都会返回一个包含请求的元素的新列表
列表支持连接操作
列表是可变的,允许修改元素
可以对切片赋值,此操作可以改变列表的尺寸,或清空它
内置函数 len() 同样适用于列表
允许嵌套列表(创建一个包含其它列表的列表)

在列表的末尾添加新的元素,append(x) 
在指定位置插入一个元素。第一个参数是准备插入到其前面的那个元素的索引,list.insert(ix)
删除列表中值为 x 的第一个元素,list.remove(x);如果没有这样的元素,就会返回一个错误
从列表的指定位置删除元素,并将其返回,list.pop([i]);如果没有指定索引,a.pop() 返回最后一个元素
从列表中删除所有元素,list.clear()
返回列表中第一个值为 x 的元素的索引,list.index(x);如果没有匹配的元素就会返回一个错误
返回 x 在列表中出现的次数,list.count(x);
对列表中的元素就地进行排序,list.sort()
就地倒排列表中的元素,list.reverse()
返回列表的一个浅拷贝,list.copy();等同于 a[:]
将一个给定列表中的所有元素都添加到另一个列表中,list.extend(L)

把列表当作堆栈使用
堆栈作为特定的数据结构,最先进入的元素最后一个被释放(后进先出)
用 append() 方法可以把一个元素添加到堆栈顶。用不指定索引的 pop() 方法可以把一个元素从堆栈顶释放出来

把列表当作队列使用
队列作为特定的数据结构,最先进入的元素最先释放(先进先出)
不过,列表这样用效率不高。相对来说从列表末尾添加和弹出很快;在头部插入和弹出很慢(因为,为了一个元素,要移动整个列表中的所有元素)
要实现队列,使用 collections.deque,它为在首尾两端快速插入和删除而设计

列表推导式
列表推导式由包含一个表达式的括号组成,表达式后面跟随一个 for 子句,之后可以有零或多个 for或 if 子句。结果是一个列表,由表达式依据其后面的 for 和 if 子句上下文计算而来的结果构成
列表推导式为从序列中创建列表提供了一个简单的方法
squares x ()squares.append(x)
注意这个 for 循环中的被创建(或被重写)的名为 x 的变量在循环完毕后依然存在
squares xx ()
列表推导式可使用复杂的表达式和嵌套函数

嵌套的列表推导式
列表解析中的第一个表达式可以是任何表达式,包括列表解析

del语句
从列表中按给定的索引而不是值来删除一个子项: del 语句
不同于有返回值的 pop() 方法,语句 del 可以从列表中删除切片或清空整个列表
del 也可以删除整个变量

元组
一个元组由数个逗号分隔的值组成
元组在输出时总是有括号的,以便于正确表达嵌套结构。
在输入时可以有或没有括号,不过经常括号都是必须的(如果元组是一个更大的表达式的一部分)
不能给元组的一个独立的元素赋值(尽管你可以通过联接和切割来模拟)。
可以创建包含可变对象的元组,例如列表
元组有很多用途。例如 (x, y) 坐标对,数据库中的员工记录等等。
元组就像字符串, 不可变的
通常包含不同种类的元素并通过分拆(参阅本节后面的内容) 或索引访问(如果是 namedtuples,甚至可以通过属性)
一对空的括号可以创建空元组; empty = ()
要创建一个单元素元组可以在值后面跟一个逗号:singleton = 'hello',
(在括号中放入一个单值不够明确)


集合
集合是一个无序不重复元素的集
基本功能包括关系测试和消除重复元素
大括号或 set() 函数可以用来创建集合
创建空集合,你必须使用 set() 而不是 {},后者用于创建空字典
集合推导式语法,a = {x for x in 'abracadabra' if x not in 'abc'}

字典
字典在某些语言中可能称为 联合内存 ( associative memories )或 联合数组 ( associative arrays )
字典以 关键字 为索引,关键字可以是任意不可变类型,通常用字符串或数值
理解字典的最佳方式是把它看做无序的键: 值对 (key:value 对)集合,键必须是互不相同的(在同一个字典之内)
一对大括号创建一个空的字典: {} 
初始化列表时,在大括号内放置一组逗号分隔的键:值对,这也是字典输出的方式
字典的主要操作是依据键来存储和析取值
可以用 del 来删除键:值对(key:value)
用一个已经存在的关键字存储值,以前为该关键字分配的值就会被遗忘
试图从一个不存在的键中取值会导致错误。
对一个字典执行 list(d.keys()) 将返回一个字典中所有关键字组成的无序列表
如果你想要排序,只需使用 sorted(d.keys()) 
使用 in 关键字(指Python语法)可以检查字典中是否存在某个关键字(指字典)
dict() 构造函数可以直接从 key-value 对中创建字典
dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
字典推导式可以从任意的键值表达式中创建字
如果关键字都是简单的字符串,有时通过关键字参数指定 key-value 对更为方便
dict(sape=4139, guido=4127, jack=4098)
循环技巧
在字典中循环时,关键字和对应的值可以使用 items() 方法同时解读出来:
knights = {'gallahad': 'the pure', 'robin': 'the brave'}
for k, v in knights.items():
    print(k, v)
 
在序列中循环时,索引位置和对应值可以使用 enumerate() 函数同时得到
for i, v in enumerate(['tic', 'tac', 'toe']):
    print(i, v)
 
同时循环两个或更多的序列,可以使用 zip() 整体打包:
questions = ['name', 'quest', 'favorite color']
answers = ['lancelot', 'the holy grail', 'blue']
for q, a in zip(questions, answers):
    print('What is your {0}?  It is {1}.'.format(q, a))
 
需要逆向循环序列的话,先正向定位序列,然后调用 reversed() 函数:
 for i in reversed(range(1, 10, 2)):
    print(i)
 
要按排序后的顺序循环序列的话,使用 sorted() 函数,它不改动原序列,而是生成一个新的已排序的序列:
basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
for f in sorted(set(basket)):
    print(f)
 
若要在循环内部修改正在遍历的序列(例如复制某些元素),建议您首先制作副本。在序列上循环不会隐式地创建副本。切片表示法使这尤其方便:
words = ['cat', 'window', 'defenestrate']
for w in words[:]:  # Loop over a slice copy of the entire list.
    if len(w) > 6:
        words.insert(0, w)


深入条件控制
while 和 if 语句中使用的条件不仅可以使用比较,而且可以包含任意的操作
比较操作符 in 和 not in 审核值是否在一个区间之内
操作符 is 和 is not 比较两个对象是否相同
所有的比较操作符具有相同的优先级,低于所有的数值操作

比较操作可以传递。例如 a < b == c 审核是否 a 小于 b 并且 b 等于 c
比较操作可以通过逻辑操作符 and 和 or 组合,比较的结果可以用 not 来取反义

逻辑操作符 and 和 or 也称作短路操作符,它们的参数从左向右解析,一旦结果可以确定就停止
如果 A 和 C 为真而 B 为假, A and B and C 不会解析 C
作用于一个普通的非逻辑值时,短路操作符的返回值通常是最后一个变量

可以把比较或其它逻辑表达式的返回值赋给一个变量
需要注意的是 Python 与 C 不同,在表达式内部不能赋值

比较序列和其它类型
序列对象可以与相同类型的其它对象比较
比较操作按 字典序 进行:首先比较前两个元素,如果不同,就决定了比较的结果;如果相同,就比较后两个元素,依此类推,直到所有序列都完成比较
如果两个元素本身就是同样类 型的序列,就递归字典序比较。如果两个序列的所有子项都相等,就认为序列相等
如果一个序列是另一个序列的初始子序列,较短的一个序列就小于另一个
字符串的字典序按照单字符的 ASCII 顺序
需要注意的是如果通过 < 或者 > 比较的对象只要具有合适的比较方法就是合法的

控制流程

 if 语句

if … elif … elif … 序列用于替代其它语言中的 switch 或 case 语句

 for 语句

Python 的 for 语句依据任意序列(链表或字符串)中的子项,按它们在序列中的顺序来进行迭代
(通常的循环可能会依据一个等差数值步进过程(如 Pascal),或由用户来定义迭代步骤和中止条件)
在迭代过程中修改迭代序列不安全(只有在使用链表这样的可变序列时才会有这样的情况)

range() 函数

如果你需要一个数值序列,内置函数 range() 会很方便,它生成一个等差级数链表:
for i in range(5):
    print(i)
range(10) 生成了一个包含 10 个值的链表,它用链表的索引值填充了这个长度为 10 的列表,所生成的链表中不包括范围中的结束值。也可以让 range() 操作从另一个数值开始,或者可以指定一个不同的步进值(甚至是负数,有时这也被称为 “步长”):
range(5, 10)
   5 through 9range(0, 10, 3)
   0, 3, 6, 9range(-10, -100, -30)
  -10, -40, -70
需要迭代链表索引的话,如下所示结合使 用 range() 和 len()
a = ['Mary', 'had', 'a', 'little', 'lamb']
for i in range(len(a)):
    print(i, a[i])
输出
0 Mary1 had2 a3 little4 lamb
不过,这种场合可以方便的使用 enumerate()

在不同方面 range() 函数返回的对象表现为它是一个列表,但事实上它并不是。当你迭代它时,它是一个能够像期望的序列返回连续项的对象;但为了节省空间,它并不真正构造列表。

我们称此类对象是 可迭代的,即适合作为那些期望从某些东西中获得连续项直到结束的函数或结构的一个目标(参数)。我们已经见过的 for 语句就是这样一个迭代器。list() 函数是另外一个( 迭代器 ),它从可迭代(对象)中创建列表
 


 break 和 continue 语句, 以及循环中的 else 子句

break 语句和 C 中的类似,用于跳出最近的一级 for 或 while 循环
循环可以有一个 else 子句;它在循环迭代完整个列表(对于 for )或执行条件为 false (对于 while)时执行,但循环被 break 中止的情况下不会执行

与循环一起使用时,else 子句与 try 语句的 else 子句比与 if 语句的具有更多的共同点:try 语句的 else 子句在未出现异常时运行,循环的 else 子句在未出现 break 时运行。更多关于 try 语句和异常的内容,请参见 异常处理

continue 语句是从 C 中借鉴来的,它表示循环继续执行下一次迭代,(结束本次循环执行语句,开始下一次循环)

 pass 语句

pass 语句什么也不做。它用于那些语法上必须要有什么语句,但程序什么也不做的场合
这通常用于创建最小结构的类:
class MyEmptyClass:
    pass
 
另一方面,pass 可以在创建新代码时用来做函数或控制体的占位符。可以让你在更抽象的级别上思考。pass 可以默默的被忽视:
def initlog(*args):
    pass   # Remember to implement this!
 

定义函数

关键字 def 引入了一个函数 定义。在其后必须跟有函数名和包括形式参数的圆括号。函数体语句从下一行开始,必须是缩进的
函数体的第一行语句可以是可选的字符串文本,这个字符串是函数的文档字符串,或者称为 docstring
在你的代码中包含 docstrings 是一个好的实践
函数 调用 会为函数局部变量生成一个新的符号表,所有函数中的变量赋值都是将值存储在局部符号表
变量引用首先在局部符号表中查找,然后是包含函数的局部符号表,然后是全局符号表,最后是内置名字表
全局变量不能在函数中直接赋值(除非用 global 语句命名),尽管他们可以被引用

函数引用的实际参数在函数调用时引入局部符号表,因此,实参总是 传值调用 (这里的  总是一个对象 引用 ,而不是该对象的值)。[1] 一个函数被另一个函数调用时,一个新的局部符号表在调用过程中被创建

一个函数定义会在当前符号表内引入函数名。函数名指代的值(即函数体)有一个被 Python 解释器认定为 用户自定义函数 的类型。 这个值可以赋予其他的名字(即变量名),然后它也可以被当做函数使用。这可以作为通用的重命名机制
return 语句从函数中返回一个值,不带表达式的 return 返回 None
过程结束后也会返回 None
语句 result.append(b) 称为链表对象 result 的一个 方法。方法是一个“属于”某个对象的函数,它被命名为 obj.methodename,这里的 obj 是某个对象(可能是一个表达式), methodename 是某个在该对象类型定义中的方法的命名
不同的类型定义不同的方法。不同类型可能有同样名字的方法,但不会混淆。(当你定义自己的对象类型和方法时,可能会出现这种情况,class 的定义方法详见  )

深入 Python 函数定义

在 Python 中,可以定义包含若干参数的函数
默认参数值
最常用的一种形式是为一个或多个参数指定默认值
1
2
3
4
5
6
7
8
9
10
11
def  ask_ok(prompt, retries = 4 , complaint = 'Yes or no, please!' ):
     while  True :
         ok  =  input (prompt)
         if  ok  in  ( 'y' 'ye' 'yes' ):
             return  True
         if  ok  in  ( 'n' 'no' 'nop' 'nope' ):
             return  False
         retries  =  retries  -  1
         if  retries <  0 :
             raise  OSError( 'uncooperative user' )
         print (complaint)

这个函数可以通过几种不同的方式调用:

  • 只给出必要的参数:

    ask_ok('Do you really want to quit?')

  • 给出一个可选的参数:

    ask_ok('OK to overwrite the file?', 2)

  • 或者给出所有的参数:

    ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')

这个例子还介绍了 in 关键字。它测定序列中是否包含某个确定的值。

默认值在函数 定义 作用域被解析

重要警告: 默认值只被赋值一次。这使得当默认值是可变对象时会有所不同,比如列表、字典或者大多数类的实例

关键字参数

函数可以通过 关键字参数 的形式来调用,形如 keyword = value

在函数调用中,关键字的参数必须跟随在位置参数的后面。传递的所有关键字参数必须与函数接受的某个参数相匹配 (例如 actor 不是 parrot 函数的有效参数),它们的顺序并不重要

任何参数都不可以多次赋值

引入一个形如 **name 的参数时,它接收一个字典(参见 Mapping Types — dict ),该字典包含了所有未出现在形式参数列表中的关键字参数
 
可变参数列表
一个最不常用的选择是可以让函数调用可变个数的参数。这些参数被包装进一个元组
通常,这些 可变 参数是参数列表中的最后一个,因为它们将把所有的剩余输入参数传递给函数。任何出现在 *args 后的参数是关键字参数,这意味着,他们只能被用作关键字,而不是位置参数:

 参数列表的分拆
当要传递的参数已经是一个列表,但要调用的函数却接受分开一个个的参数值,要把已有的列表拆开来,可以在调用函数时加一个 * 操作符来自动把参数列表拆开
同样的方式,可以使用 ** 操作符分拆关键字参数为字典

Lambda 形式
通过 lambda 关键字,可以创建短小的匿名函数
这里有一个函数返回它的两个参数的和: lambda a, b: a+b。 Lambda 形式可以用于任何需要的函数对象。出于语法限制,它们只能有一个单独的表达式。语义上讲,它们只是普通函数定义中的一个语法技巧。类似于嵌套函数定义,lambda 形式可以从外部作用域引用变量

文档字符串
第一行应该是关于对象用途的简介
如果文档字符串有多行,第二行应该空出来,与接下来的详细描述明确分隔
接下来的文档应该有一或多段描述对象的调用约定、边界效应等
Python 的解释器不会从多行的文档字符串中去除缩进,所以必要的时候应当自己清除缩进
第一行之后的第一个非空行决定了整个文档的缩进格式
每一行都不应该有缩进,如果有缩进的话,所有的留白都应该清除掉
1
2
3
4
5
6
7
8
def  my_function():
     """Do nothing, but document it.
     No, really, it doesn't do anything.
     """
     pass
print (my_function.__doc__)

函数注解
函数注解 是关于用户自定义的函数的完全可选的、随意的元数据信息
注解是以字典形式存储在函数的 __annotations__ 属性中,对函数的其它部分没有任何影响。参数注解(Parameter annotations)是定义在参数名称的冒号后面,紧随着一个用来表示注解的值得表达式。返回注释(Return annotations)是定义在一个 -> 后面,紧随着一个表达式,在冒号与 -> 之间

编码风格

  • 使用 4 空格缩进,而非 TAB

    在小缩进(可以嵌套更深)和大缩进(更易读)之间,4空格是一个很好的折中。TAB 引发了一些混乱,最好弃用

  • 折行以确保其不会超过 79 个字符

    这有助于小显示器用户阅读,也可以让大显示器能并排显示几个代码文件

  • 使用空行分隔函数和类,以及函数中的大块代码
  • 可能的话,注释独占一行
  • 使用文档字符串
  • 把空格放到操作符两边,以及逗号后面,但是括号里侧不加空格:a = f(1, 2) + g(3, 4)
  • 统一函数和类命名

    推荐类名用 驼峰命名, 函数和方法名用 小写_和_下划线。总是用 self 作为方法的第一个参数(关于类和方法的知识详见 初识类 )

  • 不要使用花哨的编码,如果你的代码的目的是要在国际化环境。Python 的默认情况下,UTF-8,甚至普通的 ASCII 总是工作的最好
  • 同样,也不要使用非 ASCII 字符的标识符,除非是不同语种的会阅读或者维护代码

模块

Python 提供了一个方法可以从文件中获取定义,在脚本或者解释器的一个交互式实例中使用。这样的文件被称为 模块
模块中的定义可以 导入 到另一个模块或 主模块 中(在脚本执行时可以调用的变量集位于最高级,并且处于计算器模式)
模块是包括 Python 定义和声明的文件,文件名就是模块名加上 .py 后缀
模块的模块名(做为一个字符串)可以由全局变量 __name__ 得到
如果打算频繁使用一个函数,你可以将它赋予一个本地变量
fib = fibo.fib
print(fib(500))

深入模块

除了包含函数定义外,模块也可以包含可执行语句,
这些语句一般用来初始化模块。他们仅在 第一次 被导入的地方执行一次
每个模块都有自己私有的符号表,被模块内所有的函数定义作为全局符号表使用
模块的作者可以在模块内部使用全局变量,而无需担心它与某个用户的全局变量意外冲突
可以使用引用模块函数的表示法访问模块的全局变量,modname.itemname
模块可以导入其他的模块
一个(好的)习惯是将所有的 import 语句放在模块的开始
被导入的模块名会放入当前模块的全局符号表中
import 语句的一个变体直接从被导入的模块中导入命名到本模块的语义表中
 
 
from fibo import fib, fib2
fib(500)

这样不会从局域语义表中导入模块名
有种方式可以导入模块中的所有定义,可以导入所有除了以下划线( _ )开头的命名
from fibo import *
需要注意的是在实践中往往不鼓励从一个模块或包中使用 * 导入所有,因为这样会让代码变得很难读
出于性能考虑,每个模块在每个解释器会话中只导入一遍。因此,如果你修改了你的模块,需要重启解释器;或者,如果你就是想交互式的测试这么一个模块,可以用 imp.reload() 重新加载,例如 import imp; imp.reload(modulename)

作为脚本来执行模块

使用以下方式运行 Python 模块时,模块中的代码便会被执行
 
 
python fibo.py <arguments>
模块中的代码会被执行,就像导入它一样,不过此时 __name__ 被设置为 "__main__"。这相当于,如果你在模块后加入如下代码
if __name__ == "__main__":
    import sys
    fib(int(sys.argv[1]))
可以让此文件像作为模块导入时一样作为脚本执行。此代码只有在模块作为 “main” 文件执行时才被调用
通常用来为模块提供一个便于测试的用户接口(将模块作为脚本执行测试需求)

模块的搜索路径

导入一个叫 spam 的模块时,解释器先在当前目录中搜索名为 spam.py 的文件。如果没有找到的话,接着会到 sys.path 变量中给出的目录列表中查找。 sys.path 变量的初始值来自如下:
  • 输入脚本的目录(当前目录)。
  • 环境变量 PYTHONPATH 表示的目录列表中搜索

    (这和 shell 变量 PATH 具有一样的语法,即一系列目录名的列表)。

  • Python 默认安装路径中搜索
在支持符号连接的文件系统中,输入的脚本所在的目录是符号连接指向的目录。 换句话说也就是包含符号链接的目录不会被加到目录搜索路径
解释器由 sys.path 变量指定的路径目录搜索模块,该变量初始化时默认包含了输入脚本(或者当前目录), PYTHONPATH 和安装目录

 “编译的” Python 文件

为了加快加载模块的速度,Python 会在 __pycache__ 目录下以 module.version.pyc 名字缓存每个模块编译后的版本,这里的版本编制了编译后文件的格式。它通常会包含 Python 的版本号
  • 为了减少一个编译模块的大小,你可以在 Python 命令行中使用 -O 或者 -OO-O 参数删除了断言语句,-OO 参数删除了断言语句和 __doc__ 字符串。

    因为某些程序依赖于这些变量的可用性,你应该只在确定无误的场合使用这一选项。“优化的” 模块有一个 .pyo 后缀而不是 .pyc 后缀。未来的版本可能会改变优化的效果。

  • 来自 .pyc 文件或 .pyo 文件中的程序不会比来自 .py 文件的运行更快;.pyc 或 .pyo文件只是在它们加载的时候更快一些。
  • compileall 模块可以为指定目录中的所有模块创建 .pyc 文件(或者使用 -O 参数创建 .pyo 文件)。
  • 在 PEP 3147 中有很多关这一部分内容的细节,并且包含了一个决策流程。

标准模块

 sys ,这个模块内置于所有的 Python 解释器
变量 sys.path 是解释器模块搜索路径的字符串列表

 dir() 函数

内置函数 dir() 用于按模块名搜索模块定义,它返回一个字符串类型的存储列表
无参数调用时,dir() 函数返回当前定义的命名
dir() 不会列出内置函数和变量名。如果你想列出这些内容,它们在标准模块 builtins 中定义

包通常是使用用“圆点模块名”的结构化模块命名空间
当导入这个包时,Python 通过 sys.path 搜索路径查找包含这个包的子目录
用户可以每次只导入包里的特定模块
import sound.effects.echo
这样就导入了 sound.effects.echo 子模块,必需通过完整的名称来引用
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
导入包时有一个可以选择的方式
from sound.effects import echo
这样就加载了 echo 子模块,并且使得它在没有包前缀的情况下也可以使用,所以它可以如下方式调用:
echo.echofilter(input, output, delay=0.7, atten=4)
还有另一种变体用于直接导入函数或变量
from sound.effects.echo import echofilter
这样就又一次加载了 echo 子模块,但这样就可以直接调用它的 echofilter() 函数:
echofilter(input, output, delay=0.7, atten=4)
需要注意的是使用 from package import item 方式导入包时,这个子项(item)既可以是包中的一个子模块(或一个子包),也可以是包中定义的其它命名,像函数、类或变量
import 语句首先核对是否包中有这个子项,如果没有,它假定这是一个模块,并尝试加载它。如果没有找到它,会引发一个 ImportError 异常
相反,使用类似 import item.subitem.subsubitem 这样的语法时,这些子项必须是包,最后的子项可以是包或模块,但不能是前面子项中定义的类、函数或变量

从 * 导入包

给提供一个明确的包索引,
import 语句按如下条件进行转换:执行 from package import * 时,如果包中的 __init__.py 代码定义了一个名为 __all__ 的列表,就会按照列表中给出的模块名进行导入
__all__ = ["echo", "surround", "reverse"]
这意味着 from sound.effects import * 语句会从 sound 包中导入以上三个已命名的子模块
如果没有定义 __all__ , from sound.effects import * 语句 不会 从 sound.effects 包中导入所有的子模块。无论包中定义多少命名,只能确定的是导入了 sound.effects 包(可能会运行 __init__.py 中的初始化代码)以及包中定义的所有命名会随之导入。
尽管某些模块设计为使用 import * 时它只导出符合某种规范/模式的命名,仍然不建议在生产代码中使用这种写法
记住,from Package import specific_submodule 没有错误!事实上,除非导入的模块需要使用其它包中的同名子模块,否则这是推荐的写法

包内引用

果包中使用了子包结构(就像示例中的 sound 包),可以按绝对位置从相邻的包中引入子模块。例如,如果 sound.filters.vocoder 包需要使用 sound.effects 包中的 echo 模块,它可以 from sound.Effects import echo
可以用这样的形式 from module import name 来写显式的相对位置导入。那些显式相对导入用点号标明关联导入当前和上级包
需要注意的是显式或隐式相对位置导入都基于当前模块的命名。因为主模块的名字总是 "__main__",Python 应用程序的主模块应该总是用绝对导入

 多重目录中的包

包支持一个更为特殊的特性, __path__
在包的 __init__.py 文件代码执行之前,该变量初始化一个目录名列表。该变量可以修改,它作用于包中的子包和模块的搜索功能
事实上函数定义既是“声明”又是“可执行体”;执行体由函数在模块全局语义表中的命名导入

输入和输出

格式化输出

函数 str() 用于将值转化为适于人阅读的形式,而 repr() 转化为供解释器读取的形式
 str.rjust() 方法,把字符串输出到一列,并通过向左侧填充空格来使其右对齐
 str.zfill() 它用于向数值的字符串表达左侧填充 0,该函数可以正确理解正负号
print('We are the {} who say "{}!"'.format('knights', 'Ni'))
大括号和其中的字符会被替换成传入 str.format() 的参数
大括号中的数值指明使用传入 str.format() 方法的对象中的哪一个
如果在 str.format() 调用时使用关键字参数,可以通过参数名来引用值
print('This {food} is {adjective}.'.format(food='spam', adjective='absolutely horrible'))
'!a' (应用 ascii()),'!s' (应用 str() )和 '!r' (应用 repr() )可以在格式化之前转换值
import math
print('The value of PI is approximately {}.'.format(math.pi))
print('The value of PI is approximately {!r}.'.format(math.pi))
输出
The value of PI is approximately 3.141592653589793.
The value of PI is approximately 3.141592653589793.
在字段后的 ':' 后面加一个整数会限定该字段的最小宽度,这在美化表格时很有用
table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
for name, phone in table.items():
    print('{0:10} ==> {1:10d}'.format(name, phone))
输出
Sjoerd ==> 4127
Jack ==> 4098
Dcab ==> 7678

 旧式的字符串格式化

操作符 % 也可以用于字符串格式化
 print('The value of PI is approximately %5.3f.' % math.pi)

文件读写

函数 open() 返回 文件对象,通常的用法需要两个参数:open(filename, mode)
f = open('workfile', 'w')

错误和异常

语法错误

语法错误,也被称作解析错误

异常

运行期检测到的错误称为 异常,并且程序不会无条件的崩溃
异常也有不同的类型
零除错误( ZeroDivisionError )
命名错误( NameError) 
 类型错误( TypeError )
打印错误信息时,异常的类型作为异常的内置名显示
内置的异常 列出了内置异常和它们的含义

异常处理

通过编程处理选择的异常是可行的
while True:
    try:
        x = int(input("Please enter a number: "))
        break
    except ValueError:
        print("Oops!  That was no valid number.  Try again...")

try 语句按如下方式工作。

  • 首先,执行 try 子句 (在 try 和 except 关键字之间的部分)。
  • 如果没有异常发生, except 子句 在 try 语句执行完毕后就被忽略了。
  • 如果在 try 子句执行过程中发生了异常,那么该子句其余的部分就会被忽略。

    如果异常匹配于 except 关键字后面指定的异常类型,就执行对应的except子句。然后继续执行 try 语句之后的代码。

  • 如果发生了一个异常,在 except 子句中没有与之匹配的分支,它就会传递到上一级 try 语句中。

    如果最终仍找不到对应的处理语句,它就成为一个 未处理异常,终止程序运行,显示提示信息。

一个 try 语句可能包含多个 except 子句,分别指定处理不同的异常
try … except 语句可以带有一个 else子句,该子句只能出现在所有 except 子句之后。当 try 语句没有抛出异常时,需要执行一些代码,可以使用这个子句
在异常名(列表)之后,也可以为 except 子句指定一个变量。这个变量绑定于一个异常实例,它存储在 instance.args 的参数中

抛出异常

raise 语句允许程序员强制抛出一个指定的异常

用户自定义异常

在程序中可以通过创建新的异常类型来命名自己的异常
异常类通常应该直接或间接的从 Exception 类派生

定义清理行为

不管有没有发生异常,finally子句 在程序离开 try 后都一定会被执行。当 try 语句中发生了未被 except 捕获的异常(或者它发生在 except 或 else 子句中),在 finally 子句执行完后它会被重新抛出。 try 语句经由 break ,continue 或 return 语句退 出也一样会执行 finally 子句

预定义清理行为


Python 的类机制通过最小的新语法和语义在语言中实现了类。
它是 C++ 或者 Modula-3 语言中类机制的混合
类的大多数重要特性都被完整的保留下来:类继承机制允许多重继承,派生类可以覆盖(override)基类中的任何方法或类,可以使用相同的方法名称调用基类的方法。对象可以包含任意数量的私有数据

Python 作用域和命名空间

命名空间 是从命名到对象的映射
当前命名空间主要是通过 Python 字典实现的,不过通常不关心具体的实现方式
不同命名空间中的命名没有任何联系,用户必须以模块名为前缀来引用它们
Python 中任何一个“.”之后的命名为 属性,例如,表达式 z.real 中的 real 是对象 z 的一个属性
表达式 modname.funcname中,modname 是一个模块对象,funcname 是它的一个属性
模块的属性和模块中的全局命名有直接的映射关系:它们共享同一命名空间
属性可以是只读过或写的,可写的属性也可以用 del 语句删除
例如: del modname.the_answer 会从 modname 对象中删除 the_answer 属性
不同的命名空间在不同的时刻创建,有不同的生存期
模块的全局命名空间在模块定义被读入时创建,通常,模块命名空间也会一直保存到解释器退出
包含内置命名的命名空间在 Python 解释器启动时创建,会一直保留,不被删除
由解释器在最高层调用执行的语句,不管它是从脚本文件中读入还是来自交互式输入,都是 __main__ 模块的一部分,所以它们也拥有自己的命名空间(内置命名也同样被包含在一个模块中,它被称作 builtins)。
当调用函数时,就会为它创建一个局部命名空间,并且在函数返回或抛出一个并没有在函数内部处理的异常时被删除
作用域 就是一个 Python 程序可以直接访问命名空间的正文区域。
通常,局部作用域引用当前函数的命名
在函数之外,局部作用域与全局使用域引用同一命名空间:模块命名空间。
类定义也是局部作用域中的另一个命名空间。
一个定义于某模块中的函数的全局作用域是该模块的命名空间,而不是该函数的别名被定义或调用的位置
如果没有使用 global 语法,其赋值操作总是在最里层的作用域。赋值不会复制数据,只是将命名绑定到对象
特别是 import 语句和函数定义将模块名或函数绑定于局部作用域(可以使用 global 语句将变量引入到全局作用域)。
global 语句用以指明某个特定的变量为全局作用域,并重新绑定它。
nonlocal 语句用以指明某个特定的变量为封闭作用域,并重新绑定它

类定义语法

类的定义就像函数定义( def 语句),要先执行才能生效
class ClassName:
    <statement-1>
    .
    .
    .
    <statement-N>
进入类定义部分后,会创建出一个新的命名空间,作为局部作用域,所有的赋值成为这个新命名空间的局部变量。特别是函数定义在此绑定了新的命名
类定义完成时(正常退出),就创建了一个 类对象
原始的局部作用域(类定义引入之前生效的那个)得到恢复,类对象在这里绑定到类定义头部的类名(

类对象

类对象支持两种操作:属性引用和实例化
属性引用 使用和 Python 中所有的属性引用一样的标准语法:obj.name。类对象创建后,类命名空间中所有的命名都是有效属性名
__doc__ 也是一个有效的属性,返回类的文档字符串
类的 实例化 使用函数符号,只要将类对象看作是一个返回新的类实例的无参数函数即可
x = MyClass()
以上创建了一个新的类 实例 并将该对象赋给局部变量 x
很多类都倾向于将对象创建为有初始状态的。因此类可能会定义一个名为 __init__() 的特殊方法
def __init__(self):  self.data = []
类定义了 __init__() 方法的话,类的实例化操作会自动为新创建的类实例调用 __init__() 方法
__init__() 方法可以有参数,参数通过 __init__() 传递到类的实例化操作上
class Complex:
    def __init__(self, realpart, imagpart):
         self.r = realpart
        self.i = imagpart

x = Complex(3.0, -4.5)
x.r, x.i

实例对象

实例对象唯一可用的操作就是属性引用
和局部变量一样,数据属性不需要声明,第一次使用时它们就会生成。
另一种为实例对象所接受的引用属性是 方法。方法是“属于”一个对象的函数
实例对象的有效名称依赖于它的类
按照定义,类中所有(用户定义)的函数对象对应它的实例中的方法

方法对象

通常,方法通过右绑定方式调用,x.f()
方法的特别之处在于实例对象作为函数的第一个参数传给了函数
通常,以 n 个参数的列表去调用一个方法就相当于将方法的对象插入到参数列表的最前面后,以这个列表去调用相应的函数
引用非数据属性的实例属性时,会搜索它的类。如果这个命名确认为一个有效的函数对象类属性,就会将实例对象和函数对象封装进一个抽象对象:这就是方法对象
以一个参数列表调用方法对象时,它被重新拆封,用实例对象和原始的参数列表构造一个新的参数列表,然后函数对象调用这个新的参数列表

类和实例变量

一般来说,实例变量用于对每一个实例都是唯一的数据,类变量用于类的所有实例共享的属性和方法

一些说明

数据属性会覆盖同名的方法属性
大写方法名称的首字母,使用一个唯一的小字符串(也许只是一个下划线)作为数据属性名称的前缀,或者方法使用动词而数据属性使用名词
数据属性可以被方法引用,也可以由一个对象的普通用户(客户)使用
换句话说,类不能用来实现纯净的数据类型
事实上,Python 中不可能强制隐藏数据
命名约定可以避免很多麻烦
从方法内部引用数据属性(或其他方法)并没有快捷方式
一般,方法的第一个参数被命名为 self。这仅仅是一个约定:对 Python 而言,名称 self 绝对没有任何特殊含义

类属性的任何函数对象都为那个类的实例定义了一个方法
函数定义代码不一定非得定义在类中:也可以将一个函数对象赋值给类中的一个局部变量

方法可以像引用普通的函数那样引用全局命名。与方法关联的全局作用域是包含类定义的模块。

(类本身永远不会做为全局作用域使用

每个值都是一个对象,因此每个值都有一个 类( class ) (也称为它的 类型( type ) ),它存储为 object.__class__ 


 

继承

派生类的定义如下所示
class DerivedClassName(BaseClassName):
    <statement-1>
    .
    .
    .
    <statement-N>
命名 BaseClassName (示例中的基类名)必须与派生类定义在一个作用域内。除了类,还可以用表达式,基类定义在另一个模块中时这一点非常有用
class DerivedClassName(modname.BaseClassName):
构造派生类对象时,就记住了基类
如果在类中找不到请求调用的属性,就搜索基类。如果基类是由别的类派生而来,这个规则会递归的应用上去
方法引用按如下规则解析:搜索对应的类属性,必要时沿基类链逐级搜索,如果找到了函数对象这个方法引用就是合法的
派生类可能会覆盖其基类的方法。因为方法调用同一个对象中的其它方法时没有特权,基类的方法调用同一个基类的方法时,可能实际上最终调用了派生类中的覆盖方法
派生类中的覆盖方法可能是想要扩充而不是简单的替代基类中的重名方法
Python 有两个用于继承的函数:
  • 函数 isinstance() 用于检查实例类型: isinstance(obj, int) 只有在 obj.__class__ 是 int 或其它从 int 继承的类型
  • 函数 issubclass() 用于检查类继承: issubclass(bool, int) 为 True,因为 bool 是 int 的子类。

    然而, issubclass(float, int) 为 False,因为 float 不是 int 的子类

多继承
Python 同样有限的支持多继承形式
class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    <statement-N>
super() 可以动态的改变解析顺序
为了防止重复访问基类,通过动态的线性化算法,每个类都按从左到右的顺序特别指定了顺序,每个祖先类只调用一次,这是单调的(意味着一个类被继承时不会影响它祖先的次序)

私有变量

有一个变通的访问用于大多数 Python 代码:以一个下划线开头的命名(例如 _spam )会被处理为 API 的非公开部分(无论它是一个函数、方法或数据成员)。它会被视为一个实现细节,无需公开
name mangling (命名编码)
任何形如 __spam 的标识(前面至少两个下划线,后面至多一个),被替代为 _classname__spam ,去掉前导下划线的 classname 即当前的类名。此语法不关注标识的位置,只要求在类定义内
名称重整是有助于子类重写方法,而不会打破组内的方法调用
class Mapping:  def __init__(self, iterable):  self.items_list = []  self.__update(interable) def update(self, interable):  for item in interable:  self.items_list.append(item) __update = update class MappingSubclass(Mapping):  def update(self, keys, values):  for item in zip(keys, values):  self.items_list.append(item)
需要注意的是编码规则设计为尽可能的避免冲突,被认作为私有的变量仍然有可能被访问或修改
要注意的是代码传入 exec(), eval() 时不考虑所调用的类的类名,视其为当前类,这类似于 global 语句的效应,已经按字节编译的部分也有同样的限制。这也同样作用于 getattr(), setattr() 和 delattr(),像直接引用 __dict__ 一样。

 补充

将一组已命名的数据项绑定在一起。一个空的类定义可以很好的实现它
class Employee:  pass  john = Employee() # Create an empty employee record  # Fill the fields of the record john.name = 'John Doe' john.dept = 'computer lab' john.salary = 1000
实例方法对象也有属性:m.__self__ 是一个实例方法所属的对象,而 m.__func__ 是这个方法对应的函数对象

异常也是类

用户自定义异常也可以是类。利用这个机制可以创建可扩展的异常体系
以下是两种新的,有效的(语义上的)异常抛出形式,使用 raise 语句:
raise Class raise Instance
第一种形式中,Class 必须是 type 或其派生类的一个实例。第二种形式是以下形式的简写
raise Class() 
发生的异常其类型如果是 except 子句中列出的类,或者是其派生类,那么它们就是相符的(反过来说--发生的异常其类型如果是异常子句中列出的类的基类,它们就不相符)
class B(Exception):  pass class C(B):  pass class D(C):  pass  for cls in [B, C, D]:  try:  raise cls() except D:  print("D") except C:  print("C") except B:  print("B") 
打印一个异常类的错误信息时,先打印类名,然后是一个空格、一个冒号,然后是用内置函数 str()将类转换得到的完整字符串

 迭代器

大多数容器对象都可以用 for 遍历:
迭代器的用法在 Python 中普遍而且统一
在后台, for 语句在容器对象中调用 iter() 。该函数返回一个定义了 __next__() 方法的迭代器对象,它在容器中逐一访问元素。没有后续的元素时, __next__() 抛出一个 StopIteration 异常通知 for 语句循环结束
给自己的类添加迭代器行为
定义一个 __iter__() 方法,使其返回一个带有 __next__() 方法的对象
如果这个类已经定义了 __next__() ,那么 __iter__() 只需要返回 self:
class Reverse:  """Iterator for looping over a sequence backwards."""  def __init__(self, data):  self.data = data self.index = len(data) def __iter__(self):  return self  def __next__(self):  if self.index == 0:  raise StopIteration  self.index = self.index - 1  return self.data[self.index]

生成器

Generator 是创建迭代器的简单而强大的工具。它们写起来就像是正规的函数,需要返回数据的时候使用 yield 语句。每次 next() 被调用时,生成器回复它脱离的位置(它记忆语句最后一次执行的位置和所有的数据值)
def reverse(data):  for index in range(len(data)-1, -1, -1):  yield data[index]  for char in reverse('golf'):  print(char)
基于类的迭代器,它能作的每一件事生成器也能作到
因为自动创建了 __iter__() 和 __next__() 方法,生成器显得如此简洁
另一个关键的功能在于两次执行之间,局部变量和执行状态都自动的保存下来
除了创建和保存程序状态的自动方法,当发生器终结时,还会自动抛出 StopIteration 异常
 生成器表达式
有一个例外。模块对象有一个隐秘的只读对象,名为 __dict__ ,它返回用于实现模块命名空间的字典,命名 __dict__ 是一个属性而非全局命名。显然,使用它违反了命名空间实现的抽象原则,应该被严格限制于调试中。
来自
https://www.runoob.com/manual/pythontutorial3/docs/html/appetite.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值