文章目录
一、python 使用注意事项
-
python帮助系统使用help(需要查询的命令)函数, dir()和help()很类似,只不过它返回的是一个模组中一系列的被定义过的方法的列表
-
python2 和 python3 的主要区别:
- python 2 输入使用
raw_input()
; python 3 使用input()
- python 2 输出不换行使用
print 'hello',
; python 3 使用print("Hello:", end=" ")
- python 2 整除使用 a / b a / b a/b, 浮点除法使用 a / f l o a t ( b ) a / float(b) a/float(b); python 3 整除使用 a / / b a // b a//b, 浮点除法使用 a / b a / b a/b
- xrange 重命名为 range, 返回迭代器对象
- python 2 输入使用
-
python的输入 input() 和输出 print()
- int(input()): 将输入的单个字符型数据转换为整数(多个就不能转换了),
num = input('显示一个字符串来提醒用户输入: ')
函数可以输入多个字符,num
以字符串的形式存在,num1 = num.split() # 返回以空格分割的字符组成的列表
print('str1', 'str2')
遇到逗号会输出一个空格,打印完字符串会自动添加一个换行符,print("Hello:", end=" ")
中end = " "
参数告诉print函数用空格代替换行符\n
- int(input()): 将输入的单个字符型数据转换为整数(多个就不能转换了),
-
python 的内存管理机制:
- 垃圾回收机制:
- 以引用计数为主,分代收集为辅
- 当系统判断没有变量引用某内存地址时(引用计数为 0),系统就会回收这部分内存地址
- 内存池机制:
- 概念:预先在内存中申请一定数量的,大小相等的内存块留作备用(以防频繁调用
new/malloc
导致大量的内存碎片) - 使用:针对小对象(eg:<=512 bytes)
Pymalloc
会在内存池中申请内存空间,大对象(eg:>512 bytes),则会使用PyMem_RawMalloc()
和PyMem_RawRealloc()
来申请新的内存空间
- 概念:预先在内存中申请一定数量的,大小相等的内存块留作备用(以防频繁调用
- 缓存的重用机制
- 缓存:程序会在内存里开辟一个空间,用来存放读取比较频繁且内存占用较少的对象
- 重用:顾名思义,重复使用(直接寻找缓存地址)
- 垃圾回收机制:
-
python 中对于
2 < a < 13
的判断可以写成if 2 < a < 13: or if 2 < a and a < 13:
-
E 记法:E的意思就是指数为10,E后边的数字就是10的多少次幂。
Eg: 1e2=100.0 # E不区分大小写,但前面的 1 不能省略
-
python最重要的就是缩进,代码块以缩进的方式体现,if, else, while等不需要加括号,但需在最后加入冒号
:
,print函数要有括号()
。 -
原始字符串:只需在字符串前面加上英文字母 r 即可;习惯上用全部大写的变量名表示常量,eg:
PI = 3.14159265359
-
python 语言的几个要件:数据和过程
-
浮点数的精度问题
# 由于计算机内部使用二进制保存数值,十进制的小数转为二进制的时候就可能产生误差 0.1 + 0.2 >>> 0.30000000000000004 # 所以浮点数的相等判断不能简单的使用相等关系符判断 0.1 + 0.2 == 0.3 >>> False # 1、两个浮点数做差小于一个很小的数(eg: 0.00001) (0.1 + 0.2) - 0.3 < 1e-5 >>> True # 2、使用 round 函数设置小数点后的有效位数(四舍五入到指定的小数位)后再进行判断 round(0.1+0.2, 5) == round(0.3, 5) >>> True
-
python 变量机制:引用数据对象,变量的类型就是它所指向数据对象的类型,变量类型可以随时改变
二、python 常用操作符
- 按照优先级排列:
- 幂运算(**) > 正负号(+, -) > 算术操作符(+, - , *, /, %取余, //取整) > 比较操作符(==, >, <, !=,
可链式使用
) >逻辑运算符(and, or, not) - 注意:
and
运算符从左到右计算表达式,若所有值为真,则返回最后一个值,若存在假,则返回 0;or
运算符也是从左到右计算表达式,若有一个为假,则返回0,否则返回第一个为真的值。eg: 3 and 4 --> 4 ; 3 or 4 --> 3
- 位运算符:把数字看作二进制来进行计算
- 幂运算(**) > 正负号(+, -) > 算术操作符(+, - , *, /, %取余, //取整) > 比较操作符(==, >, <, !=,
- 绝对值
abs()
, 四舍五入round()
, 最大最小值max(),min()
- 一些常用的数学函数如
sqrt/sin/cos
等都在 math 模块中import math
math.sqrt(2)
- 赋值语句小技巧
- 级联赋值语句:
x = y = z = 1
- 多个变量分解赋值:
a, b = ['hello', 'world']
- 变量交换:
a, b = b, a
- 自操作:
i += 1, n *= 45
- 级联赋值语句:
三、python 常用数据类型
3.1、可变数据类型和不可变数据类型
- 不同数据类型可以相互转换,相同数据类型可以进行拼接。
- 使用
type()
或者isinstance()
函数可查看数据的类型。eg:type(x)
或者isinstance(x, int)
。 - python是一门动态语言(脚本语言),它无需直接声明变量,但使用变量之前,需要先对其赋值,python会根据你的赋值决定数据类型,它会在你新建一个变量时开辟内存。
- 不可变数据类型赋值过后,其内存的内容是不可变的;可变数据类型赋值过后,其内存内容是可变的。可用
id()
查看变量的内存地址。x = [500, 501, 502] y = x # y指向x的内存地址, 系统并没有为y分配新的内存 z = x # z指向x的内存地址, 系统并没有为z分配新的内存 z = [1, 2, 3] # 此时系统为z分配新的内存, x = [500, 501, 502]保持不变 y[1] = 600 # 此时x的值发生改变,x = [500, 600, 502] = y y = [700, 800] # 此时系统为y分配新的内存,x = [500, 600, 502] 保持不变 # y = x 属于传引用操作,通过对y进行索引或者切片重新赋值时x的值会发生变化 # 只有,直接对y直接进行赋值时,系统为y分配了新的内存,此时x的值才不会发生改变。
3.2、对象赋值、浅拷贝和深拷贝
3.2.1、对象赋值
- 首先,创建了一个名为
will
的变量,这个变量指向一个 list 对象,从第一张图中可以看到所有对象的地址(每次运行,结果可能不同) - 然后,通过
will
变量对wilber
变量进行 赋值,那么wilber
变量将 指向will
变量对应的对象(内存地址)- 也就是说
wilber is will; wilber[i] is will[i]
- 可以理解为,Python 中,对象的赋值都是进行对象引用(内存地址)传递
- 也就是说
- 最后,第三张图中,由于
will
和wilber
指向同一个对象,所以对will
的任何修改都会体现在wilber
上- 这里需要注意的一点是,
str
是不可变类型,所以当修改的时候会替换旧的对象,产生一个新的地址 39758496
- 这里需要注意的一点是,
3.2.2、浅拷贝
- 首先,依然使用一个
will
变量,指向一个 list 类型的对象 - 然后,通过 copy 模块里面的浅拷贝函数 copy(),对
will
指向的对象进行浅拷贝,然后浅拷贝生成的新对象赋值给wilber
变量- 浅拷贝会创建一个新的对象,这个例子中
wilber is not will
- 但是,对于对象中的元素,浅拷贝就只会使用 原始元素的引用(内存地址),也就是说
wilber[i] is will[i]
- 浅拷贝会创建一个新的对象,这个例子中
- 最后,当对
will
进行修改的时候- 由于 list 的第一个元素是不可变类型,所以
will
对应的 list 的第一个元素会使用一个新的对象39758496 - 但是 list 的第三个元素是一个可变类型,修改操作不会产生新的对象,所以
will
的修改结果会相应的反应到wilber
上
- 由于 list 的第一个元素是不可变类型,所以
- 总结一下,当我们使用下面的操作的时候,会产生浅拷贝的效果:
- 使用切片操作
- 使用工厂函数(如
list/dir/set
) - 使用copy模块中的
copy()
函数
3.2.3、深拷贝
- 首先,同样使用一个
will
变量,指向一个 list 类型的对象 - 然后,通过 copy 模块里面的深拷贝函数 deepcopy(),对 will 指向的对象进行深拷贝,然后深拷贝生成的新对象赋值给
wilber
变量- 跟浅拷贝类似,深拷贝也会创建一个新的对象,这个例子中
wilber is not will
- 但是,对于对象中的元素,深拷贝都会重新生成一份(有特殊情况,下面会说明),而不是简单的使用原始元素的引用(内存地址)
- 例子中 will 的第三个元素指向39737304,而 wilber 的第三个元素是一个全新的对象 39773088,也就是说,
wilber[2] is not will[2]
- 但 list 中的前两项是字符串和数字,它们属于 不可变数据类型,为了提升效率,在 python 语言中,内存中只存一份不可变对象,并将其地址(即引用)赋值给其它变量,所以说
wilber[0 or 1] is will[0 or 1]
- 例子中 will 的第三个元素指向39737304,而 wilber 的第三个元素是一个全新的对象 39773088,也就是说,
- 跟浅拷贝类似,深拷贝也会创建一个新的对象,这个例子中
- 最后,当对
will
进行修改的时候- 由于 list 的第一个元素是不可变类型,所以
will
对应的 list 的第一个元素会使用一个新的对象39758496 - 但是 list 的第三个元素是一个可变类型,修改操作不会产生新的对象,但是由于
wilber[2] is not will[2]
,所以will
的修改不会影响wilber
- 由于 list 的第一个元素是不可变类型,所以
3.2.4、总结
-
容器类型(list、tuple、dict、set)的赋值、浅拷贝和深拷贝
- numpy 数据的
.copy()
方法是深拷贝 - 赋值(使用
=
)- 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址( 旧瓶装旧酒 )
- 修改不可变对象(str、tuple)需要开辟新的空间
- 修改可变对象(list等)不需要开辟新的空间
- 浅拷贝(使用
copy.copy()
)- 浅拷贝是在另一块地址中创建一个
新的变量或容器
,但是容器内的元素的地址均是源对象的元素的地址的拷贝 - 也就是说新的容器中的元素指向了旧的地址( 新瓶装旧酒 )
- 浅拷贝是在另一块地址中创建一个
- 深拷贝(使用
copy.deepcopy()
)- 深拷贝是在另一块地址中创建一个
新的变量或容器
,同时容器内的元素的 地址也是新开辟的 仅仅是值相同而已,是完全的副本 - 也就是说新的容器中的元素指向了新的地址( 新瓶装新酒 )
- 注意:对于容器中的
不可变元素类型
,考虑到效率,依然使用原始的引用
- 深拷贝是在另一块地址中创建一个
- numpy 数据的
-
非容器类型:对于非容器类型(如数字、字符串等
原子
类型的对象)没有被拷贝
一说
四、序列序列(列表,元组,字符串)
1. 序列定义-----最基本的数据结构
- 在 Python 中最基本的数据结构是序列(
Sequence
),序列中的每个元素被分配一个序号——即元素的位置,也称为索引,第一个索引是 0
2. 通用序列操作
-
不同类型的序列可以相互转换,相同类型的序列可以进行拼接
- 转换序列类型:
eg: a = 'hhh', list(a) = ['h', 'h', 'h']
- 加(adding):相同类型的序列可以进行相加(拼接),
eg: 'hello' + ' world' = 'hello world' # 注意空格
- 乘(multiplying):可以复制同一序列 n 次进行扩展,
eg: a = [5], a*3 = [5, 5, 5]
- 转换序列类型:
-
索引(indexing,x[i],假设序列长度为n)
- 正序索引:序列的正序索引值
i
从0
开始到n-1
- 逆序索引:序列的逆序索引值
i
从-n
开始到 -1
,逆序索引值就等于它的正序索引的值减去字符串的长度
- 正序索引:序列的正序索引值
-
切片(slicing,x[lower: upper: step] 三个边界均为可选项,默认step=1)
- 正序切片(step 为正数):省略lower意味着
从开头开始分片
,省略upper意味着一直分片到结尾
,eg: x[:5], x[5:], x[3:5], x[:]
- 逆序切片(step 为负数):省略lower意味着
从结尾开始分片
,省略upper意味着一直分片到开头
,eg: x[::-1], x[:-5:-1], x[-2::-1]
- 切片的时候,即便
start
或end
索引越界也不会出现问题,利用这一特性,我们可以限定输入序列的最大长度;需要注意的是,索引时下标不能越界! - 注意:对 list 进行切片赋值的时候
eg: a[2:7] = [1, 2, 3]
,即便它们的长度不同
也依然可以替换!
- 正序切片(step 为正数):省略lower意味着
-
掩膜处理(mask):
import numpy as np labels = np.array([0, 1, 2, 3]) mask = np.array([False, True, True, False]) labels[mask]-->array([1, 2]) # 把 mask 元素值为真对应 labels 中的元素挑选出来
-
enumerate(seq)
- 产生用于迭代的 (索引,值) 对,可以通过 list(enumerate(seq)) 将(索引,值)在 列表 中打印出来
-
计算序列中所包含元素的数量(len)、找出最大/最小元素(max/min)、求得序列中所有元素的和(sum)、迭代(iteration)、
-
检查某个元素是否属于序列的成员(成员资格in/not in——eg:检查用户名和PIN码)
# 检查用户名和PIN码 database = [ ['bob', '1234'], ['pet', '5678'], ['sam', '9898'] ] username = input('User Name:') pin = input('PIN Code:') if [username, pin] in database: print('Access granted!')
3. 列表、元组、字符串(列表是可变的,而元组和字符串是不可变的)
- 列表(一个"打了激素"的数组)
- 数组是把一大堆
同种类型的数据
挨个摆在一块,然后通过数组下标
进行索引;而列表是把一大堆各种类型的数据
挨个摆在一块,然后通过列表下标
进行索引 - 列表的增删改查拷
- 增加列表元素:
list.append('str') or list.extend('str')
,这两者的区别在于后者会展开所添加的序列,亦可使用列表的 insert 方法在指定位置添加元素list.insert(index, obj)
- 删除列表元素:
list.remove('删除首次出现元素') or list.pop(index) or del list[i] or del list
- 改变列表元素:使用索引或切片对相应的位置进行
赋值
,或者使用赋值语句重写整个序列 - 查看列表元素:使用
索引或切片
来实现 - 拷贝列表元素:
list2 = list1[:] # 深拷贝,新增列表,值不会同时变化(切片)
list2 = list1 #浅拷贝,只增加了一个标签,值会同时变化
- 增加列表元素:
- 列表的常用方法
- 列表排序:
list.sort() # 正序,从小到大排序,从大到小排序可用:list.sort(reverse=True)
list.reverse() # 逆序排列,将整个列表原地翻转(不进行比较),可用切片实现list[: : -1]
sorted(list) # 正序排列(从小到大)
sorted(list, reverse=True) # 逆序排列(从大到小)
- sorted 函数按照大小、长短、英文字母的顺序给列表中的元素进行排序,它返回新的有序列表(对原 list 没有影响),而 list 中的sort()是就地排序(没有返回值),对原 list 产生影响。
- 列表元素索引查询:
list.index(v) # 找到 v 的首次出现位置
- 列表元素频次统计:
list.count(v)
- 列表排序:
- 数组是把一大堆
- 元组(戴了枷锁的列表)
- 封闭的列表,一旦定义,就不可改变(不能添加、删除、修改),使用小括号()。
- 所有的多对象的、逗号分隔的、没有明确用符号定义的这些集合默认的类型都是元组。
- 为什么需要元组?
- 旧式字符串格式化中的参数要用元组表示
- 可在字典中当作键来使用
- 它是一些内建函数和方法的返回值
- 字符串
-
转义字符含义及字符串格式化符号
-
\
继续上一行,\n
换行符,-
用于左对齐 -
字符串前加
r
表示将其转成原始字符串,加b
表示将其转成二进制字符串,加u
表示将其转成Unicode
编码的字符串。 **注意:**由于转义字符串可能给目录的表达带来某些问题,所以无论在 win 或 linux 下,优先考虑使用正斜杠/
来分隔目录 -
传统字符串格式化符号:% 占位符
- 占位符
m.n%f or m.n%d
: m 代表设定的总位数(包括小数点,不够则补空格
),n 代表设定小数点后的位数或整数前的位数(不够则补 0
) - 在字符串内部,
%s
表示用字符串替换,%d
表示用整数替换,有几个%?
占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个%?
,括号
可以省略。eg:"%.2f + %.3f = %.1f " % (1,2,1+2) # 输出 '1.00 + 2.000 = 3.0 '
;print('%.3d' % 5) # 输出 005
在字符串内部,%s
表示用字符串替换,%d
表示用整数替换,有几个%?
占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个%?
,括号
可以省略。eg:"%.2f + %.3f = %.1f " % (1,2,1+2) # 输出 '1.00 + 2.000 = 3.0 '
;print('%.3d' % 5) # 输出 005
- 占位符
-
现代字符串格式化符号:str.format() 方法
- 字符串中花括号 {} 的部分会被 format 传入的参数替代,传入的值可以是字符串,也可以是数字或者别的对象。
- 位置参数:
'{2:.2f} {1} {0}'.format('a', 'b', 5) # 冒号表示格式化符号的开始, 输出'5.00 b a'
, 可以用数字指定传入参数的相对位置 - 关键字参数:
'{color} {n} {x}'.format(n=10, x=1.5, color='blue')
,还可以指定传入参数的名称 - format()方法的填充与对齐: 格式为
:[填充字符][对齐方式][宽度]
- 填充字符:只能是一个字符,不指定的话默认是用
空格填充
- 对齐方式:
^、<、>
分别表示居中、左对齐、右对齐 - 宽度:
m.nf
: m 代表设定的总位数(包括小数点,不够则补填充字符
),n 代表设定小数点后的位数或整数前的位数(不够则补 0
),注意:如果不追求具体的显示格式,可以不必指定变量类型,系统会根据变量类型自动匹配对应的占位符
# str '{:>8}'.format('189') >>> ' 189' '{:0>8}'.format('189') >>> '00000189' '{:a>8}'.format('189') >>> 'aaaaa189' # float '{:>8.3f}'.format(1.5) >>> ' 1.500' '{:0>8.3f}'.format(1.5) >>> '0001.500' '{:->8.3f}'.format(1.5) >>> '---1.500' # seq alist = [5, 25, 125] 'use * to unpack seq: {}, {}, {}'.format(*alist) >>> 'use * to unpack seq: 5, 25, 125' # dict,在模板里只需要填入字典里对应的名称即可 adict = {'x': 5, 'xx': 25, 'xxx': 125} 'use ** to unpack dict: {x}, {xx}, {xxx}'.format(**adict) >>> 'use ** to unpack dict: 5, 25, 125'
- 填充字符:只能是一个字符,不指定的话默认是用
-
Python3.6 以后字符串格式化:f 字符串,可参考 https://geek-docs.com/python/python-tutorial/python-fstring.html
# f 字符串的前缀为f,并使用 {} 括号评估值 #!/usr/bin/env python3.8 name = 'Peter' age = 23 print(f'{name} is {age} years old') # Peter is 23 years old # 多行 f 字符串 msg = ( f'Name: {name}\n' f'Age: {age}\n' ) print(msg) # Name: John Doe # Age: 32 # f字符串转义字符 print(f'This was a \'great\' film') # This was a 'great' film # f 字符串格式化浮点数 val = 12.3 print(f'{val:.2f}') # 12.30 print(f'{val:.5f}') # 12.30000 # f 字符串格式化宽度 for x in range(1, 11): print(f'{x:02} {x*x:3} {x*x*x:4}') # 输出如下 01 1 1 02 4 8 03 9 27 04 16 64 05 25 125 06 36 216 07 49 343 08 64 512 09 81 729 10 100 1000 # f 字符串对齐字符串 s1 = 'a' s2 = 'ab' s3 = 'abc' s4 = 'abcd' print(f'{s1:>10}') print(f'{s2:>10}') print(f'{s3:>10}') print(f'{s4:>10}') # 输出如下: a ab abc abcd
-
-
改变字符串的值
- 虽然字符串属于不可变数据类型,但我们可以通过重新赋值的方法来改变它(实质是创建了一个新的同名变量,然后原来的变量被垃圾回收了,可用 id() 来验证)
str1 = str1[:140]
str1 = str1.replace('old', 'new')
-
常用的字符串方法
-
'sep'.join(seq)
:以字符sep作为分隔符,插入到序列seq
的所有字符之间。它用来连接序列中的元素,需要被连接的序列元素都必须是字符串 -
str1.strip(chars)
:删除字符串(不包括内部)两侧所有的空格(包括 \n
),chars 参数可定制删除的字符(首尾),为可选参数 -
str1.upper/lower/swapcase/captilize/title(
):转换字符串中的所有小/大写字符为大/小写、大小写互换、首字母大写、所有单词首字母大写,这些方法均不会改变原来字符串的值 -
str1.split(sep=' ', maxsplit=-1
):不带参数,默认是以空格为分割符切片字符串。如果maxsplit参数有设置,则分割成maxsplit+1
个子字符串,返回切片后包含子字符串的列表。注意:python 2 中不能使用此关键字参数 -
str1.count(sub, start= 0,end=len(string))
:返回[start, end)
范围内str1
中子字符串 sub
的数目 -
str1.startswith(str2)
&str1.endswith(str2)
:str1 是否以 str2 开头或结尾,若是则返回True
,否则返回False
-
ord(char)
:返回单个字符的 Unicode 码,eg:ord('a') = 97
-
chr(num)
:根据 Unicode 码返回相应的字符,eg:chr(97) = 'a'
import numpy as np # eg1 with open(r'E:\record.txt') as f: for each_line in f: (role, line_speaking) = each_line.split(':', 1) # 以冒号':'为分隔符,分成两个子字符串组成的列表 # eg2 annotation = '0--Parade/0_Parade_Parade_0_904 360.92 97.92 623.91 436.46 \n' annotation = annotation.strip().split(' ') # ['0--Parade/0_Parade_Parade_0_904', '360.92', '97.92', '623.91', '436.46'] # py3 中为 map object, str-->float bbox = list(map(float, annotation[1:])) # 把所有的 bbox 重新排列一下,因为一张图中可能有多张人脸 boxes = np.array(bbox, dtype=np.float32).reshape(-1, 4) # [[448.51 329.63 570.09 478.23]]
-
使用
find
方法和enumerate
实现字符串模糊匹配索引
# find: 在一个字符串中查找另一个字符串 # 找到了返回第一次出现的位置, 没找到返回 -1 a = [u'左转信号灯_红', u'机动车信号灯_绿', u'左转信号灯_红', u'左转信号灯_红'] [i for i,x in enumerate(a) if x.find(u'左转')!=-1] >>> [0, 2, 3]
-
使用
ljust
方法和rjust
实现字符串输出对齐
display_text = '%s %f %s' % (val.ljust(max_filename_len), max_prob, str(max_prob_index).rjust(3)) print(display_text )
-
-
五、字典&&集合
1. 字典(当索引不好用时)
- 字典简介
- 序列是通过编号对其进行索引,字典是一种通过名字(key)来引用值(value)的数据结构。序列讲究顺序,字典讲究映射,不讲顺序。
- python对键的要求相对严格一些,要求它们必须是可哈希(Hash)的对象,不能是可变类型(包括变量、列表、字典本身等),但可以是(数字、字符串、元组等)不可变类型;python对值是没有任何限制的,它可以是任意类型的python对象。
- 用中括号 [ ] 括起来的数据叫做 列表,但用大括号 { } 括起来的数据不一定是 字典,若没有反应出数据的映射关系,则叫做集合。
- 字典的创建
dict1 = {}
,可以加入键值对进行初始化dict1 = {'one':1, 'two':2}
dict1 = dict()
,可以传入包含键值对的列表来初始化,dict1 =dict([('one',1), ('two', 2)])
- 字典的增删改查以及拷贝和引用
- 增:
dict1['three'] = 3
- 删:
dict1.pop('two') or del dict1['two']
- 改:
- 改值:
dict1['one'] = 5 or dict1.update([('one', 11), ('three', 3)])
改值+新增键值对,输出{'one':11, 'two':2, 'three':3)}
- 改键:
dict1['new_key'] = dict1.pop('old_key')
- 改值:
- 查:
dict1['one']
, 通过键来查看其对应的值,注意不要用小括号(),会报TypeError:'dict' object is not callable
的错误dict1.__getitem__('one')
,通过键来查看其对应的值,等价于dict1['one']
- 拷贝和引用:
dict2 = dict1.copy(),值不会同时变化,内存地址不同; dict2 = dict1,值会同时变化,内存地址相同
- 增:
- 获取字典dict1的键、值和项(键,值)列表
dict1.keys(); dict1.values(); dict1.items()
- 可通过
list(dict1.keys())
转化为列表
- str 和 dict 相互转换
- str2dict:
mydict=eval(mystr)
eval
中的第一个参数是字符串,而字符串中的内容一定要是可执行的代码eval
这个函数会把里面的字符串参数的引号去掉,把中间的内容当成 Python 的代码,eval
函数会执行这段代码并且返回执行结果- 字符串转字典:
eval("{'name': '小夏', 'age': 30}")
- 字符串转列表:
eval("[1, 2, 3, 4]")
- 字符串转元组:
eval("(1, 2, 3, 4)")
- 字符串转换数据类型:
a = eval('2 + 2')
, a 为 int 型变量 - 字符串函数执行:
self.network = eval(self.netw_name + '(self.cfg, self.logger, *self.netw_args)')
- dict2str:
mystr=str(mydict)
- str2dict:
def my_function(x, y):
return x + y
# 当可变关键字参数通过字符串来传递时,可以像下面那样使用
param_str = "{'x': 3, 'y': 4}"
params = eval(param_str)
result = my_function(**params)
print(result) # 7
from cls_dataset import ClsDataset
self.train_data = eval(cfg.DATA.NAME + '(txt_path=cfg.DATA.TRAIN_TXT_PATH, transform=self.train_transform)')
# 等价于下面这句,前提是 lsDataset 要提前导入
self.train_data = ClsDataset(txt_path=cfg.DATA.TRAIN_TXT_PATH, transform=self.train_transform)
easydict
- 安装:
pip install easydict
- 作用:可以用
.
以属性的方式去访问字典的值 - 实践代码:
from easydict import EasyDict as edict # 创建一个空的 easydict config = edict() # 配置网络的一些超参数(初始化 easydict) config.BATCH_SIZE = 384 config.EPS = 1e-14 config.LR_EPOCH = [6, 14, 20] # 用传统字典初始化一个 easydict d = {'BATCH_SIZE':64, 'LR_EPOCH': [6, 14, 20], 'bar':{'x':1, 'y':2}} config = edict(d)
- 安装:
2. 集合(在我的世界里,你就是唯一)
- 集合简介
- 集合几乎所有的作用就是确保里边元素的唯一性,集合内不可能存在两个相同的元素。
- 由于集合中的元素是无序的,所以不能像序列那样用下标来进行访问,但是可以使用迭代把集合中的数据一个个的读取出来。
- 集合的常见操作
|:取两个集合的并集
&:去两个集合的交集
-:取两个集合的差集
in:判断元素是否属于集合
add(x):集合中添加元素
update(seq):集合中添加多个元素,相当于extend
remove(x):集合中删除指定元素
pop():删除集合任意元素并返回其值
clear():清空集合使其成为空集
六、推导式
1. 列表、字典和集合推导式
- 执行效率要比使用循环操作高的多
- 列表推导式会保留元素的顺序,而字典和集合推导式是无序类型,所以会添加元素时会随机操作
2. 生成器推导式和生成器函数(yield)
- 生成器包含两种:生成器推导式和生成器函数(yield)
- 其特点是一边循环一边计算的机制,可极大的节省内存,但只能迭代一次
- 可使用返回的迭代器作为参数,逐次调用内置的
next
函数,即可使其按照生成器表达式来输出下一个值
- 注意生成器推导式和其它推导式的区别是:最外面是一个小括号
()
def next(iterator, default=None): # real signature unknown; restored from __doc__ """ next(iterator[, default]) Return the next item from the iterator. If default is given and the iterator is exhausted, it is returned instead of raising StopIteration. """ # 1、生成器推导式 a = (x**2 for x in range(10) if x < 3) next(a, -1) >>> 0 next(a, -1) >>> 1 next(a, -1) >>> 4 next(a, -1) # 默认值改为 -1, 不再触发 StopIteration >>> -1 # 2、使用 yield 语句而不是 return 语句返回结果的生成器函数 # 每次调用 next() 的时候执行,遇到 yield 语句返回,再次执行时从上次返回的 yield 语句处继续执行 def odd(): print('step 1') yield 1 print('step 2') yield(3) print('step 3') yield(5) >>> o = odd() >>> next(o) step 1 1 >>> next(o) step 2 3 >>> next(o) step 3 5 >>> next(o) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
七、可迭代对象(iterable),迭代器对象(iterator),生成器对象(generator)的关系
-
集合数据类型如
list
、dict
、str
、set
等是可迭代对象iterable
但不是iterator
,不过可以通过iter()
函数获得一个迭代器对象iterator
(内容放在内存,时间优先) -
生成器推导式和生成器函数(
yield
)都是生成器对象generator
类型,而且它们属于 一种高级迭代器对象iterator
类型,表示一个惰性计算的序列(内容不放内存,用时再存,空间优先),且只能迭代一次 -
凡是可作用于 for 循环的对象都是可迭代对象
iterable
类型,Python 的 for 循环本质上就是通过不断调用next()
函数实现的
-
for 语句的内部实现,需要三个条件实现迭代器协议:
__iter__()、 __next__()、StopIteration
__iter__()
:获取迭代器对象__next__()
:从迭代器中获取值StopIteration
:检测异常来跳出
for element in iterable: # do something with element # create an iterator object from that iterable iter_obj = iter(iterable) # iter() 内置函数调⽤的是对象的 __iter__() ⽅法 # infinite loop while True: try: # get the next item element = next(iter_obj) # next() 内置函数调⽤的是对象的 __next__() ⽅法 # do something with element except StopIteration: # if StopIteration is raised, break from loop break # 通过内置函数 iter 将可迭代对象列表转换为迭代器对象 myIter = iter([1, 2, 3]) # 通过内置函数 next 依次获取迭代器的下一个元素 print(next(myIter)) # 1 print(next(myIter)) # 2 print(next(myIter)) # 3 print(next(myIter)) # StopIteration
-
python 四大神器:迭代器(iterator)、生成器(generator)、装饰器(decorator)、上下文管理器(context manager)
- 装饰器:对函数和类的封装
- 上下文管理器(
with
):对任意形式代码块的封装(__init__() optional、__enter__()、__exit__()
)
八、Python 控制流(顺序/分支/循环)
1. Python 程序运⾏完整流程
Python 编译器
:将源代码在内存中编译为byte code
Python 虚拟机
:调⽤ Built-in 库⽂件,同时将内存中的byte code
逐⾏解析并执⾏,运⾏结束后,byte code
在内存中直接销毁- 若存在 import 脚本,则将其
byte code
持久化到硬盘上,成为.pyc
⽂件,第⼆次运⾏相同且⽆修改的模块时,免去编译过程,直接读取编译好的 .pyc 放⼊虚拟机中执行 - Note:python 解释器由编译器和虚拟机构成
2. 控制流简介
- if/elif/else,可使用 and or not 进行多条件组合判断
- for ,while(需要加分号进行缩进),在 for 和 while 循环中也可以使用 else 语句
- 跳出循环:break , continue,不需要加分号
- 占位语句:pass,在 python 中空代码是非法的
3. for 循环详解
- for 循环格式
- 在 for 循环中常用
_
作为 item 来表示:it’s a dummy variable, its value isn’t used in the loop body
- 在 for 循环中常用
- for 循环相关函数
- range() 函数用法:
range([start], stop, [step=1]) #[]为可选参数,步长默认为1,stop的值取不到
,它产生一个数字序列,可以通过list()函数将其转换为列表。 - enumerate(seq, start=0): 产生用于迭代的 (索引,值) 对
- 可以通过
list(enumerate(seq))
将(索引,值)在 列表 中打印出来 - 可以通过
start
参数指定函数开始计数时所用的值
- 可以通过
- zip (seq1, seq2, …):并行迭代,接受一系列可迭代的对象作为参数,将对象中对应的元素打包成一个个tuple,然后返回由这些 tuples 组成的 list。若传入参数的长度不等,则返回 list 的长度和参数中长度最短的对象相同。
- range() 函数用法:
zip(*x)
函数:函数中的对象至少是二维数组,使用*
进行解包,把数组中的每个对应元素提取出来,组成一个新的元组的可迭代对象
import torch
def collate_fn(batch):
"""Merges a list of samples to form a mini-batch of Tensor(s)"""
imgs, labels_value, label_name = zip(*batch)
return torch.stack(imgs, dim=0), torch.tensor(labels_value, dtype=torch.long), list(label_name) # 2,3,2,2; 2; 2
if __name__ == "__main__":
# zip() 函数: 将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组
a = [1, 3, 5, 7, 9]
b = [2, 4, 6, 8, 10]
print(zip(a, b)) # 返回一个 zip 对象,其内部元素为元组, <zip object at 0x7f7e4b6642c0>
print(list(zip(a, b))) # 可以转化为列表或元组, [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
# 可通过循环迭代每个 tuple 对应的元素
for i, j in zip(a, b):
print(i, j)
# zip(*)函数:函数中的对象至少是二维数组,使用 * 进行解包,把数组中的每个对应元素提取出来,组成一个新的元组的可迭代对象
c = [[1, 5], [2, 6], [3, 7], [4, 8]]
print(zip(*c)) # 返回一个 zip 对象,其内部元素为元组, <zip object at 0x7f7e4b664340>
# 可通过赋值取出每个 tuple
c1, c2 = zip(*c)
print(c1, c2) # (1, 2, 3, 4) (5, 6, 7, 8)
# zip(*) 在 pytorch 中的使用方式
a = torch.randn((3, 2, 2))
b = torch.randn((3, 2, 2))
batch = [(a, 1, 'dog'), (b, 0, 'cat')]
collate_fn(batch) # 主要调用的是 zip(*batch)
九、Python 字符编码及工作环境配置
-
- ASCII:最早只有127个字母被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码,
python2
中默认使用 ASCII。 - Unicode:它把所有语言都统一到一套编码里,这样就不会再有乱码问题了,但是它每个字符用3~4个字节表示,浪费空间。
- UTF-8:用 Unicode 编码 (通常2bytes) 比 ASCII 编码 (1byte) 需要多一倍的存储空间,在存储和传输上就十分不划算。所以,本着节约的精神,又出现了把Unicode编码转化为 “可变长编码” 的 UTF-8 编码。常用的英文字母被编码成 1 个字节,汉字通常是 3 个字节,只有很生僻的字符才会被编码成4-6个字节,
python3
中默认使用 UTF-8。 - 在计算机内存中,统一使用 Unicode 编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8 编码。
- 内存中使用的编码是
unicode
,用空间换时间
(程序都需要加载到内存才能运行,因而内存应该是尽可能的保证快) - 硬盘中或者网络传输用
utf-8
(或gbk
等),网络I/O延迟或磁盘I/O延迟要远大与utf-8的转换延迟
,而且I/O应该是尽可能地节省带宽
,保证数据传输的稳定性。
- 内存中使用的编码是
- encode 和 decode 方法
- decode 的作用:是将
其他
编码的字符串转换成Unicode
编码,如str1.decode(‘gbk’)
,表示将 gbk 编码的字符串 str1 转换成 Unicode 编码 - encode 的作用:是将
Unicode
编码转换成其他
编码的字符串,如str2.encode(‘UTF-8’)
,表示将Unicode 编码的字符串 str2 转换成 UTF-8 编码 - 因此,使用 Python 转码的时候一定要先搞明白,字符串 str 是什么编码,先 decode 成 Unicode,然后再 encode 成其他需要的编码格式
- decode 的作用:是将
- ASCII:最早只有127个字母被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码,
-
源码的保存(硬盘)与加载(内存)
- 保存到硬盘时,若不指定保存时的编码方式(字符到二进制),在 linux 操作系统中为
utf-8
,windows 操作系统中为gbk
- 在 linux 操作系统的终端中可使用
locale
查看 - 或者在 python 程序中导入
import locale
,然后通过print(locale.getdefaultlocale())
查看
- 在 linux 操作系统的终端中可使用
- 加载到内存时,使用源码开头声明的编码方式(需和保存时一致),若不声明,python2 默认使用
ASCII
,python3 默认使用utf-8
- python3 中会自动将加载后的源码转换成
unicode
编码(内存中),此时只能encode
,不能decode
- python2 中不会主动帮你转 unicode,所以加载到内存中的
编码方式不会改变,为二进制码流
(内存中)
# 更改 py2 解释器的默认编码方式(ASCII-->UTF-8) import sys import locale reload(sys) # 这里必须reload一下才能找到 setdefaultencoding method sys.setdefaultencoding('utf-8') # 打印python解释器的默认编码方式和操作系统的默认编码方式 print(sys.getdefaultencoding()) print(locale.getdefaultlocale()) # py2 通过在codecs.open中设置保存的编码方式 import codecs with codecs.open("filename", "w", encoding="utf-8") as f: f.write(u) with codecs.open("somefile", "r", encoding="utf-8") as f: content = f.read()
- python3 中会自动将加载后的源码转换成
- 保存到硬盘时,若不指定保存时的编码方式(字符到二进制),在 linux 操作系统中为
-
python 中的字符串表示形式:
- 在 python2 中,
str
contains sequences of 8-bit values,unicode
contains sequences of Unicode characters - 在 python3 中,
bytes
contains sequences of 8-bit values,str
contains sequences of Unicode characters
# coding:utf-8 # py2,ubuntu x = '满' print x, repr(x), len(x), type(x) print x.decode('utf-8'), repr(x.decode('utf-8')), len(x.decode('utf-8')), type(x.decode('utf-8')) # python2 中 print x,会按照终端的编码执行 x.decode('终端编码'),变成 unicode 后,再打印 # python3 中,是什么就打印什么 # 输出结果,注意切片的时候长度的提取,必要时转成 unicode 来切片 满 '\xe6\xbb\xa1' 3 <type 'str'> 满 u'\u6ee1' 1 <type 'unicode'> # python3 总是返回 str def to_str(bytes_or_str): if isinstance(bytes_or_str, bytes): value = bytes_or_str.decode(‘utf-8’) else: value = bytes_or_str return value # Instance of str # python3 总是返回 bytes def to_bytes(bytes_or_str): if isinstance(bytes_or_str, str): value = bytes_or_str.encode(‘utf-8’) else: value = bytes_or_str return value # Instance of bytes ####################################### # python2 总是返回 unicode def to_unicode(unicode_or_str): if isinstance(unicode_or_str, str): value = unicode_or_str.decode(‘utf-8’) else: value = unicode_or_str return value # Instance of unicode # python2 总是返回 str def to_str(unicode_or_str): if isinstance(unicode_or_str, unicode): value = unicode_or_str.encode(‘utf-8’) else: value = unicode_or_str return value # Instance of str
- 在 python2 中,
-
Anaconda:集成了很多关于Python科学计算的第三方库
- Jupyter QtConsole(IPython ):验证思路
- Jupyter Notebook(New-----Python[Root]-----新建.ipynb文件):分享和演示
- Numpy、Scipy、Pandas、Matplotlib…
- 在终端/命令行中安装第三方库:
conda install package
- 在终端中查看安装了哪些工具包:
conda list
- 在终端中产生一个自定义环境:
conda create -n py35(可换成其它名字) python=3.5 source activate py35 # 使用时激活环境 source deactivate # 离开此环境
-
Pycharm(编写完整项目与调试):
- 设置编译器和编码方式
# -*- coding: utf-8 -*-
# !/usr/bin/env python2
# 文件头部的编码方式要与编译器中保存的编码方式一致
- 环境配置--------Pycharm 常用配置及快捷键
- 设置编译器和编码方式
十、参考资料
1、吐血总结,彻底明白 python3 编码原理
2、Python字符编码之理解
3、python 之路,致那些年,我们依然没搞明白的编码
4、廖雪峰字符串和编码
5、Python字符编码详解
6、图解 Python 深拷贝和浅拷贝
7、谈谈 Python 中的深拷贝和浅拷贝
8、Python 直接赋值、浅拷贝和深度拷贝解析