[强化学习代码笔记]Python复习

本文是Python编程的复习笔记,涵盖了Python的基础语法、编写规范、异常处理、内置函数、数据结构、元编程等内容。此外,还讨论了Python在强化学习(RL)中的应用,包括数据送入网络、进度条功能、生成器和谷歌的ABC库等。通过这篇笔记,读者可以巩固Python知识并了解其在RL领域的运用。
摘要由CSDN通过智能技术生成

文章目录

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 发布日程;提供代码风格、文档或者其他指导意见;对提交的功能进行说明。

PEP0PEP1

版本
①实现语言
我们默认使用的是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. 编写规范

PEP8规范
下划线用法

# 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(
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值