呓语 | 杨英明的个人博客

专注于c++、Python,欢迎交流

By

Python 学习拾遗

该博文主要适应于python2.7,并没有对py3进行测试。

主要记录学习python过程中容易出现的一些小问题、小错误,相信能给你启发。

1、剔除一个字符串中的所有空格(假设该字符串是s)

"".join(s.split())

样例输入:

s = '    123  4567    8   '

样例输出:

'12345678'

2、剔除文件名字符串中的非法字符(假设字符串是s)

有的时候从网上抓取下来的标题需要作为文件名保存到本地(windows环境)

但是windows下文件名中不能包含一些非法字符,例如‘/’、‘\’、‘?’ 神马的

你可以用下面这段代码剔除标题中的非法字符

pattern = re.compile(r'[/\:*?"<>|]')
s = re.sub(pattern,'',s)

样例输入:

s = '<<?I am title?>>'

样例输出:

'I am title'

3、列表解析、base64解密、chr()、int()、join()函数使用

这是知道创宇的余弦出的一个解密小游戏,给你一个字符串,你需要解密出一个邮箱地址。

密码字符串 为:

XDY1XDc2XDY5XDZjXDYzXDZmXDczXDQwXDY3XDZkXDYxXDY5XDZjXDJlXDYzXDZmXDZk

解密方式为先用base64解密,再进行16进制转换,最后按ASCII码输出每个字符。

这个小游戏很有意思,可以学习到一些python知识。

下面是 解题步骤

1.base64解密

s = 'XDY1XDc2XDY5XDZjXDYzXDZmXDczXDQwXDY3XDZkXDYxXDY5XDZjXDJlXDYzXDZmXDZk'
print base64.b64decode(s)

输出:

\65\76\69\6c\63\6f\73\40\67\6d\61\69\6c\2e\63\6f\6d

这一看就是一种字符编码,最先猜到是ASCII编码。

为了方便观察,将其拆分:

print base64.b64decode(s).split('\\')

输出:

['', '65', '76', '69', '6c', '63', '6f', '73', '40', '67', '6d', '61', '69', '6c', '2e', '63', '6f', '6d']

由于每个元素带有字母,猜测是16进制数,转换进制。

int(c,16) 可以将字符串c当成16进制数,将其转换成10进制数返回(注意返回的是一个整数)

chr(n) 可以将一个整数n转换成对应的ASCII字符。

于是有代码:

for c in base64.b64decode(s).split('\\'):
  if c:   # 剔除空元素
    print chr(int(c,16)),

输出:

e v i l c o s @ g m a i l . c o m 

以上代码可以用 列表解析 简洁表示:

print [chr(int(c,16)) for c in base64.b64decode(s).split('\\') if c]

输出:

['e', 'v', 'i', 'l', 'c', 'o', 's', '@', 'g', 'm', 'a', 'i', 'l', '.', 'c', 'o', 'm']

2.join()连接为字符串

s.join(l) 表示将列表l中每个字符连接成一个字符串,用字符串s作为间隔

so,可以将上述列表连接成字符串:

print ''.join([chr(int(c,16)) for c in base64.b64decode(s).split('\\') if c])

输出:

evilcos@gmail.com

以上,就得到余弦大大的邮箱咯,是不是很有意思?

4.理解python继承的小例子

复制代码

 1 #coding=gbk
 2 class A(object):
 3     name = 'boss'
 4     print name
 5     def __init__(self,name=None):
 6         print '2'
 7         if name is not None:
 8             self.name = name
 9         print self.name
10 
11 
12 class B(A):
13     print '1'
14     name = 'Boss'
15 
16 te = B()

运行结果:

boss
1
2
Boss

可以看出类中变量和函数的继承调用关系。

实例化B,先是运行从A继承来的语句,运行结果:

boss

再运行B中的语句,运行结果:

1

最后调用从A继承过来的构造函数init(),运行结果:

2
Boss

5.python列表解析中的双重循环

如何将B列表中的每一个元素与A列表中的元素相加,生成一个新列表?

请看以下代码:

1 A = [1,2,3]
2 B = [4,5,6]
3 print [x+y for x in A for y in B]

以上列表解析的写法相当于一个双重循环,先遍历B中每一个元素,再遍历A中每一个元素,每轮循环进行元素相加操作,最后得到一个新列表。

6.max()的比较函数

max()函数原型:

max(arg1, arg2, *args[, key]) 

例子:

