Python编程语言鲜为人知但有用的功能是什么?

尝试将答案限制在Python核心。
每个答案一个功能。功能的示例和简短说明,而不仅仅是指向文档的链接。
以标题作为第一行来标记功能。

快速答案答案:

参数解压缩
大括号
链式比较运算符
修饰符
默认参数陷阱/可变默认参数的危险
描述符
字典默认.get
Docstring测试
省略号切片语法
枚举
For / else
作为iter()参数的功能
生成器表达式
import this
就地值交换
列表步进
__missing__项目
多行正则表达式
命名字符串格式
嵌套列表/生成器理解
运行时的新型
.pth文件
ROT13编码
正则表达式调试
发送到Ge nerators
交互式解释器中的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,但是键入较少,并且每个术语仅计算一次。

评论


这很有帮助。对于所有语言,它应该是标准的。可悲的是,事实并非如此。

–stalepretzel
08-10-17在2:23

您应该添加一些返回false的示例。如>>> 10
–鞋带
08年11月21日在14:34

这也适用于其他比较运算符,这就是为什么人们有时会惊讶为什么代码([5]中的5为True)为False(但明确地对像布尔值这样的布尔值进行测试是不合逻辑的)的原因。

–里程
09年3月2日在18:35

很好,但是要注意同等的优先级,例如“ in”和“ =”。 “ B中的A == D中的C”表示“(B中的A)和(B == C)和(D中的C)”,这可能是意外的。

–查尔斯·梅里亚姆
2010-2-10在10:28

Azafe:Lisp的比较自然是这样的。这不是特殊情况,因为没有其他(合理的)解释方式(<1 x 10)。您甚至可以将它们应用于单个参数,例如(= 10):cs.cmu.edu/Groups/AI/html/hyperspec/HyperSpec/Body / ...

–肯
2010年5月26日20:31

#2 楼

获取python regex解析树以调试regex。

正则表达式是python的一个强大功能,但是调试它们可能很麻烦,而且很容易使regex错误。

幸运的是,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)


评论


除了使用正则表达式解析HTML之外,这都是缓慢而痛苦的。甚至内置的“ html”解析器模块也不会使用正则表达式来完成工作。而且,如果html模块不令人满意,那么有很多XML / HTML解析器模块可以完成这项工作,而无需重新发明。

–BatchyX
2010-3-26在21:59

指向有关输出语法的文档的链接会很棒。

–人
2010-4-15的16:13

这应该是Python的正式部分,而不是实验性的部分。RegEx总是很棘手,并且能够跟踪正在发生的事情确实很有帮助。

–凯蒂
2010年7月14日在23:27

#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


评论


令我惊讶的是,在有关python列表的教程中并未常规涵盖这一点。

– ra龙
08-11-27在4:06

一直以来,我都是这样编码的:对于range(len(a))中的i:...然后使用a [i]获取当前项。

–费尔南多·马丁(Fernando Martin)
09年6月22日在16:35

@Berry Tsakala:据我所知,它尚未被弃用。

– JAB
09年7月27日在17:36

天哪,这太棒了。对于xrange(len(a))中的i:一直是我最不喜欢的python习惯用法。

–人
2010-4-15的16:22

枚举可以从任意索引开始,不必为0。例如:“对于i,enumerate(list,start = 1)中的项目:print i,item”将从1开始,而不是0。

– dmitry_romanov
2011年8月22日11:00



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


评论


您也可以使用嵌套列表推导,是吗?

–shapr
09年5月5日在16:53

特别值得注意的是节省了内存开销。值是按需计算的,因此您永远不会在内存中获得列表理解的全部结果。如果您以后仅遍历列表理解的一部分,则这是特别理想的。

–saffsd
09年5月17日在2:41

这不是特别“隐藏”的imo,但值得注意的是,您无法倒带生成器对象,而可以在列表上重复任意次。

–怀疑
2010-4-15的16:26

发电机的“不倒带”功能可能会引起一些混乱。具体来说,如果您打印生成器的内容以进行调试,然后在以后使用它来处理数据,则它将不起作用。产生的数据由print()消耗,然后不能用于正常处理。这不适用于列表推导,因为它们已完全存储在内存中。

