文章目录
- Python复习
-
- 1. 介绍
- 2. 编写规范
- 3. 基本语法
-
- 一切都是对象
- 查看帮助
- 空/否定
- 保留字
- 操作符
- 语句/函数
- 内置函数
-
- all、any
- ascii、repr
- bin、hex、oct
- bool、float、int、complex
- breakpoint
- bytearray、bytes
- callable
- chr、ord
- classmethod、staticmethod
- compile、exec、eval
- delattr、getattr 和 setattr
- dict、set、frozenset、list、tuple、str
- dir
- divmod
- \_\_import__
- enumerate、zip
- filter、map、reversed、sorted
- len、max、min、sum
- format
- globals、locals
- hash
- help
- id
- input
- isinstance、issubclass
- iter、next
- memoryview
- object
- open
- property
- range
- round
- slice
- super
- type
- vars
- 数据类型/类
- 元编程
- 异常机制
- 模块及包
- 推导式
- 2和3的兼容问题
- \*和\*\*
- 拷贝:深拷贝、浅拷贝
- 迭代:可迭代、迭代器、生成器
- 缓存重用机制
- 字符串格式化:格式规格迷你语言
- 自由变量
- 序列化 YAML
- 4. 特殊变量
- 5. struct
- Python与RL
Python复习
// 还需要修订!
主要参考http://c.biancheng.net/python/。
必须要强调,如果想要彻底掌握Python,那么就有大量需要学习的东西,而不是专门从事相关工作的不太可能彻底研究清楚每个细节,所以应该针对基本内容和所需进行学习。学习 C++ 也是类似的。
本文只考虑 Python3,且只记录个人不熟悉部分,大部分也是基于对C的理解基础上的,另外 Python 之所以强大主要是因为丰富的库,而库是学不完的。
1. 介绍
编译型与解释型:
对于编译型语言,编译过程需要进行五个操作:词法分析、语法分析、语义分析、性能优化、生成可执行文件,解释型语言也有类似的过程,但是不需要生成可执行文件,而是在运行时逐行直接翻译成机器语言,具体可以研究下编译原理。而Python是解释型语言。而Java 和 C# 是半编译半解释型的语言,首先根据程序生成字节码,然后在虚拟机中执行字节码。
解释型语言的主要优势在于跨平台,且由于发明时间较晚语法上更现代,开发效率高。但是也有很多劣势:在运行解释型语言的时候,我们始终都需要源代码和解释器,所以说它无法脱离开发环境。而且解释型语言效率较低,计算机的一些底层功能,或者关键算法,一般都使用 C/C++ 实现,只有在应用层面(比如网站开发、批处理、小工具等)才会使用解释型语言。
胶水语言:
Python 有很多功能强大的库,程序员可以把使用其它语言制作的各种模块(尤其是C语言和 C++)很轻松地联结在一起,因此 Python 又常被称为“胶水”语言。
Python和C++的混合编程参考这里。
应用领域:
自动化运维、人工智能、网路爬虫、科学计算、游戏开发(很多游戏使用 C++ 编写图形显示等高性能模块,而使用 Python 或 Lua 编写游戏的逻辑)等。
跟进:
PEP(Python Enhancement Proposal),全称是 Python 改进方案。它是提交 Python 变化的书面文档,也是社区对这一变化进行讨论的出发点。PEP 文档主要有 3 个用途:汇总 Python 核心开发者重要的信息,并通过 Python 发布日程;提供代码风格、文档或者其他指导意见;对提交的功能进行说明。
版本:
①实现语言
我们默认使用的是CPython,也就是使用C实现的,此外还有使用Java的JPython,使用.net的IronPython,使用Python实现的PyPy,C的增强版Stackless Python。
②Python2和Python3
Python3和Python2代码有很多差别,可以参考这里实现自动转换。
Jupyter Notebook:
Jupyter 发展到现在,已经成为了一个几乎支持所有语言,能够把软件代码、计算输出、解释文档、多媒体资源整合在一起的多功能科学运行平台。
它的优势:整合了所有资源、交互性编程体验、轻松运行他人编写的代码。Jupyter 官方的 Binder 平台以及 Google 提供的 Google Colab 环境,它们可以让 Jupyter Notebook 变得和 Google Doc 在线文档一样。比如用 Binder 打开一份 GitHub 上的 Jupyter Notebook 时,就不需要安装任何 Python 库,直接在打开代码就能运行。
具体参考这里。
2. 编写规范
# 1. 注释
# 这是一个单行注释
"""
多行注释通常用来为 Python 文件、模块、
类或者函数等添加版权或者功能描述信息。
"""
'''
多行注释通常用来为 Python 文件、模块、
类或者函数等添加版权或者功能描述信息。
!多行注释不支持嵌套!
!不管是多行注释还是单行注释,当注释符作为字符串的一部分出现时,
就不能再将它们视为注释标记,而应该看做正常代码的一部分:
print('''Hello,World!''')!
'''
# 2. 缩进规则
'''
在 Python 中,对于类定义、函数定义、流程控制语句、异常处理语句等,行尾
的冒号和下一行的缩进,表示下一个代码块的开始,而缩进的结束则表示此代码
块的结束。
Python 中实现对代码的缩进,可以使用空格或者 Tab 键实现。但无论是手动敲
空格,还是使用 Tab 键,通常情况下都是采用 4 个空格长度作为一个缩进量。
!同一个级别代码块的缩进量必须一样,否则解释器会报 SyntaxError 异常错误!
'''
# 3. 编码规范
'''
Python 采用 PEP 8 作为编码规范,其中 PEP 是 Python Enhancement
Proposal(Python 增强建议书)的缩写,8 代表的是 Python 代码的样式指
南。一些基本规则为:
1. 每个 import 语句只导入一个模块
2. 不要在行尾添加分号,也不要用分号将两条命令放在同一行
3. s=("建议每行不超过 80 个字符,如果超过,建议使用小括号将多行内容隐"
"式的连接起来,而不推荐使用反斜杠 \ 进行连接。除了导入模块的语句过长"
"或者注释里的 URL。")
4. 使用必要的空行可以增加代码的可读性,通常在顶级定义(如函数或类的定
义)之间空两行,而方法定义之间空一行,另外在用于分隔某些功能的位置也可
以空一行。
5. 通常情况下,在运算符两侧、函数参数之间以及逗号两侧,
都建议使用空格进行分隔。
'''
# 4. 标识符命名
'''
标识符是由字符(A~Z 和 a~z)、下划线和数字组成,但第一个字符不能是数
字。识符不能和 Python 中的保留字相同。在 Python 中,标识符中的字母是
严格区分大小写的。
Python 语言中,以下划线开头的标识符有特殊含义:
1. 以单下划线开头的标识符(如 _width),表示不能直接访问的类属性,其无
法通过 from...import* 的方式导入;
2. 以双下划线开头的标识符(如__add)表示类的私有成员;
3. 以双下划线作为开头和结尾的标识符(如 __init__),是专用标识符。
4. 规范:
> 当标识符用作模块名时,应尽量短小,并且全部使用小写字母,可以使用下划线分割多个字母,例如 game_mian
> 当标识符用作包的名称时,应尽量短小,也全部使用小写字母,不推荐使用下划线,例如 com.mr
> 当标识符用作类名时,应采用单词首字母大写的形式,例如 MyBook
> 模块内部的类名,可以采用 "下划线+首字母大写" 的形式,如 _Book
> 函数名、类中的属性名和方法名,应全部使用小写字母,多个单词之间可以用
下划线分割
> 常量命名应全部使用大写字母,单词之间可以用下划线分割
'''
3. 基本语法
!!!很多东西都是简要了解,详细学习的话,内容太多了!!!
要善于使用 help 和 dir 函数,不懂得就用这两个去查。还有,下面很多地方使用 str 指带某个字符串,实际编程中不要使用 str,因为 str
一切都是对象
查看帮助
善于使用 dir 和 help 以及 vars 函数,可以快速查看不懂的函数和对象。
help(obj/obj.func...) # 除了可以查看内置函数、内置类,还能查看对象的成员,注意成员函数不要加括号
dir(obj/obj.func...) # 查看对象的所有属性、函数
vars(obj) # 查看对象的所有局部变量,更细节
事实上,无论是 Python 提供给我们的函数,还是自定义的函数,其说明文档都需要设计该函数的程序员自己编写。也就是实现__doc__ 属性属性。我们不必直接覆盖__doc__属性,只需要按照如下格式编写注释:
def my_func():
'''
这里是 help 显示的帮助文档。这里其实也最好按照 PEP8 要求编写。
'''
空/否定
Python 中,语句、运算符对于是否继续进行的处理很不同,以下几种都被视作否定:
0 # 数值0
0.0 # 数值0
False # 假
"" # 空字符串
[ ] # 空列表
( ) # 空元组
{
} # 空字典
dict() # 空字典
set() # 空集合
None # 空值, None 是 NoneType 数据类型的唯一值
这些都相当于 C 中的 0,也就是对于逻辑判断或者if这样的句子,一旦发现上述的“否定”,就不再继续向下进行了。
保留字
查看方法:
import keyword
print(keyword.kwlist)
包含:
[‘False’, ‘None’, ‘True’, ‘and’, ‘as’, ‘assert’, ‘break’, ‘class’, ‘continue’, ‘def’, ‘del’, ‘elif’, ‘else’, ‘except’, ‘finally’, ‘for’, ‘from’, ‘global’, ‘if’, ‘import’, ‘in’, ‘is’, ‘lambda’, ‘nonlocal’, ‘not’, ‘or’, ‘pass’, ‘raise’, ‘return’, ‘try’, ‘while’, ‘with’, ‘yield’]
和 C 相比,比较特殊的有:
as
# 1. with...as...
with open("file_path", "mode") as src_name:
...
# 2. import...as...
import tensorflow as tf
# 3. except...as...
# 异常抓取
except Exception as e:
...
assert
'''
assert expression [, arguments]
等价于:
if not expression:
raise AssertionError([arguments])
'''
assert np.isscalar(a), 'a must be scalar'
del
'''
因为Python有GC,所以Python的del不同于C的free和C++的delete,它只是单纯地删除变量引用。
'''
a = 1
b = a
c = [1, 2]
del a
print(b) # 输出 1
del c[0]
print(c) # 输出 [2]
在实际过程中,即便使用 del 关键字删除了指定变量,且该变量所占用的内存再没有其他变量使用,此内存空间也不会真正地被系统回收并进行二次使用,它只是会被标记为无效内存。如果想让系统回收这些可用的内存,需要借助 gc 库中的 collect() 函数。
#引入gc库
import gc
tt = 'hello'
#定义一个包含多个类型的 list
list1 = [1,4,tt,3.4,"yes",[1,2]]
del list1
#回收内存地址
gc.collect()
try…except…finally
异常处理:
try:
fp = open(filename, "r")
print(fp.readline())
except IOError as e:
print('error!')
finally: # 一定会执行
fp.close()
global
# 如果需要在局部修改全局变量的值,那么必须用global修饰
foo = 100
def change_foo():
# foo = 200 # 不能改变全局
global foo # 这两句才能改变
foo = 200
# 另一种用法:
# 在函数体内也可定义全局变量:用 global 关键字对变量进行修饰后,该变量
# 就会变为全局变量。
goto
官方没有,需要额外安装。
in
判断集合中是否有某个元素。对于dict,则检测是否有某个key。
is
==是python标准操作符中的比较操作符,用来比较判断两个对象的value(值)是否相等,只比较值;is也被叫做同一性运算符,这个运算符比较判断的是对象间的唯一身份标识,也就是id是否相同,也就是是否是同一个的引用。
a = b = [1,2,3]
c = [1,2,3]
a == b # True
a == c # True
a is b # True
a is c # False
lambda
匿名函数。
# 数字 + 1 的匿名函数
g = lambda x:x+1
g(2) # 3
nonlocal和闭包
闭包是指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的非全局变量。闭包(closure)是函数式编程的重要的语法结构。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。创建一个闭包必须满足:必须有一个内嵌函数、内嵌函数必须引用外部函数中的变量、外部函数的返回值必须是内嵌函数。
首先举个例子说明内部函数对外部函数变量的遮蔽问题:
#全局函数
def outdef ():
name = "所在函数中定义的 name 变量"
#局部函数
def indef():
print(name) # 报错,未定义就使用
name = "局部函数中定义的 name 变量" # 遮蔽了外部 name
indef()
举例说明闭包和nonlocal的使用,下面是一个平均值计算的函数:
def make_averager(): # 闭包
count = 0 # 自由变量
total = 0
def averager(new_value): # 内嵌函数
# 如果没有nonlocal关键字声明一下,会报UnboundLocalError错
# 误,因为count和total都是不可变对象,因此我们一旦在内嵌函
# 数中试图修改其值,实际上是创建了新的对象,并把变量设置为
# 其引用,但是闭包绑定的是原来的不可变对象,因此变量会转变为
# 局部变量,而count += 1这种操作之前,并没有为其赋值,因此
# 报错。nonlocal的作用是把变量标记为自由变量,如果为nonlocal
# 声明的变量赋予新值,闭包中保存的绑定会更新。
nonlocal count, total
count += 1 # 总元素数
total += new_value # 总和
return total / count # 返回平均值
return averager # 返回内嵌函数
avg = make_average()
print(avg(10)) # 10
print(avg(20)) # 15
上面例子中,avg.__closure__是闭包绑定自由变量的地方,每个自由变量对应一个cell对象,其cell_content属性可以得到自由变量的值:avg.__closure__[0].cell_contents # 2, 对应 count 变量。
值得一提的是,avg中的自由变量名可以通过avg.__code__.co_freevars得到,这个属性是一个自由变量名称的tuple。
上述关于闭包的使用比较复杂,一个更简单的例子:
#闭包函数,其中 exponent 称为自由变量
def nth_power(exponent):
def exponent_of(base):
return base ** exponent # exponent 是自由变量
return exponent_of # 返回值是 exponent_of 函数
square = nth_power(2) # 计算一个数的平方
cube = nth_power(3) # 计算一个数的立方
print(square(2)) # 计算 2 的平方
print(cube(2)) # 计算 2 的立方
raise
raise 用来抛出异常。一旦执行了raise语句,raise后面的语句将不能执行。语法格式为:raise [exceptionName [(reason)]]。语句中 Exception 是异常的类型(例如,NameError)参数标准异常中任一种,args 是自已提供的异常参数。不指定异常类型,则默认触发RuntimeError 异常;在 except 中使用 raise,则会原样抛出捕获到的这个异常。
举例:
try:
a = input("输入一个数:")
#判断用户输入的是否为数字
if(not a.isdigit()):
raise ValueError("a 必须是数字")
except ValueError as e:
print("引发异常:",repr(e))
操作符
优先级
不应该过于依赖优先级顺序,可读性会下降。
结合性
同级别运算符计算顺序:
Python 中大部分运算符都具有左结合性,也就是从左到右执行;只有单目运算符(例如 not 逻辑非运算符)、赋值运算符和三目运算符例外,它们具有右结合性,也就是从右向左执行。上表中列出了所有 Python 运算符的结合性。
没有i++
Python 不支持自增,i++这样的语法是没有的。但是 ++i 不会报错,因为前面的加号不过是表示正数罢了,写几个加号都没有区别。
\
表达式和字符串换行,字符串转义。
//
整除(只保留商的整数部分)。
**
次方。2**3 = 8
#
单行注释。
=
连续赋值:a = b = c = 1,=的右结合性。
下面的写法是错误的:
(a = 10) # 括号里不能给实参赋值?
位、逻辑、比较
注意位运算是单个的 a & b;逻辑是and\or\not,没有&&、||这样的。
注意逻辑运算的短路功能,而且 Python 的逻辑运算被扩展了(and 和 or 运算符会将其中一个表达式的值作为最终结果,而不是将 True 或者 False 作为最终结果):
True and False # False
100 and 200 # 100
45 and 0 # 0
"" or "a" # "a"
比较运算比正常多了:
is 判断两个变量所引用的对象是否相同
is not 判断两个变量所引用的对象是否不相同
比较运算符还可以连续使用:
0 <= score <= 100
三目
不是 (a>b)?a:b这种,而是如下这种:
max = a if a>b else b
还可以嵌套:
a if a>b else c if c>d else d # a if a>b else (c if c>d else d)
‘x’、“x”
‘字符串’、“字符串”
‘’‘x’’’、""“x”""
多行注释或者长字符串,支持直接换行。
当程序中有大段文本内容需要定义成字符串时,优先推荐使用长字符串形式,因为这种形式非常强大,可以在字符串中放置任何内容,包括单引号和双引号。
语句/函数
缩进
Python 用缩进表示代码块,在任何地方都可以缩进,先当与C中添加了一对{}的效果。判断、循环、函数定义等语句要求必须有缩进,且关键句后要用冒号,例如:
for _ in range(4):
if (_ % 2 == 0):
print(_)
所有循环、判断、函数定义等结构,如果啥都不填写,只是预留的,需要使用 pass 语句,例如:
if a > 10:
pass
else
pass
if…elif…else
Python 中没有 switch 语句,注意每句后面的冒号。
for/while…else(break, continue)
for 循环,相当于C++中的foreach。只能用于 Iterator,或者能转化为 Iterator 的对象,具体参考关于迭代的部分。
while和C差不多。
for/while都可以跟一个else,在循环正常退出的时候会执行else,但是如果break/continue出来就忽略掉else了:
i = 0
while i < 10:
i += 1
else: # 如果 while 中运行了 break/continue,那么就不执行
print(i)
函数及参数
Python 函数支持接收多个( ≥0 )参数,且Python 函数还支持返回多个( ≥0 )值。Python 中,根据实际参数的类型不同,函数参数的传递方式可分为 2 种,分别为值传递和引用(地址)传递:①值传递:适用于实参类型为不可变类型(字符串、数字、元组);
②引用(地址)传递:适用于实参类型为可变类型(列表,字典)。
def my_func(para1, para2, *args, para3='default', **kwargs):
'''
这四种参数的顺序不能改变。
para1、para2:位置参数
*args:可变参数,形式为一个 tuple
para3:默认参数
kwargs:可变关键字参数,是一个 dict
'''
print(para1, para2, para3)
for i,(j,k) in zip(args, kwargs.items()):
print(i,j,k)
my_func(1, para2=2, 'args_1', 'args_2', kwargs_1=10, kwargs_2=2)
# para2=2这种叫做关键字参数,默认参数para3省略率
可以使用“函数名.__defaults__”查看函数的默认值参数的当前值,其返回值是一个元组。参数默认值具有继承性,比如默认参数是个 list,那么如果函数中修改了这个参数的值,默认值也会修改,也就是下次调用函数的时候就变成了不同的默认值。此外,我们还可以使用逆向参数收集过程,拆分序列作为函数参数:
def my_func(para1, para2, *args, para3 = [], **kwargs):
if len(para3): # 利用默认值继承性实现调用次数计数
para3[0] += 1
else:
para3.append(0)
print(para1, para2, para3)
print(args, kwargs)
l = [0, 0, 1, 2, 3]
d = {
'd_ele1':4, 'd_ele2':5}
# 逆向参数收集
my_func(*l, **d)
'''输出
0 0 [1]
(1,2,3) {'d_ele1':4, 'd_ele2':5}
'''
my_func(*l, **d)
'''输出
0 0 [2]
(1,2,3) {'d_ele1':4, 'd_ele2':5}
'''
函数如果不写 return 语句,则默认返回 None。如果 return 多个值,例如 return a1, a2,那么会自动包装成 tuple 返回,我们用一个变量接收,就是元组,用两个接收,就是 a1 和 a2 两个值,参考下面关于序列的部分。
还可以使用函数注解:
def my_func(a:str='123')->str:
# 形参和返回值指定,但是如果不按照这个,并不会报错,主要是给编译器用
# 函数注解没有任何语法上的意义,只是为函数参数和返回值做注解,并在运行
# 获取这些注解,仅此而已。
# 注意,如果参数有默认值,参数注解位于冒号和等号之间。
# 函数注解可以是类、字符串或者表达式比如:a:'这是一个注解',也没问题
# 给函数定义好注解之后,可以通过函数对象的 __annotations__ 属性获取,
# 它是一个字典,在应用运行期间可以获取。
print(a)
return 'ok'
print(my_func(