1 #coding=gbk
2 A = [1,3,5,2,4]
3 
4 print max(A)                #  输出A中最大的元素
5 print max(A,key=lambda x:x>2)    # 输出A中第一个大于2的元素
6 
7 print max('af', 'be','cd')       # 输出最大的字符串,比较规则为默认的字符串比较规则
8 print max('af', 'be','cd', key=lambda x: x[1])  # 输出第二个字符最大的字符串

输出:

5
3
cd
af

key参数值为一个函数入口指针,你可以将自己定义的函数作为参数传进来,max函数将根据你定义的函数来进行比较,你可以自己制订比较规则。

lambda为python中一种定义函数(匿名函数)的方法,你也可以用def定义。

7.reverse()函数有返回值吗?

请看以下代码,猜猜它会输出什么呢?

1 A = [1,2,3]
2 print A.reverse()

是不是第一眼以为会输出 [3,2,1] ?哈哈,错了。真正的输出是:

None

看一下reverse()的语法:

list.reverse()
参数:NA
返回值:该方法没有返回值,但是会对列表的元素进行反向排序。

8.提取url中的host(域名)

使用urllib库提供的函数,用法如下:

 1 import urllib  
 2   
 3 protocol, rest = urllib.splittype('https://www.cnblogs.com:80/yym2013')  
 4 # protocol : https
 5 # rest     : //www.cnblogs.com:80/yym2013
 6   
 7 host, rest =  urllib.splithost(rest)  
 8 # host : www.cnblogs.com:80
 9 # rest : /yym2013
10   
11 host, port = urllib.splitport(host)  
12 # host : www.cnblogs.com
13 # port : 80

参考自: python 获取url的host

9.if-else 单行书写方法

if a>b:
    t = a
else:
    t = b

转换成if-else单行形式:

t = a if a>b else b

10.open的文件打开模式

r或rt 默认模式,文本模式读
rb 二进制文件
w或wt 文本模式写,打开前文件存储被清空
wb 二进制写,文件存储同样被清空
a 追加模式,只能写在文件末尾
a+ 可读写模式,写只能写在文件末尾
w+ 可读写,与a+的区别是要清空文件内容
r+ 可读写,与a+的区别是可以写到文件任何位置

特别注意:a+、w+、r+的区别,他们虽然都是读写模式,但使用效果是有差别的。

参考自:Python open()文件处理使用介绍

11.关于文件读写错误 IOError: [Errno 0] Error

  该错误通常在windows环境下,使用a+读写模式打开文件,并对其先进行read()读操作,然后进行write()写入操作后出现的。原因是没有进行flush或者seek等操作,python不知道文件位置在哪了。

参考自:python IOError: [Errno 0] Error - freeDynasty

12、使用python发送邮件

使用python发送邮件需要使用smtplib和email两个模块。

首先需要在你的邮箱中开启smtp服务,并获得相应的授权码(网易邮箱貌似不需要)

然后按教程将你的用户名和授权码配置好,这部分代码是死的,具有通用性,这里不赘述。

最后准备好 发送邮箱发送内容,调用smtp.sendmail()发送即可。

具体配置可参考:使用python发送邮件

13、python的编号迭代

有时候想用下标记录当前遍历的位置,同时又想有当前元素,自建一个变量变量作为下标又太low,优雅一些的做法是用内建函数enumerate()进行迭代。

这样既会有下标,又会有当前迭代元素。

for index,s in enumerate(str):
    print '%d==>%s'%(index,s)

14、list元素去重

当你处理list列表变量时,可能会出现列表元素重复的现象。这个时候你该怎样用简便的方法将重复的元素去掉呢?

方法有很多,我比较喜欢用set集合进行去重,当list变量转换成set集合变量时,重复的元素会自动合并,只保留一个。再将set集合变量转换为list即可。

另外如果要保留list的顺序的话,需要使用sort进行排序。

详见:python中对list去重的多种方法

15、如何用尽量简洁的代码输出一个列表(list)的前五个元素

最直观的写法,定义一个下标变量index来控制输出:

1 li = [1,2,3,4,5,6,7,8,9,10]
2 index = 0
3 for x in li:
4     if index>=5:
5         break
6     print x
7     index+=1

观察代码,发现下标index可以用enumerate函数直接分离出来:

1 li = [1,2,3,4,5,6,7,8,9,10]
2 for index,x in enumerate(li):
3     if index>=5:
4         break
5     print x

还有更简单的写法,使用列表的切片操作来简化代码:

