题 Python的隐藏功能[关闭]


Python编程语言有哪些鲜为人知但有用的功能?

  • 尝试限制Python核心的答案。
  • 每个答案的一个功能。
  • 举一个示例和该功能的简短描述,而不仅仅是文档的链接。
  • 使用标题作为第一行标记要素。

快速链接到答案:


1420


起源




答案:


链接比较运算符:

>>> 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,但键入较少,每个术语仅评估一次。


741



这非常有帮助。它应该是所有语言的标准。可悲的是,事实并非如此。 - stalepretzel
你应该添加一些返回false的例子。例如>>> 10 <x <20假 - ShoeLace
这也适用于其他比较运算符,这就是为什么人们有时会惊讶为什么像(5 [5]中的5是真的)这样的代码是假的(但是开始时明确地测试这样的布尔值是非单音的)。 - Miles
很好,但要注意同等的优点,比如'in'和'='。 “A中的A = D中的C”表示“(B中的A)和(B == C)和(C中的D)”可能是意外的。 - Charles Merriam
Azafe:Lisp的比较自然就是这样的。这不是一个特例,因为没有其他(合理的)解释方式 (< 1 x 10)。您甚至可以将它们应用于单个参数,例如 (= 10): cs.cmu.edu/Groups/AI/html/hyperspec/HyperSpec/Body/... - Ken


获取python正则表达式解析树来调试你的正则表达式。

正则表达式是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)

512



除了使用正则表达式解析HTML是缓慢而痛苦的。即使是内置的'html'解析器模块也不使用正则表达式来完成工作。如果html模块不讨好你,那么有很多XML / HTML解析器模块可以完成这项工作而无需重新发明轮子。 - BatchyX
有关输出语法的文档链接会很棒。 - Personman
这应该是Python的官方部分,而不是实验性的...... RegEx总是很棘手,能够追踪正在发生的事情真的很有帮助。 - Cahit


枚举

用枚举包装一个iterable,它将产生项目及其索引。

例如:


>>> 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
>>>

参考文献:


460



我很惊讶在关于python列表的教程中没有经常介绍这个问题。 - Draemon
而且这一次我用这种方式编码:对于范围内的我(len(a)):...然后使用[i]来获取当前项目。 - Fernando Martin
@Berry Tsakala:据我所知,它还没有被弃用。 - JAB
神圣的废话这太棒了。 for x in xrange(len(a)):一直是我最不喜欢的python成语。 - Personman
枚举可以从任意索引开始,而不是必需的0.例如:'for i,枚举中的项(名单,start = 1):print i,item'将从1开始枚举,而不是0。 - dmitry_romanov


创建生成器对象

如果你写

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)

419



您也可以使用嵌套列表理解,是吗? - shapr
特别值得注意的是节省内存开销。值是按需计算的,因此您永远不会在内存中获得列表推导的整个结果。如果您稍后只迭代部分列表理解,那么这是特别理想的。 - saffsd
这不是特别“隐藏”的imo,但值得注意的是你无法回放生成器对象,而你可以重复列表任意次。 - susmits
发电机的“无倒带”功能可能会引起一些混乱。具体来说,如果您打印生成器的内容以进行调试,然后再使用它来处理数据,它就不起作用。生成的数据由print()消耗,然后不能用于正常处理。这不适用于列表推导,因为它们完全存储在内存中。 - johntellsall
类似(dup?)答案: stackoverflow.com/questions/101268/hidden-features-of-python/... 但请注意,我在此处链接的答案提到了关于发电机功率的真正好的介绍。你真的应该检查一下。 - Denilson Sá Maia


iter()可以采用可调用的参数

例如:

def seek_next_line(f):
    for c in iter(lambda: f.read(1),'\n'):
        pass

iter(callable, until_value) 功能反复调用 callable 直到产生结果 until_value 被退回。


353



@Cristian这个更清楚吗? - badp
作为python的新手,你能解释一下为什么 lambda 关键字是必要的吗? - SiegeX
没有lambda的@SiegeX,f.read(1)将在传递给iter函数之前被计算(返回一个字符串)。相反,lambda创建一个匿名函数并将其传递给iter。 - jmilloy


小心可变的默认参数

>>> def foo(x=[]):
...     x.append(1)
...     print x
... 
>>> foo()
[1]
>>> foo()
[1, 1]
>>> foo()
[1, 1, 1]