– johntellsall
2010年5月28日在16:01

类似的答案(dup?):stackoverflow.com/questions/101268/hidden-features-of-python / ...但是,请注意,我在此处链接的答案提到了有关发电机功率的非常好的介绍。您真的应该检查一下。

– DenilsonSáMaia
2010年8月4日在20:21



#5 楼

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

例如:

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


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

评论


作为python的新手,您能否解释为什么在这里必须使用lambda关键字?

– SiegeX
11年8月12日在20:29

@SiegeX没有lambda,f.read(1)将在传递给iter函数之前进行评估(返回字符串)。相反,lambda创建一个匿名函数并将其传递给iter。

– jmilloy
2011年12月31日下午16:43

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


评论


那绝对是最讨厌的隐藏功能之一。我不时遇到它。

–斯托恩·马雷克(Torten Marek)
08-09-23在17:42

当我知道默认参数存在于作为函数属性的元组中时,我发现这很容易理解。 foo.func_defaults。作为元组,这是不可变的。

–罗伯特·罗斯尼(Robert Rossney)
09年11月5日在20:43

@grayger:在执行def语句时,其参数由解释器评估。这将创建(或重新绑定)名称到代码对象(函数集)。但是,默认参数在定义时被实例化为对象。这适用于默认对象的任何时候,但仅当对象可变时才有意义(公开可见的语义)。尽管显然可以为任何调用覆盖默认参数名称,也可以重新定义整个功能,但无法在函数的闭包中重新绑定该默认参数名称。

–吉姆·丹尼斯(Jim Dennis)
2010年6月24日4:20



@Robert当然,参数元组可能是不可变的,但是它指向的对象不一定是不可变的。

–池
2010年7月22日14:59

一个快速的技巧可以使您的初始化过程更短:x = x或[]。您可以使用它代替2行if语句。

–戴夫·曼科夫
2010年8月27日在2:58

#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


评论


同意让我们将其视为Python的隐藏功能的讨厌示例:)

–拉法·道吉德(RafałDowgird)
08-09-19 at 13:41

用其他语言,我相信这种神奇的装置被称为“变量”。

– Finnw
09年5月5日17:49

协程应该是协程,并且发生器也应该自己,不要混合。超级链接以及此处的讨论和示例:dabeaz.com/coroutines

–u0b34a0f6ae
09年8月23日在21:39

@finnw:该示例实现了类似于变量的功能。但是,该功能可以通过多种其他方式使用...与变量不同。同样明显的是,可以使用对象(尤其是一个类,隐含Python的调用方法)来实现类似的语义。

–吉姆·丹尼斯(Jim Dennis)
2010年6月24日在4:07

对于从未见过(可能不会理解)例程的人来说,这是一个微不足道的例子。一个实现运行平均值而又不存在总和变量溢出风险的示例是一个很好的例子。

– Prashant Kumar
2011年6月11日6:16



#8 楼

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

from __future__ import braces


评论


太恶心了:)

–杰森·贝克(Jason Baker)
08/09/22在4:32

>>>从__future__导入大括号文件“ ”,第1行语法错误:没有机会:P

–本杰明·史密斯(Benjamin W. Smith)
08-09-22在19:55



这是亵渎!

– Berk D. Demir
09年3月22日在13:24

我认为我们这里可能有一个语法错误,那不应该是“来自__past__导入括号”吗?

– Bill K
09年4月29日在21:26



从__cruft__导入括号

– Phillip B Oldham
09年6月18日在7:16



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


评论


在我看来,reversed()函数更加清晰。 >>> list(reversed(range(4)))[3,2,1,0]

–克里斯蒂安·奥达德(Christian Oudard)
09年1月2日在18:35

那么如何以更好的方式写“ this i a string” [::-1]?扭转似乎没有帮助

–贝里·察卡拉(Berry Tsakala)
09年6月21日在20:56