1 li = [1,2,3,4,5,6,7,8,9,10]
2 for x in li[:5]:
3     print x

16、readlines()会读入换行符!

在一次用smtp服务发送邮件给自己的时候,我用readlines()读取本地文件。

将带有email的username和password的那两行行直接赋值给用户名和密码变量。

结果导致收到的邮件开头带有乱码,检查了各个地方都没有发现bug在哪里。

最后找到原因是输入的用户名和密码最后分别带有一个换行符,而邮件将多余的内容作为乱码来处理了。

解决方法是readlines()读取每一行内容后,赋值的时候需要先strip()去除字符串前后端多余的空白符。

17、如何创建一个二维列表?

加入我想创建一个2000*7的二维列表,列表每个元素是0,该怎么创建呢?

可以使用 嵌套的列表解析 来达成这个目的。

cols = 7
rows = 2000
2d_list = [[0 for col in range(cols)] for row in range(rows)]

18、使用 eval 函数将列表样式字符串转换为真正的列表类型

ip = "['123.43.54.3',80,10]"
print type(ip)
ip = eval(ip)
print type(ip)

输出:

<type 'str'>
<type 'list'>

19、使用 tee 同时重定向到标准输出和文件

本例子基于 linux 环境

创建一个脚本 tmp.py ,它使用 python 输出到标准输出

import sys,time
while True:
    sys.stdout.write('[%s]'%time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())) + ' ')
    sys.stdout.flush()

这个脚本的作用是循环输出当前时间到linux的标准输出,一般来说,linux 的标准输出就是屏幕,但如果在 linux 中运行该脚本时使用重定向符 > output.txt ,那么标准输出就变成了文件 output.txt,也就是说以上代码循环输出的当前时间就会输出到文件 output.txt 中。

但是使用重定向符 > 只能单向指定,不能将输出同时指向两个位置,也就是说我没法将该脚本的运行结果同时输出到屏幕和一个文件中

这个需求可以用 linux 自带的工具 tee 解决。

tee 的作用是:从标准输入中读取并同时写入到标准输出和指定的文件上

于是我们在linux 中用以下命令将脚本的运行结果同时输出到屏幕和文件中。

python tmp.py | tee output.txt

注意:python 脚本中一定不要忘了使用 flush() 输出缓冲区内容,不然你会看不到输出。

参考资料:Linux即输出到屏幕,又保存到文件 - csdn

20、linux小知识:nohup 和 & 的区别

这两个操作的作用都是 使命令在后台运行

区别是 能否被 SIGHUP信号 中断

简单一些讲就是使用 & 启动的后台任务,关闭终端连接之后会被 SIGHUP信号 中断。而使用 nohup 启动的后台任务则不受影响。

仅仅这么说不太严谨,详情可以见:linux 的nohup & 和daemon 总结

21、linux小知识:使用 tail 动态查看日志文件

查看 .log 日志文件的时候,经常需要重新打开文件才能查看日志的更新情况。

那么可不可以动态的查看日志文件呢?

这个时候一个被我忽视了很久的命令 tail 跳入了我的眼帘。

tail 的作用是查看一个文件的末尾N行内容,如果 为 tail 添加上参数 -f 便可以动态查看日志啦!默认每秒刷新一次,即:

tail -f test.log

如此 test.log 最新写入的内容就可以立刻显示在屏幕上了,这对于查看日志文件十分有用。

参考资料:我使用过的Linux命令之tail - 输出文件尾部/动态监视文件尾部

22、logging 模块:按时间切分日志

如果将日志输出导向到一个文件,那么时间久了,这个文件会变得很大。

这个时候我们可以使用 logging 模块提供的 RotatingFileHandler 和 TimedRotatingFileHandler 来自动切分日志。

这里以 TimedRotatingFileHandler 举例,它的作用是设置 logging 按时间切分日志文件,比如下面这个例子,就是以“天”为单位,自动切分日志:

LOG_FILE = 'test.log'  # 日志文件输出位置

logging.basicConfig(level=logging.INFO)     # 基本设置

