关于本页
一些Python的基本知识、操作积累,记录下方便日后查找使用。
python文件读写模式
1
2
3
4
5
6
7
8
9
| r r+ w w+ a a+
------------------|--------------------------
read | + + + +
write | + + + + +
write after seek | + + +
create | + + + +
truncate | + +
position at start | + + + +
position at end | + +
Some example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# write
with open(filename, 'w+') as f:
f.write(<something>)
# read - way1
with open(filename, 'r') as f:
for line in f.readlines():
str1, str2 = line.split()
# read - way2
file = open(filename)
for line in file:
str1, str2 = line.split()
file.close()
多次访问文件的话, 下次访问前需回到文件头
1
file.seek(0)
杂记
-
使用
map
方便地把line.split()
得到的str列表统一映射到其他数据格式>1
num1, num2, num3, num4 = map(float, line.split())
-
json文件的读写
1 2 3 4 5 6 7
# 读取json文件为字典 with open(<json_path>, 'r') as f: loaded_dict = json.load(f) # 将new_dict写入json_path with open(<json_path>, "w") as f: json.dump(new_dict, f)
Python中的深拷贝与浅拷贝
- 直接赋值: 实质上就是对象的引用(别名)。
- 浅拷贝(copy): 拷贝父对象,不会拷贝对象的内部的子对象。
-
深拷贝(deepcopy):
copy
模块的deepcopy
方法,完全拷贝了父对象及其子对象。
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import copy
a = [1, 2, 3.0, {'key': 'val'}, ['a', 'b']] # 原始对象
b = a # 赋值,传对象的引用
c = copy.copy(a) # 浅拷贝, 也可简单使用 c = a.copy()
d = copy.deepcopy(a) # 深拷贝
a.append(5) # 修改对象a (父对象)
a[4].append('c') # 修改对象a中的['a', 'b']数组对象 (子对象)
a[0] += 1
# 从输出值验证一下
print( 'a = ', a )
print( 'b = ', b )
print( 'c = ', c )
print( 'd = ', d )
'''output
a = [2, 2, 3.0, {'key': 'val'}, ['a', 'b', 'c'], 5]
b = [2, 2, 3.0, {'key': 'val'}, ['a', 'b', 'c'], 5] # b为a的引用,与a共享同一块内存地址,因此与a同步改变
c = [1, 2, 3.0, {'key': 'val'}, ['a', 'b', 'c']] # c为浅拷贝,拷贝了父对象但没拷贝子对象
d = [1, 2, 3.0, {'key': 'val'}, ['a', 'b']] # d为深拷贝,拷贝了父对象也拷贝了子对象
'''
# 从内存地址验证一下, id()函数可返回变量的内存地址
print(id(a), id(a[0]), id(a[1]), id(a[2]), id(a[3]), id(a[4]))
print(id(b), id(b[0]), id(b[1]), id(b[2]), id(b[3]), id(b[4]))
print(id(c), id(c[0]), id(c[1]), id(c[2]), id(c[3]), id(c[4]))
print(id(d), id(d[0]), id(d[1]), id(d[2]), id(d[3]), id(d[4]))
'''output
139864587372672 94459641159008 94459641159008 139864585514544 139864587372928 139864587371008
139864587372672 94459641159008 94459641159008 139864585514544 139864587372928 139864587371008
139864587370560 94459641158976 94459641159008 139864585514544 139864587372928 139864587371008
139864587370944 94459641158976 94459641159008 139864585514544 139864587373440 139864587373696
'''
内存地址返回值单独分析下:对于每一个变量均返回了六个内存地址,第一个为了验证父对象地址的变化情况;第二个为了验证发生变化的整型数地址的变化情况;第三个,第四个分别验证未发生变化的整型数、浮点数的地址的变化情况;第五个,第六个分别验证类型为字典和列表的子对象的地址变化情况。可以看出:
- 引用(b):父对象地址(第一个)和各子对象地址与原对象保持完全一致。
- 浅拷贝(c):重新开辟了一个新的父对象地址(第一个),但列表/字典类型的子对象(第五、第六个)地址仍指向原对象。
- 深拷贝(d):重新开辟了一个新的父对象地址(第一个),以及列表/字典类型的子对象(第五、第六个)地址。
第二、第三、第四个真是还存在疑惑。对单纯的整型数或浮点数似乎有些特殊:对值未更改的整型数、浮点数(第三、第四个),不管是深浅拷贝,作为子对象都会仍和原元素共享同一块地址;而似乎在对其进行重新赋值操作时(第二个),会自动重新为其分配一块新地址,可能是和Python的数据存储方式有关。欢迎讨论。
最后验证一下过程中产生的一个细思极恐的问题
1
2
3
4
5
6
7
8
a = 1
b = a
print(id(a)) # 94767788772672
print(id(b)) # 94767788772672
a += 1
print(id(a)) # 94767788772704
print(id(b)) # 94767788772672
整型数/浮点数的赋值操作,确实也是一个引用的过程。但是当对一个变量(a)进行修改后,从表现上看,它会剥离原内存空间,开辟一块新空间来保存修改后的数,另一个变量(b)保持在原来的内存空间,它的值不会受到相应的影响。换句话说,二者值的变化是自动解耦的。要不之前写了这么多整型数/浮点数的赋值操作却没有意识到错误,真就完犊子。
Python之import的机理
-
如果程序的入口文件在顶级目录,由于该顶级目录已经在
sys.path
里了,所以将导入路径从顶级目录一路写下来即可。比如,在该顶级目录(及其子目录)下的任意一文件中,导入其他文件1
from mmt.evaluation_metrics import accuracy # mmt为顶级目录下的一个模块
-
如果希望从其他位置导入模块,可以
1 2
import sys sys.path.append('<your_import_path>')
-
python
的引用分为绝对导入和相对导入,以下为一些相对导入的例子1
from .package_name import module_name # 导入和自己同目录的包的模块。
如果要把一个文件夹封装为一个包,需要在该文件夹下建立一个
__init__.py
文件,即使为空文件。
格式化时间
1
2
3
4
5
6
7
8
9
10
11
import time
# 格式化成2016-03-20 11:45:39形式
print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
# 格式化成Sat Mar 28 22:24:24 2016形式
print(time.strftime("%a %b %d %H:%M:%S %Y", time.localtime()))
# 将格式字符串转换为时间戳
a = "Sat Mar 28 22:24:24 2016"
print(time.mktime(time.strptime(a,"%a %b %d %H:%M:%S %Y")))
python给数字前面补零
1
2
3
4
n = "123"
s = n.zfill(5) # 或 s = "%05d" % n
assert s == "00123"
获得当前目录的父目录
1
2
3
4
5
6
import os
cwd = os.getcwd()
parent_dir = os.path.dirname(cwd)
print(cwd) # /parent_dir/child_dir
print(parent_dir) # /parent_dir
Python相对路径与绝对路径相互转化
1
2
3
4
5
6
7
import os
# convert relative path to absolute path
abs_path = os.path.abspath(rel_path)
# convert absolute path to relative path
rel_path = os.path.relpath(abs_path)
获取Python包的安装目录
有的时候我们想查看或修改Python包的源码,它们在哪里呢?可通过
1
2
import module
print(module.__file__)
查看,如
1
2
import torch
print(torch.__file__)
理解Python中的if __name__ == ‘__main__’
通俗地理解__name__ == __main__'
:假如你叫小明.py
,在朋友眼中,你是小明(__name__ == '小明'
);在你自己眼中,你是你自己(__name__ == '__main__'
)。
if __name__ == '__main__'
的意思是:当.py
文件被直接运行时,if __name__ == '__main__'
之下的代码块将被运行;当.py
文件以模块形式被导入时,if __name__ == '__main__'
之下的代码块不被运行。
根据字符串返回函数
-
使用
getattr
getattr()
是 python 的内建函数,getattr(object,name)
就相当于object.name
1 2 3 4 5 6 7 8 9 10 11 12
class Foo: def __init__(self): self.func1 = ... def call_func1(self): getattr(self, func1) # 返回 self.func1,执行需要getattr(self, func1)() def func2(self): print("I am func2") foo = Foo() getattr(foo, "func2")()
-
使用
eval
1 2 3 4 5
def func1(): print("I am func1") eval("func1")() # out: I am func1
Python文件新建,删除,复制,移动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import os
import shutil
# 判断文件(夹)是否存在
os.path.exists()
# 新建文件夹
os.makedirs()
# 删除文件
os.remove()
# 删除文件夹
shutil.rmtree()
# 复制文件
shutil.copy(src_file, dst_file_or_dir)
# 复制文件夹
shutil.copytree(src_dir, dst_dir)
# 移动文件
shutil.move(old_pos, new_pos)
# 重命名
os.rename(old_name, new_name)
os.mkdir()与os.makedirs()
os.makedirs()
会递归地建立输入的路径,如果父级路径不存在,会自动建立。而os.mkdir()
只能一级一级地建立目录,如果父级路径不存在,则会报错。
with … as …用法
with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭/线程中锁的自动获取和释放等。
如
1
2
with open("1.txt") as file:
data = file.read()
是比
1
2
3
4
5
file = open("1.txt")
data = file.read()
file.close()
# 1. 文件读取发生异常,但没有进行任何处理;
# 2. 可能忘记关闭文件句柄。
更加优雅鲁棒的编程方式。
工作原理
先看例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Sample:
def __init__(self):
pass
def __enter__(self):
print ("in __enter__")
return "Foo"
def __exit__(self, exc_type, exc_val, exc_tb):
print ("in __exit__")
sample = Sample()
with sample as s:
print ("sample: ", s)
输出:
1
2
3
in __enter__
Sample: Foo
in __exit__
可以看出,with ... as ...
的工作原理为:
- 紧跟with后面变量的
__enter__()
方法首先被调用,其返回值赋给as
后面的变量; - 当with后面的代码块全部被执行完之后, 执行with后面变量的
__exit__
方法。
Python运算符重载
一些常见的记录
方法 | 重载 | 调用 |
---|---|---|
__init__ | 构造函数 | X = ClassX(args) |
__len__ | 获取长度 | len(X) |
__repr__ | 打印 | print(X) |
__getitem__ | 索引 | X[i] |
__setitem__ | 索引赋值 | X[i] = value |
__call__ | 把一个类当作函数调用 | X(*args,**kargs) |
__enter__, __exit__ | 环境管理器 | with obj as var: |
来看demo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class ToyClass:
def __init__(self, data):
self.data = data
def __repr__(self):
# return self.data # TypeError: __str__ returned non-string (type list)
return str(self.data)
def __len__(self):
return len(self.data)
def __getitem__(self, i):
return self.data[i]
def __setitem__(self, i, value):
self.data[i] = value
def __call__(self, param):
print("__call__() is called, your input param is {}".format(param))
toy = ToyClass(data=['I', 'LOVE', 'SJTU'])
# __repr__
print(toy) # out: ['I', 'LOVE', 'SJTU']
# __len__
print(len(toy)) # out: 3
# __getitem__
print(toy[0]) # out: I
print(toy[0:3]) # out: ['I', 'LOVE', 'SJTU']
# __setitem__
toy[0] = 'U'
print(toy) # out: ['U', 'LOVE', 'SJTU']
# __call__
toy('S') # out: __call__() is called, your input param is S
土味断点——使用input()
在需要程序暂停的地方插入一个input()
,程序运行至此会暂停,按下回车后继续
1
2
3
for i in range(10):
print(i)
input() # Loop continues after <Enter> is pressed
一些基本语法
遍历字典
1
2
3
4
5
6
7
8
9
10
11
12
13
demo_dict = dict(...)
# 遍历所有key
for key in demo_dict.keys():
print(key)
# .keys()返回dict_keys对象,不支持索引,如需索引,需要强制转换list(demo_dict.keys()),.values()同理
# 遍历所有value
for value in demo_dict.values():
print(value)
# 遍历所有键值对
for key, value in demo_dict.items():
print(key, value)
字典相关
-
pop()
:demo_dict.pop(key)
,从demo_dict
中删去键值为key
的键值对 -
__contains__()
:demo_dict.__contains__(key)
,判断demo_dict
中是否还有key
键值,时间复杂度为O(1)
- Python3.X不支持
has_key()
- Python3.X不支持
字符串相关
- 大写转小写:
c.lower()
- 小写转大写:
c.upper()
assert语句
使用方法:
1
assert expression [, arguments]
注意,当需要加arguments
时,使用方法为assert expression, arguments
,而不是assert (expression, arguments)
(即不加括号)!!!原因在于assert
是一个关键字,而不是一个函数。当使用assert (expression, arguments)
时,(expression, arguments)
会被解释为一个元组,而元组是True
的,所以会得到
SyntaxWarning: assertion is always true, perhaps remove parentheses?
的warning,assert
实际上也就失效了。所以切记不能用错了。参考
杂项
- 生成0~1区间内的随机数
1 2
import random rand = random.random()
一些使用trick
排序
-
字典排序
- 按key排序:
sorted(d.items(), key = lambda x : x[0])
- 按value排序:
sorted(d.items(), key = lambda x : x[1])
1 2 3
d = {'a' : 3, 'b' : 2, 'c' : 3} d_key_sorted = sorted(d.items(), key = lambda x : x[0]) # [('a', 3), ('b', 2), ('c', 1)] d_value_sorted = sorted(d.items(), key = lambda x : x[1]) # [('c', 1), ('b', 2), ('a', 3)]
- 按key排序:
-
多条件排序
1 2 3
intervals = [[0, 1], [2, 4], [2, 5]] # 第一个元素升序排列,第二个元素降序排列 intervals.sort(key = lambda x : (x[0], -x[1])) # [[0, 1], [2, 5], [2, 4]]
python -c <command>
不进入python解释器的交互模式,直接执行python代码。在进行一些简单的测试时很方便。
1
$ python -c "import torch" # 查看torch是否能正常导入