Python 进阶

Author Avatar
Tr0y 10月 08, 2018 16:17:24 本文共 2.5k 字
  • 文为知己者书
  • 在其它设备中阅读本文章

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

报错解决

socket 端口占用问题

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

添加参数:

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。示例:

    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

知识与技巧

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

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

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

In [2]: info = "This is a test."
In [3]: any(discard in info for discard in [
   ...:                 "overflow", "Overflow"
   ...:                 "test", "null",
   ...:                 "DLL",
   ...:             ])
Out[3]: False
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 异常

用以下方法太挫了:

try:
    do_something
except Exception, e:
    if "Key" in str(e):
        print "ctrl+c caught..."
    else:
        do_something

这样显得优雅些:

try:
    do_something
except KeyboardInterrupt:
    print "ctrl+c caught..."
except:
    do_something

Python 的 “三元运算”

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
原理:var 用于返回对象 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:

#-*- 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)

结果是

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

for…else

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

sum 的妙用

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

给点颜色

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

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)

固定行输出

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

获得终端的大小

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

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 与转义

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

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

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

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

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

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

这个时候,我们可以利用 repr 来解决这个需求:

repr() 函数将对象转化为供解释器读取的形式。通常情况下 obj == eval(repr(obj)) 这个等式是成立的。

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

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

但是,repr 不仅仅处理 \

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') 也是:

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

更进一步的

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

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。暂时还没有好办法,后续想到再加进来。


去除转义

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)

In [365]: import codecs
     ...: print(codecs.decode(myString, 'unicode_escape'))
     ...:
spam
eggs

‘\u’ 字符串转中文

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

或者

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)

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

re 中的 re.Unicode/re.U

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

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 权限运行

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

生成时间序列

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

import pandas

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

for t in data:
    print t  #20140101-20161231

捕获异常到底怎么写?

except: 还是 except Exception, e: 还是 except Exception as e: ?

首先,except Exception, e: 已经是很旧很旧的写法了,还不兼容 3.x。所以,except Exception, e: 先不考虑。

except:except Exception, e: 的区别在于,except: 会捕获所有异常,包括键盘中断程序退出请求(用 sys.exit() 就无法退出程序了,因为异常被捕获了):

import sys

while 1:
    try:
        while 1: pass
    except:
        print sys.exc_info()[2]

按下 ctrl+c 也不会中断:

而这样就可以:

import sys

while 1:
    try:
        while 1: pass
    except Exception as e:
        print sys.exc_info()[2]

当然,你要是非要用 except: 也不是不可以,这个篇博文上面一点有例子 捕获 ctrl+c 异常

同样,对于 sys.exit()

import sys

while 1:
    try:
        while 1: sys.exit()
    except:
        print sys.exc_info()[2]

except Exception as e:

import sys

while 1:
    try:
        while 1: sys.exit()
    except Exception as e:
        print sys.exc_info()[2]

正常退出:

最后,在 except Exception as e: 中,e 作为 Exception 的一个实例,会有很多用处。比如,当你在使用 urllib 抓取一个页面的时候,服务器返回 500。这个时候 Python 会报错,但是页面却有返回内容,而你又想拿到这个页面的内容时,就可以在异常中获取:

import urllib.request
from urllib.error import HTTPError

result = urllib.request.Request(url="xxx")
try:
    handler = urllib.request.urlopen(result)
    handler.read() # 由于 500 会报错,所以代码不会走到这里
except HTTPError as e:
    content = e.read() # 输出 500 页面的内容

其他有用的博文

python 的多线程

传送门

解决蛋疼的编码问题

传送门

pip – 安装库的无痛工具

传送门

如果同时存在 py2 与 3,强烈建议使用 pyenv

py 的库记录单独成文。
传送门

Python 书籍笔记

End

What do you think?

本文标题: Python 进阶
原始链接: http://www.tr0y.wang/2018/10/08/pytrick/
发布时间: 2018.10.08-16:17
最后更新: 2018.11.07-16:28
版权声明: 本站文章均采用CC BY-NC-SA 4.0协议进行许可。转载请注明出处!