reversed()的问题在于它返回一个迭代器,因此,如果您想保留反向序列的类型(元组,字符串,列表,unicode,用户类型...),则需要额外的步骤将其转换回。

–拉法·道吉德(RafałDowgird)
09年6月24日在14:08

def reverse_string(string):返回字符串[::-1]

– Pi。
2010-2-3在9:42

@pi我认为,如果您足够了解如何定义reverse_string,那么您可以将[::-1]留在代码中,并对其含义和适当的感觉感到满意。

–physicsmichael
2010年5月28日下午6:17

#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


评论


定义装饰器时,建议使用@decorator装饰装饰器。它创建一个装饰器,当对它进行自省时,该装饰器将保留函数签名。此处提供更多信息:phyast.pitt.edu/~micheles/python/documentation.html

– ir不休
08/09/22在15:53

这是如何隐藏的功能?

–车
08年10月2日在13:52

嗯,在大多数简单的Python教程中都没有,它在我开始使用Python很久以后才偶然发现。这就是我所说的隐藏功能,与此处的其他热门帖子大致相同。

– DzinX
08年10月3日在6:51

兽医,这些问题要求“ Python编程语言鲜为人知但有用的功能”。您如何衡量“鲜为人知但有用的功能”?我的意思是这些响应中有什么是隐藏的功能?

–约翰德
09年5月23日在21:14

@vetler这里的大多数事情几乎都不是“隐藏”的。

–汉弗莱·鲍嘉(Humphrey Bogart)
2010-4-6 15:33

#11 楼

for ... else语法(请参阅http://docs.python.org/ref/for.html)

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


“ else”块通常是在for循环的末尾执行,除非调用break。

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

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


评论


我认为for / else语法很尴尬。如果从未执行循环的主体,就好像应该执行else子句一样“感觉”到。

– codeape
08-10-10在6:44

啊。没见过那个!但是我必须说这有点用词不当。谁会期望else块仅在break永远不会执行时执行?我同意codeape:看来为空foo输入了else。

–达伦·托马斯(Daren Thomas)
08-10-13在17:31

好像关键字应该是最后,不是其他

–贾亚罗
09-6-25 at 16:31

除非最终已经以始终执行该套件的方式使用过。

–罗杰·佩特
09年6月27日在22:34

绝对不应该是“ else”。也许是'then'或其他东西,然后是'else',以表示从未执行过循环。

– Tor Valamo
2010年1月10日下午16:26

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

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


我建议在将此类dict传递给不希望此类子类的函数之前,将其转换为常规dict。许多代码使用d[a_key]并捕获KeyErrors来检查是否存在将向dict添加新项目的项目。

评论


我更喜欢使用setdefault。 m = {}; m.setdefault('foo',1)

–灰色
09年12月3日,3:03

@grayger表示此m = {}; m.setdefault('foo',[])。append(1)。

–克里斯蒂安·丘皮图
2010年5月28日在18:53

但是,在某些情况下,通过defaultdict非常方便。例如,该函数可能会遍历该值,并且它适用于未定义键而无需额外代码的情况,因为默认值为空列表。

–玛丽安
2010年5月29日下午2:22

在某些情况下,defaultdict比setdefault更好,因为除非缺少键,否则它不会创建默认对象。无论是否丢失,setdefault都会创建它。如果默认对象的创建成本很高,那么这可能会降低性能-仅通过更改所有setdefault调用,我就可以使一个程序获得不错的加速。

– Whatang
2011年2月12日在17:56

在其他情况下,defaultdict也比setdefault方法更强大。例如,对于一个计数器,dd = collections.defaultdict(int)... dd [k] + = 1与d.setdefault(k,0)+ = 1。

–迈克·格雷厄姆(Mike Graham)
2011年4月10日21:56

#13 楼

就地值交换

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

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


赋值的右侧是创建新元组的表达式。分配的左侧立即将(未引用的)元组解压缩为名称ab

分配后,新的元组将被取消引用并标记为垃圾回收,并且值绑定到ab已被交换。

如数据结构的Python教程部分所述,


请注意,多次赋值实际上只是元组打包的组合和顺序拆包。


评论


这会比传统方式使用更多的实际内存吗?我猜想是因为您正在创建一个元组,而不只是一个交换变量

–内森(Nathan)
2010-6-14 14:24



它不会使用更多的内存。它使用更少。.我只是用两种方式编写它,然后反编译了字节码。.编译器进行了优化,如您所愿。 dis结果表明它正在设置var,然后是ROT_TWOing。 ROT_TWO的意思是“交换两个最顶部的堆栈变量”……实际上非常时髦。

–皇家
2010年7月13日在22:41



您还会无意间指出Python的另一个不错的功能,那就是您可以通过用逗号分隔项目来隐式创建项目元组。

– asmeurer
2010-12-28在5:12

Dana the Sane:Python中的赋值是一个语句,而不是一个表达式,因此,如果=具有更高的优先级,则该表达式将无效(即,它被解释为a,(b = b),a)。

– hbn
2011年1月23日在21:27



这是我在这里读到的最少隐藏的功能。很好,但是在每个Python教程中都有明确描述。

– Thiago Chaves
2011年5月25日在16:56



#14 楼

可读的正则表达式

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

示例详细语法(从Dive到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})$"


