Python编程语言鲜为人知但有用的功能是什么?
- 尝试将答案限于Python核心。
- 每个答案一个功能。
- 给出该功能的示例和简短描述,而不仅仅是指向文档的链接。
- 使用标题作为第一行标记功能。
Python编程语言鲜为人知但有用的功能是什么?
Answers:
>>> x = 5
>>> 1 < x < 10
True
>>> 10 < x < 20
False
>>> x < 10 < x*10 < 100
True
>>> 10 > x <= 9
True
>>> 5 == x > 4
True
如果您以为它在做1 < x
,它显示为True
,然后比较True < 10
,它也是True
,那么不,那实际上不是什么事情(请参阅最后一个示例。)它实际上是翻译成1 < x and x < 10
,和x < 10 and 10 < x * 10 and x*10 < 100
,但键入和每个输入较少该术语仅评估一次。
(< 1 x 10)
。您甚至可以将它们应用于单个参数,例如(= 10)
:cs.cmu.edu/Groups/AI/html/hyperspec/HyperSpec/Body/…–
获取python regex解析树以调试您的regex。
正则表达式是python的一个很棒的功能,但是调试它们可能会很麻烦,而且很容易使正则表达式出错。
幸运的是,python可以通过将未记录的实验性隐藏标记re.DEBUG
(实际上是128)传递给,从而输出正则表达式分析树re.compile
。
>>> re.compile("^\[font(?:=(?P<size>[-+][0-9]{1,2}))?\](.*?)[/font]",
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]
。
当然,您可以将其与所需的任何标志(例如带注释的正则表达式)结合使用:
>>> re.compile("""
^ # 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)
枚举
用enumerate包装一个可迭代对象,它将产生该项目及其索引。
例如:
>>> a = ['a', 'b', 'c', 'd', 'e']
>>> for index, item in enumerate(a): print index, item
...
0 a
1 b
2 c
3 d
4 e
>>>
参考文献:
创建生成器对象
如果你写
x=(n for n in foo if bar(n))
您可以找出生成器并将其分配给x。现在,这意味着您可以
for n in x:
这样做的好处是您不需要中间存储,如果需要的话
x = [n for n in foo if bar(n)]
在某些情况下,这可能会导致速度显着提高。
您可以在生成器的末尾附加许多if语句,基本上是复制嵌套的for循环:
>>> n = ((a,b) for a in range(0,2) for b in range(4,6))
>>> for i in n:
... print i
(0, 4)
(0, 5)
(1, 4)
(1, 5)
iter()可以接受可调用参数
例如:
def seek_next_line(f):
for c in iter(lambda: f.read(1),'\n'):
pass
该iter(callable, until_value)
函数反复调用callable
并产生其结果,直到until_value
返回为止。
lambda
在这里解释为什么需要关键字?
注意可变的默认参数
>>> def foo(x=[]):
... x.append(1)
... print x
...
>>> foo()
[1]
>>> foo()
[1, 1]
>>> foo()
[1, 1, 1]
相反,您应该使用表示“未给定”的前哨值,并默认将其替换为您想要的可变变量:
>>> def foo(x=None):
... if x is None:
... x = []
... x.append(1)
... print x
>>> foo()
[1]
>>> foo()
[1]
foo.func_defaults
。作为元组,这是不可变的。
将值发送到生成器函数中。例如,具有以下功能:
def 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 value
您可以:
>>> g = mygen()
>>> g.next()
5
>>> g.next()
5
>>> g.send(7) #we send this back to the generator
7
>>> g.next() #now it will yield 7 until we send something else
7
如果您不喜欢使用空格来表示作用域,则可以通过发出以下命令来使用C样式的{}:
from __future__ import braces
切片运算符中的step参数。例如:
a = [1,2,3,4,5]
>>> a[::2] # iterate over the whole list in 2-increments
[1,3,5]
特殊情况x[::-1]
是“ x反转”的有用成语。
>>> a[::-1]
[5,4,3,2,1]
装饰工
装饰器允许将一个函数或方法包装在另一个函数中,该函数或方法可以添加功能,修改参数或结果等。您可以在函数定义上方一行以“ at”符号(@)开头编写装饰器。
示例显示了一个print_args
装饰器,该装饰器在调用之前打印装饰后的函数的参数:
>>> def print_args(function):
>>> def wrapper(*args, **kwargs):
>>> print 'Arguments:', args, kwargs
>>> return function(*args, **kwargs)
>>> return wrapper
>>> @print_args
>>> def write(text):
>>> print text
>>> write('foo')
Arguments: ('foo',) {}
foo
for ... else语法(请参阅http://docs.python.org/ref/for.html)
for i in foo:
if i == 0:
break
else:
print("i was never 0")
除非调用break,否则“ else”块通常会在for循环的末尾执行。
上面的代码可以模拟如下:
found = False
for i in foo:
if i == 0:
found = True
break
if not found:
print("i was never 0")
从2.5开始,字典有一个特殊的方法__missing__
用于缺失项的调用:
>>> class MyDict(dict):
... def __missing__(self, key):
... self[key] = rv = []
... return rv
...
>>> m = MyDict()
>>> m["foo"].append(1)
>>> m["foo"].append(2)
>>> dict(m)
{'foo': [1, 2]}
collections
调用defaultdict
中还有一个dict子类,它的功能几乎相同,但是为不存在的项目调用不带参数的函数:
>>> from collections import defaultdict
>>> m = defaultdict(list)
>>> m["foo"].append(1)
>>> m["foo"].append(2)
>>> dict(m)
{'foo': [1, 2]}
我建议在将此类字典传递给不希望此类子类的函数之前,将其转换为常规字典。许多代码使用d[a_key]
并捕获KeyErrors来检查是否存在某个项目,从而将新项目添加到dict中。
m={}; m.setdefault('foo', []).append(1)
。
defaultdict
setdefault
在其他情况下也比该方法更强大。例如,对于一个计数器dd = collections.defaultdict(int) ... dd[k] += 1
-vs d.setdefault(k, 0) += 1
。
就地价值交换
>>> a = 10
>>> b = 5
>>> a, b
(10, 5)
>>> a, b = b, a
>>> a, b
(5, 10)
分配的右侧是创建新元组的表达式。作业的左侧立即将(未引用的)元组解压缩到名称a
和b
。
分配后,新的元组将不被引用并标记为垃圾回收,并且值绑定到a
并且b
已经交换。
注意,多重分配实际上只是元组打包和序列拆包的组合。
可读的正则表达式
在Python中,您可以将正则表达式分成多行,命名匹配项并插入注释。
详细语法示例(从Dive into Python):
>>> 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
... """
>>> re.search(pattern, 'M', re.VERBOSE)
命名匹配示例(来自正则表达式HOWTO)
>>> p = re.compile(r'(?P<word>\b\w+\b)')
>>> m = p.search( '(((( Lots of punctuation )))' )
>>> m.group('word')
'Lots'
您还可以冗长地编写一个正则表达式,而不必使用re.VERBOSE
多亏了字符串文字连接。
>>> 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})$"
函数参数解压缩
您可以使用*
和将列表或字典作为函数参数解压缩**
。
例如:
def draw_point(x, y):
# do some magic
point_foo = (3, 4)
point_bar = {'y': 3, 'x': 2}
draw_point(*point_foo)
draw_point(**point_bar)
由于列表,元组和字典广泛用作容器,因此非常有用的快捷方式。
当您在代码文件的顶部使用正确的编码声明时,ROT13是源代码的有效编码:
#!/usr/bin/env python
# -*- coding: rot13 -*-
cevag "Uryyb fgnpxbiresybj!".rapbqr("rot13")
cevag h"Uryyb fgnpxbiresybj!"
>>> NewType = type("NewType", (object,), {"x": "hello"})
>>> n = NewType()
>>> n.x
"hello"
完全一样
>>> class NewType(object):
>>> x = "hello"
>>> n = NewType()
>>> n.x
"hello"
可能不是最有用的东西,但很高兴知道。
编辑:新类型的固定名称,应NewType
与with class
语句完全相同。
编辑:调整标题以更准确地描述功能。
上下文管理器和“ with
”语句
在PEP 343中引入的上下文管理器是一个对象,它充当一组语句的运行时上下文。
由于该功能使用了新的关键字,因此逐步引入了该功能:通过__future__
指令在Python 2.5中可用。Python 2.6及更高版本(包括Python 3)默认情况下可用。
我经常使用“ with”语句,因为我认为这是一个非常有用的结构,下面是一个快速演示:
from __future__ import with_statement
with open('foo.txt', 'w') as f:
f.write('hello!')
在幕后发生的事情是,“ with”语句调用了文件对象上的special __enter__
和__exit__
method。__exit__
如果with语句主体引发了任何异常,则异常详细信息也将传递到该异常,从而允许在那里进行异常处理。
在这种特殊情况下,这为您执行的操作是,它保证在执行超出with
套件范围时关闭文件,无论是正常发生还是引发异常。从根本上讲,它是一种抽象通用异常处理代码的方法。
其他常见用例包括使用线程锁定和数据库事务。
withs
:) with open('filea') as filea and open('fileb') as fileb: ...
with open('filea') as filea, open('fileb') as fileb: ...
字典有一个“ get()”方法。如果执行d ['key']而键不存在,则会出现异常。如果执行d.get('key'),则如果'key'不存在,则返回None。您可以添加第二个参数来取回该项目,而不是无,例如:d.get('key',0)。
这非常适合诸如加号之类的事情:
sum[value] = sum.get(value, 0) + 1
get(key, None)
。None
默认情况下没有提供任何想法。
它们是一大堆Python核心功能背后的魔力。
当您使用点分访问来查找成员(例如xy)时,Python首先在实例字典中查找该成员。如果找不到,它将在类字典中查找。如果它在类字典中找到它,并且该对象实现了描述符协议,而不是仅仅返回它,Python就会执行它。一个描述符是实现任何类__get__
,__set__
或__delete__
方法。
这是使用描述符实现自己的(只读)属性版本的方法:
class Property(object):
def __init__(self, fget):
self.fget = fget
def __get__(self, obj, type):
if obj is None:
return self
return self.fget(obj)
您将像内置的property()一样使用它:
class MyClass(object):
@Property
def foo(self):
return "Foo!"
描述符在Python中用于实现属性,绑定方法,静态方法,类方法和插槽等。理解它们可以很容易地弄清为什么以前看起来像Python的“怪癖”的很多东西都是它们的样子。
Raymond Hettinger 的教程很棒,比我做得更好。
foo = property(lambda self: self.__foo)
property
它本身是通过描述符实现的,这就是我的帖子的重点。
条件分配
x = 3 if (y == 1) else 2
它确实听起来像:“如果y为1,则将3分配给x,否则将2分配给x”。请注意,不需要括号,但是出于可读性考虑,我喜欢它们。如果您有更复杂的东西,也可以将其链接起来:
x = 3 if (y == 1) else 2 if (y == -1) else 1
尽管在某个时候,它有点太过分了。
请注意,您可以在任何表达式中使用if ... else。例如:
(func1 if y == 1 else func2)(arg1, arg2)
如果y为1,则调用func1,否则调用func2。在这两种情况下,将使用参数arg1和arg2调用相应的函数。
类似地,以下内容也有效:
x = (class1 if y == 1 else class2)(arg1, arg2)
其中class1和class2是两个类。
x = ((y == 1) ? 3 : 2)
对我来说更有意义
从Python文档中提取的示例:
def factorial(n):
"""Return the factorial of n, an exact integer >= 0.
If the result is small enough to fit in an int, return an int.
Else return a long.
>>> [factorial(n) for n in range(6)]
[1, 1, 2, 6, 24, 120]
>>> factorial(-1)
Traceback (most recent call last):
...
ValueError: n must be >= 0
Factorials of floats are OK, but the float must be an exact integer:
"""
import math
if not n >= 0:
raise ValueError("n must be >= 0")
if math.floor(n) != n:
raise ValueError("n must be exact integer")
if n+1 == n: # catch a value like 1e300
raise OverflowError("n too large")
result = 1
factor = 2
while factor <= n:
result *= factor
factor += 1
return result
def _test():
import doctest
doctest.testmod()
if __name__ == "__main__":
_test()
locals()
然后在您的doctest中做locals().update(setUp())
= D
%-formatting需要一个字典(也适用于%i /%s等。验证)。
>>> print "The %(foo)s is %(bar)i." % {'foo': 'answer', 'bar':42}
The answer is 42.
>>> foo, bar = 'question', 123
>>> print "The %(foo)s is %(bar)i." % locals()
The question is 123.
并且由于locals()也是一个字典,因此您可以简单地将其作为字典传递,并从本地变量中获取%替换。我认为这是不满意的,但可以简化。
新样式格式
>>> print("The {foo} is {bar}".format(foo='answer', bar=42))
为了添加更多的python模块(尤其是第三方模块),大多数人似乎使用PYTHONPATH环境变量,或者在其站点包目录中添加符号链接或目录。另一种方法是使用* .pth文件。这是python官方文档的解释:
“ [修改python搜索路径的最方便的方法是将路径配置文件添加到Python路径上已经存在的目录中,通常是... / site-packages /目录。路径配置文件的扩展名为.pth。 ,并且每行必须包含一个附加到sys.path的路径。(由于新路径附加到sys.path,因此添加目录中的模块不会覆盖标准模块。这意味着您不能使用此机制用于安装标准模块的固定版本。)”
例外else子句:
try:
put_4000000000_volts_through_it(parrot)
except Voom:
print "'E's pining!"
else:
print "This parrot is no more!"
finally:
end_sketch()
使用else子句比向try子句添加其他代码更好,因为它避免了意外捕获try ... except语句保护的代码未引发的异常。
重新引发异常:
# Python 2 syntax
try:
some_operation()
except SomeError, e:
if is_fatal(e):
raise
handle_nonfatal(e)
# Python 3 syntax
try:
some_operation()
except SomeError as e:
if is_fatal(e):
raise
handle_nonfatal(e)
错误处理程序中不带任何参数的'raise'语句告诉Python重新引发具有完整原始追溯的异常,允许您说“哦,对不起,对不起,我不是要抓住那个,对不起,对不起。 ”
如果您希望打印,存储或摆弄原始回溯,可以通过sys.exc_info()来获取,并像Python一样通过“回溯”模块完成打印。
raise e
,但不会保留原始的追溯。
exc_info = sys.exc_info(); raise exc_info[0], exc_info[1], exc_info[2]
等效于此,但是您可以更改这些值(例如,更改异常类型或消息)
主要信息:)
import this
# btw look at this module's source :)
解密:
提姆·彼得斯(Tim Peters)撰写的《 Python之禅》
美丽胜于丑陋。
显式胜于隐式。
简单胜于复杂。
复杂胜于复杂。
扁平比嵌套更好。
稀疏胜于密集。
可读性很重要。
特殊情况还不足以打破规则。
尽管实用性胜过纯度。
错误绝不能默默传递。
除非明确地保持沉默。
面对模棱两可的想法,拒绝猜测的诱惑。应该有一种-最好只有一种-显而易见的方法。
尽管除非您是荷兰人,否则一开始这种方式可能并不明显。
现在总比没有好。
虽然从来没有比这更好正确的现在。
如果实现难以解释,那是个坏主意。
如果实现易于解释,则可能是个好主意。
命名空间是一个很棒的主意-让我们做更多这些吧!
print s.translate("".join(chr(64<i<91 and 65+(i-52)%26 or 96<i<123 and 97+(i-84)%26 or i) for i in range(256)))
它替换了旧代码,现在看起来好多了!!:-D
交互式口译员选项卡完成
try:
import readline
except ImportError:
print "Unable to load readline module."
else:
import rlcompleter
readline.parse_and_bind("tab: complete")
>>> class myclass:
... def function(self):
... print "my function"
...
>>> class_instance = myclass()
>>> class_instance.<TAB>
class_instance.__class__ class_instance.__module__
class_instance.__doc__ class_instance.function
>>> class_instance.f<TAB>unction()
您还必须设置PYTHONSTARTUP环境变量。
easy_install ipdb
那么您可以使用import ipdb; ipdb.set_trace()
readline.parse_and_bind ("bind ^I rl_complete")
嵌套列表推导和生成器表达式:
[(i,j) for i in range(3) for j in range(i) ]
((i,j) for i in range(4) for j in range(i) )
这些可以替换大量的嵌套循环代码。
for
语句的顺序应按照您希望它们从外部到内部以标准for循环的顺序编写。
for
s和if
s yield x
内部开始的。要将其转换为生成器表达式,x
请先移动,删除所有冒号(和yield
),然后将整个内容括在括号中。要使列表更完整,请用方括号替换外部括号。
set
内置运算符重载:
>>> a = set([1,2,3,4])
>>> b = set([3,4,5,6])
>>> a | b # Union
{1, 2, 3, 4, 5, 6}
>>> a & b # Intersection
{3, 4}
>>> a < b # Subset
False
>>> a - b # Difference
{1, 2}
>>> a ^ b # Symmetric Difference
{1, 2, 5, 6}
标准库参考中的更多详细信息:设置类型