Python编程语言有哪些鲜为人知但有用的功能?
尝试限制Python核心的答案.
每个答案一个功能.
举一个示例和该功能的简短描述,而不仅仅是文档的链接.
使用标题作为第一行标记要素.
参数解包
背带
链式比较运算符
装饰
默认参数陷阱/可变默认参数的危险
叙
字典默认.get
值
文档字符串测试
省略号切片语法
列举
对于/其他
作为iter()参数的函数
生成器表达式
import this
就地价值交换
列出步进
__missing__
项目
多行正则表达式
命名字符串格式
嵌套列表/生成器理解
运行时的新类型
.pth
档
ROT13编码
正则表达式调试
发送给生成器
交互式解释器中的选项卡完成
三元表达
try/except/else
拆包+ print()
功能
with
声明
Thomas Woute.. 740
>>> x = 5 >>> 1 < x < 10 True >>> 10 < x < 20 False >>> x < 10 < x*10 < 100 True >>> 10 > x <= 9 True >>> 5 == x > 4 True
如果你正在考虑它正在做什么1 < x
,True
然后进行比较True < 10
,然后比较,那么True
,那么不,那真的不是发生了什么(参见最后一个例子.)它真正转化为1 < x and x < 10
,并且x < 10 and 10 < x * 10 and x*10 < 100
,但是打字较少,每个术语仅评估一次.
>>> x = 5 >>> 1 < x < 10 True >>> 10 < x < 20 False >>> x < 10 < x*10 < 100 True >>> 10 > x <= 9 True >>> 5 == x > 4 True
如果你正在考虑它正在做什么1 < x
,True
然后进行比较True < 10
,然后比较,那么True
,那么不,那真的不是发生了什么(参见最后一个例子.)它真正转化为1 < x and x < 10
,并且x < 10 and 10 < x * 10 and x*10 < 100
,但是打字较少,每个术语仅评估一次.
获取python正则表达式解析树来调试你的正则表达式.
正则表达式是python的一个很好的特性,但调试它们可能会很麻烦,并且很容易让正则表达式出错.
幸运的是,python可以通过传递未记录的,实验性的隐藏标志re.DEBUG
(实际上是128)来打印正则表达式解析树re.compile
.
>>> re.compile("^\[font(?:=(?P[-+][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# 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)
枚举
用枚举包装一个iterable,它将产生项目及其索引.
例如:
>>> a = ['a', 'b', 'c', 'd', 'e'] >>> for index, item in enumerate(a): print index, item ... 0 a 1 b 2 c 3 d 4 e >>>
参考文献:
Python教程循环技术
Python docs内置函数 - enumerate
PEP 279
创建生成器对象
如果你写
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]
相反,你应该使用表示"not given"的sentinel值,并将你想要的mutable替换为默认值:
>>> 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
切片运算符中的步骤参数.例如:
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]
装饰
装饰器允许将函数或方法包装在另一个可以添加功能,修改参数或结果等的函数中.您可以在函数定义上方一行编写装饰器,以"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循环结束时执行.
上面的代码可以模拟如下:
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
调用defaultdict
中还有一个dict子类几乎完全相同,但是对于不存在的项调用没有参数的函数:
>>> from collections import defaultdict >>> m = defaultdict(list) >>> m["foo"].append(1) >>> m["foo"].append(2) >>> dict(m) {'foo': [1, 2]}
我建议将这些dicts转换为常规dicts,然后再将它们传递给不期望这些子类的函数.很多代码使用d[a_key]
并捕获KeyErrors来检查是否存在可以向dict添加新项的项.
就地价值交换
>>> a = 10 >>> b = 5 >>> a, b (10, 5) >>> a, b = b, a >>> a, b (5, 10)
赋值的右侧是一个创建新元组的表达式.赋值的左侧立即将该(未引用的)元组解包为名称a
和b
.
转让后,新的元组是未引用和标记为垃圾回收,和值必然a
与b
被交换.
如关于数据结构的Python教程部分所述,
请注意,多重赋值实际上只是元组打包和序列解包的组合.
可读的正则表达式
在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)
命名匹配的示例(来自Regular Expression HOWTO)
>>> p = re.compile(r'(?P\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})$"
函数参数解包
您可以解压缩列表或使用字典作为函数的参数*
和**
.
例如:
def draw_point(x, y): # do some magic point_foo = (3, 4) point_bar = {'y': 3, 'x': 2} draw_point(*point_foo) draw_point(**point_bar)
非常有用的快捷方式,因为列表,元组和dicts被广泛用作容器.
当您在代码文件的顶部使用正确的编码声明时,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"
可能不是最有用的东西,但很高兴知道.
编辑:固定新类型的名称,应该NewType
与class
语句完全相同.
编辑:调整标题以更准确地描述该功能.
上下文管理器和" 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__
方法.__exit__
如果从with语句体引发任何异常,也会传递异常详细信息,从而允许在那里进行异常处理.
在这种特殊情况下,它对您的作用是它保证在执行超出with
套件范围时关闭文件,无论是否正常发生或是否抛出异常.它基本上是一种抽象掉常见异常处理代码的方法.
其他常见用例包括使用线程和数据库事务进行锁定.
字典有一个'get()'方法.如果你执行d ['key']并且键不存在,则会出现异常.如果你做了d.get('key'),如果'key'不存在,你就会回来.您可以添加第二个参数来获取该项而不是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)
你就像内置属性()一样使用它:
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".请注意,parens不是必需的,但我喜欢它们以便于阅读.如果你有更复杂的东西,你也可以链接它:
x = 3 if (y == 1) else 2 if (y == -1) else 1
虽然在某个时刻,它有点太过分了.
请注意,您可以在任何表达式中使用if ... else.例如:
(func1 if y == 1 else func2)(arg1, arg2)
如果y为1,则调用func1,否则调用func2.在这两种情况下,将使用参数arg1和arg2调用相应的函数.
类似地,以下内容也是有效的:
x = (class1 if y == 1 else class2)(arg1, arg2)
其中class1和class2是两个类.
从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模块(特别是第三方模块),大多数人似乎都使用PYTHONPATH环境变量,或者在他们的site-packages目录中添加符号链接或目录.另一种方法是使用*.pth文件.这是官方python doc的解释:
"[修改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语句保护的代码未引发的异常.
请参见http://docs.python.org/tut/node10.html
重新提出例外:
# 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'模块进行打印.
主要信息:)
import this # btw look at this module's source :)
解密:
Tim Peters的Python之禅
美丽胜过丑陋.
显式优于隐式.
简单比复杂更好.
复杂比复杂更好.
Flat优于嵌套.
稀疏优于密集.
可读性很重要.
特殊情况不足以打破规则.
虽然实用性胜过纯洁.
错误不应该默默地传递.
除非明确沉默.
面对模棱两可,拒绝猜测的诱惑.应该有一个 - 最好只有一个 - 明显的方法来做到这一点.
虽然这种方式起初可能并不明显,除非你是荷兰人.
现在比永远好.正确的现在.
虽然从来没有经常好过
如果实施很难解释,这是一个坏主意.
如果实现很容易解释,那可能是个好主意.
命名空间是一个很棒的主意 - 让我们做更多的事情吧!
Interactive Interpreter选项卡完成
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.class_instance.__class__ class_instance.__module__ class_instance.__doc__ class_instance.function >>> class_instance.f unction()
您还必须设置PYTHONSTARTUP环境变量.
嵌套列表推导和生成器表达式:
[(i,j) for i in range(3) for j in range(i) ] ((i,j) for i in range(4) for j in range(i) )
这些可以取代大块的嵌套循环代码.
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}
标准库参考的更多细节:设置类型
负圆
该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)
不幸的是,它几乎没有用,因为只有在涉及元组的情况下才支持省略号.
事实上你可以在每次匹配正则表达式时调用函数都非常方便.在这里,我有一个样本,用"Hi"替换每个"Hello",用"Fred"等替换"there"等.
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 for a in xrange(10000))>>> b = 'blah' >>> _
一个方便的Web浏览器控制器:
>>> import webbrowser >>> webbrowser.open_new_tab('http://www.stackoverflow.com')
内置的http服务器.要提供当前目录中的文件:
python -m SimpleHTTPServer 8000
atexit对
>>> import atexit
有一个鲜为人知的内置pow()
函数的第三个参数,它允许你比简单地做更有效地计算x y modulo 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
已经部分涵盖了这个答案,但最近我发现了一个更隐藏的功能enumerate
,我觉得应该发表自己的帖子,而不仅仅是评论.
从Python 2.6开始,您可以enumerate
在第二个参数中指定起始索引:
>>> l = ["spam", "ham", "eggs"] >>> list(enumerate(l)) >>> [(0, "spam"), (1, "ham"), (2, "eggs")] >>> list(enumerate(l, 1)) >>> [(1, "spam"), (2, "ham"), (3, "eggs")]
我发现它非常有用的一个地方就是我在列举对称矩阵的条目时.由于矩阵是对称的,我可以通过迭代上三角形来节省时间,但在这种情况下,我必须enumerate
在内部for
循环中使用不同的起始索引来正确跟踪行和列索引:
for ri, row in enumerate(matrix): for ci, column in enumerate(matrix[ri:], ri): # ci now refers to the proper column index
奇怪的是,这种行为enumerate
没有记录在案help(enumerate)
,仅在在线文档中.
您可以使用属性使您的类接口更严格.
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__)
在Python 2.6和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
要了解有关属性如何工作的更多信息,请参阅描述符.
很多人不了解"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.
集/ frozenset
可能容易被忽视的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}
有关更多详细信息,请访问:http: //docs.python.org/library/stdtypes.html#set
内置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'
当然,你可以腐烂13:
>>> '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) [, '', '/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的构造函数接受关键字参数:
>>> 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
模块运算符中的Getter功能
函数attrgetter()
和itemgetter()
模块operator
可用于生成快速访问函数,以用于排序和搜索对象和字典
Python库文档中的第6.7章
交错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之前,我从未意识到这一点.
元组拆包:
>>> (a, (b, c), d) = [(1, 2), (3, 4), (5, 6)] >>> a (1, 2) >>> b 3 >>> c, d (4, (5, 6))
更晦涩的是,您可以在函数参数中执行此操作(在Python 2.x中; Python 3.x将不再允许此操作):
>>> def addpoints((x1, y1), (x2, y2)): ... return (x1+x2, y1+y2) >>> addpoints((5, 0), (3, 5)) (8, 5)
显然,反重力模块. xkcd#353
Python解释器
>>>
也许不是鲜为人知,但肯定是我最喜欢的Python功能之一.
Python排序函数正确排序元组(即使用熟悉的词典顺序):
a = [(2, "b"), (1, "a"), (2, "a"), (3, "c")] print sorted(a) #[(1, 'a'), (2, 'a'), (2, 'b'), (3, 'c')]
如果您想在年龄之后对人员列表进行排序然后命名,则非常有用.
您可以引用列表推导,因为它是由符号'_ [1]'构建的.例如,以下函数unique-ifies元素列表,而不通过引用其列表推导来更改其顺序.
def unique(my_list): return [x for x in my_list if x not in locals()['_[1]']]
解压缩语法已在最新版本中升级,如示例中所示.
>>> 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)
简单:
>>> 'str' in 'string' True >>> 'no' in 'yes' False >>>
是我喜欢Python的东西,我已经看到很多不那么像这样的pythonic成语:
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 ... """
同样很酷:不必逃避正则表达式,使用原始字符串避免可怕的反斜杠沙拉:
str2 = r"\n" print str2 >> \n
发电机
我认为很多初学的Python开发人员都没有真正掌握它们的用途或者对它们的能力有所了解.直到我读到David M. Beazley关于发电机的PyCon演示文稿(它可以在这里找到),我才意识到它们是多么有用(必不可少).那个演示文稿阐明了对我来说是一种全新的编程方式,我推荐给那些对发电机没有深刻理解的人.
隐式连接:
>>> print "Hello " "World" Hello World
想要在脚本中的多行上创建长文本时很有用:
hello = "Greaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Hello " \ "Word"
要么
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] >>>
textwrap.dedent
python中的实用程序函数可以非常方便地测试返回的多行字符串是否等于预期输出而不会破坏单元测试的缩进:
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("""\""") self.assertEqual(expected_value, returned_xml) my_content
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,因为它应该是-1,而不是像其他语言一样的JavaScript.这使得"环绕式窗口"在Python中变得更干净,你只需这样做:
index = (index + increment) % WINDOW_SIZE
一流的功能
它并不是一个隐藏的功能,但功能是一流的对象这一事实非常棒.你可以像任何其他变量一样传递它们.
>>> 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'
但是,如果您要使用的值将被视为false,则存在差异:
>>> [] if True else 'spam' [] >>> True and [] or 'spam' 'spam'
分配和删除切片:
>>> 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 doNothing(): pass doNothing.monkeys = 4 print doNothing.monkeys 4
很多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': {...}}}
使用负步骤反转可迭代
>>> 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]
不是"隐藏"但非常有用且不常用
像这样快速创建字符串连接功能
comma_join = ",".join semi_join = ";".join print comma_join(["foo","bar","baz"]) 'foo,bar,baz
和
能够比引号,逗号混乱更优雅地创建字符串列表.
l = ["item1", "item2", "item3"]
取而代之
l = "item1 item2 item3".split()
可以说,这本身并不是一个编程功能,但是非常有用,我会发布它.
$ python -m http.server
......其次是$ wget http://
其他地方.
如果您仍在运行较旧的(2.x)版本的Python:
$ python -m SimpleHTTPServer
您也可以指定一个端口python -m http.server 80
(例如,如果您在服务器端有根,则可以省略URL中的端口)
对迭代器的多个引用
您可以使用列表乘法创建对同一迭代器的多个引用:
>>> 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'?????' >>> s u'\uff11\uff10\uff15\uff18\uff15' >>> print s ????? >>> 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}
itertools
这个模块经常被忽视.以下示例用于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.
您可以直接操作模块缓存,使模块可用或不可用:
>>> import sys >>> import ham Traceback (most recent call last): File "", line 1, in 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 " ", line 1, in ImportError: No module named ham
这适用于可用的模块,在某种程度上适用于已导入的模块:
>>> import os # Stop future imports of 'os'. >>> sys.modules['os'] = None >>> import os Traceback (most recent call last): File "", line 1, in ImportError: No module named os # Our old imported module is still available. >>> os
如最后一行所示,更改sys.modules只影响将来的import
语句,而不是过去的语句,所以如果你想影响其他模块,在给他们机会尝试导入模块之前进行这些更改是很重要的 - 所以在你之前通常导入它们.None
是一个特殊的值sys.modules
,用于负缓存(表示第一次找不到模块,所以再看一遍没有意义.)任何其他值都将是import
操作的结果- 即使它不是模块对象.您可以使用它来替换具有与您想要的行为完全相同的对象的模块.sys.modules
完全删除条目会导致下一个条目import
正常搜索模块,即使它之前已经导入过.
您可以通过查看其__ 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}
蟒蛇的轻微错误.将字符串列表连接在一起的正常快速方法是,
''.join(list_of_strings)
创建枚举
在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
对象数据模型
您可以使用您自己的类的语言覆盖任何运算符.有关完整列表,请参阅此页面.一些例子:
您可以覆盖任何运算符(* + - / // % ^ == < > <= >= .
等).所有这一切都是通过重写完成__mul__
,__add__
等你的对象.你甚至可以忽略的东西喜欢__rmul__
单独处理your_object*something_else
和something_else*your_object
. .
是属性access(a.b
),可以b
通过使用来覆盖任何任意处理__getattr__
.此处还包括a(…)
使用__call__
.
你可以创建自己的切片语法(a[stuff]
),这是非常复杂的,从列表(numpy的有这在他们的阵列电源的一个很好的例子),用于使用任意组合的标准语法完全不同,
,:
和…
你喜欢,使用Slice对象.
特别处理语言中的许多关键字会发生什么.包括的是del
,in
,import
,和not
.
处理当您的对象调用许多内置函数时会发生什么.标准的__int__
,__str__
等去这里,但这么做__len__
,__reversed__
,__abs__
,和三个参数__pow__
(模块化幂).
该re.Scanner
班.http://code.activestate.com/recipes/457664-hidden-scanner-functionality-in-re-module/
"解包"到功能参数
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!
在运行时更改功能标签:
>>> 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 ...
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') ??????, ????!
解压缩Python中不需要的东西
有人在博客上发表了关于Python没有解压缩功能的解决方案.unzip是直接计算的,因为:
>>> t1 = (0,1,2,3) >>> t2 = (7,6,5,4) >>> [t1,t2] == zip(*zip(t1,t2)) True
但是在反思时,我宁愿有一个明确的解压缩().
创建具有相关数据的两个序列的字典
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}
最高机密属性
>>> 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 "", 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'
>>> 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
.
动态添加属性
如果您考虑通过调用它们来为类添加一些属性,这可能会很有用.这可以通过覆盖__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()
.该sum()
内置函数可用于小号一起,提供了一种方便的方法,以一个扁平化的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
可以在任何实例中重新分配值,并且所有值都将更新,您甚至可以重新分配整个dict.博格是一个完美的名字,在这里阅读更多.
class Borg: __shared_state = {'foo': 'bar'} def __init__(self): self.__dict__ = self.__shared_state # rest of your class here
这非常适合共享eventlet.GreenPool来控制并发性.
pdb - Python调试器
作为程序员,严肃的程序开发需要的第一件事就是调试器.Python有一个内置的,可以作为一个名为pdb的模块使用(对于"Python DeBugger",当然!).
http://docs.python.org/library/pdb.html
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.
看到
... dict.get()
具有默认值 None,从而避免了KeyErrors:
In [1]: test = { 1 : 'a' } In [2]: test[2] ---------------------------------------------------------------------------Traceback (most recent call last) <ipython console> in () : 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
是你的朋友.但是,了解其原因仍然有用.
原始字符串中的反斜杠仍然可以转义引号.看到这个:
>>> 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文件名中的反斜杠.
操作符可以作为函数调用:
from operator import add print reduce(add, [1,2,3,4,5,6])
列表中的无限递归
>>> 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
能够替换文件删除,文件打开等内容 - 直接操作语言库.这在测试时是一个巨大的优势.您不必将所有内容都包装在复杂的容器中.只需替换一个函数/方法然后去.这也称为猴子修补.
内置方法或函数不实现描述符协议,这使得无法执行以下操作:
>>> class C(object): ... id = id ... >>> C().id() Traceback (most recent call last): File "", line 1, in 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
嵌套函数参数重新绑定
def create_printers(n): for i in xrange(n): def printer(i=i): # Doesn't work without the i=i print i yield printer
您可以使用元类覆盖类的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 "", line 1, in AttributeError: 'C' object has no attribute 'a_method' >>> cls.b_method() B >>> type(cls).__bases__ ( ,) >>> type(cls).__mro__ ( , , )
这可能是有充分理由隐藏的.:)
小整数(-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
>>>
您可以使用类来装饰函数 - 用类实例替换函数:
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)
操纵递归限制
使用sys.getrecursionlimit()和sys.setrecursionlimit()获取或设置最大递归深度.
我们可以限制它以防止由无限递归引起的堆栈溢出.
切片和可变性
复制列表
>>> 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 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)
要工作,必须在左侧的切片上指定相同长度右侧的列表.
舍入整数: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
谢谢Gregor.
列表理解
列表理解
比较更传统(没有列表理解):
foo = [] for x in xrange(10): if x % 2 == 0: foo.append(x)
至:
foo = [x for x in xrange(10) if x % 2 == 0]
懒得初始化字典中的每个字段?没问题:
在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()
清洁.
__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 缓冲协议,以暴露可变面向字节的缓冲器在Python(2.5/2.6).
(对不起,此处没有代码.需要使用低级C API或现有适配器模块).
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的隐藏功能.
Monkeypatching对象
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!')
我不确定它在Python文档中的位置(或者是否),但是对于python 2.x(至少2.5和2.6,我刚试过),print
可以使用parenthenses调用该语句.如果您希望能够轻松地将一些Python 2.x代码移植到Python 3.x,这将非常有用.
示例:
print('We want Moshiach Now')
应该We want Moshiach Now
在python 2.5,2.6和3.x中打印工作.
此外,not
可以在Python 2和3中使用parenthenses调用运算符:
not False
并且
not(False)
都应该返回True
.
parenthenses也可以与其他语句和运算符一起使用.
编辑:不是一个好主意把括号放在not
运算符(可能还有其他运算符)周围,因为它可以产生令人惊讶的情况,就像这样(这种情况发生是因为括号只是真的围绕着1
):
>>> (not 1) == 9 False >>> not(1) == 9 True
对于某些值(我认为它不是有效的标识符名称),这也可以工作,如下所示:
not'val'
应该返回False
,并且print'We want Moshiach Now'
应该返回We want Moshiach Now
.(但not552
会引发NameError,因为它是一个有效的标识符名称).
除了之前由haridsv提到的:
>>> foo = bar = baz = 1 >>> foo, bar, baz (1, 1, 1)
它也可以这样做:
>>> foo, bar, baz = 1, 2, 3 >>> foo, bar, baz (1, 2, 3)
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
但是你不必说服每个人继承你的基类,只是为了设置一个属性.
简单的内置基准测试工具
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,你应该看到复活节彩蛋.
Dict理解
>>> {i: i**2 for i in range(5)} {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
Python文档
维基百科条目
设置理解
>>> {i**2 for i in range(5)} set([0, 1, 4, 16, 9])
Python文档
维基百科条目