评论


我不知道我是否真的考虑过Python功能,大多数RE引擎都有详细选项。

–杰里米
08-09-21在20:44

是的,但是因为您无法在grep或大多数编辑器中做到这一点,所以很多人都不知道它的存在。其他语言具有等效功能的事实并不意味着它不是python的有用且鲜为人知的功能

–马克·贝克(Mark Ba​​ker)
08-10-17在9:08

在一个具有许多优化正则表达式的大型项目中(请阅读:针对机器而非人类进行了优化),我咬紧牙关,将所有这些都转换为冗长的语法。现在,将新开发人员介绍给项目变得更加容易。从现在开始,我们在每个项目上强制执行详细的RE。

– Berk D. Demir
09年3月22日在13:18

我只想说:数百=“((CM | CD | D?C {0,3})”#900(CM),400(CD)等。该语言已经有一种给事物命名的方法,添加注释的方法以及组合字符串的方法。为什么在这里使用特殊的库语法来处理已经很好的语言呢?这似乎直接违背了玻璃市的Epigram 9。

–肯
09年5月5日在18:19

@Ken:regex可能并不总是直接位于源代码中,可以从设置或配置文件中读取。允许注释或仅附加空白(出于可读性)可能会很有帮助。

–罗杰·佩特
09年6月27日在22:30

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


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

评论


*也称为splat运算符

–加百列
2010年7月15日在7:03

我喜欢这个功能,但是pylint并不令人遗憾。

–斯蒂芬·保罗(Stephen Paulger)
2011-3-25 14:46

皮林特的建议不是法律。从2.3开始不推荐使用apply(callable,arg_seq,arg_map)。

–扬·韦尼尔(Yann Vernier)
2011年5月4日,11:50

皮林特的建议可能不是法律,但这是很好的建议。过度沉迷于此类代码的调试代码纯粹是地狱。如原始张贴者所述,这是一个有用的快捷方式。

–安德鲁(Andrew)
11年11月21日在19:40

我看到它曾经在代码中使用过,不知道它做了什么。不幸的是,很难用Google搜索“ Python **”

–弗雷泽·格雷厄姆(Fraser Graham)
2012年1月13日23:33

#16 楼

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

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

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


评论


大!请注意,字节字符串是按字面值获取的,而Unicode字符串则是被解码的:尝试cevag h“ Uryyb fgnpxbiresybj!”

–u0b34a0f6ae
09年10月4日,1:12

不幸的是,它已从py3k中删除

–mykhal
09年12月19日在18:33

这对于绕过防病毒程序很有用。

– L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
2010年5月13日在20:37

这与编码无关,只是用威尔士语编写的Python。 :-P

–奥利维尔·威尔第(Olivier Verdier)
2010年7月14日在9:45

Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn!

– Manuel Ferreria
2010年8月26日在22:08

#17 楼

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

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




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


完全相同最有用的东西,但很高兴知道。

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

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

评论


这具有很大的实用价值,例如JIT ORM

– Mark Cidade
08-09-22在18:44

我用它来基于动态输入生成HTML-Form类。非常好!

– Pi。
09年3月18日在16:00

注意:所有类均在运行时创建。因此,您可以在条件或函数中使用“ class”语句(对于创建类族或充当闭包的类非常有用)。 “类型”带来的改进是能够整齐地定义动态生成的一组属性(或基数)的能力。

– spookylukey
2010年1月1日14:02

您还可以使用空白字符串创建匿名类型,例如:type('',(object,),{'x':'blah'})

– bluehavana
2011年6月16日23:49

对于代码注入可能非常有用。

– Avihu Turzion
2011年7月18日在8:49

#18 楼

上下文管理器和“ 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”语句调用了文件对象上的特殊__enter____exit__方法。如果在with语句主体中引发了任何异常,则异常详细信息也将传递给__exit__,从而允许在该处进行异常处理。

在这种特殊情况下,这为您提供了保证,以确保该文件当执行超出with套件的范围时,无论执行是正常发生还是引发异常,都会关闭该命令。从根本上讲,这是一种抽象通用异常处理代码的方法。

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

评论


我不会批准从将来导入任何内容的代码审查。这些功能可爱而不是有用,而且通常只会使Python新手感到困惑。

–付费书呆子
09年5月11日下午4:36

是的,嵌套作用域和生成器之类的“可爱”功能最好留给知道自己在做什么的人使用。任何想要与未来版本的Python兼容的人。对于嵌套作用域和生成器,Python的“未来版本”分别表示2.2和2.5。对于with语句,Python的“未来版本”表示2.6。

–克里斯·B。
09年6月17日在18:33

这可能不言而喻,但是使用python v2.6 +,您不再需要从将来导入。现在是头等关键字。

– fitzgeraldsteele
09年11月23日14:28

在2.7中,您可以有多个withs :),其中open('filea')作为filea,open('fileb')作为fileb:...

