Python 编程语言有哪些鲜为人知但有用的特性?
- 尝试将答案限制在 Python 核心。
- 每个答案一个功能。
- 给出该功能的示例和简短描述,而不仅仅是文档链接。
- 使用标题作为第一行标记要素。
Python 编程语言有哪些鲜为人知但有用的特性?
>>> x = 5
>>> 1 < x < 10
True
>>> 10 < x < 20
False
>>> x < 10 < x*10 < 100
True
>>> 10 > x <= 9
True
>>> 5 == x > 4
True
In case you're thinking it's doing 1 < x
, which comes out as True
, and then comparing True < 10
, which is also True
, then no, that's really not what happens (see the last example.) It's really translating into 1 < x and x < 10
, and x < 10 and 10 < x * 10 and x*10 < 100
, but with less typing and each term is only evaluated once.
获取 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)
枚举
用 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
返回。
小心可变的默认参数
>>> 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]
将值发送到生成器函数中。例如具有此功能:
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 reversed”的有用习语。
>>> 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")
"else" 块通常会在 for 循环结束时执行,除非调用了 break。
上面的代码可以模拟如下:
found = False
for i in foo:
if i == 0:
found = True
break
if not found:
print("i was never 0")
从 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
在调用中还有一个 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]
并捕获 KeyErrors 来检查是否存在将向字典添加新项目的项目。
就地价值交换
>>> 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})$"
Function argument unpacking
You can unpack a list or a dictionary as function arguments using *
and **
.
For example:
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)
Very useful shortcut since lists, tuples and dicts are widely used as containers.
当您在代码文件的顶部使用正确的编码声明时,ROT13 是源代码的有效编码:
#!/usr/bin/env python
# -*- coding: rot13 -*-
cevag "Uryyb fgnpxbiresybj!".rapbqr("rot13")
>>> NewType = type("NewType", (object,), {"x": "hello"})
>>> n = NewType()
>>> n.x
"hello"
这与
>>> class NewType(object):
>>> x = "hello"
>>> n = NewType()
>>> n.x
"hello"
可能不是最有用的东西,但很高兴知道。
编辑:新类型的固定名称,应该与 with语句NewType
完全相同。class
编辑:调整标题以更准确地描述该功能。
上下文管理器和“ 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__
和方法。如果从 with 语句体中引发任何异常,__exit__
也会传递异常详细信息,从而允许在那里进行异常处理。__exit__
在这种特殊情况下,它对您的作用是保证在执行超出套件范围时关闭文件with
,无论是否正常发生或是否引发异常。它基本上是一种抽象出常见异常处理代码的方法。
其他常见的用例包括线程锁定和数据库事务。
字典有一个 'get()' 方法。如果您执行 d['key'] 并且 key 不存在,则会出现异常。如果你执行 d.get('key'),如果 'key' 不存在,你会返回 None。您可以添加第二个参数来取回该项目而不是 None,例如:d.get('key', 0)。
它非常适合添加数字之类的事情:
sum[value] = sum.get(value, 0) + 1
它们是一大堆核心 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 有一个很好的教程,它比我更好地描述了它们。
条件赋值
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 是两个类。
从 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()
% -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))
要添加更多 python 模块(尤其是第 3 方模块),大多数人似乎使用 PYTHONPATH 环境变量,或者他们在他们的站点包目录中添加符号链接或目录。另一种方法是使用 *.pth 文件。这是官方python文档的解释:
“[修改python的搜索路径]最方便的方法是将路径配置文件添加到Python路径上已经存在的目录中,通常是.../site-packages/目录。路径配置文件的扩展名为.pth ,并且每一行必须包含一个将附加到 sys.path 的路径。(因为新路径附加到 sys.path,所以添加的目录中的模块不会覆盖标准模块。这意味着您不能使用此机制用于安装标准模块的固定版本。)"
例外条款:
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 一样使用“回溯”模块完成打印。
主要信息:)
import this
# btw look at this module's source :)
解密:
Python 之禅,蒂姆·彼得斯 (Tim Peters)
美丽总比丑陋好。
显式优于隐式。
简单胜于复杂。
复杂胜于复杂。
平面优于嵌套。
稀疏比密集好。
可读性很重要。
特殊情况不足以打破规则。
虽然实用胜过纯洁。
错误永远不应该悄无声息地过去。
除非明确沉默。
面对模棱两可,拒绝猜测的诱惑。应该有一种——最好只有一种——明显的方法来做到这一点。
虽然这种方式一开始可能并不明显,除非你是荷兰人。
现在总比没有好。
虽然从不经常比现在。
如果实现很难解释,那是个坏主意。
如果实现很容易解释,那可能是个好主意。
命名空间是一个很棒的想法——让我们做更多的事情!
交互式解释器选项卡完成
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 环境变量。
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}
标准库参考中的更多详细信息:Set Types
嵌套列表推导和生成器表达式:
[(i,j) for i in range(3) for j in range(i) ]
((i,j) for i in range(4) for j in range(i) )
这些可以替换大量的嵌套循环代码。
负轮
该round()
函数将浮点数四舍五入到十进制数字中的给定精度,但精度可以为负数:
>>> str(round(1234.5678, -2))
'1200.0'
>>> str(round(1234.5678, 2))
'1234.57'
注意: round()
总是返回一个浮点数,str()
在上面的例子中使用,因为浮点数学是不精确的,在 2.x 下第二个例子可以打印为1234.5700000000001
. 另请参阅decimal
模块。
乘以一个布尔值
我在 Web 开发中经常做的一件事是选择性地打印 HTML 参数。我们都在其他语言中看到过这样的代码:
class='<% isSelected ? "selected" : "" %>'
在 Python 中,您可以乘以一个布尔值,它完全符合您的预期:
class='<% "selected" * isSelected %>'
这是因为乘法将布尔值强制为整数(0 表示 False,1 表示 True),并且在 python 中,将字符串乘以 int会重复字符串 N 次。
Python 的高级切片操作有一个鲜为人知的语法元素,即省略号:
>>> class C(object):
... def __getitem__(self, item):
... return item
...
>>> C()[1:2, ..., 3]
(slice(1, 2, None), Ellipsis, 3)
不幸的是,它几乎没有用,因为只有在涉及元组时才支持省略号。
每次匹配正则表达式时都可以调用函数这一事实非常方便。在这里,我将每个“Hello”替换为“Hi”,将“there”替换为“Fred”等。
import re
def Main(haystack):
# List of from replacements, can be a regex
finds = ('Hello', 'there', 'Bob')
replaces = ('Hi,', 'Fred,', 'how are you?')
def ReplaceFunction(matchobj):
for found, rep in zip(matchobj.groups(), replaces):
if found != None:
return rep
# log error
return matchobj.group(0)
named_groups = [ '(%s)' % find for find in finds ]
ret = re.sub('|'.join(named_groups), ReplaceFunction, haystack)
print ret
if __name__ == '__main__':
str = 'Hello there Bob'
Main(str)
# Prints 'Hi, Fred, how are you?'
python 3中的元组解包
在 python 3 中,您可以使用与函数定义中的可选参数相同的语法进行元组解包:
>>> first,second,*rest = (1,2,3,4,5,6,7,8)
>>> first
1
>>> second
2
>>> rest
[3, 4, 5, 6, 7, 8]
但是一个鲜为人知但更强大的功能允许您在列表中间拥有未知数量的元素:
>>> first,*rest,last = (1,2,3,4,5,6,7,8)
>>> first
1
>>> rest
[2, 3, 4, 5, 6, 7]
>>> last
8
多行字符串
一种方法是使用反斜杠:
>>> sql = "select * from some_table \
where id > 10"
>>> print sql
select * from some_table where id > 10
另一种是使用三引号:
>>> sql = """select * from some_table
where id > 10"""
>>> print sql
select * from some_table where id > 10
这些问题是它们没有缩进(在您的代码中看起来很糟糕)。如果您尝试缩进,它只会打印您放置的空格。
我最近发现的第三种解决方案是将字符串分成几行并用括号括起来:
>>> sql = ("select * from some_table " # <-- no comma, whitespace at end
"where id > 10 "
"order by name")
>>> print sql
select * from some_table where id > 10 order by name
请注意行之间没有逗号(这不是元组),并且您必须考虑字符串需要具有的任何尾随/前导空格。顺便说一下,所有这些都与占位符一起使用(例如"my name is %s" % name
)。
根据许多人的要求,此答案已移至问题本身。
>>> (a 代表 xrange(10000) 中的 a) <0x81a8fcc 处的生成器对象> >>> b = '废话' >>> _ <0x81a8fcc 处的生成器对象>
>>> 导入浏览器 >>> webbrowser.open_new_tab('http://www.stackoverflow.com')
python -m SimpleHTTPServer 8000
>>> 进口 atexit
内置pow()
函数有一个鲜为人知的第三个参数,它允许您比简单地计算 x y模 z 更有效(x ** y) % z
:
>>> x, y, z = 1234567890, 2345678901, 17
>>> pow(x, y, z) # almost instantaneous
6
相比之下,(x ** y) % z
在我的机器上一分钟内没有给出相同值的结果。
您可以使用 zip 轻松转置数组。
a = [(1,2), (3,4), (5,6)]
zip(*a)
# [(1, 3, 5), (2, 4, 6)]
enumerate
has partly been covered in this answer, but recently I've found an even more hidden feature of enumerate
that I think deserves its own post instead of just a comment.
Since Python 2.6, you can specify a starting index to enumerate
in its second argument:
>>> l = ["spam", "ham", "eggs"]
>>> list(enumerate(l))
>>> [(0, "spam"), (1, "ham"), (2, "eggs")]
>>> list(enumerate(l, 1))
>>> [(1, "spam"), (2, "ham"), (3, "eggs")]
One place where I've found it utterly useful is when I am enumerating over entries of a symmetric matrix. Since the matrix is symmetric, I can save time by iterating over the upper triangle only, but in that case, I have to use enumerate
with a different starting index in the inner for
loop to keep track of the row and column indices properly:
for ri, row in enumerate(matrix):
for ci, column in enumerate(matrix[ri:], ri):
# ci now refers to the proper column index
Strangely enough, this behaviour of enumerate
is not documented in help(enumerate)
, only in the online documentation.
You can use property to make your class interfaces more strict.
class C(object):
def __init__(self, foo, bar):
self.foo = foo # read-write property
self.bar = bar # simple attribute
def _set_foo(self, value):
self._foo = value
def _get_foo(self):
return self._foo
def _del_foo(self):
del self._foo
# any of fget, fset, fdel and doc are optional,
# so you can make a write-only and/or delete-only property.
foo = property(fget = _get_foo, fset = _set_foo,
fdel = _del_foo, doc = 'Hello, I am foo!')
class D(C):
def _get_foo(self):
return self._foo * 2
def _set_foo(self, value):
self._foo = value / 2
foo = property(fget = _get_foo, fset = _set_foo,
fdel = C.foo.fdel, doc = C.foo.__doc__)
In Python 2.6 and 3.0:
class C(object):
def __init__(self, foo, bar):
self.foo = foo # read-write property
self.bar = bar # simple attribute
@property
def foo(self):
'''Hello, I am foo!'''
return self._foo
@foo.setter
def foo(self, value):
self._foo = value
@foo.deleter
def foo(self):
del self._foo
class D(C):
@C.foo.getter
def foo(self):
return self._foo * 2
@foo.setter
def foo(self, value):
self._foo = value / 2
To learn more about how property works refer to descriptors.
很多人不知道“dir”函数。这是从解释器中找出对象可以做什么的好方法。例如,如果您想查看所有字符串方法的列表:
>>> dir("foo")
['__add__', '__class__', '__contains__', (snipped a bunch), 'title',
'translate', 'upper', 'zfill']
然后,如果您想了解有关特定方法的更多信息,可以对其调用“帮助”。
>>> help("foo".upper)
Help on built-in function upper:
upper(...)
S.upper() -> string
Return a copy of the string S converted to uppercase.
可能一个容易被忽视的 python 内置函数是“set/frozenset”。
当你有这样的列表时很有用,[1,2,1,1,2,3,4] 并且只想要这样的唯一值 [1,2,3,4]。
使用 set() 这正是你得到的:
>>> x = [1,2,1,1,2,3,4]
>>>
>>> set(x)
set([1, 2, 3, 4])
>>>
>>> for i in set(x):
... print i
...
1
2
3
4
当然,要获取列表中唯一的数量:
>>> len(set([1,2,1,1,2,3,4]))
4
您还可以使用 set().issubset() 查找列表是否是另一个列表的子集:
>>> set([1,2,3,4]).issubset([0,1,2,3,4,5])
True
从 Python 2.7 和 3.0 开始,您可以使用花括号来创建一个集合:
myset = {1,2,3,4}
以及集合推导:
{x for x in stuff}
内置 base64、zlib 和 rot13 编解码器
字符串有encode
和decode
方法。通常这用于转换str
为unicode
,反之亦然,例如使用u = s.encode('utf8')
. 但是还有一些其他方便的内置编解码器。无需显式导入即可使用 zlib(和 bz2)进行压缩和解压缩:
>>> s = 'a' * 100
>>> s.encode('zlib')
'x\x9cKL\xa4=\x00\x00zG%\xe5'
同样,您可以对 base64 进行编码和解码:
>>> 'Hello world'.encode('base64')
'SGVsbG8gd29ybGQ=\n'
>>> 'SGVsbG8gd29ybGQ=\n'.decode('base64')
'Hello world'
而且,当然,你可以 rot13:
>>> 'Secret message'.encode('rot13')
'Frperg zrffntr'
口译员中的口译员
标准库的代码模块让您可以在程序中包含自己的 read-eval-print 循环,或者运行整个嵌套的解释器。例如(从这里复制我的例子)
$ python
Python 2.5.1 (r251:54863, Jan 17 2008, 19:35:17)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> shared_var = "Set in main console"
>>> import code
>>> ic = code.InteractiveConsole({ 'shared_var': shared_var })
>>> try:
... ic.interact("My custom console banner!")
... except SystemExit, e:
... print "Got SystemExit!"
...
My custom console banner!
>>> shared_var
'Set in main console'
>>> shared_var = "Set in sub-console"
>>> import sys
>>> sys.exit()
Got SystemExit!
>>> shared_var
'Set in main console'
这对于您希望接受用户的脚本输入或实时查询 VM 状态的情况非常有用。
TurboGears通过一个 WebConsole 来使用它,您可以从中查询您的实时 Web 应用程序的状态。
>>> from functools import partial
>>> bound_func = partial(range, 0, 10)
>>> bound_func()
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> bound_func(2)
[0, 2, 4, 6, 8]
不是真正的隐藏功能,但部分功能对于后期评估功能非常有用。
您可以根据需要在初始调用中绑定尽可能多或尽可能少的参数,然后使用任何剩余参数调用它(在此示例中,我将开始/结束参数绑定到范围,但第二次调用它一个步骤 arg)
请参阅文档。
在调试复杂的数据结构时,pprint模块会派上用场。
从文档中引用..
>>> import pprint
>>> stuff = sys.path[:]
>>> stuff.insert(0, stuff)
>>> pprint.pprint(stuff)
[<Recursion on list with id=869440>,
'',
'/usr/local/lib/python1.5',
'/usr/local/lib/python1.5/test',
'/usr/local/lib/python1.5/sunos5',
'/usr/local/lib/python1.5/sharedmodules',
'/usr/local/lib/python1.5/tkinter']
Python 有 GOTO
...它是由外部纯 Python 模块实现的:)
from goto import goto, label
for i in range(1, 10):
for j in range(1, 20):
for k in range(1, 30):
print i, j, k
if k == 3:
goto .end # breaking out from a deeply nested loop
label .end
print "Finished"
dict's constructor accepts keyword arguments:
>>> dict(foo=1, bar=2)
{'foo': 1, 'bar': 2}
序列乘法和反射操作数
>>> 'xyz' * 3
'xyzxyzxyz'
>>> [1, 2] * 3
[1, 2, 1, 2, 1, 2]
>>> (1, 2) * 3
(1, 2, 1, 2, 1, 2)
我们使用反射(交换)操作数得到相同的结果
>>> 3 * 'xyz'
'xyzxyzxyz'
它是这样工作的:
>>> s = 'xyz'
>>> num = 3
评估表达式s * num解释器调用s.___mul___(num)
>>> s * num
'xyzxyzxyz'
>>> s.__mul__(num)
'xyzxyzxyz'
要评估表达式num * s解释器调用num.___mul___(s)
>>> num * s
'xyzxyzxyz'
>>> num.__mul__(s)
NotImplemented
如果调用返回NotImplemented则解释器调用反射操作s.___rmul___(num)如果操作数具有不同的类型
>>> s.__rmul__(num)
'xyzxyzxyz'
请参阅http://docs.python.org/reference/datamodel.html#object。rmul
交错if
和for
列表推导
>>> [(x, y) for x in range(4) if x % 2 == 1 for y in range(4)]
[(1, 0), (1, 1), (1, 2), (1, 3), (3, 0), (3, 1), (3, 2), (3, 3)]
直到我学习了 Haskell,我才意识到这一点。
模块运算符中的 getter 函数
函数attrgetter()
和itemgetter()
in 模块operator
可用于生成快速访问函数,用于排序和搜索对象和字典
Python 库文档中的第 6.7 章
Tuple unpacking:
>>> (a, (b, c), d) = [(1, 2), (3, 4), (5, 6)]
>>> a
(1, 2)
>>> b
3
>>> c, d
(4, (5, 6))
More obscurely, you can do this in function arguments (in Python 2.x; Python 3.x will not allow this anymore):
>>> def addpoints((x1, y1), (x2, y2)):
... return (x1+x2, y1+y2)
>>> addpoints((5, 0), (3, 5))
(8, 5)
Obviously, the antigravity module. xkcd #353
Python 解释器
>>>
也许不是鲜为人知,但肯定是我最喜欢的 Python 特性之一。
如示例中所示,最近版本中的解包语法已升级。
>>> a, *b = range(5)
>>> a, b
(0, [1, 2, 3, 4])
>>> *a, b = range(5)
>>> a, b
([0, 1, 2, 3], 4)
>>> a, *b, c = range(5)
>>> a, b, c
(0, [1, 2, 3], 4)
您可以引用一个列表推导,因为它是由符号“_[1]”构建的。例如,以下函数通过引用其列表推导来唯一化一个元素列表而不改变它们的顺序。
def unique(my_list):
return [x for x in my_list if x not in locals()['_[1]']]
Python sort 函数对元组进行正确排序(即使用熟悉的字典顺序):
a = [(2, "b"), (1, "a"), (2, "a"), (3, "c")]
print sorted(a)
#[(1, 'a'), (2, 'a'), (2, 'b'), (3, 'c')]
如果您想在年龄之后对人员列表进行排序然后命名,这很有用。
的简单性:
>>> 'str' in 'string'
True
>>> 'no' in 'yes'
False
>>>
是我喜欢 Python 的地方,我见过很多这样的不是很 Python 的成语:
if 'yes'.find("no") == -1:
pass
元类
当然 :-) Python 中的元类是什么?
我个人喜欢这3个不同的报价
str = "I'm a string 'but still I can use quotes' inside myself!"
str = """ For some messy multi line strings.
Such as
<html>
<head> ... </head>"""
也很酷:不必逃避正则表达式,通过使用原始字符串避免可怕的反斜杠沙拉:
str2 = r"\n"
print str2
>> \n
发电机
我认为很多 Python 新手开发者都忽略了生成器,但并没有真正掌握它们的用途,也没有意识到它们的强大功能。直到我阅读了 David M. Beazley 关于生成器的 PyCon 演示文稿(可在此处获得),我才意识到它们是多么有用(真的很重要)。该演示文稿阐明了对我来说是一种全新的编程方式,我向所有对生成器没有深入了解的人推荐它。
python 中的textwrap.dedent
实用函数可以非常方便地测试返回的多行字符串是否等于预期输出,而不会破坏单元测试的缩进:
import unittest, textwrap
class XMLTests(unittest.TestCase):
def test_returned_xml_value(self):
returned_xml = call_to_function_that_returns_xml()
expected_value = textwrap.dedent("""\
<?xml version="1.0" encoding="utf-8"?>
<root_node>
<my_node>my_content</my_node>
</root_node>
""")
self.assertEqual(expected_value, returned_xml)
Implicit concatenation:
>>> print "Hello " "World"
Hello World
Useful when you want to make a long text fit on several lines in a script:
hello = "Greaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Hello " \
"Word"
or
hello = ("Greaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Hello "
"Word")
使用交互式 shell 时,“_”包含最后打印项的值:
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> _
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
Lambda 函数通常用于将一个值快速转换为另一个值,但它们也可用于将值包装在函数中:
>>> f = lambda: 'foo'
>>> f()
'foo'
他们也可以接受通常的*args
和**kwargs
语法:
>>> g = lambda *args, **kwargs: args[0], kwargs['thing']
>>> g(1, 2, 3, thing='stuff')
(1, 'stuff')
使用关键字参数作为赋值
有时,人们想根据一个或多个参数构建一系列函数。然而,这可能很容易导致所有闭包都引用相同的对象和值:
funcs = []
for k in range(10):
funcs.append( lambda: k)
>>> funcs[0]()
9
>>> funcs[7]()
9
可以通过将 lambda 表达式转换为仅取决于其参数的函数来避免这种行为。关键字参数存储绑定到它的当前值。函数调用不必更改:
funcs = []
for k in range(10):
funcs.append( lambda k = k: k)
>>> funcs[0]()
0
>>> funcs[7]()
7
Mod 与负数一起正常工作
-1 % 5 应该是4,而不是像 JavaScript 这样的其他语言中的 -1 。这使得 Python 中的“环绕窗口”更干净,你只需这样做:
index = (index + increment) % WINDOW_SIZE
不是很隐蔽,但函数有属性:
def doNothing():
pass
doNothing.monkeys = 4
print doNothing.monkeys
4
分配和删除切片:
>>> a = range(10)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[:5] = [42]
>>> a
[42, 5, 6, 7, 8, 9]
>>> a[:1] = range(5)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> del a[::2]
>>> a
[1, 3, 5, 7, 9]
>>> a[::2] = a[::-2]
>>> a
[9, 3, 5, 7, 1]
注意:分配给扩展切片(s[start:stop:step]
)时,分配的迭代必须与切片具有相同的长度。
一流的功能
这并不是一个真正的隐藏功能,但函数是一流对象这一事实简直太棒了。您可以像传递任何其他变量一样传递它们。
>>> def jim(phrase):
... return 'Jim says, "%s".' % phrase
>>> def say_something(person, phrase):
... print person(phrase)
>>> say_something(jim, 'hey guys')
'Jim says, "hey guys".'
三元运算符
>>> 'ham' if True else 'spam'
'ham'
>>> 'ham' if False else 'spam'
'spam'
这是在 2.5 中添加的,在此之前您可以使用:
>>> True and 'ham' or 'spam'
'ham'
>>> False and 'ham' or 'spam'
'spam'
但是,如果您要使用的值被认为是错误的,则存在差异:
>>> [] if True else 'spam'
[]
>>> True and [] or 'spam'
'spam'
许多 Python 函数都接受元组,但它似乎也不接受。例如你想测试你的变量是否是一个数字,你可以这样做:
if isinstance (number, float) or isinstance (number, int):
print "yaay"
但是如果你把元组传递给我们,这看起来更干净:
if isinstance (number, (float, int)):
print "yaay"
字典中无限递归的好处理:
>>> a = {}
>>> b = {}
>>> a['b'] = b
>>> b['a'] = a
>>> print a
{'b': {'a': {...}}}
可以说,这本身并不是一个编程功能,但它非常有用,所以我还是会发布它。
$ python -m http.server
...其次是$ wget http://<ipnumber>:8000/filename
其他地方。
如果您仍在运行较旧 (2.x) 版本的 Python:
$ python -m SimpleHTTPServer
您还可以指定一个端口,例如python -m http.server 80
(如果您在服务器端有根,则可以省略 url 中的端口)
不是“隐藏”但非常有用且不常用
像这样快速创建字符串连接函数
comma_join = ",".join
semi_join = ";".join
print comma_join(["foo","bar","baz"])
'foo,bar,baz
和
能够比引号、逗号更优雅地创建字符串列表。
l = ["item1", "item2", "item3"]
取而代之
l = "item1 item2 item3".split()
使用负步骤反转可迭代
>>> s = "Hello World"
>>> s[::-1]
'dlroW olleH'
>>> a = (1,2,3,4,5,6)
>>> a[::-1]
(6, 5, 4, 3, 2, 1)
>>> a = [5,4,3,2,1]
>>> a[::-1]
[1, 2, 3, 4, 5]
对迭代器的多次引用
您可以使用列表乘法创建对同一迭代器的多个引用:
>>> i = (1,2,3,4,5,6,7,8,9,10) # or any iterable object
>>> iterators = [iter(i)] * 2
>>> iterators[0].next()
1
>>> iterators[1].next()
2
>>> iterators[0].next()
3
这可用于将可迭代对象分组为块,例如,如itertools
文档中的此示例所示
def grouper(n, iterable, fillvalue=None):
"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return izip_longest(fillvalue=fillvalue, *args)
从 python 3.1 ( 2.7 ) 开始支持字典和集合理解:
{ a:a for a in range(10) }
{ a for a in range(10) }
Python可以理解任何类型的 unicode 数字,而不仅仅是 ASCII 类型:
>>> s = u'10585'
>>> s
u'\uff11\uff10\uff15\uff18\uff15'
>>> print s
10585
>>> int(s)
10585
>>> float(s)
10585.0
__slots__
是一种节省内存的好方法,但很难获得对象值的字典。想象以下对象:
class Point(object):
__slots__ = ('x', 'y')
现在该对象显然有两个属性。现在我们可以创建它的一个实例并以这种方式构建它的字典:
>>> p = Point()
>>> p.x = 3
>>> p.y = 5
>>> dict((k, getattr(p, k)) for k in p.__slots__)
{'y': 5, 'x': 3}
但是,如果点被子类化并添加了新插槽,这将不起作用。但是 Python 会自动实现__reduce_ex__
以帮助该copy
模块。这可以被滥用来获取值的字典:
>>> p.__reduce_ex__(2)[2][1]
{'y': 5, 'x': 3}
您可以直接操作模块缓存,根据需要使模块可用或不可用:
>>> import sys
>>> import ham
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named ham
# Make the 'ham' module available -- as a non-module object even!
>>> sys.modules['ham'] = 'ham, eggs, saussages and spam.'
>>> import ham
>>> ham
'ham, eggs, saussages and spam.'
# Now remove it again.
>>> sys.modules['ham'] = None
>>> import ham
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named ham
这甚至适用于可用的模块,并且在某种程度上适用于已经导入的模块:
>>> import os
# Stop future imports of 'os'.
>>> sys.modules['os'] = None
>>> import os
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named os
# Our old imported module is still available.
>>> os
<module 'os' from '/usr/lib/python2.5/os.pyc'>
正如最后一行所示,更改 sys.modules 只会影响未来import
的语句,而不是过去的语句,所以如果你想影响其他模块,在你给他们机会尝试导入模块之前进行这些更改很重要——所以在你之前通常导入它们。None
是 中的一个特殊值sys.modules
,用于负缓存(表示第一次没有找到模块,所以再找也没用。)任何其他值都将是import
操作的结果——即使它不是模块对象. 您可以使用它来用行为与您想要的完全一样的对象替换模块。完全删除条目sys.modules
会导致 nextimport
对模块进行正常搜索,即使它之前已经导入。
迭代工具
这个模块经常被忽视。以下示例用于itertools.chain()
展平列表:
>>> from itertools import *
>>> l = [[1, 2], [3, 4]]
>>> list(chain(*l))
[1, 2, 3, 4]
有关更多应用程序,请参阅http://docs.python.org/library/itertools.html#recipes。
您可以通过查看其 __ module__ 属性来询问任何对象它来自哪个模块。这很有用,例如,如果您在命令行上进行试验并导入了很多东西。
同样,您可以通过查看模块的 __file__ 属性来询问模块的来源。这在调试路径问题时很有用。
一些内置的收藏夹,map()、reduce() 和 filter()。所有这些都非常快速和强大。
一个词:IPython
标签内省,漂亮的打印,,%debug
历史管理,pylab
......非常值得花时间好好学习。
猜测整数基数
>>> int('10', 0)
10
>>> int('0x10', 0)
16
>>> int('010', 0) # does not work on Python 3.x
8
>>> int('0o10', 0) # Python >=2.6 and Python 3.x
8
>>> int('0b10', 0) # Python >=2.6 and Python 3.x
2
您可以从一组长度为 2 的序列中构建字典。当你有一个值列表和一个数组列表时非常方便。
>>> dict([ ('foo','bar'),('a',1),('b',2) ])
{'a': 1, 'b': 2, 'foo': 'bar'}
>>> names = ['Bob', 'Marie', 'Alice']
>>> ages = [23, 27, 36]
>>> dict(zip(names, ages))
{'Alice': 36, 'Bob': 23, 'Marie': 27}
有时,扩展(修改)子类中描述符“返回”的值很有用。可以通过以下方式轻松完成super()
:
class A(object):
@property
def prop(self):
return {'a': 1}
class B(A):
@property
def prop(self):
return dict(super(B, self).prop, b=2)
将其存储test.py
并运行python -i test.py
(另一个隐藏功能:-i
选项执行脚本并允许您以交互模式继续):
>>> B().prop
{'a': 1, 'b': 2}
创建枚举
在 Python 中,您可以这样做来快速创建枚举:
>>> FOO, BAR, BAZ = range(3)
>>> FOO
0
但是“枚举”不必具有整数值。你甚至可以这样做:
class Colors(object):
RED, GREEN, BLUE, YELLOW = (255,0,0), (0,255,0), (0,0,255), (0,255,255)
#now Colors.RED is a 3-tuple that returns the 24-bit 8bpp RGB
#value for saturated red
python的一个轻微的错误特征。将字符串列表连接在一起的正常快速方法是,
''.join(list_of_strings)
“解包”到函数参数
def foo(a, b, c):
print a, b, c
bar = (3, 14, 15)
foo(*bar)
执行时打印:
3 14 15
reversed()
内置的。在许多情况下,它使迭代更加清晰。
快速示例:
for i in reversed([1, 2, 3]):
print(i)
产生:
3
2
1
但是,reversed()
也适用于任意迭代器,例如文件中的行或生成器表达式。
Python之禅
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
string-escape
和unicode-escape
编码
假设您有一个来自外部源的字符串,其中包含\n
,\t
依此类推。如何将它们转换为换行符或制表符?只需使用string-escape
编码解码字符串!
>>> print s
Hello\nStack\toverflow
>>> print s.decode('string-escape')
Hello
Stack overflow
另一个问题。您有带有 unicode 文字的普通字符串,例如\u01245
. 如何让它发挥作用?只需使用unicode-escape
编码解码字符串!
>>> s = '\u041f\u0440\u0438\u0432\u0456\u0442, \u0441\u0432\u0456\u0442!'
>>> print s
\u041f\u0440\u0438\u0432\u0456\u0442, \u0441\u0432\u0456\u0442!
>>> print unicode(s)
\u041f\u0440\u0438\u0432\u0456\u0442, \u0441\u0432\u0456\u0442!
>>> print unicode(s, 'unicode-escape')
Привіт, світ!
在运行时更改函数标签:
>>> class foo:
... def normal_call(self): print "normal_call"
... def call(self):
... print "first_call"
... self.call = self.normal_call
>>> y = foo()
>>> y.call()
first_call
>>> y.call()
normal_call
>>> y.call()
normal_call
...
有人在博客中提到 Python 没有与 zip() 一起使用的解压缩函数。unzip 计算起来很简单,因为:
>>> t1 = (0,1,2,3)
>>> t2 = (7,6,5,4)
>>> [t1,t2] == zip(*zip(t1,t2))
True
不过经过反思,我宁愿有一个明确的 unzip()。
Top Secret 属性
>>> class A(object): pass
>>> a = A()
>>> setattr(a, "can't touch this", 123)
>>> dir(a)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', "can't touch this"]
>>> a.can't touch this # duh
File "<stdin>", line 1
a.can't touch this
^
SyntaxError: EOL while scanning string literal
>>> getattr(a, "can't touch this")
123
>>> setattr(a, "__class__.__name__", ":O")
>>> a.__class__.__name__
'A'
>>> getattr(a, "__class__.__name__")
':O'
创建具有相关数据的两个序列的字典
In [15]: t1 = (1, 2, 3)
In [16]: t2 = (4, 5, 6)
In [17]: dict (zip(t1,t2))
Out[17]: {1: 4, 2: 5, 3: 6}
动态添加的属性
如果您考虑通过调用它们来为您的类添加一些属性,这可能会很有用。这可以通过覆盖__getattribute__
使用点操作数时调用的成员函数来完成。因此,让我们看一个虚拟类,例如:
class Dummy(object):
def __getattribute__(self, name):
f = lambda: 'Hello with %s'%name
return f
当您实例化一个 Dummy 对象并进行方法调用时,您将获得以下信息:
>>> d = Dummy()
>>> d.b()
'Hello with b'
最后,您甚至可以将属性设置为您的类,以便可以动态定义它。如果您使用 Python Web 框架并希望通过解析属性名称来进行查询,这可能会很有用。
我在 github 上有一个要点,其中包含这个简单的代码以及一个朋友在 Ruby 上的等效代码。
小心!
list
用sum()
展平 asum()
内置函数可用于s一起,提供了一种方便的方法来展平 a的s:__add__
list
list
list
Python 2.7.1 (r271:86832, May 27 2011, 21:41:45)
[GCC 4.2.1 (Apple Inc. build 5664)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> l = [[1, 2, 3], [4, 5], [6], [7, 8, 9]]
>>> sum(l, [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]
这是来自Alex Martelli的杀手。Borg
共享状态的所有实例。这消除了使用单例模式的需要(实例在状态共享时无关紧要)并且相当优雅(但对于新类来说更复杂)。
的值foo
可以在任何情况下重新分配,并且所有内容都会更新,您甚至可以重新分配整个字典。博格是一个完美的名字,在这里阅读更多。
class Borg:
__shared_state = {'foo': 'bar'}
def __init__(self):
self.__dict__ = self.__shared_state
# rest of your class here
这非常适合共享 eventlet.GreenPool 来控制并发。
>>> node = namedtuple('node', "a b")
>>> node(1,2) + node(5,6)
(1, 2, 5, 6)
>>> (node(1,2), node(5,6))
(node(a=1, b=2), node(a=5, b=6))
>>>
一些更多的实验来回应评论:
>>> from collections import namedtuple
>>> from operator import *
>>> mytuple = namedtuple('A', "a b")
>>> yourtuple = namedtuple('Z', "x y")
>>> mytuple(1,2) + yourtuple(5,6)
(1, 2, 5, 6)
>>> q = [mytuple(1,2), yourtuple(5,6)]
>>> q
[A(a=1, b=2), Z(x=5, y=6)]
>>> reduce(operator.__add__, q)
(1, 2, 5, 6)
所以,namedtuple
是 的一个有趣的子类型tuple
。
threading.enumerate() 可以访问系统中的所有 Thread 对象,而 sys._current_frames() 返回系统中所有线程的当前堆栈帧,因此将这两者结合起来,您将获得 Java 风格的堆栈转储:
def dumpstacks(signal, frame):
id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
code = []
for threadId, stack in sys._current_frames().items():
code.append("\n# Thread: %s(%d)" % (id2name[threadId], threadId))
for filename, lineno, name, line in traceback.extract_stack(stack):
code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
if line:
code.append(" %s" % (line.strip()))
print "\n".join(code)
import signal
signal.signal(signal.SIGQUIT, dumpstacks)
在多线程 python 程序的开头执行此操作,您可以随时通过发送 SIGQUIT 访问线程的当前状态。您也可以选择 signal.SIGUSR1 或 signal.SIGUSR2。
pdb — Python 调试器
作为程序员,您需要进行认真的程序开发的第一件事就是调试器。Python 有一个内置模块,它可以作为一个名为 pdb 的模块使用(自然是“Python 调试器”!)。
检查模块也是一个很酷的功能。
原始字符串中的反斜杠仍然可以转义引号。看到这个:
>>> print repr(r"aaa\"bbb")
'aaa\\"bbb'
请注意,反斜杠和双引号都出现在最终字符串中。
因此,您不能以反斜杠结束原始字符串:
>>> print repr(r"C:\")
SyntaxError: EOL while scanning string literal
>>> print repr(r"C:\"")
'C:\\"'
发生这种情况是因为实现了原始字符串以帮助编写正则表达式,而不是编写 Windows 路径。在 Gotcha 阅读有关此问题的长篇讨论— Windows 文件名中的反斜杠。
...默认值为dict.get()
None,从而避免 KeyErrors:
In [1]: test = { 1 : 'a' }
In [2]: test[2]
---------------------------------------------------------------------------
<type 'exceptions.KeyError'> Traceback (most recent call last)
<ipython console> in <module>()
<type 'exceptions.KeyError'>: 2
In [3]: test.get( 2 )
In [4]: test.get( 1 )
Out[4]: 'a'
In [5]: test.get( 2 ) == None
Out[5]: True
甚至在“现场”指定这个:
In [6]: test.get( 2, 'Some' ) == 'Some'
Out[6]: True
您可以使用setdefault(
) 设置一个值,如果它不存在则返回:
>>> a = {}
>>> b = a.setdefault('foo', 'bar')
>>> a
{'foo': 'bar'}
>>> b
'bar
重新加载模块启用“实时编码”风格。但是类实例不会更新。这就是为什么,以及如何解决它。记住,一切,是的,一切都是对象。
>>> from a_package import a_module
>>> cls = a_module.SomeClass
>>> obj = cls()
>>> obj.method()
(old method output)
现在您更改 a_module.py 中的方法并想要更新您的对象。
>>> reload(a_module)
>>> a_module.SomeClass is cls
False # Because it just got freshly created by reload.
>>> obj.method()
(old method output)
这是更新它的一种方法(但考虑使用剪刀运行):
>>> obj.__class__ is cls
True # it's the old class object
>>> obj.__class__ = a_module.SomeClass # pick up the new class
>>> obj.method()
(new method output)
这是“用剪刀运行”,因为对象的内部状态可能与新类所期望的不同。这适用于非常简单的情况,但除此之外,pickle
是你的朋友。不过,理解它为什么会起作用仍然很有帮助。
列表中的无限递归
>>> a = [1,2]
>>> a.append(a)
>>> a
[1, 2, [...]]
>>> a[2]
[1, 2, [...]]
>>> a[2][2][2][2][2][2][2][2][2] == a
True
运算符可以称为函数:
from operator import add
print reduce(add, [1,2,3,4,5,6])
您可以使用元类覆盖类的 mro
>>> class A(object):
... def a_method(self):
... print("A")
...
>>> class B(object):
... def b_method(self):
... print("B")
...
>>> class MROMagicMeta(type):
... def mro(cls):
... return (cls, B, object)
...
>>> class C(A, metaclass=MROMagicMeta):
... def c_method(self):
... print("C")
...
>>> cls = C()
>>> cls.c_method()
C
>>> cls.a_method()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'a_method'
>>> cls.b_method()
B
>>> type(cls).__bases__
(<class '__main__.A'>,)
>>> type(cls).__mro__
(<class '__main__.C'>, <class '__main__.B'>, <class 'object'>)
它可能是有充分理由的。:)
内置方法或函数没有实现描述符协议,这使得不可能做这样的事情:
>>> class C(object):
... id = id
...
>>> C().id()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: id() takes exactly one argument (0 given)
但是,您可以创建一个小的绑定描述符来实现这一点:
>>> from types import MethodType
>>> class bind(object):
... def __init__(self, callable):
... self.callable = callable
... def __get__(self, obj, type=None):
... if obj is None:
... return self
... return MethodType(self.callable, obj, type)
...
>>> class C(object):
... id = bind(id)
...
>>> C().id()
7414064
您可以用类装饰函数 - 用类实例替换函数:
class countCalls(object):
""" decorator replaces a function with a "countCalls" instance
which behaves like the original function, but keeps track of calls
>>> @countCalls
... def doNothing():
... pass
>>> doNothing()
>>> doNothing()
>>> print doNothing.timesCalled
2
"""
def __init__ (self, functionToTrack):
self.functionToTrack = functionToTrack
self.timesCalled = 0
def __call__ (self, *args, **kwargs):
self.timesCalled += 1
return self.functionToTrack(*args, **kwargs)
小整数对象(-5 .. 256)从未创建过两次:
>>> a1 = -5; b1 = 256
>>> a2 = -5; b2 = 256
>>> id(a1) == id(a2), id(b1) == id(b2)
(True, True)
>>>
>>> c1 = -6; d1 = 257
>>> c2 = -6; d2 = 257
>>> id(c1) == id(c2), id(d1) == id(d2)
(False, False)
>>>
编辑:列表对象永远不会被破坏(仅列表中的对象)。Python 有一个数组,其中最多可以保存 80 个空列表。当您销毁列表对象时-python 将其放入该数组,当您创建新列表时-python 从该数组中获取最后放置的列表:
>>> a = [1,2,3]; a_id = id(a)
>>> b = [1,2,3]; b_id = id(b)
>>> del a; del b
>>> c = [1,2,3]; id(c) == b_id
True
>>> d = [1,2,3]; id(d) == a_id
True
>>>
嵌套函数参数重新绑定
def create_printers(n):
for i in xrange(n):
def printer(i=i): # Doesn't work without the i=i
print i
yield printer
甚至可以替代文件删除、文件打开等内容 - 直接操作语言库。这是测试时的巨大优势。您不必将所有东西都包装在复杂的容器中。只需替换一个函数/方法即可。这也称为猴子修补。
操纵递归限制
使用 sys.getrecursionlimit() 和 sys.setrecursionlimit() 获取或设置最大递归深度。
我们可以限制它以防止无限递归导致的堆栈溢出。
如果在序列的最后一个元素之后找到逗号,Python 2.x 会忽略逗号:
>>> a_tuple_for_instance = (0,1,2,3,)
>>> another_tuple = (0,1,2,3)
>>> a_tuple_for_instance == another_tuple
True
尾随逗号导致单个带括号的元素被视为一个序列:
>>> a_tuple_with_one_element = (8,)
切片作为左值。这个 Eratosthenes 筛产生一个具有质数或 0 的列表。元素在循环中的切片分配中被 0 淘汰。
def eras(n):
last = n + 1
sieve = [0,0] + list(range(2, last))
sqn = int(round(n ** 0.5))
it = (i for i in xrange(2, sqn + 1) if sieve[i])
for i in it:
sieve[i*i:last:i] = [0] * (n//i - i + 1)
return filter(None, sieve)
要工作,左边的切片必须在右边分配一个相同长度的列表。
切片和可变性
复制列表
>>> x = [1,2,3]
>>> y = x[:]
>>> y.pop()
3
>>> y
[1, 2]
>>> x
[1, 2, 3]
替换列表
>>> x = [1,2,3]
>>> y = x
>>> y[:] = [4,5,6]
>>> x
[4, 5, 6]
整数舍入:Python 有函数 round,它返回 double 类型的数字:
>>> print round(1123.456789, 4)
1123.4568
>>> print round(1123.456789, 2)
1123.46
>>> print round(1123.456789, 0)
1123.0
这个函数有一个奇妙的神奇属性:
>>> print round(1123.456789, -1)
1120.0
>>> print round(1123.456789, -2)
1100.0
如果您需要整数作为结果,请使用 int 转换类型:
>>> print int(round(1123.456789, -2))
1100
>>> print int(round(8359980, -2))
8360000
谢谢格雷戈尔。_
懒得初始化字典中的每个字段?没问题:
在 Python > 2.3 中:
from collections import defaultdict
在 Python <= 2.3 中:
def defaultdict(type_):
class Dict(dict):
def __getitem__(self, key):
return self.setdefault(key, type_())
return Dict()
在任何版本中:
d = defaultdict(list)
for stuff in lots_of_stuff:
d[stuff.name].append(stuff)
更新:
谢谢肯·阿诺德。我重新实现了一个更复杂的 defaultdict 版本。它的行为应该与标准库中的完全一样。
def defaultdict(default_factory, *args, **kw):
class defaultdict(dict):
def __missing__(self, key):
if default_factory is None:
raise KeyError(key)
return self.setdefault(key, default_factory())
def __getitem__(self, key):
try:
return dict.__getitem__(self, key)
except KeyError:
return self.__missing__(key)
return defaultdict(*args, **kw)
如果您在类上使用描述符,Python 会完全绕过__dict__
该键,这使得它成为存储此类值的好地方:
>>> class User(object):
... def _get_username(self):
... return self.__dict__['username']
... def _set_username(self, value):
... print 'username set'
... self.__dict__['username'] = value
... username = property(_get_username, _set_username)
... del _get_username, _set_username
...
>>> u = User()
>>> u.username = "foo"
username set
>>> u.__dict__
{'username': 'foo'}
这有助于保持dir()
清洁。
列表推导
比较更传统的(没有列表理解):
foo = []
for x in xrange(10):
if x % 2 == 0:
foo.append(x)
至:
foo = [x for x in xrange(10) if x % 2 == 0]
__getattr__()
getattr
是创建泛型类的一种非常好的方法,如果您正在编写 API,这尤其有用。例如,在FogBugz Python API中,getattr
用于将方法调用无缝地传递给 Web 服务:
class FogBugz:
...
def __getattr__(self, name):
# Let's leave the private stuff to Python
if name.startswith("__"):
raise AttributeError("No such attribute '%s'" % name)
if not self.__handlerCache.has_key(name):
def handler(**kwargs):
return self.__makerequest(name, **kwargs)
self.__handlerCache[name] = handler
return self.__handlerCache[name]
...
当有人调用FogBugz.search(q='bug')
时,他们实际上并没有调用search
方法。相反,getattr
通过创建一个包装该方法的新函数来处理调用,该makerequest
方法为 Web API 制作适当的 HTTP 请求。任何错误都将由 Web 服务分派并传回给用户。
简单的内置基准测试工具
Python 标准库带有一个非常易于使用的基准测试模块,称为“timeit”。您甚至可以从命令行使用它来查看几种语言结构中哪一种是最快的。
例如,
% python -m timeit 'r = range(0, 1000)' 'for i in r: pass'
10000 loops, best of 3: 48.4 usec per loop
% python -m timeit 'r = xrange(0, 1000)' 'for i in r: pass'
10000 loops, best of 3: 37.4 usec per loop
这是2个复活节彩蛋:
python本身的一个:
>>> import __hello__
Hello world...
模块中的另一个Werkzeug
,显示起来有点复杂,这里是:
通过查看Werkzeug
的源代码,在 中werkzeug/__init__.py
,有一行应该引起您的注意:
'werkzeug._internal': ['_easteregg']
如果你有点好奇,这应该会引导你看一下,在werkzeug/_internal.py
那里,你会发现一个_easteregg()
函数,它在参数中接受一个 wsgi 应用程序,它还包含一些 base64 编码数据和 2 个嵌套函数,这似乎如果macgybarchakku
在查询字符串中找到名为的参数,请执行一些特殊操作。
所以,为了展示这个彩蛋,似乎你需要在_easteregg()
函数中包装一个应用程序,让我们开始吧:
from werkzeug import Request, Response, run_simple
from werkzeug import _easteregg
@Request.application
def application(request):
return Response('Hello World!')
run_simple('localhost', 8080, _easteregg(application))
现在,如果您运行应用程序并访问http://localhost:8080/?macgybarchakku,您应该会看到复活节彩蛋。
I'm not sure where (or whether) this is in the Python docs, but for python 2.x (at least 2.5 and 2.6, which I just tried), the print
statement can be called with parenthenses. This can be useful if you want to be able to easily port some Python 2.x code to Python 3.x.
Example:
print('We want Moshiach Now')
should print We want Moshiach Now
work in python 2.5, 2.6, and 3.x.
Also, the not
operator can be called with parenthenses in Python 2 and 3:
not False
and
not(False)
should both return True
.
Parenthenses might also work with other statements and operators.
EDIT: NOT a good idea to put parenthenses around not
operators (and probably any other operators), since it can make for surprising situations, like so (this happens because the parenthenses are just really around the 1
):
>>> (not 1) == 9
False
>>> not(1) == 9
True
This also can work, for some values (I think where it is not a valid identifier name), like this:
not'val'
should return False
, and print'We want Moshiach Now'
should return We want Moshiach Now
. (but not552
would raise a NameError since it is a valid identifier name).
getattr
接受第三个参数getattr(obj, attribute_name, default)
就好像:
try:
return obj.attribute
except AttributeError:
return default
除了attribute_name
可以是任何字符串。
这对于鸭子打字非常有用。也许你有类似的东西:
class MyThing:
pass
class MyOtherThing:
pass
if isinstance(obj, (MyThing, MyOtherThing)):
process(obj)
(顺便说一句,isinstance(obj, (a,b))
意思是isinstance(obj, a) or isinstance(obj, b)
。)
当你制作一种新的东西时,你需要将它添加到它出现的任何地方的元组中。(这种结构在重新加载模块或以两个名称导入同一个文件时也会导致问题。这种情况比人们愿意承认的要多。)但是你可以说:
class MyThing:
processable = True
class MyOtherThing:
processable = True
if getattr(obj, 'processable', False):
process(obj)
添加继承,它会变得更好:所有可处理对象的示例都可以继承自
class Processable:
processable = True
但是您不必说服所有人从您的基类继承,只需设置一个属性。
pythonic 成语x = ... if ... else ...
远远优于,x = ... and ... or ...
原因如下:
虽然声明
x = 3 if (y == 1) else 2
相当于
x = y == 1 and 3 or 2
如果你使用这个x = ... and ... or ...
成语,有一天你可能会被这个棘手的情况所困扰:
x = 0 if True else 1 # sets x equal to 0
因此不等于
x = True and 0 or 1 # sets x equal to 1
有关执行此操作的正确方法的更多信息,请参阅Python 的隐藏功能。
猴子修补对象
Python 中的每个对象都有一个__dict__
成员,用于存储对象的属性。因此,您可以执行以下操作:
class Foo(object):
def __init__(self, arg1, arg2, **kwargs):
#do stuff with arg1 and arg2
self.__dict__.update(kwargs)
f = Foo('arg1', 'arg2', bar=20, baz=10)
#now f is a Foo object with two extra attributes
这可以被用来向对象任意添加属性和功能。这也可以被利用来创建一个快速和肮脏的struct
类型。
class struct(object):
def __init__(**kwargs):
self.__dict__.update(kwargs)
s = struct(foo=10, bar=11, baz="i'm a string!')
除了前面提到的haridsv:
>>> foo = bar = baz = 1
>>> foo, bar, baz
(1, 1, 1)
也可以这样做:
>>> foo, bar, baz = 1, 2, 3
>>> foo, bar, baz
(1, 2, 3)
一切的第一类(“一切都是对象”),以及这可能造成的混乱。
>>> x = 5
>>> y = 10
>>>
>>> def sq(x):
... return x * x
...
>>> def plus(x):
... return x + x
...
>>> (sq,plus)[y>x](y)
20
最后一行创建一个包含两个函数的元组,然后计算 y>x (True) 并将其用作元组的索引(通过将其转换为 int, 1),然后使用参数 y 调用该函数并显示结果。
为了进一步滥用,如果您返回一个带有索引的对象(例如列表),您可以在末尾添加更多方括号;如果内容是可调用的,更多的括号,等等。对于额外的变态,使用这样的代码的结果作为另一个示例中的表达式(即用此代码替换 y>x):
(sq,plus)[y>x](y)[4](x)
这展示了 Python 的两个方面 - 将“一切都是对象”的哲学发挥到了极致,以及对语言语法的不当或构思不当使用可能导致完全不可读、不可维护的意大利面条代码的方法,这些代码适合单个表达式.
Python3 中的 Unicode 标识符:
>>> 'Unicode字符_تكوين_Variable'.isidentifier()
True
>>> Unicode字符_تكوين_Variable='Python3 rules!'
>>> Unicode字符_تكوين_Variable
'Python3 rules!'
您可以将多个变量分配给相同的值
>>> foo = bar = baz = 1
>>> foo, bar, baz
(1, 1, 1)
以紧凑的方式将多个变量初始化为 None 很有用。
利用 python 的动态特性,在 python 语法中拥有一个应用程序配置文件。例如,如果您在配置文件中有以下内容:
{
"name1": "value1",
"name2": "value2"
}
然后你可以像这样简单地阅读它:
config = eval(open("filename").read())
for 循环、列表推导和生成器表达式中的元组解包:
>>> l=[(1,2),(3,4)]
>>> [a+b for a,b in l ]
[3,7]
在这个习惯用法中用于迭代字典中的(键,数据)对:
d = { 'x':'y', 'f':'e'}
for name, value in d.items(): # one can also use iteritems()
print "name:%s, value:%s" % (name,value)
印刷:
name:x, value:y
name:f, value:e
特殊方法
对象实例的方法替换
您可以替换已创建的对象实例的方法。它允许您创建具有不同(异常)功能的对象实例:
>>> class C(object):
... def fun(self):
... print "C.a", self
...
>>> inst = C()
>>> inst.fun() # C.a method is executed
C.a <__main__.C object at 0x00AE74D0>
>>> instancemethod = type(C.fun)
>>>
>>> def fun2(self):
... print "fun2", self
...
>>> inst.fun = instancemethod(fun2, inst, C) # Now we are replace C.a by fun2
>>> inst.fun() # ... and fun2 is executed
fun2 <__main__.C object at 0x00AE74D0>
我们可以C.a
被替换为fun2()
实例inst
(self
没有改变)。
或者,我们可以使用new
模块,但它自 Python 2.6 起就贬值了:
>>> def fun3(self):
... print "fun3", self
...
>>> import new
>>> inst.fun = new.instancemethod(fun3, inst, C)
>>> inst.fun()
fun3 <__main__.C object at 0x00AE74D0>
节点:这个解决方案不应该作为继承机制的通用替代!但在某些特定情况下(调试、模拟)可能非常方便。
警告:此解决方案不适用于内置类型和使用插槽的新样式类。
通过少量的工作,线程模块变得非常容易使用。这个装饰器改变了一个函数,使它在自己的线程中运行,返回一个占位符类实例而不是它的常规结果。您可以通过检查 placeolder.result 来寻找答案,或者通过调用 placeholder.awaitResult() 等待它
def threadify(function):
"""
exceptionally simple threading decorator. Just:
>>> @threadify
... def longOperation(result):
... time.sleep(3)
... return result
>>> A= longOperation("A has finished")
>>> B= longOperation("B has finished")
A doesn't have a result yet:
>>> print A.result
None
until we wait for it:
>>> print A.awaitResult()
A has finished
we could also wait manually - half a second more should be enough for B:
>>> time.sleep(0.5); print B.result
B has finished
"""
class thr (threading.Thread,object):
def __init__(self, *args, **kwargs):
threading.Thread.__init__ ( self )
self.args, self.kwargs = args, kwargs
self.result = None
self.start()
def awaitResult(self):
self.join()
return self.result
def run(self):
self.result=function(*self.args, **self.kwargs)
return thr
访问字典元素作为属性(属性)。因此,如果 a1=AttrDict() 具有键 'name' -> 而不是 a1['name'] 我们可以使用 -> a1.name 轻松访问 a1 的 name 属性
class AttrDict(dict):
def __getattr__(self, name):
if name in self:
return self[name]
raise AttributeError('%s not found' % name)
def __setattr__(self, name, value):
self[name] = value
def __delattr__(self, name):
del self[name]
person = AttrDict({'name': 'John Doe', 'age': 66})
print person['name']
print person.name
person.name = 'Frodo G'
print person.name
del person.age
print person
插入与追加
不是功能,但可能很有趣
假设您想在列表中插入一些数据,然后将其反转。最简单的事情是
count = 10 ** 5
nums = []
for x in range(count):
nums.append(x)
nums.reverse()
然后你会想:不如从头开始插入数字呢?所以:
count = 10 ** 5
nums = []
for x in range(count):
nums.insert(0, x)
但它变慢了 100 倍!如果我们设置 count = 10 ** 6,它会慢 1000 倍;这是因为插入是 O(n^2),而追加是 O(n)。
造成这种差异的原因是每次调用 insert 时都必须移动列表中的每个元素;append 只需在列表末尾添加元素(有时它必须重新分配所有内容,但它仍然更快)
Python 对于非常意想不到的事情有例外:
进口
如果缺少库,这使您可以导入替代方案
try:
import json
except ImportError:
import simplejson as json
迭代
For 循环在内部执行此操作,并捕获 StopIteration:
iter([]).next()
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
iter(a).next()
StopIteration
断言
>>> try:
... assert []
... except AssertionError:
... print "This list should not be empty"
This list should not be empty
虽然这对于一次检查来说更加冗长,但可以通过这种方式缩短混合异常和布尔运算符与相同错误消息的多个检查。
结合拆包和打印功能:
# in 2.6 <= python < 3.0, 3.0 + the print function is native
from __future__ import print_function
mylist = ['foo', 'bar', 'some other value', 1,2,3,4]
print(*mylist)
Python 中没有秘密 ;)
模块在其命名空间中导出所有内容
包括从其他模块导入的名称!
# this is "answer42.py"
from operator import *
from inspect import *
现在测试可以从模块中导入的内容。
>>> import answer42
>>> answer42.__dict__.keys()
['gt', 'imul', 'ge', 'setslice', 'ArgInfo', 'getfile', 'isCallable', 'getsourcelines', 'CO_OPTIMIZED', 'le', 're', 'isgenerator', 'ArgSpec', 'imp', 'lt', 'delslice', 'BlockFinder', 'getargspec', 'currentframe', 'CO_NOFREE', 'namedtuple', 'rshift', 'string', 'getframeinfo', '__file__', 'strseq', 'iconcat', 'getmro', 'mod', 'getcallargs', 'isub', 'getouterframes', 'isdatadescriptor', 'modulesbyfile', 'setitem', 'truth', 'Attribute', 'div', 'CO_NESTED', 'ixor', 'getargvalues', 'ismemberdescriptor', 'getsource', 'isMappingType', 'eq', 'index', 'xor', 'sub', 'getcomments', 'neg', 'getslice', 'isframe', '__builtins__', 'abs', 'getmembers', 'mul', 'getclasstree', 'irepeat', 'is_', 'getitem', 'indexOf', 'Traceback', 'findsource', 'ModuleInfo', 'ipow', 'TPFLAGS_IS_ABSTRACT', 'or_', 'joinseq', 'is_not', 'itruediv', 'getsourcefile', 'dis', 'os', 'iand', 'countOf', 'getinnerframes', 'pow', 'pos', 'and_', 'lshift', '__name__', 'sequenceIncludes', 'isabstract', 'isbuiltin', 'invert', 'contains', 'add', 'isSequenceType', 'irshift', 'types', 'tokenize', 'isfunction', 'not_', 'istraceback', 'getmoduleinfo', 'isgeneratorfunction', 'getargs', 'CO_GENERATOR', 'cleandoc', 'classify_class_attrs', 'EndOfBlock', 'walktree', '__doc__', 'getmodule', 'isNumberType', 'ilshift', 'ismethod', 'ifloordiv', 'formatargvalues', 'indentsize', 'getmodulename', 'inv', 'Arguments', 'iscode', 'CO_NEWLOCALS', 'formatargspec', 'iadd', 'getlineno', 'imod', 'CO_VARKEYWORDS', 'ne', 'idiv', '__package__', 'CO_VARARGS', 'attrgetter', 'methodcaller', 'truediv', 'repeat', 'trace', 'isclass', 'ior', 'ismethoddescriptor', 'sys', 'isroutine', 'delitem', 'stack', 'concat', 'getdoc', 'getabsfile', 'ismodule', 'linecache', 'floordiv', 'isgetsetdescriptor', 'itemgetter', 'getblock']
>>> from answer42 import getmembers
>>> getmembers
<function getmembers at 0xb74b2924>
>>>
这是一个很好的理由不去from x import *
定义__all__ =
。
Everything is dynamic
"There is no compile-time". Everything in Python is runtime. A module is 'defined' by executing the module's source top-to-bottom, just like a script, and the resulting namespace is the module's attribute-space. Likewise, a class is 'defined' by executing the class body top-to-bottom, and the resulting namespace is the class's attribute-space. A class body can contain completely arbitrary code -- including import statements, loops and other class statements. Creating a class, function or even module 'dynamically', as is sometimes asked for, isn't hard; in fact, it's impossible to avoid, since everything is 'dynamic'.
布尔上下文中的对象
空元组、列表、字典、字符串和许多其他对象在布尔上下文中等效于 False(非空等效于 True)。
empty_tuple = ()
empty_list = []
empty_dict = {}
empty_string = ''
empty_set = set()
if empty_tuple or empty_list or empty_dict or empty_string or empty_set:
print 'Never happens!'
这允许逻辑操作返回其操作数之一而不是 True/False,这在某些情况下很有用:
s = t or "Default value" # s will be assigned "Default value"
# if t is false/empty/none
如果您在通过 Pickle 加载用户保存的文件的应用程序中重命名了一个类,并且其中一个重命名的类存储在用户的旧保存中,您将无法加载该 pickle 文件。
但是,只需添加对您的类定义的引用,一切都很好:
例如,之前:
class Bleh:
pass
现在,
class Blah:
pass
因此,您用户的腌制保存文件包含对 Bleh 的引用,由于重命名,该引用不存在。修复?
Bleh = Blah
简单的!
一次一屏打印多行字符串
隐藏在site._Printer
类中的不是真正有用的功能,其license
对象是一个实例。后者在调用时会打印 Python 许可证。可以创建另一个相同类型的对象,传递一个字符串——例如文件的内容——作为第二个参数,然后调用它:
type(license)(0,open('textfile.txt').read(),0)()
这将打印一次由一定数量的行分割的文件内容:
...
file row 21
file row 22
file row 23
Hit Return for more, or q (and Return) to quit:
** Using sets to reference contents in sets of frozensets**
您可能知道,集合是可变的,因此不可散列,因此如果您想创建一组集合(或使用集合作为字典键),则有必要使用 freezesets:
>>> fabc = frozenset('abc')
>>> fxyz = frozenset('xyz')
>>> mset = set((fabc, fxyz))
>>> mset
{frozenset({'a', 'c', 'b'}), frozenset({'y', 'x', 'z'})}
但是,可以仅使用普通集合来测试成员资格并删除/丢弃成员:
>>> abc = set('abc')
>>> abc in mset
True
>>> mset.remove(abc)
>>> mset
{frozenset({'y', 'x', 'z'})}
引用 Python 标准库文档:
请注意, 、 和 方法的参数可能
elem
是 一个集合。为了支持搜索等效的frozenset,该 set 在搜索过程中临时变异,然后恢复。在搜索过程中,不应读取或更改该集合,因为它没有有意义的值。__contains__()
remove()
discard()
elem
elem
不幸的是,也许令人惊讶的是,字典并非如此:
>>> mdict = {fabc:1, fxyz:2}
>>> fabc in mdict
True
>>> abc in mdict
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
TypeError: unhashable type: 'set'
使用 and 和 or 模拟三次运算符。
python 中的 and 和 or 运算符返回对象本身而不是布尔值。因此:
In [18]: a = True
In [19]: a and 3 or 4
Out[19]: 3
In [20]: a = False
In [21]: a and 3 or 4
Out[21]: 4
但是,Py 2.5 似乎添加了一个显式的三级运算符
In [22]: a = 5 if True else '6'
In [23]: a
Out[23]: 5
好吧,如果您确定您的 true 子句不会评估为 False,则此方法有效。例子:
>>> def foo():
... print "foo"
... return 0
...
>>> def bar():
... print "bar"
... return 1
...
>>> 1 and foo() or bar()
foo
bar
1
为了让它正确,你只需要多做一点:
>>> (1 and [foo()] or [bar()])[0]
foo
0
然而,这并不那么漂亮。如果您的 python 版本支持它,请使用条件运算符。
>>> foo() if True or bar()
foo
0
功能支持。
特别是生成器和生成器表达式。
Ruby 再次成为主流,但 Python 也可以做到这一点。在库中不像在 Ruby 中那样无处不在,这太糟糕了,但我更喜欢语法,它更简单。
因为它们并不普遍,所以我没有看到太多关于它们为什么有用的例子,但它们让我能够编写更清晰、更高效的代码。
Python 中有一个常见的习惯用法,即通过为方法和其他类成员指定以下划线开头的名称来表示它们不打算成为类的外部 API 的一部分。这很方便并且在实践中工作得很好,但它给人一种错误的印象,即 Python 不支持对私有代码和/或数据的真正封装。事实上,Python 会自动为您提供词法闭包,这使得在情况确实需要时以更加防弹的方式封装数据变得非常容易。这是一个使用这种技术的类的人为示例:
class MyClass(object):
def __init__(self):
privateData = {}
self.publicData = 123
def privateMethod(k):
print privateData[k] + self.publicData
def privilegedMethod():
privateData['foo'] = "hello "
privateMethod('foo')
self.privilegedMethod = privilegedMethod
def publicMethod(self):
print self.publicData
这是一个人为的使用示例:
>>> obj = MyClass()
>>> obj.publicMethod()
123
>>> obj.publicData = 'World'
>>> obj.publicMethod()
World
>>> obj.privilegedMethod()
hello World
>>> obj.privateMethod()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyClass' object has no attribute 'privateMethod'
>>> obj.privateData
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyClass' object has no attribute 'privateData'
关键是它根本不是 obj 的真正属性privateMethod
,privateData
所以它们不能从外部访问,也不能出现在dir()
或类似的地方。它们是构造函数中的局部变量,在__init__
. 但是,由于闭包的魔力,它们实际上是每个实例变量,与它们关联的对象具有相同的生命周期,即使除了(在此示例中)通过调用之外无法从外部访问它们privilegedMethod
。通常这种非常严格的封装是矫枉过正的,但有时它确实可以非常方便地保持 API 或命名空间的干净。
在 Python 2.x 中,拥有可变私有状态的唯一方法是使用可变对象(例如本例中的 dict)。许多人评论说这有多烦人。Python 3.x 将通过引入PEP 3104nonlocal
中描述的关键字来消除此限制。
测试键是否在字典中的简单方法:
>>> 'key' in { 'key' : 1 }
True
>>> d = dict(key=1, key2=2)
>>> if 'key' in d:
... print 'Yup'
...
Yup
以双下划线开头但不以双下划线结尾的变量成为私有变量,而不仅仅是按照惯例。实际上 __var 变成了 _Classname__var,其中 Classname 是创建变量的类。它们不是继承的,也不能被覆盖。
>>> class A:
... def __init__(self):
... self.__var = 5
... def getvar(self):
... return self.__var
...
>>> a = A()
>>> a.__var
Traceback (most recent call last):
File "", line 1, in
AttributeError: A instance has no attribute '__var'
>>> a.getvar()
5
>>> dir(a)
['_A__var', '__doc__', '__init__', '__module__', 'getvar']
>>>
事实上,一切都是对象,因此是可扩展的。我可以将成员变量作为元数据添加到我定义的函数中:
>>> def addInts(x,y):
... return x + y
>>> addInts.params = ['integer','integer']
>>> addInts.returnType = 'integer'
这对于编写动态单元测试非常有用,例如
getattr 内置函数:
>>> class C():
def getMontys(self):
self.montys = ['Cleese','Palin','Idle','Gilliam','Jones','Chapman']
return self.montys
>>> c = C()
>>> getattr(c,'getMontys')()
['Cleese', 'Palin', 'Idle', 'Gilliam', 'Jones', 'Chapman']
>>>
如果您想根据上下文调度函数,这很有用。请参阅 Dive Into Python (这里)中的示例
如果exec
在函数中使用,变量查找规则会发生巨大变化。闭包不再可能,但 Python 允许在函数中使用任意标识符。这为您提供了一个“可修改的 locals()”,并可用于星号导入标识符。不利的一面是,它使每次查找都变慢了,因为变量最终出现在 dict 而不是帧中的插槽中:
>>> def f():
... exec "a = 42"
... return a
...
>>> def g():
... a = 42
... return a
...
>>> import dis
>>> dis.dis(f)
2 0 LOAD_CONST 1 ('a = 42')
3 LOAD_CONST 0 (None)
6 DUP_TOP
7 EXEC_STMT
3 8 LOAD_NAME 0 (a)
11 RETURN_VALUE
>>> dis.dis(g)
2 0 LOAD_CONST 1 (42)
3 STORE_FAST 0 (a)
3 6 LOAD_FAST 0 (a)
9 RETURN_VALUE
关于 Nick Johnson 的Property 类的实现(只是描述符的演示,当然,不是内置的替代品),我会包含一个引发 AttributeError 的 setter:
类属性(对象): def __init__(self, fget): 自我.fget = fget def __get__(self, obj, type): 如果 obj 为无: 回归自我 返回 self.fget(obj) def __set__(self, obj, value): raise AttributeError, '只读属性'
包含 setter 使其成为数据描述符,而不是方法/非数据描述符。数据描述符优先于实例字典。现在一个实例不能有一个不同的对象分配给属性名称,并且尝试分配给该属性将引发属性错误。
spam
标准 Python 中的模块它用于测试目的。
我从ctypes
教程中选择了它。自己试试:
>>> import __hello__
Hello world...
>>> type(__hello__)
<type 'module'>
>>> from __phello__ import spam
Hello world...
Hello world...
>>> type(spam)
<type 'module'>
>>> help(spam)
Help on module __phello__.spam in __phello__:
NAME
__phello__.spam
FILE
c:\python26\<frozen>
曾经使用过 xrange(INT) 而不是 range(INT) .... 它的内存使用量更少,并且并不真正取决于整数的大小。耶!!那不是很好吗?
>>> float('infinity')
inf
>>> float('NaN')
nan
更多信息:
根本不是隐藏的功能,但仍然很好:
import os.path as op
root_dir = op.abspath(op.join(op.dirname(__file__), ".."))
操作路径时节省大量字符!
内存管理
Python 动态分配内存并使用垃圾回收来恢复未使用的空间。一旦对象超出范围,并且没有其他变量引用它,它将被恢复。我不必担心缓冲区溢出和缓慢增长的服务器进程。内存管理也是其他动态语言的一个特性,但 Python 做得很好。
当然,我们必须注意循环引用并保留对不再需要的对象的引用,但弱引用在这里有很大帮助。
类作为第一类对象(通过动态类定义显示)
还要注意闭包的使用。如果这个特定示例看起来像是解决问题的“正确”方法,请仔细重新考虑......几次:)
def makeMeANewClass(parent, value):
class IAmAnObjectToo(parent):
def theValue(self):
return value
return IAmAnObjectToo
Klass = makeMeANewClass(str, "fred")
o = Klass()
print isinstance(o, str) # => True
print o.theValue() # => fred
不是真正的隐藏功能,但可能会派上用场。
用于成对循环列表中的项目
for x, y in zip(s, s[1:]):
mapreduce 使用 map 和 reduce 函数
以这种方式创建一个简单的 sumproduct :
def sumprod(x,y):
return reduce(lambda a,b:a+b, map(lambda a, b: a*b,x,y))
例子:
In [2]: sumprod([1,2,3],[4,5,6])
Out[2]: 32
bash
不是编程功能,但在将 Python 与或一起使用时很有用shell scripts
。
python -c"import os; print(os.getcwd());"
请参阅此处的 python 文档。在此讨论中可以看到编写较长 Python 脚本时需要注意的其他事项。
>>> x=[1,1,2,'a','a',3]
>>> y = [ _x for _x in x if not _x in locals()['_[1]'] ]
>>> y
[1, 2, 'a', 3]
“locals()['_[1]']”是正在创建的列表的“秘密名称”。当正在构建的列表状态影响后续构建决策时非常有用。
Python 的位置和关键字扩展可以即时使用,而不仅仅是来自存储列表。
l=lambda x,y,z:x+y+z
a=1,2,3
print l(*a)
print l(*[a[0],2,3])
它通常对这样的事情更有用:
a=[2,3]
l(*(a+[3]))
reduce 和 operator 的一些很酷的功能。
>>> from operator import add,mul
>>> reduce(add,[1,2,3,4])
10
>>> reduce(mul,[1,2,3,4])
24
>>> reduce(add,[[1,2,3,4],[1,2,3,4]])
[1, 2, 3, 4, 1, 2, 3, 4]
>>> reduce(add,(1,2,3,4))
10
>>> reduce(mul,(1,2,3,4))
24
脚本的交互式调试(和 doctest 字符串)
我不认为这是众所周知的,但将此行添加到任何 python 脚本中:
import pdb; pdb.set_trace()
将导致 PDB 调试器在代码中的该点弹出运行光标。我认为,更鲜为人知的是,您可以在 doctest 中使用同一行:
"""
>>> 1 in (1,2,3)
Becomes
>>> import pdb; pdb.set_trace(); 1 in (1,2,3)
"""
然后,您可以使用调试器检查 doctest 环境。您不能真正单步执行 doctest,因为这些行都是自主运行的,但它是调试 doctest glob 和环境的好工具。
这是我在调试类型错误时使用的一个有用的功能
def typePrint(object):
print(str(object) + " - (" + str(type(object)) + ")")
它只是打印输入,然后是类型,例如
>>> a = 101
>>> typePrint(a)
101 - (<type 'int'>)
您可以根据需要构造函数 kwargs:
kwargs = {}
kwargs[str("%s__icontains" % field)] = some_value
some_function(**kwargs)
不知何故需要 str() 调用,因为 python 抱怨它不是字符串。不知道为什么;)我将它用于 Django 对象模型中的动态过滤器:
result = model_class.objects.filter(**kwargs)
将字符串相乘以使其重复
print "SO"*5
给
SOSOSOSOSO
在 Python 2 中,您可以通过用反引号括起来来生成表达式的字符串表示:
>>> `sorted`
'<built-in function sorted>'
这在 python 3.X 中消失了。
commands.getoutput
如果您想获得一个函数的输出,该函数直接输出到stdout
或stderr
与 一样os.system
,commands.getoutput
来救援。整个模块是由真棒组成的。
>>> print commands.getoutput('ls')
myFile1.txt myFile2.txt myFile3.txt myFile4.txt myFile5.txt
myFile6.txt myFile7.txt myFile8.txt myFile9.txt myFile10.txt
myFile11.txt myFile12.txt myFile13.txt myFile14.txt module.py
is_ok() and "Yes" or "No"
for line in open('foo'):
print(line)
which is equivalent (but better) to:
f = open('foo', 'r')
for line in f.readlines():
print(line)
f.close()
在接受它的 IDE 中激活自动完成功能(如 IDLE、Editra、IEP),而不是制作:“hi”。(然后你点击TAB),你可以在IDE中作弊,只需打个招呼“。(然后你加热TAB)(如你所见,开头没有单引号)因为它只会跟随最新的标点符号,就像当你添加 : 并回车时,它直接添加一个缩进,不知道它是否会改变,但它不再是一个提示:)
大括号
def g():
print 'hi!'
def f(): (
g()
)
>>> f()
hi!