Python 进阶

使用 Python2 or 3 的过程中遇到的一些 trick。

报错解决

socket 端口占用问题

正常退出程序后,再次启动也会报错:socket.error: [Errno 48] Address already in use。原因是先前的执行使套接字处于 TIME_WAIT 状态,并且无法立即重用。SO_REUSEADDR 标志告诉内核在 TIME_WAIT 状态下重用本地套接字,而不等待其自然超时到期。

添加参数:

1
2
3
4
from socket import *

sock=socket()
sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)

安装 gmpy2 的时候报错

  • src/gmpy.h:106:19: 致命错误:gmp.h:没有那个文件或目录
    > 安装 gmp

    • 在 debian、ubuntu 上使用命令:sudo apt-get install libgmp-dev
    • 在 Fedora、RedHat、CentOS 上使用命令:sudo yum install gmp-devel
  • src/gmpy.h:252:20: 致命错误:mpfr.h:没有那个文件或目录
    > 下载 MPFRhttps://www.mpfr.org/mpfr-current/#download。示例:

    1
    2
    3
    4
    5
    6
    wget https://www.mpfr.org/mpfr-current/mpfr-4.0.1.tar.xz
    tar -xf mpfr-4.0.1.tar.xz
    cd mpfr-4.0.1
    ./configure
    make
    make install

  • src/gmpy.h:261:19: 致命错误:mpc.h:没有那个文件或目录
    > 安装 gmp

    • 在 debian、ubuntu 上使用命令:sudo apt-get install libmpc-dev
    • 在 Fedora、RedHat、CentOS 上使用命令:sudo yum install libmpc-devel

知识与技巧

充当连接符的“空格字符”

1
2
In [1]: "1" "2" == "12"
Out[1]: True

这里的充当,有时候会造成奇怪的 bug:

1
2
3
4
5
6
7
In [2]: info = "This is a test."
In [3]: any(discard in info for discard in [
...: "overflow", "Overflow"
...: "test", "null",
...: "DLL",
...: ])
Out[3]: False

1
2
3
4
5
6
7
In [2]: info = "This is a test."
In [3]: any(discard in info for discard in [
...: "overflow", "Overflow",
...: "test", "null",
...: "DLL",
...: ])
Out[3]: True

捕获 ctrl+c 异常

用以下方法太挫了:

1
2
3
4
5
6
7
try:
do_something
except Exception, e:
if "Key" in str(e):
print "ctrl+c caught..."
else:
do_something

这样显得优雅些:
1
2
3
4
5
6
try:
do_something
except KeyboardInterrupt:
print "ctrl+c caught..."
except:
do_something

Python 的 “三元运算”

1
2
3
4
5
6
7
8
In [14]: print 1 if 1 else 2 if 1 else 3
1
In [15]: print 1 if 1 else 2 if 0 else 3
1
In [16]: print 1 if 0 else 2 if 1 else 3
2
In [17]: print 1 if 0 else 2 if 0 else 3
3

兼容 2.x 与 3.x 的 字符串输入:

vars(__builtins__).get('raw_input', input)("please input something")
相当于在 2.x 与 3.x 下均可使用 raw_input
原理:vars 用于返回对象 object 的 属性 和 属性值 的 字典对象。所以 vars(__builtins__) 返回的是 内建模块 的字典。get 呢,自然就是获取键值了,但是它有一个可选参数,用于在没有获取到键值的时候返回。即,.get('raw_input', input) 在没有获取到 'raw_input' 的时候,默认返回 input。所以,在 py2.x 看来,vars(__builtins__).get('raw_input', input) 的值就是 <function raw_input>;在 py.x 看来,值就是 <function input(prompt=None, /)>。Coooool ~

pprint.pprint() 编码

Python 中的 dict 含有非 ASCII 字符的时候,使用 pprint.pprint() 输出时显示的是 unicode。可以继承 pprint.PrettyPrinter,重写 format:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#-*- coding:utf-8 -*-

import pprint


class MyPrettyPrinter(pprint.PrettyPrinter):
def format(self, object, context, maxlevels, level):
if isinstance(object, unicode):
return (object.encode('utf8'), True, False)
return pprint.PrettyPrinter.format(self, object, context, maxlevels, level)


d = {'foo': u'中文'}
pprint.pprint(d)
MyPrettyPrinter().pprint(d)

结果是

1
2
{'foo': u'\u4e2d\u6587'}
{'foo': 中文}

for...else

1
2
3
4
for i in mylist: #or while
... # balabalabala
else: # else 会在 for 循环遍历结束后执行, 若循环过程中 break or 出错就不会执行
... # balabalabala

sum 的妙用

1
2
3
# 压扁
sum([[1,2,3],[4,5,6],[7,8,9]],[])
# 输出 [1, 2, 3, 4, 5, 6, 7, 8, 9]

给点颜色

给输出加点颜色的话,在 Python 中可以这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def PutColor(string, color):
colors = {
u"gray": "2",
u"red": "31",
u"green": "32",
u"yellow": "33",
u"blue": "34",
u"pink": "35",
u"cyan": "36",
u"white": "37",
}

return u"\033[40;1;%s;40m%s\033[0m" % (colors[color], string)

固定行输出

控制光标位置的则可以用来固定打印行。
控制字符见这篇博文
如:

获得终端的大小

获得终端的大小,限制打印的文字数量,防止打印的时候出现换行

1
2
3
4
5
6
7
8
In [2]: import shutil
...: shell = shutil.get_terminal_size((80, 20))

