Python 实用技巧
1. 和时间有关的函数和用法
1. 导入 time 模块
import time
2. 设置时间格式
TIMEFORMAT = "%Y-%m-%d %H:%M:%S"
3. time.time() 用于取当前时间,为一个小数,代表从 epoch: 1970 年 1 月 1 日 00:00:00 开始到当前的秒数。
>>> time.time()
1180759620.859
4. time.localtime() 用于返回我们熟悉的用 tuple 存储的时间
>>> time.localtime()
(2007, 6, 2, 12, 47, 7, 5, 153, 0)
5. time.strftime() 用于按照我们之前所定义的时间格式来返回时间
>>> time.strftime( TIMEFORMAT, time.localtime() )
‘2007-06-02 12:54:31′
6. 表示时间的tuple数组结构
索引(Index) | 属性(Attribute) | 值(Values) |
---|---|---|
0 | tm_year(年) | 比如2011 |
1 | tm_mon(月) | 1 - 12 |
2 | tm_mday(日) | 1 - 31 |
3 | tm_hour(时) | 0 - 23 |
4 | tm_min(分) | 0 - 59 |
5 | tm_sec(秒) | 0 - 61 |
6 | tm_wday(weekday) | 0 - 6(0表示周日) |
7 | tm_yday(一年中的第几天) | 1 - 366 |
8 | tm_isdst(是否是夏令时) | 默认为-1 |
7. time.mktime(time.localtime())
使用mktime函数,传入tuple数组,可以返回对应的时间戳。
2. Python 读写文件
1. 读文件
input = open('data', 'r')
record = input.readline()
input.close()
使用 input.read() 可以将文件内容读作一个大字符串进行返回
2. 写文件,'w' 参数代表将原有文件清空后再写入,'a' 参数表示向文件追加
output = open('data', 'w')
output.write("test")
output.close()
3. Python 续行符 '\'
1. 第一种是直接用回车,但是不是很安全。
2. 第二种是在当前行的尾部加上 '\' 符,在下一行继续输入,此时后续的行可以以任何方式缩进,不用遵守python的严格缩进规则。一般在小括号、中括号、大括号里定义的内容不需要用'\'分割,直接回车即可。
4. 使用 Python 命令执行文件时的参数传递
python 命令后的内容共同组成参数列表实现参数传递,可以通过 sys 模块的 argv 变量访问到。
例如: python kfk_setter.py video-meta video2feeds -1
print sys.argv
结果:['kfk_setter.py', 'video-meta', 'video2feeds', '-1'] 参数是以字符串的形式传递
5. 字符串连接的三种方式
1. 直接通过加号(+)操作符连接 例如:website = 'python' + 'tab' + '.com'
由于python中字符串属于不可变的对象,因此每次执行一个+操作就会生成一个新的字符串,因此连续相加的字符串很多时效率较低。
2. join 方法
listStr = ['python', 'tab', '.com']
website = ''.join(listStr)
对多个字符进行连接时效率高,只会有一次内存的申请。而且如果是对list的字符进行连接的时候,这种方法必须是首选。
3. 字符串格式化
website = '%s%s%s' % ('python', 'tab', '.com')
这种方式非常常用
6. Python中的eval()、exec()及其相关函数
1. eval() 与 exec() 函数的形式和功能类似,都是通过传递全局变量和局部变量参数来实现字符串或者code对象的执行。区别在于,eval() 是用来计算指定表达式的值,也就是说它执行的python语句只能是单个表达式(不支持任意形式的赋值运算),而不是是复杂的逻辑,eval() 函数有返回值;exec() 函数可以动态运行代码段,exec() 函数的返回值永远为 None。
2. eval() 函数
1. 函数形式 eval(expression, globals = None, locals = None)
其中:expression 为表达式参数,可以为字符串,也可以是由 compile() 函数创建的 code 对象实例。如果它是一个字符串,它会被当作一个(使用globals和locals参数作为全局和本地命名空间的)Python表达式进行分析和解释。
globals 为可选参数,表示全局变量空间(存放全局变量),必须是一个字典对象。
locals 表示局部变量空间(存放局部变量),如果被提供,可以是任何映射对象。如果该参数被忽略,那么它将会取与globals相同的值。
上述两个参数可以自己赋予新的变量名和值,也可以用当前已经定义变量进行映射,对后者的更改会产生实际效果(在 exec() 函数中才能进行更改)
如果 globals 和 locals 参数都被忽略,那么它们将取 eval() 函数被调用环境下的全局变量空间和局部变量空间。
返回值:如果 expression 是一个 code 对象,且创建该 code 对象时,compile() 函数的 mode 参数为 'exec',那么返回值为 None;否则如果 expression 语句是一个输出语句,如 print,那么返回为 None;其他情况下 expression 表达式的结果就是 eval() 函数的返回值。
2. 用法示例
self.filter_exp = data.get('ucp_trespassing_field', {}).get('surface_type', '') != 'ARTICLE_SURFACE'
eval('#coding=%s\n%s' % ('utf8', self.filter_exp), {}, local_vars) 在 expression 表达式中声明了编码类型和具体表达式的内容通过 self.filter_exp 变量(逻辑表达式)进行传递,local_vars 为字典定义的局部空间
3. exec() 函数
1. exec(object [, globals [, locals ]])
参数说明:obeject 为需要执行的 Python 代码,可以是字符串或者code对象;后面参数同上
返回值:exec() 函数的返回值始终为 None
2. 用法示例
mapped_data = {} mapped_data['url'] = 'http://baijiahao.baidu.com/u?app_id=%s' % data['id'] mapped_data['status'] = 'publish' if data['status'] == 'pass' else 'unpublish' mapped_data_dict['data'] = mapped_data
self.mapping_code = compile(file(mapping_code_conf).read(), '', 'exec')
exec(self.mapping_code, {}, {'data': data, 'mapped_data_dict': mapped_data_dict, 'json':json, 'mongo_reader': self.mongo_reader, 'convert_time': self._convert_time, 'cgi': cgi, 'time': time, 'hashlib': hashlib, 'parse_html': self.parse_html, 'random': random, 're': re})
需要 exec 执行的代码写在 mapping_code_conf 文件中(不带有格式),然后使用 compile() 函数进行解析生成 self.mapping_code 对象,最后使用 exec() 函数调用,并赋给局部变量
4. global() 函数
返回一个表示当前全局标识符表的字典。这永远是当前模块的字典(在一个函数或方法内部,这是指定义该函数或方法的模块,而不是调用该函数或方法的模块)
5. local() 函数
更新并返回一个表示当前局部标识符表的字典。自由变量在函数内部被调用时,会被locals()函数返回;自由变量在类累不被调用时,不会被locals()函数返回。
总结:
1. globals()函数以字典的形式返回的定义该函数的模块内的全局作用域下的所有标识符(变量、常量等)
2. locals()函数以字典的形式返回当前函数内的局域作用域下的所有标识符
3. 如果直接在模块中调用globals()和locals()函数,它们的返回值是相同的
6. compile() 函数
compile(source, filename, mode[, flags[, dont_inherit]])
将源码编译为 code 对象或者 AST 对象,code对象能够通过exec()函数来执行或者通过eval()函数进行计算求值。
参数说明:
1. source:字符串或AST(Abstract Syntax Trees)对象,表示需要进行编译的Python代码
2. filename:指定需要编译的代码文件名称,如果不是从文件读取代码则传递一些可辨认的值(通常是用'<string>')
3. mode:用于标识必须当做那类代码来编译;如果source是由一个代码语句序列组成,则指定mode='exec';如果source是由单个表达式组成,则指定mode='eval';如果source是由一个单独的交互式语句组成,则指定mode='single'。
如果是用 file(filename).read() 作为source参数,从代码文件中读取代码,filename 参数为 '' 即可。
更多示例可以查看:http://www.cnblogs.com/yyds/p/6276746.html?utm_source=itdadao&utm_medium=referral
7. 在 Python 函数内如果需要对函数外的不可变对象进行更改,需要在函数内用 global 关键字声明某个变量是函数外的变量。
在函数传递过程中是混合传递,如果参数是可变对象,那么就是地址传递;如果参数是不可变对象,那么就是值传递。
Python 中的可变对象包括列表和字典,不可变对象包括数值、字符串和元祖。
8. 日志记录和 logging 模块使用
logger 为日志对象,是 logging 模块中最基础的对象,用 logging.getLogger(name) 方法进行初始化,name 可以不填。通常 logger 的名字我们对应模块名,如聊天模块、数据库模块、验证模块等。
日志对象需要设置日志等级,日志对象只有调用比当前日志等级高的信息输出函数才会输出。日志等级关系如下:从上到下,等级依次增加。
1. DEBUG:debug 级输出
2. INFO:info 级输出,重要信息
3. WARNING:warning 级输出,警告信息
4. ERROR:error 级输出,错误信息
5. CRITICAL:critical 级输出,严重错误信息
初始化日志对象模版:
import logging
from logging.handlers import RotatingFileHandler
log = logging.getLogger(name) #初始化日志对象,名字可以任意,也可为空 log.setLevel(logging.INFO) #设置日志等级,决定其可以调用的日志输出函数,只能调用比等级高的 format = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") #日志输出格式化对象,表示时间-日志等级-日志内容 fh = RotatingFileHandler(logfile, maxBytes=(1048576*500), backupCount=7) #要进行日志输出必需要有一个handler,这里使用RotatingFileHandler,参数logfile为指定日志文件地址 fh.setFormatter(format) log.addHandler(fh) log.info("info msg")
1. Handler
用来指定日志内容输出的定向,包括 StreamHandler (标准输出)、FileHandler (日志文件输出,默认追加)、RotatingFileHandler(指定日志文件的路径和名字,设置每个文件的最大字节数和文件个数,自动更新最新的日志到日志文件中,保存指定数量的过往日志)。
一个 logger 对象可以指定多个 Handler,用于将日志定向到不同的输出。同时可以从每个 Handler 指定日志级别,用于分级别输出。
日志输出格式也由 Handler 设置。有很多代表特定意义的字段,网上自查。
1. StreamHandler 的设置
import sys
fh = logging.StreamHandler(sys.stderr)
9. 字典中 key/value 值中出现的 u'string' 表示该字符串已经是用 Unicode 编码表示的。
10. Json: JavaScript Object Notation (JS 对象标记)
1. Json 是一种轻量级的数据交换格式
它的本质是字符串,使用 Json 格式生成的字符串易于人阅读和编写,而且易于机器解析和生成。这是一种完全独立于编程语言的数据交换格式,而且由于 JavaScipt 中,任何数据都是对象,因此 Json 表示的值可以是数值、字符串、逻辑值、数组(用[]方括号包围)、对象(用{}花括号包围)。
Json 的对象是包含在{}内的,可以由多个键/值对组成,在 Json 中,字符串都是由双引号""包围起来的,数字就直接是数字的形式。
Json 的数组也可以包含多个对象。
2. Json 的特质
1. Json 是纯文本
2. Json 具有自我描述性(人类可读)
3. Json 具有层次结构(值中存在值)
3. 相比 XML 的不同之处
1. 没有结束标签
2. 更简洁,读写速度更快,可以用来提升网络传输速度
3. 不使用保留字
4. 在 Python 中使用 Json
import json test = {'a':123, 'b': 'dag'} a = json.dumps(test) #使用 json.dumps() 函数生成对象对应的 json 字符串 b = json.loads(a) #使用 json.loads() 函数将 json 字符串进行解析,恢复为 dict 类型对象
注意在 json 中所有的字符串都是由双引号("")包围,与 python 中字典对象中的字符串由单引号('')包围不同。
5. Json是采用Unicode编码,不方便显示的字符(如中文汉字)就直接用Unicode编码进行存储
11. repr() 与 str() 函数的区别
repr() 将 python 对象转化为字符串是给机器看的,支持各种数据类型,用 eval() 函数可以重新解析出原数据。
str() 转换为字符串是方便人阅读的,不具有解析为原数据的功能。
12. handler
Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。
13. 函数中的 *args,**args 参数
这两者为 Python 函数中的可变参数,可以选择性传入,*args 表示任意多个无名参数,是一个 tuple;**args 表示关键字参数,是一个 dict。如果同时使用 *args 和 **args 参数时,必须 *args 参数在 **args 参数前面。
14. 字符串编码问题
常用的字符串编码包括:utf-8,gb2312,cp936,gbk等。
在python中使用unicode编码作为字符串的内部编码类型,因此如果需要实现上述编码类型字符串之间的相互转换,需要首先使用decode进行解码,将原本编码的字符串转换为unicode编码,然后再使用encode函数进行编码。简单来说,decode()是将其他类型的编码转换为unicode编码,encode()将unicode编码转换为其他类型编码。
u = '中文' #指定字符串类型对象u str = u.encode('gb2312') #以gb2312编码对u进行编码,获得bytes类型对象str u1 = str.decode('gb2312')#以gb2312编码对字符串str进行解码,获得字符串类型对象u1 u2 = str.decode('utf-8')#如果以utf-8的编码对str进行解码得到的结果,将无法还原原来的字符串内容
另外,文件中的字符串默认编码类型与文件的编码类型一致:
如 str = '中文',如果是在utf-8编码的文件中初始化,该字符串就是utf-8编码;如果是在gb2312编码的文件中,那该字符串就是gb2312编码。这种情况下需要先进行解码,再进行其他类型的编码。如果显示声明编码类型 str = u'中文',则该字符串的编码类型就被指定为unicode编码,而与文件的编码形式无关。
15. xrange() 与 range() 的区别
xrange() 与 range() 的用法和作用相同,区别在于实现的过程,range() 会一次生成完整的列表,而 xrange() 是返回一个生成器。如果需要生成一个很大的数字列表时,xrange() 的性能更好,因为不用一开始就开辟很大的内存空间。
16. sys 模块
1. 标准输入流、标准输出流、标准错误流
stdin,stdout,stderr 是内建在每个 UNIX 系统中的管道,当其被调用时信息自动进入对应的管道。
sys.stdout 和 sys.stderr 可以看作是文件对象,调用其 write() 函数可以向标准输出流和标准错误流打印信息(默认为屏幕),也可以用文件对象对其重新赋值,以实现输出重定向。
import sys sys.stdout.write('hope') #向屏幕打印 'hope' save = sys.stdout #保存原有的输出对象 file = open('out.txt', 'w') #打开一个文件对象 sys.stdout = file #对其进行赋值 sys.stdout.write('test') #向文件中输出 'test'
实际上 print 语句就是在需要输出的内容后加一个换行符,再调用 sys.stdout.write() 函数进行输出。
17. list 列表
1. del 语句删除列表中的元素
del list[0] #删除列表中的第一个元素
del list[:] #删除列表中所有元素
list.remove(obj) #移除列表中某个值的第一个匹配项
2. list.extend(seq) 在列表的尾部一次性追加参数列表 seq 的多个值
3. enumerate() 函数可以将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
for i, j in enumerate(list)
print i, j #可以同时输出元素在列表中的索引和元素本身
18. Socket 编程
1. TCP/UDP 协议
TCP/IP 协议是一个协议簇,里面包含应用层、传输层、网络层、网络访问层等多种协议,由于 TCP 和 IP 协议是其中比较重要的,因此用此命名。
1. TCP(Transmission Control Protocol)
是面向连接的,在收发数据前双方需要建立可靠通信,经过“三次握手”。对应的是可靠性要求高的应用,支持的应用层协议有 Telnet(远程登录)协议、FTP、SMTP(简单消息传输协议)。
2. UDP(User Datagram Protocol)
是面向无连接的,在传输数据之前源端和终端不需要建立连接,在接收端,UDP 把每个消息放在消息队列中,应用程序每次从消息中读一个消息段。UDP 是尽最大努力交付的,不保证可靠交付,面向报文传输。其支持的应用层协议有:NFS(网络文件系统)、SNMP(简单网络管理协议)、DNS(域名系统)、TFTP(通用文件传输协议)。Ping 命令就使用了 UDP 协议。
2. Socket 含义
socket.SOCK_STREAM 流式socket ,
for
TCP (默认)
socket.SOCK_DGRAM 数据报式socket ,
for
UDP
socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
socket.SOCK_SEQPACKET 可靠的连续数据包服务
与特定的地址家族相关的协议,如果是
0
,则系统就会根据地址格式和套接类别,自动选择一个合适的协议
格式:
re.sub(pattern, repl, string, count)
待完善,参考:http://www.cnblogs.com/tina-python/p/5508402.html
21. yield 语句生成器
比如需要打印一个数列,可以先通过函数生成完整的序列,然后使用 for 循环对序列的迭代进行打印。但是如果序列很大,会造成内存的大量占用,此时如果使用 generator (生成器)进行迭代序列中的每一个元素会优化很多。
yield 语句就是将需要返回的数据(数据是什么类型,就会按照什么样的类型进行循环迭代,可以为列表,也可以为元素)作为生成器进行返回,方便代码的其他部分对该函数的返回内容进行迭代,迭代完该函数会自动从 yield 语句执行完成的地方继续执行,保持原本的局部变量,好像并没有被外部代码迭代一样。生成器函数执行时遇到 return 语句,会返回 StopIteration 错误。生成器自动包含 next() 函数返回迭代的下一个数。
通过类也可以定义 self.__iter__() 和 next() 函数形成生成器。
22. heapq 模块
上述模块是用来申请堆内存的,生成小根堆的结构。可以创建一个空列表作为堆空间,也可以通过 heapify() 函数将一个现存的列表转换为堆空间。
1. heappush(heap, item)
向堆空间中插入元素,heap 为之前创建的列表名
2. heappop(heap)
出堆并返回最小的元素,可以使用 heap[0] 来察看最小的元素(小根堆根结点上的值,如果值为列表,那么会优先按照第一个元素的大小比较排序)
通过堆空间能够进行本地的缓存,实现轮询机制。
23. 静态方法、类方法、实例方法
类中的静态方法声明前需要在前一行加上 @staticmethod 标识符,以表示下面的成员函数是静态函数。静态函数不需要传递和类有关的参数。
类方法声明前需要加上 @classmethod 标识符,类方法需要加隐藏参数 cls 表示该类。
实例方法需要加隐藏参数 self 表示某个实例。
三者的区别:
1. 类方法和静态方法皆可以访问类的静态变量(类变量),但不能访问实例变量。
2. 类的实例可以访问类方法、静态方法和实例方法,但是类只能访问类方法和静态方法,访问实例方法需要显式传递实例参数。类方法和静态方法由多个实例和类共享。
24. sorted() 函数使用
sorted(iterable[, cmp[, key[, reverse]]])
iterable 为可迭代对象,例如:
vocab = vectorizer.vocabulary_ #vocab为一个字典,通过items()可以返回由键值对元祖组成的列表,itemgetter()为一个操作符函数,表示取调用其对象的特定元素 X_vocab = sorted(vocab.items(), key=itemgetter(1))
25. set() 集合操作
Python 的集合由集合对象来实现,可以用一个顺序结构来进行初始化。
x = set('test') #x为set(['t', 'e', 's']) x.add('l') #在集合中添加一项 x.update('hope') #在集合中添加多项 x.remove('t') #在集合中移除元素
26. 转义字符
Escape Sequence | Meaning | Notes |
---|---|---|
\newline |
Ignored | |
\\ |
Backslash (\ ) |
|
\' |
Single quote (' ) |
|
\" |
Double quote (" ) |
|
\a |
ASCII Bell (BEL) | |
\b |
ASCII Backspace (BS) | |
\f |
ASCII Formfeed (FF) | |
\n |
ASCII Linefeed (LF) | |
\N{name} |
Character named name in the Unicode database (Unicode only) | |
\r |
ASCII Carriage Return (CR) | |
\t |
ASCII Horizontal Tab (TAB) | |
\uxxxx |
Character with 16-bit hex value xxxx (Unicode only) | (1) |
\Uxxxxxxxx |
Character with 32-bit hex value xxxxxxxx (Unicode only) | (2) |
\v |
ASCII Vertical Tab (VT) | |
\ooo |
Character with octal value ooo | (3,5) |
\xhh |
Character with hex value hh | (4,5) |
27. 内置函数 zip()
内置函数接受一系列可迭代的对象作为参数,将对象中对应的元素组成一个个元组的形式,然后返回这些元组组成的列表。zip(*a) 实现反过程,由元组列表返回对应的迭代对象序列。
a = [1, 2, 3] b = [4, 5, 6] c = [4, 5, 6, 7, 8] zipped = zip(a, b) #zipped 为 [(1, 4), (2, 5), (3, 6)] zip(a, c) #结果为 [(1, 4), (2, 5), (3, 6)] zip(*zipped) #进行解压,结果为 [(1, 2, 3), (4, 5, 6)]
28. 路径及文件判断
1. os.path.exists(path) 判断一个目录是否存在
2. os.makedirs(path) 多层创建目录,如果上级目录不存在会自动创建
3. os.mkdir(path) 创建目录
29. 将多维列表打平为一维
from compiler.ast import flatten flatten(a)
30. cx_Freeze
由于 python 文件的执行必须依赖解释器的存在,因此如果我们需要在没有解释器的机器上执行 python 程序,就需要对原本的 python 文件进行打包,转换为 exe 可执行文件。
打包命令为:cxfreeze hello.py --target-dir dist #dist为打包后存放的目录
hello.py 是我们要打包的模块,注意只能一个模块,即启动模块;所有 .py 文件都不能有中文字符,否则会出现编码异常;发布后,可执行文件执行路径不能有中文(最好也不要有空格);启动执行的文件中不要有下面这种判断,否则可执行文件执行会没有任何效果。
if __name__ == "__main__":
main()
所以最好使用脚本去调用可执行文件。
31. Python 编码
1. 刚开始的时候使用的 ASCII 编码,将每个字符用一个字节表示。后面由于各国语言的加入,形成了统一的编码集 Unicode 编码,最常用的是用两个字节表示一个字符。但是这样的编码方式在网络传输以及存储上会使用大量的空间(尤其是全是英文字符的情况下,英文字符对应的 Unicode 编码为在原本的 ASCII 编码前加一个全 0 字节),所有衍生出了 utf-8 可变长编码,UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。
搞清楚了ASCII、Unicode和UTF-8的关系,我们就可以总结一下现在计算机系统通用的字符编码工作方式:
在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件:
浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器:
所以你看到很多网页的源码上会有类似<meta charset="UTF-8" />
的信息,表示该网页正是用的UTF-8编码。
2. 在 Python 中,u'\u674e\u535a' 表示 Unicode 编码,'\xe6\x9d\x8e\xe5\x8d\x9a' 表示 utf-8 编码。编码之间的转换用 encode() 函数实现,函数参数指定编码类型,用于将字符串按照指定的编码方式编码,在编码集无法对原字符串中的某些字符编码时会报错;相对应的函数为 decode(),用于将用某种类型编码得到的字符串用该种类型的编码进行解码得到内存所使用的 Unicode 码。
3. json.dumps() 函数
函数带有默认参数 ensure_ascii = True, 会将所有的非 ASCII 码字符转义为\uXXXX
序列,使得输出的结果中只包含 ASCII 码字符。如果 ensure_ascii 码为 False,表示原输入中含有用 ASCII 码无法编码的实例,所以不会转义为上述的这种形式,会按照 encoding 参数指定的编码方式进行编码,默认的方式是 'UTF-8' 编码。
比如 json.dumps('李博', encoding="utf-8", ensure_ascii=False) 编码结果为 '"\xe6\x9d\x8e\xe5\x8d\x9a"' 能够按照 UTF-8 编码的方式进行打印。
32. 在 Python 中安装 XGBoost 模块
1. 第一种方式:使用 anaconda 的安装器
conda install -c anaconda py-xgboost
2. 第二种方式:按照 XGBoost 官网的介绍流程,编译安装适用于多种语言的 XGB 模块。
1. 由于最新版本的 XGBoost 模块依赖于支持 C++ 11 的编译器,因此首先需要安装高版本的 g++ 编译器(g++-4.8 或更高)
1. 首先在网上下载 gcc 的编译器文件包 http://gcc.parentingamerica.com/releases/gcc-4.8.5/
2. 解压并创建同级目录 gcc-build,进入上述目录并执行:
# ../gcc-4.8.5/configure --prefix=/usr/local/gcc-4.8.5 --enable-threads=posix --disable-checking --disable-multilib --enable-languages=c,c++ --with-gmp=/usr/local/gmp-6.0.0/ --with-mpfr=/usr/local/mpfr-3.1.6/ --with-mpc=/usr/local/mpc-1.0.1/
configure 配置会报错:
configure: error: Building GCC requires GMP 4.2+, MPFR 2.4.0+ and MPC 0.8.0+. Try the --with-gmp, --with-mpfr and/or --with-mpc options to specify
说明 gcc 的编译需要一些依赖项 gmp,mpfr,mpc,m4。下载这些软件包:
# wget -c http://ftp.gnu.org/gnu/m4/m4-latest.tar.xz # wget -c http://www.mpfr.org/mpfr-current/mpfr-3.1.6.tar.gz # wget -c ftp://ftp.gnu.org/gnu/mpc/mpc-1.0.1.tar.gz # wget -c https://gmplib.org/download/gmp/gmp-6.0.0a.tar.xz
xz 是一种压缩格式,将 tar 打包后的软件包进行进一步压缩。处理这类文件需要首先使用 xz -d 命令将指定的文件解压缩为 .tar 文件,然后就可以使用 tar 命令进行处理。
解压后分别新建同级文件夹 mkdir gcc-build, m4-bulid, gmp-bulid, mpfr-bulid, mpc-bulid, gmp-bulid,在对应的 build 文件夹里执行 configure:
软件包之间存在依赖关系,所以需要按照顺序编译,依赖关系如下:
m4 -> gmp
mpfr -> gmp
mpc -> gmpmpfr
gcc -> gmp, mpfr, mpc
分别编译安装:
#m4 ../m4-xxx/configure; make; make check; make install #gmp ../gmp-xxx/configure --prefix=/usr/local/gmp-xxx; make; make check; make install #mpfr ../mpfr-xxx/configure --prefix=/usr/local/mpfr-xxx --with-gmp=/usr/local/gmp-xxx; make; make check; make install #mpc ../mpc-xxx/configure --prefix=/usr/local/mpc-xxx --with-gmp=/usr/local/gmp-xxx --with-mpfr=/usr/local/mpfr-xxx; make; make check; make install
3. 安装完上述依赖软件包之后需要添加其库路径,使 gcc 在编译的过程中能够访问到这些库,有两种方式:
1. 动态函数库加载到内存中(高速缓存 cache),当软件套件需要采用动态函数库时,就不需要重新从硬盘中读出,这样就可以提高动态函数库的读取速度。通过 ldconfig 命令和 /etc/ld.so.conf 文件来完成上述操作。
1. 首先将需要加载到内存中的动态函数库路径写入 /etc/ld.so.conf 文件中:
include ld.so.conf.d/*.conf #这一列是本身存在的,我们只需要在下面添加函数库路径即可 /usr/local/mpc-1.0.1/lib /usr/local/mpfr-3.1.6/lib /usr/local/gmp-6.0.0/lib #将我们所需要的函数库都加入高速缓存中
2. 使用 ldconfig 命令将上述文件中记录的动态函数库读入高速缓存中,这样就可以实现 gcc 编译过程中自动读取上述函数库。
2. 将动态函数库的路径写入 $LD_LIBRARY_PATH 环境变量中,然后在 gcc 的编译命令加上 -LLDPATH 参数便可自动读取上述环境变量指定的函数库。
实测方式一比较有效。
4. 安装上述命令重新配置后编译 gcc
make
如果报错 configure: error: cannot compute suffix of object files: cannot compile。是因为之前安装的依赖项没有能被正确读取导致的。
5. 安装 gcc
make install
6. 修改 gcc g++ 的符号链接
# cd /usr/bin/ # ln -si /usr/local/gcc-4.8.5/bin/gcc gcc # ln -si /usr/local/gcc-4.8.5/bin/g++ g++ # 上述使用的是相对路径修改 /usr/bin/gcc 和 /usr/bin/g++ 的符号链接为我们刚编译安装的编译器的链接,有时需要使用绝对路径
到此为止 gcc-4.8.5 的安装过程到此结束,编译器的安装涉及大量的依赖项,需要安装顺序一步步来,不能急躁,确保每一步的执行正确才能继续往前,否则会找不到错误的源头,只能进行简单的重试,浪费大量时间。
2. 安装好支持 C++ 11 的编译器,就可以开始 XGBoost 的安装。
1. 下载并编译支持各个语言的 XGB 模块
git clone --recursive https://github.com/dmlc/xgboost cd xgboost; make -j4
2. 将对应 Python 的模块安装进模块地址
cd python-package; sudo python setup.py install
注意如果需要安装进 anaconda 的软件包地址,需要指定 anaconda 包自带的 python 程序地址来执行 setup.py 文件,否则会默认使用之前安装的其他路径的 python 程序。
或者使用环境变量的方式也可以正常导入 xgboost 模块,如下:
export PYTHONPATH=~/xgboost/python-package
3. 至此,XGBoost 模块的安装到此结束,这件事情给我的体会是安装一个文件时,可以先多调研调研不同的方法,看看哪种最合适和简单再去执行,否则容易选择了复杂的方法。中途发现了简单的方法,又不愿意放弃复杂方法的状况。总的来说,一步步解决问题。
33. 模块与包的组织
1. 任何模块代码的第一个字符串都被视为这个模块的文档注释,用__author__变量可以将作者的名字写进去。一个模块可以看作是一个文件,多个模块组织成一个包(相当于一个目录下有多个文件),此时目录下必须存在__init__.py文件。当导入一个模块时,实际上是引入了一个变量名,此变量名就指向该模块文件。模块的导入之间无法自动继承,比如说模块model文件中倒入了math模块,那么在导入模块的文件中仍不能直接使用math模块,要么自己重新导入math模块,要么显式使用model文件下的math模块。
2. 私有变量和公开变量
在导入一个包时,会自动的过滤掉以'_'或者'__'开头的模块,那是因为这部分变量或者函数被声明为私有变量或者函数。在我们自己定义的模块内部,正常的函数和变量名是公开的,除此之外,__xxx__的变量名可以被直接引用,但是包含特殊用途,比如__name__,__author__,__doc__。类似_xxx和__xxx这样的函数和变量就是非公开的,不应该被直接引用。
3. __all__
在__init__.py文件中可以显式的定义__all__变量,来控制from model import *时导入的模块。否则会默认导入模块中所有不以下划线开头的全局名称。