提问



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



  • 尝试限制Python核心的答案。

  • 每个答案都有一个功能。

  • 举例说明该功能,而不仅仅是文档链接。

  • 使用标题作为第一行标记要素。



答案的快速链接:




  • 参数拆包

  • 括号

  • 链接比较运算符

  • 装修

  • 默认参数陷阱/可变默认参数的危险

  • 描述符

  • 字典默认.get

  • 文档字符串测试

  • 省略号切片语法

  • 枚举

  • 对于/ELSE

  • 作为iter()参数的函数

  • 生成器表达式

  • import this

  • 就地价值交换

  • 列出步进

  • __missing__ items

  • 多行正则表达式

  • 命名字符串格式

  • 嵌套列表/生成器理解

  • 运行时的新类型

  • .pth files

  • ROT13编码

  • 正则表达式调试

  • 发送给发电机

  • Interactive Interactiveter中的标签完成

  • 三元表达

  • try/except/else

  • 开箱+ print()功能

  • with陈述


最佳参考


链接比较运算符:



>>> 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 < xTrue,然后比较True < 10,这也是True,那么不,那是真的不是会发生什么(见最后一个例子。)它实际上转化为1 < x and x < 10x < 10 and 10 < x * 10 and x*10 < 100,但键入较少,每个术语只评估一次。

其它参考1


获取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)

其它参考2


枚举


用枚举包装一个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
>>>


参考文献:



  • Python教程循环技术

  • Python docs内置函数 - enumerate

  • PEP 279


其它参考3


创建生成器对象 [123] [124] [125]


如果你写


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)

其它参考4


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


例如:


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


iter(callable, until_value)函数重复调用callable并产生其结果,直到返回until_value

其它参考5


小心使用可变的默认参数


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

其它参考6


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


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

其它参考7


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


from __future__ import braces

其它参考8


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


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]

其它参考9


装修


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


示例显示了一个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

其它参考10


for ... else语法(参见http://docs.python.org/ref/for.html)[128]


for i in foo:
    if i == 0:
        break
else:
    print("i was never 0")


除非调用中断,否则else块通常在for循环结束时执行。


上面的代码可以模拟如下:


found = False
for i in foo:
    if i == 0:
        found = True
        break
if not found: 
    print("i was never 0")

其它参考11


从2.5开始,dicts有一个特殊的方法__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]}


我建议将这些dicts转换为常规dicts,然后再将它们传递给不期望这些子类的函数。很多代码使用d[a_key]并捕获KeyErrors以检查是否存在可以向dict添加新项的项。

其它参考12


就地价值交换


>>> a = 10
>>> b = 5
>>> a, b
(10, 5)

>>> a, b = b, a
>>> a, b
(5, 10)


赋值的右侧是一个创建新元组的表达式。赋值的左侧立即将该(未引用的)元组解包为名称ab


赋值后,新元组未被引用并标记为垃圾回收,并且已交换绑定到ab的值。


如关于数据结构的Python教程部分所述,[129]



  请注意,多重赋值实际上只是元组打包和序列解包的组合。


其它参考13


可读正则表达式


在Python中,您可以将正则表达式拆分为多行,命名匹配并插入注释。


示例详细语法(从Dive到Python):[130]


>>> 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)[131]


>>> 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})$"

其它参考14


功能参数解包


您可以使用***将列表或字典作为函数参数解压缩。


例如:


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)


非常有用的快捷方式,因为列表,元组和dicts被广泛用作容器。

其它参考15


当您在代码文件的顶部使用正确的编码声明时,ROT13是源代码的有效编码:


#!/usr/bin/env python
# -*- coding: rot13 -*-

cevag "Uryyb fgnpxbiresybj!".rapbqr("rot13")

其它参考16


以完全动态的方式创建新类型



>>> NewType = type("NewType", (object,), {"x": "hello"})
>>> n = NewType()
>>> n.x
"hello"


这完全一样


>>> class NewType(object):
>>>     x = "hello"
>>> n = NewType()
>>> n.x
"hello"


可能不是最有用的东西,但很高兴知道。


修改:固定新类型的名称,应该NewTypeclass语句完全相同。


修改:调整标题以更准确地描述该功能。

其它参考17


上下文管理器和with语句


在PEP 343中引入,上下文管理器是一个对象,它充当一系列语句的运行时上下文。[132] [133]


由于该功能使用了新的关键字,因此逐渐引入:它通过__future__指令在Python 2.5中提供。 Python 2.6及更高版本(包括Python 3)默认可用。[134]