In [3]: shell.columns
Out[3]: 85

In [4]: shell.lines
Out[4]: 25

这个方法在 py3.x 是肯定可行的。在 py2.x 只有 Unix 可用。若想兼容,则可以:
lines, columns = os.popen('stty size', 'r').read().split()

注意,这里的 columnslines 均为字符串。

Python 清屏

os.system("printf '\033c\e]50;ClearScrollback\a'")
'\033c\e]50;ClearScrollback\a' 这个是控制字符,见这篇博文

Python 与转义

转义
我们都知道 \ 是用来转义的。
比如:

1
2
3
In [1]: print "1\n1"
1
1

但是,如果我们需要输出 1\n1 呢?可以这样:

1
2
In [2]: print r"1\n1"
1\n1

然而,不是所有时候都可以加 r 的,比如:

1
2
3
4
5
In [5]: a = spider() # 假设这个是外来数据,返回 "1\n1"
...: print a
...:
1
1

这个时候,我们可以利用 repr 来解决这个需求:
> repr() 函数将对象转化为供解释器读取的形式。通常情况下 obj == eval(repr(obj)) 这个等式是成立的。

这函数用在这里的转义处理中,实际上是将 \ 变为了 \\

1
2
3
4
In [6]: a = repr(spider()) # 假设这个是外来数据,返回 "1\n1"
...: print a
...:
1\n1

但是,repr 不仅仅处理 \

1
2
3
4
5
6
7
8
9
In [11]: a = "'1'\n'1'"
...: print a
...: print "-"*10
...: print repr(a)
...:
'1'
'1'
----------
"'1'\n'1'" # 即为 print '"\'1\'\\n\'1\'"'

算是 repr 用于处理 \ 的一个小陷阱
同样,string.encode('string_escape') 也是:

1
2
In [372]: print "'1'\n'1'".encode('string_escape')
\'1\'\n\'1\'

更进一步的

1
2
3
4
5
6
7
8
9
10
In [9]: a = "1\n1"
...: for i in range(5):
...: a = repr(a)
...: print a
...:
'1\n1'
"'1\\n1'"
'"\'1\\\\n1\'"'
'\'"\\\'1\\\\\\\\n1\\\'"\''
'\'\\\'"\\\\\\\'1\\\\\\\\\\\\\\\\n1\\\\\\\'"\\\'\''

如果仅仅是想转义 \ 怎么办呢?(将 '1'\n'1' 变为 '1'\\n'1' 而不是 '"\'1\'\\n\'1\'"' 也不是 "\\'1\\'\\n\\'1\\'"),可以用 string.replace("", "") 或者 re

1
2
3
4
5
6
7
8
In [179]: s = "'1'\n'1'"

In [180]: print s
'1'
'1'

In [181]: print s.replace("\n", "\\n")
'1'\n'1'

s.replace("\n", "\\n") 只能转义 \n,想要转义 \r 就又得加一个 replace。暂时还没有好办法,后续想到再加进来。


去除转义

1
2
3
4
5
6
7
8
In [364]: myString = "spam\\neggs"
...: decoded_string = myString.decode('string_escape') # python2
...: print(decoded_string)
...:
spam
eggs

# py3.x: decoded_string = bytes(myString, "utf-8").decode("unicode_escape")

或者(兼容 py2.x 与 3.x)

1
2
3
4
5
In [365]: import codecs
...: print(codecs.decode(myString, 'unicode_escape'))
...:
spam
eggs

' 字符串转中文

  • python2 的解决办法:string.decode('unicode_escape')
  • python3 的解决办法:string.encode('utf-8').decode('unicode_escape')

或者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import re
import codecs

ESCAPE_SEQUENCE_RE = re.compile(r'''
( \\U........ # 8-digit hex escapes
| \\u.... # 4-digit hex escapes
| \\x.. # 2-digit hex escapes
| \\[0-7]{1,3} # Octal escapes
| \\N\{[^}]+\} # Unicode characters by name
| \\[\\'"abfnrtv] # Single-character escapes
)''', re.UNICODE | re.VERBOSE)

def decode_escapes(s):
def decode_match(match):
return codecs.decode(match.group(0), 'unicode-escape')

return ESCAPE_SEQUENCE_RE.sub(decode_match, s)

这个函数作用是处理转义序列,不止 ' 转中文,也可以将 1\\n1 => 1[换行]1 等等。

re 中的 re.Unicode/re.U

处理 unicode 的时候很有用。具体用处如下:

1
2
3
4
5
6
7
8
9
10
11
In [165]: re.findall("\w+", u'test test 我')
Out[165]: [u'test', u'test']

In [166]: re.findall(u"\w+", u'test test 我')
Out[166]: [u'test', u'test']

In [167]: re.findall(u"\w+", u'test test 我', re.U)
Out[167]: [u'test', u'test', u'\u6211']

In [168]: print u'\u6211'

py3.x 中默认启用

判断程序是否以 root 权限运行

1
2
3
def is_root():
if not os.geteuid() == 0:
sys.exit("Run as ROOT.")

生成时间序列

爆破字典为生日的时候,需要生成时间字典,如 20100101-20171231

1
2
3
4
5
6
import pandas

data = pandas.date_range('20140101','20161231',freq='D').strftime('%Y%m%d') #freq 为时间间隔

for t in data:
print t #20140101-20161231


来呀快活呀


Python 进阶
https://www.tr0y.wang/2018/10/08/pytrick/
作者
Tr0y
发布于
2018年10月8日
更新于
2024年4月19日
许可协议