相反,你应该使用表示“not given”的sentinel值,并将你想要的mutable替换为默认值:

>>> def foo(x=None):
...     if x is None:
...         x = []
...     x.append(1)
...     print x
>>> foo()
[1]
>>> foo()
[1]

339



这绝对是一个更讨厌的隐藏功能。我不时遇到它。 - Torsten Marek
当我得知默认参数存在于一个元组中时,我发现这更容易理解,这个元组是函数的一个属性,例如 foo.func_defaults。作为一个元组,它是不可改变的。 - Robert Rossney
@grayger:当执行def语句时,它的参数由解释器评估。这会为代码对象(函数的套件)创建(或重新绑定)名称。但是,默认参数在定义时被实例化为对象。对于默认对象的任何时间都是如此,但是当对象是可变的时,这只是显着的(暴露可见语义)。没有办法在函数的闭包中重新绑定该默认参数名称,尽管它显然可以被任何调用覆盖,或者可以重新定义整个函数)。 - Jim Dennis
@Robert当然参数元组可能是不可变的,但它指向的对象不一定是不可变的。 - poolie
一个快速入门使你的初始化更短:x = x或[]。您可以使用它而不是2行if语句。 - dave mankoff


将值发送到生成器函数。例如,具有此功能:

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

317



在其他语言中,我相信这个神奇的设备被称为“变量”。 - finnw
协程应该是协同程序,发生器也应该是自己的,没有混合。关于这个的巨大链接和谈话以及示例: dabeaz.com/coroutines - u0b34a0f6ae
这是一个非隐藏的功能 - Justin
@finnw:该示例实现了类似于变量的东西。但是,该功能可以以许多其他方式使用...与变量不同。显而易见的是,可以使用对象(一个实现Python的类)实现类似的语义 呼叫 方法,特别是)。 - Jim Dennis
对于那些从未见过(也可能不会理解)共同惯例的人来说,这是一个微不足道的例子。实现具有和变量溢出风险的运行平均值的示例是一个很好的例子。 - Prashant Kumar


如果您不喜欢使用空格来表示范围,可以通过发出以下命令来使用C风格的{}:

from __future__ import braces

314



那是邪恶的。 :) - Jason Baker
>>> from __future__ import braces File“<stdin>”,第1行语法错误:不是偶然:P - Benjamin W. Smith
这是亵渎神明! - Berk D. Demir
我认为我们可能在这里遇到语法错误,不应该是“来自 __过去__ 进口括号“? - Bill K
从 __cruft__ 进口大括号 - Phillip B Oldham


切片运算符中的步骤参数。例如:

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]

305



在我看来,更清楚的是reverse()函数。 >>> list(reverse(range(4)))[3,2,1,0] - Christian Oudard
那么如何以更好的方式写出“this i a string”[:: - 1]?逆转似乎没有帮助 - Berry Tsakala
reverse()的问题是它返回一个迭代器,所以如果你想保留反转序列的类型(元组,字符串,列表,unicode,用户类型......),你需要一个额外的步骤来转换它。 - Rafał Dowgird
def reverse_string(string):return string [:: - 1] - pi.
@pi我想如果有人知道你可以定义reverse_string,那么你可以在你的代码中保留[:: - 1],并且对它的含义和感觉是合适的。 - physicsmichael


装饰

装饰 允许将函数或方法包装在另一个可以添加功能,修改参数或结果等的函数中。您可以在函数定义上方一行编写装饰器,以“at”符号(@)开头。

示例显示a 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

289



在定义装饰器时,我建议用@decorator装饰装饰器。它创建了一个装饰器,在对其进行内省时保留了函数签名。更多信息: phyast.pitt.edu/~micheles/python/documentation.html - sirwart
这是一个隐藏的功能? - Vetle
好吧,它在大多数简单的Python教程中都没有出现,在我开始使用Python之后很长一段时间我偶然发现了它。这就是我所谓的隐藏功能,与此处的其他热门帖子差不多。 - DzinX
vetler,这些问题要求“Python编程语言鲜为人知但有用的功能”。你如何衡量'鲜为人知但有用的功能'?我是说这些响应中的任何一个隐藏了什么功能? - Johnd
@vetler这里的大部分内容都很难“隐藏”。 - Humphrey Bogart