知乎上有人问了一个问题:Python有哪些新手不会了解的深入细节。 其中的一个答案引用了stackoverflow上的一个问题解答。 鉴于一直在努力摆脱Python小白,决定好好研究下这几个特性,顺手翻译一下下,扩展一下下~~
List stepping
切片操作符中的步长(step)参数。例如: 1
2
3a = [1,2,3,4,5]
2] # iterate over the whole list in 2-increments a[::
[1,3,5]x[::-1]
对'x反转'来说相当有用。 1
21] a[::-
[5,4,3,2,1]reversed()
函数来实现反转。 区别在于,reversed()
返回一个迭代器,所以还需要一个额外的步骤来将结果转换成需要的对象类型。 这个特性在判断例如回文的时候灰常有用,一句话搞定 1
True if someseq == someseq[::-1] else False
a[10::]
会输出什么呢? 如果你猜是IndexError
,那就错了。答案是,[]
。但是其实当你试图访问一个超过列表索引值的成员,例如a[10]
时,确实会导致IndexError
。但是,试图访问一个列表的以超出列表成员数作为开始索引的切片时,却会返回一个空列表.
missing items
从2.5开始,字典就有一个特别的方法__missing__
,它在访问不存在的item时被引用: 1
2
3
4
5
6
7
8
9
10class MyDict(dict):
def __missing__(self, key):
self[key] = rv = []
return rv
m = MyDict()
"foo"].append(1) m[
"foo"].append(2) m[
dict(m)
{'foo': [1, 2]}1
2
3
4
5
6from collections import defaultdict
list) m = defaultdict(
"foo"].append(1) m[
"foo"].append(2) m[
dict(m)
{'foo': [1, 2]}
碎碎念
扩展阅读:defaultdict 和 dict.__missing__
Multi-line Regex
在Python中,你可以将一个正则表达式拆分成多行,命名你的匹配和插入注释。 详细语法举例(参考Dive into Python): 1
2
3
4
5
6
7
8
9
10
11
12""" pattern =
^ # beginning of string
M{0,4} # thousands - 0 to 4 M's
(CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
# or 500-800 (D, followed by 0 to 3 C's)
(XC|XL|L?X{0,3}) # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's),
# or 50-80 (L, followed by 0 to 3 X's)
(IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
# or 5-8 (V, followed by 0 to 3 I's)
$ # end of string
"""
'M', re.VERBOSE) re.search(pattern, 1
2
3
4compile(r'(?P<word>\b\w+\b)') p = re.
'(((( Lots of punctuation )))' ) m = p.search(
'word') m.group(
'Lots're.VERBOSE
. 1
2
3
4
5
6
7
8
9
10
11
12
13 pattern = (
"^" # beginning of string
"M{0,4}" # thousands - 0 to 4 M's
"(CM|CD|D?C{0,3})" # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
# or 500-800 (D, followed by 0 to 3 C's)
"(XC|XL|L?X{0,3})" # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's),
# or 50-80 (L, followed by 0 to 3 X's)
"(IX|IV|V?I{0,3})" # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
# or 5-8 (V, followed by 0 to 3 I's)
"$" # end of string
)
print pattern
"^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$"
Named string formatting
%-格式化接收一个字典(也适用于%i%s等) 1
2
3
4
5
6
7print "The %(foo)s is %(bar)i." % {'foo': 'answer', 'bar':42}
The answer is 42.
'question', 123 foo, bar =
print "The %(foo)s is %(bar)i." % locals()
The question is 123.locals()
也是一个字典,你可以简单的将其当做一个dict来传递,并从本地变量中获得%-字符串替换。虽然它令人难以接受,但是简化了操作。 新的格式化风格 1
"The {foo} is {bar}".format(foo='answer', bar=42)) print(
format
方法还是更被人推崇的,可能就是因为%-格式化方法能做的事它都可以做到,并且比%-格式化方法更漂亮的缘故吧。 但是,就速度来说,%-格式化方法会比str.format
方法快。
Nested list/generator comprehensions
嵌套列表推导和生成器表达式: 1
2[(i,j) for i in range(3) for j in range(i) ]
((i,j) for i in range(4) for j in range(i) )
碎碎念
前面也提到过了,这两个是由空间使用上的区别。另外,它们生成的东东也不一样哦~
New types at runtime
1 | type("NewType", (object,), {"x": "hello"}) NewType = |
它与下面完全一样 1
2
3
4
5class NewType(object):
"hello" x =
n = NewType()
n.x
"hello"
碎碎念
注意,所有的类都是在运行时创建的。因此,你可以在一个条件,或者在一个函数中使用'class'语句。而type
的使用,则是给你提供了一个简洁的动态定义一个生成一堆属性的类的方式。 可以定义一个匿名类。例如:type('', (object,), {'x': 'blah'})
可以在定义类的同时实例化。例如:x = type("X", (object,), {'val':'Hello'})()
再扩展一下下。type
这个东东,还可以用来创建高大上的metaclasses(元类)。
.pth files
为了添加更多的python模块(特别是第三方模块),大部分人似乎都会使用PYTHONPATH环境变量,或者在他们的site-packages目录中增加符号链接或目录。另一种方法是使用*.pth文件。下面是python官方文档的解释: “修改python搜索路径最方便的方法是将一个路径配置文件添加到已经存在于python搜索路径中的目录下,通常是.../site-packages/目录。路径配置文件有.pth
扩展名,并且每一行必须包含一个会添加到sys.pah的单一路径。(因为新的路径将会追加到sys.path中,新增的目录下的模块将不会覆盖掉标准模块。这意味着你不能使用这个机制来安装标准模块的修正版本。)”
碎碎念
引用如何方便地给Python环境注册新类库: 原理上, Python 运行环境查找库文件时本质是对 sys.path 列表的遍历,如果我们想给运行环境注册新的类库进来, 要么得用代码给 sys.path 列表增加新路径; 要么得调整 PYTHONPATH 环境变量; 要么就得把库文件复制到已经在 sys.path 设置中的路径中去(比如 site-packages 目录); 最简单的办法是用 .pth 文件来实现。Python 在遍历已知的库文件目录过程中,如果见到一个 .pth 文件,就会将文件中所记录的路径加入到 sys.path 设置中,于是 .pth 文件说指明的库也就可以被 Python 运行环境找到了。 python模块的绿色安装方法:只要将模块路径写入.pth文件中并把此文件放在正确的sys.path路径下即可。
ROT13 Encoding
对源代码来说,当你在代码文件顶部使用正确的编码声明时,ROT13是一种有效的编码。 1
2
3
4
5#!/usr/bin/env python
# -*- coding: rot13 -*-
cevag "Uryyb fgnpxbiresybj!".rapbqr("rot13")
cevag h"Uryyb fgnpxbiresybj!"Hello stackoverflow!
这个特性貌似在py3k中被移除了~~ 当然,有小伙伴提出来了邪恶的想法,可以用来对付反病毒工具……
Regex Debugging
正则表达式是python的一个很棒的特性,但是调试它们却是一件痛不欲生的事,然而,我们相当容易犯关于正则的错误Orz…… 幸运的是,通过传递一个未公开的实验性的隐藏标记re.DEBUG
(实际上是,128)给re.compile
函数,python可以打印正则解析树, 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
28compile("^\[font(?:=(?P<size>[-+][0-9]{1,2}))?\](.*?)[/font]", re.
re.DEBUG)
at at_beginning
literal 91
literal 102
literal 111
literal 110
literal 116
max_repeat 0 1
subpattern None
literal 61
subpattern 1
in
literal 45
literal 43
max_repeat 1 2
in
range (48, 57)
literal 93
subpattern 2
min_repeat 0 65535
any None
in
literal 47
literal 102
literal 111
literal 110
literal 116[/font]
中去掉[]
。 当然,你可以把它和任何你想要的标记组合在一起,例如注释正则: 1
2
3
4
5
6
7
8
9
10compile(""" re.
^ # start of a line
\[font # the font tag
(?:=(?P<size> # optional [font=+size]
[-+][0-9]{1,2} # size specification
))?
\] # end of tag
(.*?) # text between the tags
\[/font\] # end of the tag
""", re.DEBUG|re.VERBOSE|re.DOTALL)
Sending to Generators
发送值到生成器函数。例如下面这个函数: 1
2
3
4
5
6
7def mygen():
"""Yield 5 until something else is passed back via send()"""
a = 5
while True:
f = (yield a) #yield a and possibly get f in return
if f is not None:
a = f #store the new value1
2
3
4
5
6
7
8
9 g = mygen()
next() g.
5
next() g.
5
7) #we send this back to the generator g.send(
7
next() #now it will yield 7 until we send something else g.
7
Tab Completion in Interactive Interpreter
1 | try: |
当然,你需要设置一个环境变量:PYTHONSTARTUP
碎碎念
噢,暂时还看不懂,~~~~(>_<)~~~~
Ternary Expression
1 | x = 3 if (y == 1) else 2 |
上面这个语句会做这样的事:“将x赋值为3,如果y是1,否则将x赋值为2”。注意,括号不是必须的,但是有它们会使得代码更具可读性。如果你有更复杂的需求,也可以将它们串联起来: 1
x = 3 if (y == 1) else 2 if (y == -1) else 1
if ... else
。例如: 1
(func1 if y == 1 else func2)(arg1, arg2)
1
x = (class1 if y == 1 else class2)(arg1, arg2)
碎碎念
常用的还有return 3 if (y == 1) else 2
上面第一个例子还有一种容易让人困惑的等价写法:y == 1 and 3 or 2
。
try/except/else
1 | try: |
使用else从句比增加额外的代码在try子句中要好得多,因为它避免了无意中捕获到不是由try...except语句所保护的代码抛出的异常。
碎碎念
else语句块只有当try语句正常执行(也就是说,except语句未执行)的时候,才会执行。 python中的try/except/else/finally语句的语法如下: 1
2
3
4
5
6
7
8
9
10
11
12try:
Normal execution block
except A:
Exception A handle
except B:
Exception B handle
except:
Other exception handle
else:
if no exception,get here
finally:
this block will be excuted no matter how it goes above
with statement
PEP 343中引进的context manager是一个将一套语句作为运行时上下文的对象。由于这个特性使用了一些新的关键字,因此它是被逐步推行的:在Python 2.5中通过__future__
可用。Python 2.6及以上(包括Python 3)则默认可用。 会经常使用with语句,则是因为它是一个非常有用的结构。下面是一个快速演示: 1
2
3
4from __future__ import with_statement
with open('foo.txt', 'w') as f:
f.write('hello!')__enter__
和__exit__
方法。如果在with语句体中发生了异常,异常细节也会传给__exit__
,因此允许异常处理。 在这个特殊场景下,它为你所做的事是,保证了当执行到了with语句体之外时,file会被关闭,而忽略语句体正常结构或是否抛出了异常。它基本上是一种抽象出公用异常处理代码的方式。 其他常用场景包括线程锁和数据库事务锁。
碎碎念
支持多个with形式,例如:with open('filea') as filea, open('fileb') as fileb:...
扩展阅读: * 浅谈 Python 的 with 语句