Home Python积累
Post
Cancel

Python积累

关于本页

一些Python的基本知识、操作积累,记录下方便日后查找使用。

python文件读写模式

参考:Stack Overflow

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中的深拷贝与浅拷贝

参考1 - runoob :star:

参考2 - 掘金

  • 直接赋值: 实质上就是对象的引用(别名)。
  • 浅拷贝(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的数据存储方式有关。欢迎讨论。

最后验证一下过程中产生的一个细思极恐的问题:wave:

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的机理

参考1 参考2

  • 如果程序的入口文件在顶级目录,由于该顶级目录已经在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__'之下的代码块不被运行。

根据字符串返回函数

  1. 使用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")()
    
  2. 使用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()

字符串相关

  • 大写转小写: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)]
    
  • 多条件排序

    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是否能正常导入

setup.py

-A Practical Guide to Using Setup.py

This post is licensed under CC BY 4.0 by the author.

Ubuntu下nvidia driver和cuda版本管理

Enable Google Page Views