–奥斯汀·理查森(Austin Richardson)
2010年7月14日在21:50



@奥斯汀,我无法使该语法在2.7上工作。但这确实起作用:将open('filea')作为filea,将open('fileb')作为fileb:...

– Wim
2011年7月14日在5:29

#19 楼

字典具有get()方法

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

非常适合诸如加数字之类的事情:

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

评论


另外,请检出setdefault方法。

–达伦·托马斯(Daren Thomas)
08-10-13在17:29

另外,请检出collections.defaultdict类。

– jfs
08年11月23日在10:35

如果您使用的是Python 2.7或更高版本,或者3.1或更高版本,请在collections模块中检出Counter类。 docs.python.org/library/collections.html#collections.Counter

– Elias Zamaria
2010-10-12 1:33



哦,老兄,这整个过程我一直在做get(key,None)。不知道默认情况下没有提供。

–乔丹·瑞特(Jordan Reiter)
2011年10月19日下午16:41

#20 楼

描述符

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


,您将像内置property()一样使用它:

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


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

Raymond Hettinger的教程很棒,比描述它们做得更好我知道。

评论


这是装饰的副本,不是吗? (stackoverflow.com/questions/101268/…)

–gecco
2011年10月20日在19:18

不,装饰器和描述符是完全不同的东西,尽管在示例代码中,我正在创建描述符装饰器。 :)

–尼克·约翰逊(Nick Johnson)
11-10-20在23:11

另一种方法是使用lambda:foo = property(lambda self:self .__ foo)

–彼得·彼得森(Pete Peterson)
2011-11-2 2:50



@PetePeterson是的,但是属性本身是通过描述符实现的,这就是我的帖子的重点。

–尼克·约翰逊(Nick Johnson)
2011年11月2日,下午3:40

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

评论


任务不是特别的部分。您可以轻松地执行以下操作:如果(y == 1)否则返回3。

–布赖恩
08年9月9日在5:39

这种替代方法是我第一次看到混淆的Python。

–克雷格·麦昆(Craig McQueen)
09年9月9日在4:18