handler = logging.handlers.TimedRotatingFileHandler(LOG_FILE,when='D',interval=1,backupCount=100)   # 按时间分割日志,每天对应一个日志文件
formatter = logging.Formatter(fmt='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
handler.setFormatter(formatter) # 设置显示格式

logging.getLogger().addHandler(handler)    # 加载该处理

参考资料:

python 的日志logging模块学习 - 博客园

python 日志模块logging学习与使用(日志分割)

23、linux小知识:设置 alias 别名永久生效

使用 alias 设置命令的别名是很有用的操作,你可以在命令行直接输入以下命令设置一个别名,以 ll 为例:

alias ll = 'ls -alF'

如此便设置了一个别名,在命令行里输入 ll 和输入 ls -alF 产生的效果是相同的。

但直接在命令行里设置下次登陆便会失效,如何让别名永久生效呢,需要修改用户目录下的 .bashrc 文件。

vi .bashrc

添加

alias ll = 'ls -alF'

回到用户目录,输入以下命令使其生效:

source .bashrc

24、git小知识:git remote 远程主机常用操作

1. 查看帮助

git remote -h 

2. 显示远程主机

结构: git remote [-v]

例子:

git remote  # 显示所有远程主机名
git remote -v   # 显示所有远程主机名和对应的url

3. 添加远程主机

结构: git remote add <远程主机名> <远程仓库url>

例子:

git remote add origin http://test.com/user/repo.git

ps: 远程主机名一般会起为 origin,每个git项目的remote是独立的

4. 重命名远程主机名

结构: git remote rename <旧主机名> <新主机名>

例子:

git remote rename origin origin_bak

5. 删除远程主机

结构:git remote remove <远程主机名> 例子:

git remote remove origin

6. 重置远程主机的url

结构:git remote set-url <远程主机名> <新的远程仓库url>

例子:

git remote set-url origin http://test.com/user/repo.git

25、IP地址拼接精简写法

要实现这样一个功能,给出IP地址的四个字段,将它们拼接起来,并判断是否合法。

栗子

输入:

192 168 12 34
0 0 0 0
234 345 456 567
-1 -1 -1 -1

输出:

192.168.12.34
0.0.0.0
[Error]: cannot compose IP address.
[Error]: cannot compose IP address.

精简写法:

import sys

for line in sys.stdin:
    inputs = line.split()

    if all([0 <= int(input) <= 255 for input in inputs]):
        print(".".join(inputs))
    else:
        print("[Error]: cannot compose IP address.")

亮点:

  1. for line in sys.stdin: 循环读取标准输入的内容
  2. all() 的运用:如果列表内元素都不为 False、''、0,则返回 True;否则返回 False

来源:1.1 有个比较 pythonic 的写法,加油! #1 - WangHaizhen/py-tree

26、linux小知识:查看系统登录过的用户

Linux用户登录记录日志和相关查看命令汇总
http://www.cnblogs.com/lizhaoxian/p/5981029.html

27、python2 和 python3 的编码对比

讨论这个问题的起因是在 py3 环境中运行一段 py2 代码时出现了差异。

出问题的代码是这里:

#coding=gbk
...
if s[:2]=='【':
...

在 py2 里,这条语句的条件表达式结果为真;而在 py3 里,这条语句的条件表达式结果为假。

而如果我把代码改成:

#coding=gbk
...
if s[:1]=='【':
...

py3 中这条语句的条件表达式的结果变为真。

为什么会出现这种情况呢?

我们首先看一下,py2 中 gbk 编码的样子:

>>> s = '【'
>>> s
'\xa1\xbe'

这里中文字符‘【’,由两个区位符组成。所以s[:2]可以取到完整的‘【’。

而 py3 对编码问题做了改进,所有 str 类型变量都会自动转码为 unicode 编码存储。也就是说一个 gbk 编码的文件中,将一个中文字符赋值给一个变量,会自动转码为 unicode 编码,然后存进变量。

于是在 py3 中 gbk 编码赋值之后,变成了:

>>> s = '【'
>>> s
'\u3010'

(实际上 py3 解释器中会自动输出中文,这里理解意思就好)

所以在 py3 中,用 s[:1] 便可以取到完整的'【'。

于是if s[:1]=='【'这条语句中,条件表示式的结果变为真。

28、linux小知识:修改 shell 提示符显示格式

如果我想让linux shell 的提示符变成如下样子,该怎么做呢?

[my-PC] yym@/home/yym>

很简单,我们在 .bash_profile 中添加一条语句即可:

cd ~
vi .bash_profile
添加 export PS1='[\h] \u@$PWD>'
退出 vim
source .bash_profile

执行以上操作之后,你会发现提示符已经变成了我们想要的格式。

要修改成其它格式,依次类推。

原创声明

转载请注明:呓语 » Python 学习拾遗