Python 的隐藏特性[关闭]

2024-12-04 08:56:00
admin
原创
169
摘要:问题描述:Python 编程语言有哪些鲜为人知但有用的功能?尝试将答案限制在 Python 核心内。每个答案一个特征。给出该功能的示例和简短描述,而不仅仅是文档链接。使用标题作为第一行来标记该特征。答案的快速链接:论证解析牙套链接比较运算符装饰器默认参数陷阱/可变默认参数的危险描述符字典默认.get值文档字符...

问题描述:

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

  • 尝试将答案限制在 Python 核心内。

  • 每个答案一个特征。

  • 给出该功能的示例和简短描述,而不仅仅是文档链接。

  • 使用标题作为第一行来标记该特征。

答案的快速链接:

  • 论证解析

  • 牙套

  • 链接比较运算符

  • 装饰器

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

  • 描述符

  • 字典默认.get

  • 文档字符串测试

  • 省略号切片语法

  • 枚举

  • 否则

  • 作为 iter() 参数的函数

  • 生成器表达式

  • import this

  • 就地价值交换

  • 列表步进

  • __missing__项目

  • 多行正则表达式

  • 命名字符串格式

  • 嵌套列表/生成器推导

  • 运行时的新类型

  • .pth文件

  • ROT13 编码

  • 正则表达式调试

  • 发送到生成器

  • 交互式解释器中的 Tab 补全

  • 三元表达式

  • try/except/else

  • 解包+print()功能

  • with陈述


解决方案 1:

链接比较运算符:

>>> 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 < 10x < 10 and 10 < x * 10 and x*10 < 100,但输入更少,并且每个项仅计算一次。

解决方案 2:

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

正则表达式是 Python 的一个重要特性,但调试它们却很麻烦,而且很容易出错。

幸运的是,通过将未记录的、实验性的、隐藏的标志re.DEBUG(实际上是 128)传递给re.compile, python 可以打印正则表达式解析树。

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

一旦你理解了语法,你就可以发现自己的错误。我们可以看到我忘记对 in 进行转义[][/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)

解决方案 3:

枚举

用枚举包装一个可迭代对象,它将产生该项目及其索引。

例如:


>>> 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 文档—内置函数—enumerate

  • PEP 279

解决方案 4:

创建生成器对象

如果你写

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)

解决方案 5:

iter() 可以接受可调用参数

例如:

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

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

解决方案 6:

谨慎使用可变的默认参数

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

解决方案 7:

将值发送到生成器函数。例如具有以下函数:

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

解决方案 8:

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

from __future__ import braces

解决方案 9:

切片运算符中的 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]

解决方案 10:

装饰器

装饰器允许将一个函数或方法包装在另一个函数中,从而可以添加功能、修改参数或结果等。您可以在函数定义上方的一行上写装饰器,以“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

解决方案 11:

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

解决方案 12:

从 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在called中还有一个 dict 子类defaultdict,它的功能几乎相同,但是对于不存在的项目,它调用一个不带参数的函数:

>>> from collections import defaultdict
>>> m = defaultdict(list)
>>> m["foo"].append(1)
>>> m["foo"].append(2)
>>> dict(m)
{'foo': [1, 2]}

我建议在将这些字典传递给不需要此类子类的函数之前,先将其转换为常规字典。许多代码使用d[a_key]并捕获 KeyError 来检查某个项是否存在,这将向字典中添加新项。

解决方案 13:

就地值交换

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

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

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

分配之后,新的元组将不再被引用,并被标记为垃圾收集,并且绑定到的值ab被交换。

正如Python 教程中有关数据结构的部分所述,

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

解决方案 14:

可读的正则表达式

在 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>w+)')
>>> 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})$"

解决方案 15:

函数参数解包

*您可以使用和将列表或字典解包为函数参数**

例如:

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)

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

解决方案 16:

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

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

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

解决方案 17:

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

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

这与

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

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

编辑:固定新类型的名称,应该NewType与 with 语句完全相同class

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

解决方案 18:

上下文管理器和“ with”语句

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

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

我经常使用“with”语句,因为我认为它是一个非常有用的结构,这里有一个快速演示:

from __future__ import with_statement

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

幕后发生的事情是,“with”语句调用文件对象上的特殊__enter__方法__exit__。如果从 with 语句主体引发任何异常,异常详细信息也会传递给__exit__,以便在那里进行异常处理。

在这个特定情况下,它的作用是保证当执行超出套件范围时文件会被关闭with,无论这种情况是正常发生还是引发了异常。这基本上是一种抽象出常见异常处理代码的方法。

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

解决方案 19:

字典有一个 get() 方法

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

它对于诸如加法之类的操作非常有用:

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

解决方案 20:

描述符

它们是 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)

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

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

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

Raymond Hettinger 有一个出色的教程,对它们的描述比我好得多。

解决方案 21:

条件赋值

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

解决方案 22:

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

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

解决方案 23:

命名格式

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

解决方案 24:

要添加更多 Python 模块(尤其是第三方模块),大多数人似乎都会使用 PYTHONPATH 环境变量,或者在站点包目录中添加符号链接或目录。另一种方法是使用 *.pth 文件。以下是官方 Python 文档的解释:

“[修改 Python 搜索路径] 最方便的方法是将路径配置文件添加到 Python 路径上已有的目录,通常是 .../site-packages/ 目录。路径配置文件的扩展名为 .pth,每行必须包含一个将附加到 sys.path 的路径。(由于新路径附加到 sys.path,因此添加的目录中的模块不会覆盖标准模块。这意味着您不能使用此机制来安装标准模块的固定版本。)”

解决方案 25:

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

解决方案 26:

重新引发异常

# 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”模块打印它。

解决方案 27:

主要信息:)

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

解密:

《Python 之禅》,作者:Tim Peters

美丽胜过丑陋。

明确胜过隐晦。

简单胜过复杂。

复杂胜过繁琐。

扁平胜过嵌套。稀疏胜过

密集。

可读性很重要。

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

实用性胜过纯粹性。

错误永远不应该默默无闻。

除非明确地被压制。

面对歧义,拒绝猜测的诱惑。应该有一种——最好只有一种——明显的方法来做到这一点。

尽管除非你是荷兰人,否则这种方法一开始可能并不明显。

现在总比没有好。

尽管没有往往比现在更好如果

实现很难解释,那不是一个好主意。

如果实现很容易解释,那可能是一个好主意。

命名空间是一个非常棒的主意——让我们多做些这样的事情吧!

解决方案 28:

交互式解释器 Tab 键补全

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

解决方案 29:

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

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

它们可以替代大量嵌套循环代码。

解决方案 30:

内置运算符重载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}

标准库参考中的更多详细信息:集合类型

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用