Kylebrooks:在这种情况下,布尔运算符不会短路。如果bool(3)== False,它将只求值为2。

–RoadieRich
09年7月12日在0:23

这种向后风格的编码使我感到困惑。类似x =((y == 1)?3:2)对我来说更有意义

– mpen
09-10-20在7:12

我觉得与@Mark相反,C风格的三元运算符总是让我感到困惑,是在错误条件下评估的是右侧还是右侧?我非常喜欢Python的三元语法。

–杰弗里·哈里斯(Jeffrey Harris)
09年12月3日在16:51

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


评论


Doctests当然很酷,但是我真的不喜欢您必须键入的所有代码来测试某些东西应该引发异常

–TM。
09年10月10日在22:55

Doctests被高估并污染文档。您多久测试一次没有任何setUp()的独立功能?

–付费书呆子
2009年5月11日4:34

谁说您无法在doctest中进行设置?编写一个生成上下文并返回locals()的函数,然后在doctest中执行locals()。update(setUp())= D

–贾亚罗
09年12月2日在17:27

如果独立功能需要设置,则很有可能将其与一些无关的内容分离或放入类中。然后可以在类方法doctests中重用类doctest命名空间,因此它有点像setUp,仅DRY并且可读。

–安迪·米哈伊连科(Andy Mikhaylenko)
2010-4-17 14:38

“您多久测试一次独立功能”-很多。我发现在决定外观时,doctest通常会从设计过程中自然而然地出现。

– Gregg Lind
2011年2月11日下午4:45

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


由于本地人( )也是一本字典,您可以简单地将其作为字典传递,并从您的局部变量获取%-substitions。我认为这是不满意的,但可以简化。.

新样式格式设置

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


评论


将逐步淘汰,并最终替换为字符串的format()方法。

–康斯坦丁
08-10-5在9:41

命名格式对翻译人员非常有用,因为他们倾向于只看到格式字符串而没有上下文的变量名

–pixelbeat
08-10-12在22:45

似乎可以在python 3.0.1中使用(需要在打印调用周围添加括号)。

– Pasi Savolainen
09年7月1日在11:32

哈希,是吗?我知道你来自哪里。

–圆滑的
09年11月14日13:45

%s格式不会被淘汰。 str.format()当然是更pythonic的,但是对于简单的字符串替换来说实际上慢了十倍。我相信%s格式化仍是最佳做法。

–肯尼斯·雷茨(Kenneth Reitz)
2010年7月14日在12:34



#24 楼

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


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


评论


我从未在setuptools的site-packages目录中的.pth文件与该想法之间建立连接。太棒了

–戴夫·帕拉
2010年7月14日在2:07

#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

评论


+1太棒了。如果在没有输入任何异常块的情况下执行try块,则将输入else块。然后,当然,执行了finally块

–inspectorG4dget
2010-09-22在4:49

我终于明白了为什么“ else”在那里!谢谢。

–taynaron
2010-12-7 18:26

继续使用会更有意义,但我想它已经被使用;)

–PawełPrażak
2010-12-16 17:58

请注意,在旧版本的Python2上,您不能同时具有try:块的else:和finally:子句

–凯文·霍恩(Kevin Horn)
2011年5月26日在22:49

正如Kevin Horn提到的,@PawełPrażak,此语法是在Python的初始发行版之后引入的,向现有语言添加新的保留关键字始终是有问题的。这就是通常重新使用现有关键字的原因(参见最新的C ++标准中的“ auto”)。

–康斯坦丁
2011年10月7日上午10:28

#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(),然后使用“ traceback”模块完成打印,就像Python一样。

评论


抱歉,这是几乎所有语言的众所周知的共同特征。

–卢卡斯S.
08-09-19 at 14:04

注意斜体文本。某些人会代之以引发e,这不会保留原始的回溯。

–习惯
09年1月27日,下午3:14

也许更神奇,exc_info = sys.exc_info();提高exc_info [0],exc_info [1],exc_info [2]与此等效,但是您可以更改这些值(例如,更改异常类型或消息)

– ianb
09年5月5日在20:27