我已经使用了with语句,因为我觉得它是一个非常有用的结构,这是一个快速演示:[135]


from __future__ import with_statement

with open('foo.txt', 'w') as f:
    f.write('hello!')


幕后发生的事情是,with语句调用文件对象上的特殊__enter____exit__方法。如果有任何异常,异常细节也会传递给__exit__是从with语句体中提出的,允许在那里进行异常处理。[136]


在这种特殊情况下,它为您做的是它保证在执行超出with套件范围时关闭文件,无论是否正常发生或是否抛出异常。它基本上是一种抽象掉常见异常处理代码的方法。


其他常见用例包括使用线程和数据库事务进行锁定。

其它参考18


字典有一个get()方法



字典有一个get()方法。如果你执行d [[key]]并且键不存在,则会出现异常。如果你执行d.get(key),如果key不存在,则返回None。您可以添加第二个参数来获取该项而不是None,例如:d.get(key,0)。


这对于添加数字这样的事情非常有用:


sum[value] = sum.get(value, 0) + 1

其它参考19


描述符



它们是一大堆核心Python功能背后的神奇之处。


当您使用点线访问来查找成员(例如,x.y)时,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)


你就像内置属性()一样使用它:


class MyClass(object):
    @Property
    def foo(self):
        return "Foo!"


Python中使用描述符来实现属性,绑定方法,静态方法,类方法和插槽等。理解它们可以很容易地看出为什么许多以前看起来像Python怪癖的东西都是它们的样子。


Raymond Hettinger有一个很好的教程,比我更好地描述它们。[137]

其它参考20


有条件分配


x = 3 if (y == 1) else 2


它完全听起来像:如果y为1,则将3分配给x,否则将2分配给x。请注意,parens不是必需的,但我喜欢它们以便于阅读。如果你有更复杂的东西,你也可以链接它:


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是两个类。

其它参考21


Doctest:同时进行文档和单元测试。



从Python文档中提取的示例:[138]


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

其它参考22


命名格式



%-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()也是一个字典,你可以简单地将它作为一个字典传递,并从你的局部变量中获得%-substitions。我认为这是不赞成的,但简化了事情..


新样式格式


>>> print("The {foo} is {bar}".format(foo='answer', bar=42))

其它参考23


要添加更多python模块(特别是第三方模块),大多数人似乎都使用PYTHONPATH环境变量,或者在他们的site-packages目录中添加符号链接或目录。另一种方法是使用* .pth文件。这是官方python doc的解释:



  最方便的方式[[修改
  python的搜索路径]]是添加路径
  配置文件到目录
  那已经在Python的路上,
  通常到.../site-packages/
  目录。路径配置文件
  扩展名为.pth,每个
  line必须包含单个路径
  将附加到sys.path。 (因为
  新路径附加到
  sys.path,添加的模块
  目录不会覆盖标准
  模块。这意味着你不能使用它
  安装固定机制
  标准模块的版本。)


其它参考24


例外 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语句保护的代码未引发的异常。


见http://docs.python.org/tut/node10.html [139]

其它参考25


重新提出异常:


# 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一样使用traceback模块进行打印。

其它参考26


主要消息:)


import this
# btw look at this module's source :)





解cyphered:[140]



  Tim Peters的Python之禅

  
  美丽胜过丑陋。

  显式优于隐式。

  简单比复杂更好。

  复杂比复杂更好
  Flat比嵌套好。

  稀疏比密集更好
  可读性很重要。

  特殊情况不足以打破规则。

  虽然实用性超过了纯度
  错误不应该默默地传递。

  除非明确沉默。

  面对模棱两可,拒绝猜测的诱惑。
  应该有一个 - 最好只有一个 - 明显的做法
  虽然这种方式起初可能并不明显,除非你是荷兰人
  现在总比没有好。

  虽然现在永远不会比正确更好。

  如果实施很难解释,这是一个坏主意。

  如果实现很容易解释,那可能是个好主意
  命名空间是一个很棒的主意 - 让我们做更多的事情吧!


其它参考27


交互式翻译选项卡完成


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环境变量。

其它参考28


嵌套列表推导和生成器表达式:


[(i,j) for i in range(3) for j in range(i) ]    
((i,j) for i in range(4) for j in range(i) )


这些可以取代大块的嵌套循环代码。

其它参考29


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}


标准库参考的更多细节:设置类型[141]