把之前的python使用方法全部汇总到这里
转载记录
python自动给数字前面补0的方法
python中有一个zfill方法用来给字符串前面补0,非常有用
# zfill补0
n = "123"
s = n.zfill(5)
assert s == "00123"
# 也可以给负数补0
n = "-123"
s = n.zfill(5)
assert s == "-0123"
# 对于纯数字,我们也可以通过格式化的方式来补0
n = 123
s = "%05d" % n
assert s == "00123"
浅析python 中__name__ = ‘main’ 的作用
很多新手刚开始学习python的时候经常会看到python 中__name__ = ‘main’ 这样的代码,可能很多新手一开始学习的时候都比较疑惑,python 中__name__ = ‘main’ 的作用,到底干嘛的?
有句话经典的概括了这段代码的意义:
“Make a script both importable and executable”
意思就是说让你写的脚本模块既可以导入到别的模块中用,另外该模块自己也可执行。
这句话,可能一开始听的还不是很懂。下面举例说明:
先写一个模块:
#module.py
def main():
print "we are in %s"%__name__
if __name__ == '__main__':
main()
这个函数定义了一个main函数,我们执行一下该py文件发现结果是打印出”we are in main“,说明我们的if语句中的内容被执行了,调用了main():
但是如果我们从另我一个模块导入该模块,并调用一次main()函数会是怎样的结果呢?
#anothermodle.py
from module import main
main()
其执行的结果是:we are in module
但是没有显示”we are in main“,也就是说模块__name__ = ‘main’ 下面的函数没有执行。
这样既可以让“模块”文件运行,也可以被其他模块引入,而且不会执行函数2次。这才是关键。
总结一下:
如果我们是直接执行某个.py文件的时候,该文件中那么”name == ‘main’“是True,但是我们如果从另外一个.py文件通过import导入该文件的时候,这时__name__的值就是我们这个py文件的名字而不是__main__。
这个功能还有一个用处:调试代码的时候,在”if name == ‘main’“中加入一些我们的调试代码,我们可以让外部模块调用的时候不执行我们的调试代码,但是如果我们想排查问题的时候,直接执行该模块文件,调试代码能够正常运行!
python排序方法
C/C++与Python
C调用python接口方法
主要是参考下面两篇博文
使用c语言调用python小结
Python实例浅谈之三Python与C/C++相互调用
需要注意的是,
1.如果提示undefined reference to Py_XXXX
说明没有正确加载python的库,根据python的版本,在makefile里加入-lpython2.7
2.如果PyImport_ImportModule返回的指针为空,一定是python模块的路径搞错了,这里我开始直接给
PyImport_ImportModule的参数传的是python模块的绝对路径,返回为空。后来使用下面两句解决了问题,
应该是需要先调用第一句加入python模块的路径,PyImport_ImportModule只负责加载模块名称,不包括路径
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");
PyImport_ImportModule('voc_eval');
python与c++混合编程调试方法
这里主要是调试py-faster-rcnn,其他类似
单纯使用pdb可以调试python代码,但是无法调试其调用的c++代码。
如果要调试c++代码,使用如下指令
gdb --args python test.py
具体步骤:
1.使用指令,并且r进去,让进程跑起来
2.由于此时caffe.so并没有加载进来,所以,可能会出现即使在cpp里下断点也无法跟踪的情况,这里可以先手动ctrl+c把进程断开,再设置断点,让caffe.so加载进来。
所以,这一步是下断点,如b caffe/src/caffe/solver.cpp:228
3.c,进去cpp里即可
关于命名中的下划线
变量
主要存在四种情形
1. object # public
2. object # special, python system use, user should not define like it
3. __object # private (name mangling during runtime)
4. _object # obey python coding convention, consider it as private
核心风格:避免用下划线作为变量名的开始。
函数
- 前带_的变量: 标明是一个私有函数, 只用于标明
- 前带两个_ ,后带两个_ 的函数: 标明是特殊函数
#多层目录模块调用#
转载自这里 - 将导入模块所在目录(…/model/模块)添加到系统环境变量path下,可添加多个
import sys
sys.path.append("../model") # 模块父目录下的model文件中
from model import a
注意:不管什么样的引用,建议直接使用这种方法,避免发生引用模块里import的问题。
(所有被引用模块的调用会优先查找当前执行目录下的文件,找不到再去找所有环境变量的目录!)
程序结构如下:
– src
|-- model
| |-- a.py
| |-- b.py
|-- test.py
a模块 调用了同级目录的b --> import b
test模块 调用a模块时出错 --> from model import a
出错:在test当前目录运行时找不到b模块。
2.引用模块在 当前目录的同级以及所包含的子目录中:
2.1. 直接import 同级文件.子文件.引用模块
2.2. from 同级文件.子文件 import 引用模块
列表等分
转载自这里
这里主要借助itertools模块里的izip和iziplongest方法,代码如下
from itertools import zip_longest # zip_longest -> Python 3, izip_longest -> Python 2
chunk_list = lambda a_list, n: zip_longest(*[iter(a_list)]*n)
result_groups = list(chunk_list([x for x in range(100)], 8))
Out[12]:
[(0, 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, 39),
(40, 41, 42, 43, 44, 45, 46, 47),
(48, 49, 50, 51, 52, 53, 54, 55),
(56, 57, 58, 59, 60, 61, 62, 63),
(64, 65, 66, 67, 68, 69, 70, 71),
(72, 73, 74, 75, 76, 77, 78, 79),
(80, 81, 82, 83, 84, 85, 86, 87),
(88, 89, 90, 91, 92, 93, 94, 95),
(96, 97, 98, 99, None, None, None, None)]
这里借助了迭代器,关于*[iter(a_list)]*n ,参考了stackoverflow里的精彩说明,如下
iter() is an iterator over a sequence. [x]
- n produces a list containing n quantity of x, i.e. a list of length n, where each element is x. *arg unpacks a sequence into arguments for a function call. Therefore you’re passing the same iterator 3 times to zip(), and it pulls an item from the iterator each time.
x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)
最终产生了一个等分list的方法。
等分list可以用来批量处理数据,如等分目录下的文件等。
#数字前补0#
转载自这里
python中有一个zfill方法用来给字符串前面补0,非常有用
n = "123"
s = n.zfill(5)
assert s == "00123"
zfill()也可以给负数补0
n = "-123"
s = n.zfill(5)
assert s == "-0123"
对于纯数字,我们也可以通过格式化的方式来补0
n = 123
s = "%05d" % n
assert s == "00123"
生成voc数据集的脚本代码
# !/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'xiaobanderui'
from xml.dom import minidom
import numpy as np
import cv2
imgpath = u'e:/VOC2007/JPEGImages/' # 图片存放路径
txtpath = u'e:/data.txt' # 保存标注信息的文件,每一行的内容为[filename objectname xmin ymin xmax ymax]
xml_path = u'e:/VOC2007/Annotations/'
lastname = u'begin'
foldername = u'VOC2007'
num = 0 # 总样本数
valid_num = 0 # 有效样本数
cv2.namedWindow('img', cv2.WINDOW_NORMAL)
with open(txtpath, 'r') as fr:
for item in fr.readlines():
num += 1
print 'processing %d' % num
item = item.strip().decode('gbk')
str_item = item.split(' ')
img =cv2.imread((imgpath + str_item[0]).encode('gbk'), cv2.IMREAD_UNCHANGED)
ht. wd. cn = img.shape
xmin = int(float(str_item[2]))
xmax = int(float(str_item[3]))
ymin = int(float(str_item[4]))
ymax = int(float(str_item[5]))
cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (0, 0, 255), 2)
cv2.imshow('img', img)
cv2.waitKey()
# 过滤掉标注信息错误的样本
# 框的位置越界
if xmin >= xmax or ymin >= ymax or xmin < 0 or ymin < 0 or xmax < 0 or ymax < 0 or xmin >= wd or xmax >= wd or ymin >= ht or ymax >= ht
continue
# 框的位置不对,比如翻转时
oldx1 =xmin
oldx2 = xmax
newxmin = wd - oldx2 - 1
newxmax = wd - oldx1 - 1
if newxmin >= newxmax
continue
valid_num += 1
if str_item[0] == last_name:
object_node = doc.createElem('object')
rootNode = doc.appendChild(object_node)
node = doc.createElem('name')
node_text = doc.createTextNode(str_item[1])
node.appendChild(node_text)
object_ode = doc.appendChild(node)
'''
其他类似添加,不再赘述
'''
else:
if vars().has_key('rootNode'):
fw_xml = open(xml_path + file_full_name[0] + '.xml', 'w')
fw_xml.write(doc.toprettyxml(encoding='utf-8'))
fw_xml.close()
node = doc.createElem('name')
node_text = doc.createTextNode(str_item[1])
node.appendChild(node_text)
object_ode = doc.appendChild(node)
fw_xml = open(xml_path + file_full_name[0] + '.xml', 'w')
fw_xml.write(doc.toprettyxml(encoding='utf-8'))
fw_xml.close()
编译错误总结
代码缩进问题,错误信息如下
IndentationError: unindent does not match any outer indentation level
类型转换问题
如下字串转为int会有错误
str = '123.456'
x = int(str)
错误信息如下
invalid literal for int() with base 10
百度了一下,发现python里的int()只接受纯数字的转换,这里有的字串有‘.’,所以会报错,做如下更改即可
str = '123.456'
x = round(float(str))
numpy
savetxt
使用numpy的savetxt保存的数据维度>=3时,会出现错误,比如保存一个三通道的数组就会出错,这里可以采用每个通道分别保存的方式
np.savetxt(filename, array[0,:,:])
np.savetxt(filename, array[1,:,:])
np.savetxt(filename, array[2,:,:])
目录、文件操作
判断目录、文件是否存在
转载自这里
这篇文章主要介绍了Python判断文件和文件夹是否存在的方法,本文还讲解了判断是否为文件或者目录的方法、os.path.lexist的作用、FTP中判断文件或目录是否存在等内容,需要的朋友可以参考下
1.python判断文件和文件夹是否存在、创建文件夹,代码如下
>>> import os
>>> os.path.exists('d:/assist')
True
>>> os.path.exists('d:/assist/getTeacherList.py')
True
>>> os.path.isfile('d:/assist')
False
>>> os.path.isfile('d:/assist/getTeacherList.py')
True
>>> os.makedirs('d:/assist/set')
>>> os.path.exists('d:/assist/set')
True
2.python判断文件是否存在,代码如下
import os
filename = r'/home/tim/workspace/test.txt'
if os.path.exists(filename):
message = 'OK, the "%s" file exists.'
else:
message = "Sorry, I cannot find the "%s" file."
print message % filename
3.如何用Python判断文件是否存在
使用os.path.exists()方法可以直接判断文件是否存在,代码如下
>>> import os
>>> os.path.exists(r'C:\1.TXT')
False
>>>
如果存在返回值为True,如果不存在则返回False
4.python判断文件夹是否存在,代码如下
$ python
Python 2.7.3 (default, Jan 2 2013, 16:53:07)
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>>
>>>
>>> tobecheckdir = r'/home/tim/workspace'
>>> os.path.isdir(tobecheckdir)
True
>>>
5.python检查文件是否存在,以及路径是否为文件
from os import path, access, R_OK # W_OK for write permission.
PATH='./file.txt'
if path.exists(PATH) and path.isfile(PATH) and access(PATH, R_OK):
print "File exists and is readable"
else:
print "Either file is missing or is not readable"
你也可以通过下面的方式实现:
def file_exists(filename):
try:
with open(filename) as f:
return True
except IOError:
return False
6.python判断文件和文件夹是否存在
import os
os.path.isfile('test.txt') #如果不存在就返回False
os.path.exists(directory) #如果目录不存在就返回False
7.os.path.lexist
还有os.path.lexists(path)
对broken的link file也返回True.
8.python FTP判断文件夹是否存在
python怎样判断文件夹是否存在?广大网友给出了答案:
使用ftp库就可以了,下面是Python核心编程上的例子:
>>> from ftplib import FTP
>>> f = FTP('ftp.python.org')
>>> f.login('anonymous', 'guido@python.org')
'230 Guest login ok, access restrictions apply.'
>>> f.dir()
dir结果中无此文件,就是不存在,或者如下:
try:
f.retrbinary('RETR %s' % FILE,open(FILE, 'wb').write)
except ftplib.error_perm:
print 'ERROR: cannot read file "%s"' % FILE 40 os.unlink(FILE)
不能读此文件,也视为不存在。
命名规范
常量
常量名所有字母大写,由下划线连接各个单词,如
WHITE = 0XFFFFFF
THIS_IS_A_CONSTANT = 1
变量
普通变量
全部小写,由下划线连接各个单词,如:
color = WHITE
this_is_a_variable = 1
保护成员变量
单下划线作前缀,意思是只有类对象和子类对象自己能访问到这些变量,且不能用’from module import *'导入。如:
_name=name
私有成员变量
双下划线作前缀,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。
__ha=h
全局变量
大写字母,单词之间用_分割。
对于from M import *导入语句,如果想阻止导入模块内的全局变量可以使用旧有的规范,在全局变量上加一个前导的下划线
NUMBER
COLOR_WRITE
注意:
(1) 不论是类成员变量还是全局变量,均不使用 m 或 g 前缀
(2)变量名不应带有类型信息,因为 Python 是动态类型语言。如 iValue、names_list、dict_obj 等都是不好的命名。
函数
普通函数
与普通变量一致,函数名应该为小写,可以用下划线风格单词以增加可读性。
混合大小写仅被允许用于这种风格已经占据优势的时候,以便保持向后兼容
myfunctio()
my_example_function()
myName()
私有函数
以双下划线开头。如:
__get_name()
函数的参数
总使用“self”作为实例方法的第一个参数。总使用“cls”作为类方法的第一个参数。
如果一个函数的参数名称和保留的关键字冲突,通常使用一个后缀下划线好于使用缩写或奇怪的拼写。
模块:
应该是简短的、小写的名字,单词之间用_分割。模块就是指文件
hello.py
ad_stats.py
类
类名单词首字母大写,不使用下划线连接单词,也不加入 C、T 等前缀
实例用小写字母
class MyClass(object)
包
命名方式同模块,是文件夹
特定命名方式:xxx
主要是指 xxx 形式的系统保留字命名法。项目中也可以使用这种命名,它的意义在于这种形式的变量是只读的,这种形式的类成员函数尽量不要重载。如
class Base(object):
def __init__(self, id, parent = None):
self.__id__ = id
self.__parent__ = parent
def __message__(self, msgid):
其中 id、parent 和 message 都采用了系统保留字命名法。
#编码问题#
目前遇到的最头疼的问题,没有之一
先是一篇简要介绍,链接在这里http://in355hz.iteye.com/blog/1860787
最近业务中需要用 Python 写一些脚本。尽管脚本的交互只是命令行 + 日志输出,但是为了让界面友好些,我还是决定用中文输出日志信息。
很快,我就遇到了异常:
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)
为了解决问题,我花时间去研究了一下 Python 的字符编码处理。网上也有不少文章讲 Python 的字符编码,但是我看过一遍,觉得自己可以讲得更明白些。
下面先复述一下 Python 字符串的基础,熟悉此内容的可以跳过。
对应 C/C++ 的 char 和 wchar_t, Python 也有两种字符串类型,str 与 unicode:
# -*- coding: utf-8 -*-
# file: example1.py
import string
# 这个是 str 的字符串
s = '关关雎鸠'
# 这个是 unicode 的字符串
u = u'关关雎鸠'
print isinstance(s, str) # True
print isinstance(u, unicode) # True
print s.__class__ # <type 'str'>
print u.__class__ # <type 'unicode'>
前面的申明:# -- coding: utf-8 -- 表明,上面的 Python 代码由 utf-8 编码。
为了保证输出不会在 linux 终端上显示乱码,需要设置好 linux 的环境变量:export LANG=en_US.UTF-8
如果你和我一样是使用 SecureCRT,请设置 Session Options/Terminal/Appearance/Character Encoding 为 UTF-8 ,保证能够正确的解码 linux 终端的输出。
两个 Python 字符串类型间可以用 encode / decode 方法转换:
# 从 str 转换成 unicode
print s.decode('utf-8') # 关关雎鸠
# 从 unicode 转换成 str
print u.encode('utf-8') # 关关雎鸠
为什么从 unicode 转 str 是 encode,而反过来叫 decode?
因为 Python 认为 16 位的 unicode 才是字符的唯一内码,而大家常用的字符集如 gb2312,gb18030/gbk,utf-8,以及 ascii 都是字符的二进制(字节)编码形式。把字符从 unicode 转换成二进制编码,当然是要 encode。
反过来,在 Python 中出现的 str 都是用字符集编码的 ansi 字符串。Python 本身并不知道 str 的编码,需要由开发者指定正确的字符集 decode。
(补充一句,其实 Python 是可以知道 str 编码的。因为我们在代码前面申明了 # -- coding: utf-8 --,这表明代码中的 str 都是用 utf-8 编码的,我不知道 Python 为什么不这样做。)
如果用错误的字符集来 encode/decode 会怎样?
# 用 ascii 编码含中文的 unicode 字符串
u.encode('ascii') # 错误,因为中文无法用 ascii 字符集编码
# UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)
# 用 gbk 编码含中文的 unicode 字符串
u.encode('gbk') # 正确,因为 '关关雎鸠' 可以用中文 gbk 字符集表示
# '\xb9\xd8\xb9\xd8\xf6\xc2\xf0\xaf'
# 直接 print 上面的 str 会显示乱码,修改环境变量为 zh_CN.GBK 可以看到结果是对的
# 用 ascii 解码 utf-8 字符串
s.decode('ascii') # 错误,中文 utf-8 字符无法用 ascii 解码
# UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)
# 用 gbk 解码 utf-8 字符串
s.decode('gbk') # 不出错,但是用 gbk 解码 utf-8 字符流的结果,显然只是乱码
# u'\u934f\u51b2\u53e7\u95c6\u5ea8\u7b2d'
这就遇到了我在本文开头贴出的异常:UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position 0-3: ordinal not in range(128)
现在我们知道了这是个字符串编码异常。接下来, 为什么 Python 这么容易出现字符串编/解码异常?
这要提到处理 Python 编码时容易遇到的两个陷阱。第一个是有关字符串连接的:
# -*- coding: utf-8 -*-
# file: example2.py
# 这个是 str 的字符串
s = '关关雎鸠'
# 这个是 unicode 的字符串
u = u'关关雎鸠'
s + u # 失败,UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)
简单的字符串连接也会出现解码错误?
陷阱一:在进行同时包含 str 与 unicode 的运算时,Python 一律都把 str 转换成 unicode 再运算,当然,运算结果也都是 unicode。
由于 Python 事先并不知道 str 的编码,它只能使用 sys.getdefaultencoding() 编码去 decode。在我的印象里,sys.getdefaultencoding() 的值总是 ‘ascii’ ——显然,如果需要转换的 str 有中文,一定会出现错误。
除了字符串连接,% 运算的结果也是一样的:
# 正确,所有的字符串都是 str, 不需要 decode
"中文:%s" % s # 中文:关关雎鸠
# 失败,相当于运行:"中文:%s".decode('ascii') % u
"中文:%s" % u # UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)
# 正确,所有字符串都是 unicode, 不需要 decode
u"中文:%s" % u # 中文:关关雎鸠
# 失败,相当于运行:u"中文:%s" % s.decode('ascii')
u"中文:%s" % s # UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)
我不理解为什么 sys.getdefaultencoding() 与环境变量 $LANG 全无关系。如果 Python 用 $LANG 设置 sys.getdefaultencoding() 的值,那么至少开发者遇到 UnicodeDecodeError 的几率会降低 50%。
另外,就像前面说的,我也怀疑为什么 Python 在这里不参考 # -- coding: utf-8 -- ,因为 Python 在运行前总是会检查你的代码,这保证了代码里定义的 str 一定是 utf-8 。
对于这个问题,我的唯一建议是在代码里的中文字符串前写上 u。另外,在 Python 3 已经取消了 str,让所有的字符串都是 unicode ——这也许是个正确的决定。
其实,sys.getdefaultencoding() 的值是可以用“后门”方式修改的,我不是特别推荐这个解决方案,但是还是贴一下,因为后面有用:
# -*- coding: utf-8 -*-
# file: example3.py
import sys
# 这个是 str 的字符串
s = '关关雎鸠'
# 这个是 unicode 的字符串
u = u'关关雎鸠'
# 使得 sys.getdefaultencoding() 的值为 'utf-8'
reload(sys) # reload 才能调用 setdefaultencoding 方法
sys.setdefaultencoding('utf-8') # 设置 'utf-8'
# 没问题
s + u # u'\u5173\u5173\u96ce\u9e20\u5173\u5173\u96ce\u9e20'
# 同样没问题
"中文:%s" % u # u'\u4e2d\u6587\uff1a\u5173\u5173\u96ce\u9e20'
# 还是没问题
u"中文:%s" % s # u'\u4e2d\u6587\uff1a\u5173\u5173\u96ce\u9e20'
可以看到,问题魔术般的解决了。但是注意! sys.setdefaultencoding() 的效果是全局的,如果你的代码由几个不同编码的 Python 文件组成,用这种方法只是按下了葫芦浮起了瓢,让问题变得复杂。
另一个陷阱是有关标准输出的。
刚刚怎么来着?我一直说要设置正确的 linux $LANG 环境变量。那么,设置错误的 $LANG,比如 zh_CN.GBK 会怎样?(避免终端的影响,请把 SecureCRT 也设置成相同的字符集。)
显然会是乱码,但是不是所有输出都是乱码。
# -*- coding: utf-8 -*-
# file: example4.py
import string
# 这个是 str 的字符串
s = '关关雎鸠'
# 这个是 unicode 的字符串
u = u'关关雎鸠'
# 输出 str 字符串, 显示是乱码
print s # 鍏冲叧闆庨笭
# 输出 unicode 字符串,显示正确
print u # 关关雎鸠
为什么是 unicode 而不是 str 的字符显示是正确的? 首先我们需要了解 print。与所有语言一样,这个 Python 命令实际上是把字符打印到标准输出流 —— sys.stdout。而 Python 在这里变了个魔术,它会按照 sys.stdout.encoding 来给 unicode 编码,而把 str 直接输出,扔给操作系统去解决。
这也是为什么要设置 linux $LANG 环境变量与 SecureCRT 一致,否则这些字符会被 SecureCRT 再转换一次,才会交给桌面的 Windows 系统用编码 CP936 或者说 GBK 来显示。
通常情况,sys.stdout.encoding 的值与 linux $LANG 环境变量保持一致:
# -*- coding: utf-8 -*-
# file: example5.py
import sys
# 检查标准输出流的编码
print sys.stdout.encoding # 设置 $LANG = zh_CN.GBK, 输出 GBK
# 设置 $LANG = en_US.UTF-8,输出 UTF-8
# 这个是 unicode 的字符串
u = u'关关雎鸠'
# 输出 unicode 字符串,显示正确
print u # 关关雎鸠
但是,这里有 陷阱二:一旦你的 Python 代码是用管道 / 子进程方式运行,sys.stdout.encoding 就会失效,让你重新遇到 UnicodeEncodeError。
比如,用管道方式运行上面的 example4.py 代码:
python -u example5.py | more
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)
None
可以看到,第一:sys.stdout.encoding 的值变成了 None;第二:Python 在 print 时会尝试用 ascii 去编码 unicode.
由于 ascii 字符集不能用来表示中文字符,这里当然会编码失败。
怎么解决这个问题? 不知道别人是怎么搞定的,总之我用了一个丑陋的办法:
# -*- coding: utf-8 -*-
# file: example6.py
import os
import sys
import codecs
# 无论如何,请用 linux 系统的当前字符集输出:
if sys.stdout.encoding is None:
enc = os.environ['LANG'].split('.')[1]
sys.stdout = codecs.getwriter(enc)(sys.stdout) # 替换 sys.stdout
# 这个是 unicode 的字符串
u = u'关关雎鸠'
# 输出 unicode 字符串,显示正确
print u # 关关雎鸠
这个方法仍然有个副作用:直接输出中文 str 会失败,因为 codecs 模块的 writer 与 sys.stdout 的行为相反,它会把所有的 str 用 sys.getdefaultencoding() 的字符集转换成 unicode 输出。
# 这个是 str 的字符串
s = '关关雎鸠'
# 输出 str 字符串, 异常
print s # UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)
显然,sys.getdefaultencoding() 的值是 ‘ascii’, 编码失败。
解决办法就像 example3.py 里说的,你要么给 str 加上 u 申明成 unicode,要么通过“后门”去修改 sys.getdefaultencoding():
# 使得 sys.getdefaultencoding() 的值为 'utf-8'
reload(sys) # reload 才能调用 setdefaultencoding 方法
sys.setdefaultencoding('utf-8') # 设置 'utf-8'
# 这个是 str 的字符串
s = '关关雎鸠'
# 输出 str 字符串, OK
print s # 关关雎鸠
总而言之,在 Python 2 下进行中文输入输出是个危机四伏的事,特别是在你的代码里混合使用 str 与 unicode 时。
有些模块,例如 json,会直接返回 unicode 类型的字符串,让你的 % 运算需要进行字符解码而失败。而有些会直接返回 str, 你需要知道它们的真实编码,特别是在 print 的时候。
为了避免一些陷阱,上文中说过,最好的办法就是在 Python 代码里永远使用 u 定义中文字符串。另外,如果你的代码需要用管道 / 子进程方式运行,则需要用到 example6.py 里的技巧。
后来,为了统一Windows和Linux上的编码,不管什么字符串,我都会在前面加上u,把它们全部强制转化为Unicode。但是,在打印类似list和dict这些数据结构的时候,显示中文字符遇到了问题,只能显示unicode编码,无法显示中文。
解决办法转载自这里http://blog.csdn.net/xiaobei4929/article/details/38734047
在从元组转换到字符串时,中文字符前会出现u’例子’类似这种,具体可参考第二个文章,需要手动去除u一切就都正常了
[u'\u773c', u'\u8179\u90e8', u'\u4e94\u5b98', u'\u53e3\u8154', u'\u8179\u90e8',
u'\u53e3\u8154']
str_symptom = str(all_symptom).replace('u\'','\'')
str_symptom.decode("unicode-escape")
['眼', '腹部', '五官', '口腔', '腹部', '口腔'] #处理后的显示情况
另外,对于dict这种无序表,可以使用OrderedDict来代替,具体可以参考廖雪峰的python教程,非常棒
#其他的一些小结#
这里有一个爱可可老师的分享python-snippets
##类与属性##
参考这篇博客
主要是这句话有用
getattr(object, name[,default])
获取对象object的属性或者方法,如果存在打印出来,如果不存在,打印出默认值,默认值可选。
需要注意的是,如果是返回的对象的方法,返回的是方法的内存地址,如果需要运行这个方法,
可以在后面添加一对括号。
multiprocessing里的Process模块,多线程调用非常的恶心,有些错误,即使添加了print还是无法达到追踪的效果,有时只能改掉多线程的调用方式
#列表、字典#
使用list初始化dict的方法是
d=dict.fromkeys(l, 0)
得到一个值全为0的字典
对于字典而言,dict[‘x’] =1的操作,如果dict中没有’x’这个键,那么这时就会创建1个新的item
python2与python3的切换
raise ImportError('This package should not be accessible on Python 3. ’ ImportError 报错解决
当电脑上安装了Python2 和 python3时,在python3下执行help()报如下错误:
raise ImportError('This package should not be accessible on Python 3. ’
ImportError: This package should not be accessible on Python 3.
Either you are trying to run from the python-future src folder or your installation of python-future is corrupted
这是因为环境变量中包含PYTHONPATH=/usr/local/lib/python2.7/site-packages,而这条不适用于python3。
类
继承
关于super().init()的用法,转载自这里Python super初始化理解过程
python lmdb
python lmdb
lmdb documents
leveldb issue
并发
一个简单的多卡并行方法 – Python代码
- 应用场景
1.Python代码调用C/C++库(已用Pybind或者其他工具封装成python接口,该库内部参数已经固定住,如只允许单卡运行,固定batch_size)
2.有多卡环境下
3.需要使用该库处理大量数据 - 解决办法
1.将输入数据根据卡数进行chunk,代码如下:
import pynvml
pynvml.nvmlInit()
# 获取GPU卡数
dev_count = pynvml.nvmlDeviceGetCount()
#根据卡数chunk输入数据
from itertools import zip_longest
chunk_list = lambda a_list, n: zip_longest(*[iter(a_list)]*n)
len_samples = len(samples_fullpaths)
if len_samples % dev_count == 0:
len_each_dev = int(len_samples / dev_count)
else:
len_each_dev = int(len_samples / dev_count) + 1
result_groups = list(chunk_list([x for x in samples_fullpaths], len_each_device))
2.使用python shell命令调用.py
# 这里参数要写成*,来搜集传入的元组可变参数
def subprocess(*kwargs):
# 传入的元组个数为1
kwargs = kwargs[0]
r_q = kwargs[1]
id = kwargs['id']
"""
解析其他输入参数
"""
# 注意这里第2个参数格式化时,因为是可变参数,把字典kwargs格式化成字符串(前后都加上"),后边直接用eval解析即可
cmd = 'export CUDA_VISIBLE_DEVICES = {} && python proc.py \"{}\"'.format(id, kwargs)
ret = subprocess.call(cmd, shell=True)
r_st = '{}+{}'.format(id, ret)
r_q.put(r_st)
3.主处理单元
#假定启动py文件是main.py,处理数据的py文件是proc.py
# main.py
from multiprocessing import Process, Queue
def main_proc(params, input_fullpaths, output_fullpaths):
"""
这里粘贴1中代码,来对input_fullpaths进行chunk
"""
len_groups = len(result_groups)
try:
assert(len_groups <= device_count)
except:
return
for id, group in enumerate(results_groups):
with open('sample{}.txt'.format(id)) as f_w:
for it in group:
if it:
f_w.write(it + '\n')
kwargs = []
for gpu_id in range(len_groups):
dic_args = dict()
dic_args['id'] = params['id']
"""
其他外部配置参数
"""
kwargs.append(dic_args)
r_q = Queue()
r_q_count = len_groups
# 使用多进程或者线程封装主处理函数subprocess,完成并发
multiproc_list = [Process(target=subprocess, args=(kwargs[i], r_q)) for i in range(len_groups)]
for mp in multiproc_list:
mp.start()
while r_q_count:
r_sp = r_q.get().split('+')
id = eval(r_sp[0])
ret = eval(r_sp[1])
if ret != 0:
print('error in task {}'.format(id))
sys.exit(-1)
r_q_count -= 1
#去掉join,主进程不等待子进程,可先退出
#for mp in multiproc_list:
# if mp.is_alive():
# mp.join()
if __name__ == '__main__'
parser = getArgs()
args = parser.parse_args()
main_proc(args.params, args.input_fullpaths, args.output_fullpaths)
# proc.py
def main_subprocess(**kwargs):
id = kwargs['id']
"""
解析其他参数
"""
"""
以下代码是处理代码,比如加在模型,调用算法C/C++库的python接口获取结果等
"""
if __name__ == '__main__':
kwargs = eval(sys.argv[1])
# 传入的是字典,加上**
main_subprocess(**kwargs)
4.总结
1.多卡并行首先对输入数据进行chunk,然后使用subprocess.call或者os.system通过运行shell命令调用,实现在不同的卡上运行py脚本,这样就达到了简单的并发目的。
2.在具体实施的时候,还需要用进程或者线程来封装subprocess.call,因为subprocess.call本身并不能很好地并发。
3.一般需要2个脚本,一个是启动脚本main.py,一个是主处理脚本proc.py。通过main.py对proc.py传参时,考虑使用可变参数(字典、元组)等形式,这样更灵活。
5.参考资料
Python多进程代码调试工具
import sys
import pdb
class ForkedPdb(pdb.Pdb):
"""
PDB Subclass for debugging multi-processed code
Suggested in: https://stackoverflow.com/questions/4716533/how-to-attach-debugger-to-a-python-subproccess
"""
def interaction(self, *args, **kwargs):
_stdin = sys.stdin
try:
sys.stdin = open('/dev/stdin')
pdb.Pdb.interaction(self, *args, **kwargs)
finally:
sys.stdin = _stdin
# 用法
ForkedPdb().set_trace()
因为GIL的存在,python的多线程是伪多线程
有了GIL为什么还需要Lock
https://stackoverflow.com/questions/49859287/what-is-the-need-of-threading-lock-when-cpython-has-gil
https://stackoverflow.com/questions/40072873/why-do-we-need-locks-for-threads-if-we-have-gil
https://en.wikipedia.org/wiki/Global_interpreter_lock
https://www.zhihu.com/question/23030421/answer/93789486 **通俗解释
https://softwareengineering.stackexchange.com/questions/186889/why-was-python-written-with-the-gil
该如何选择并行的方式
最近在进行数据处理任务,发现几个问题:
- 不要使用corrent.futures里的进程池方法。之前一直使用的corrent.futures里的进程池方法,
with concurrent.futures.ProcessPoolExecutor(N) as pool:
pool.map(func, data)
发现会有数据丢失的问题,改为多进程/多线程方式不会有数据丢失问题
from threading import Thread
from multiprocessing import Process
# 前处理代码
# 如果要用多进程,将Thread改为Process
thread_list = [Thread(target=func, args=(i,) for i in range(N)]
for t in thread_list:
t.start()
for t in thread_list:
if t.is_alive():
t.join()
# 后处理代码
猜测原因,当进程池方式里的N能被len(data)整除时,不会有数据丢失,无法被整除时,会出现丢失现象,可能与进程池里的并行实现方式有关。
2. 对于IO密集型或者有更多的IO操作,使用多线程更好。因为多线程在读写数据时,可以不阻塞,将GIL释放出来给空闲线程。如果使用多进程,进程间的数据通信(python多进程间存在数据的序列化和反序列化),这个尤其在处于大存储数据,比如图片时,是非常耗时的。
3. 对于计算密集型,使用多进程。
join方法用与不用的区别
在主进程运行过程中如果想并发地执行其他的任务,我们可以开启子进程,此时主进程的任务与子进程的任务分两种情况
情况一:
在主进程的任务与子进程的任务彼此独立的情况下,主进程的任务先执行完毕后,主进程还需要等待子进程执行完毕,然后统一回收资源。 这种是没有join方法
情况二:
如果主进程的任务在执行到某一个阶段时,需要等待子进程执行完毕后才能继续执行,就需要有一种机制能够让主进程检测子进程是否运行完毕,在子进程执行完毕后才继续执行,否则一直在原地阻塞,这就是join方法的作用
以下是一个使用join和不使用join的对比例子:
from multiprocessing import Process
import time
import os
def task(name):
print('%s is running' %(name))
time.sleep(2)
print('%s is end' %(name))
if __name__ == '__main__':
start = time.time()
p1 = Process(target=task, args=('子进程1',))
p2 = Process(target=task, args=('子进程2',))
p3 = Process(target=task, args=('子进程3',))
p4 = Process(target=task, args=('子进程4',))
process_list = [p1,p2,p3,p4]
for p in process_list:
p.start()
for p in process_list:
p.join()
print('主',time.time() - start)
打印如下
#屏蔽p.join代码
子进程1 is running
子进程2 is running
子进程3 is running
主 25061 25052
子进程4 is running
子进程1 is end
子进程2 is end
子进程3 is end
子进程4 is end
#使用p.join
子进程1 is running
子进程2 is running
子进程3 is running
子进程4 is running
子进程1 is end
子进程2 is end
子进程3 is end
子进程4 is end
主 25226 25217
让主进程等着,所有子进程执行完毕后,主进程才继续执行
注意,join要单独写循环调用,不能和start方法在同一层循环里调用,否则子进程无法并发。原因解释在这里【Python】多线程中阻塞(join)使用误区详解。如果在同一层调用start和join,那在第一层循环调用join后,主进程会被子进程1阻塞,无法调用子进程2的start,直到子进程1结束。
装饰器使用方法(处理并行时用)
Decoration in Python done right: Decorating and pickling
from functools import wraps
def with_print(func):
""" Decorate a function to print its arguments.
"""
@wraps(func)
def my_func(*args, **kwargs):
print args, kwargs
return func(*args, **kwargs)
return my_func
@with_print
def f(x):
print 'f called'
logging模块
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(filename)s:%(lineno)d] - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
logger = logging.getLogger(__name__)
metaclass的使用
参考资料
cookbook里8.12例子,定义一个接口或抽象基类
from abc import ABCMeta, abstractmethod
class IStream(metaclass=ABCMeta):
@abstractmethod
def read(self, maxbytes=-1):
pass
@abstractmethod
def write(self, data):
pass
打印堆栈信息
import inspect
def function_print_stack():
stack = inspect.stack()
for frm in stack:
print(f"Function: {frm.function}, Line: {frm.lineno}, Code: {frm.code_context}, Filename: {frm.filename}")
function_print_stack()
其它问题
os.walk
、glob.glob
获取目录列表时,即使使用镜像一样,在不同的机器上跑出来的结果顺序也不一致。- 并发时常用到Queue来保存数据,保存的数据如果是
list
,那么保存后不能使用clear()
方法来清空list
。
dataload_queue = Queue()
item1 = []
item2 = []
batch_size = 16
for idx, data in enumerate(datasets):
item1.append(idx)
item2.append(data)
if idx % 16 == 0 and idx != 0:
dataload_queue.put((item1, item2))
# 下面是错误的方式,使用clear()后,已经压入dataload_queue里的数据也会被clear
#item1.clear()
#item2.clear()
# 给item1和item2重新赋值是正确的方式
item1 = []
item2 = []
新的知识
Annotated,Python3.9支持
可用于类型提示
from typing import Annotated
UserID = Annotated[int, "This is a user ID", "Must be int and positive"]
def get_user_id(user_id: UserID) -> None:
print(f"Fetching user with ID: {user_id}")
get_user_id(123)
更多使用方法参考Pydantic, Field和Annotated