@Lucas S.好吧,我不知道,很高兴它写在这里。

– e-satis
2010年6月23日在12:32

我可能在这里展示我的青春,但是我一直在python 2.7中使用python 3语法,没有任何问题

– Wim
2011年7月14日在5:49

#27 楼

主要消息:)

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



解密后的内容:


Python的禅宗,蒂姆·彼得斯

美丽胜于丑陋。
显式胜于隐含。
简单胜于复杂。
胜于复杂。
比嵌套要好。
可读性要好。
特殊情况还不足以打破规则。
尽管实用性胜过纯度。
错误
除非明确地保持沉默。
面对模棱两可时,请拒绝猜测的诱惑。
应该有一种-最好只有一种-显而易见的方法。
虽然一开始除非您是荷兰人,否则这种方式可能一开始并不明显。
现在总比没有好。
虽然从来没有比现在好。
如果实现很难解释,这是一个坏主意。
如果实现易于解释,则可能是一个好主意。
名称空间是一个很棒的主意-让我们做更多这些吧!


评论


知道为什么源是这样加密的吗?只是为了娱乐,还是有其他原因?

– MiniQuark
08-12-14在20:32

源代码的编写方式与禅宗背道而驰!

–hasen
09年1月1日,下午5:39

svn.python.org/view/python/trunk/Lib/this.py?view=markup

–erikprice
09年6月24日在19:41

我已经更新了/usr/lib/python2.6/this.py,将旧代码替换为此打印s.translate(“”。join(chr(64
– fortran
09年6月25日在13:12

@MiniQuark:快速历史课程:wefearchange.org/2010/06/import-this-and-zen-of-python.html

–user21037
2010年7月14日,0:51

#28 楼

交互式解释器选项卡的完成

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

评论


这是一个非常有用的功能。如此之多,我有一个简单的脚本即可启用它(以及其他一些自省功能):pixelbeat.org/scripts/inpy

–pixelbeat
08-10-12在22:49

IPython为您提供了此功能以及其他大量其他功能

– akaihola
09年1月10日,下午3:47

在pdb提示符下,这比常规的python提示符更有用(因为IPython仍可达到该目的)。但是,这似乎在pdb提示符下不起作用,可能是因为pdb绑定了自己的tab选项卡(用处不大)。我尝试在pdb提示符下调用parse_and_bind(),但仍然无法正常工作。使用IPython获取pdb提示符的替代方法是更多的工作,因此我倾向于不使用它。

–haridsv
2010年4月1日于22:46

@haridsv-easy_install ipdb-然后您可以使用import ipdb; ipdb.set_trace()

–道格·哈里斯(Doug Harris)
2010年5月20日在22:03

在osx上(我想像其他使用libedit的系统),您必须做readline.parse_and_bind(“ bind ^ I rl_complete”)

–富巴
2011-02-10 15:20

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


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

评论


“ for range(i)中的j”-这是错字吗?通常,您需要固定的i和j范围。如果您要访问2D数组,则会错过一半的元素。

– Peter Gibson
09年3月29日在2:27

在此示例中,我不访问任何数组。该代码的唯一目的是表明内部范围内的表达式可以访问外部范围内的表达式。副产品是(x,y)对的列表,使得4> x> y> 0。

–拉法·道吉德(RafałDowgird)
09年3月30日在8:23

有点像微积分中的双重积分或双重求和。

– oo
09-9-29 at 20:57

这里要记住的关键点(花了我很长时间才意识到)是,for语句的顺序应按照您希望它们从外部到内部以标准for循环的顺序编写。

– sykora
2010-1-26在11:17

补充一下sykora的注释:假设您从一堆fors和ifs开始,里面有yield x。要将其转换为生成器表达式,请先移动x,删除所有冒号(和yield),然后将整个内容括在括号中。要改为理解列表,请用方括号替换外部括号。

– Ken Arnold
2011年6月14日下午1:30

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


来自标准库参考的更多详细信息:设置类型

评论


在本教程中,部分为docs.python.org/tutorial/datastructures.html#sets

– XTL
2012年2月16日在7:58