Python的隐藏功能
Python编程语言鲜为人知但有用的功能是什么?
- 尝试将答案限于Python核心。
- 每个答案一个功能。
- 给出该功能的示例和简短描述,而不仅仅是文档链接。
- 使用标题作为第一行标记功能。
答案的快速链接:
- 参数解包
- 大括号
- 链接比较运算符
- 装饰工
- 默认参数陷阱/可变默认参数的危险
- 描述符
- 字典默认
.get
值 - Docstring测试
- 省略号切片语法
- 枚举
- 对于/其他
- 用作iter()参数
- 生成器表达式
导入这个
- 就地价值交换
- 列表步进
__missing__
项目- 多行正则表达式
- 命名字符串格式
- 嵌套列表/生成器理解
- 运行时的新类型
- .pth文件
- ROT13编码
- 正则表达式调试
- 发送给发电机
- 交互式解释器中的制表符完成
- 三元表达
try / except / else
- 开箱+
print()
函数 with
语句
解决方案
主要消息:)
import this # btw look at this module's source :)
解密:
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!
清单理解
清单理解
比较更传统的(没有列表理解):
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中的元类是什么?
特殊方法
绝对力量!
创建生成器对象
如果你写
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)
装饰工
装饰器允许将一个函数或者方法包装在另一个函数中,该函数或者方法可以添加功能,修改参数或者结果等。我们可以在函数定义上方一行以" 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
可读的正则表达式
在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})$"
嵌套列表推导和生成器表达式:
[(i,j) for i in range(3) for j in range(i) ] ((i,j) for i in range(4) for j in range(i) )
这些可以替换大量的嵌套循环代码。
模块运算符中的Getter函数
运算符模块中的attrgetter()和itemgetter()函数可用于生成快速访问函数,以用于对对象和字典进行排序和搜索
Python库文档中的6.7章
将值发送到生成器函数中。例如,具有以下功能:
def mygen(): """Yield 5 until something else is passed back via send()""" a = 5 while True: f = (yield a) #yield a and possibly get f in return if f is not None: a = f #store the new value
你可以:
>>> g = mygen() >>> g.next() 5 >>> g.next() 5 >>> g.send(7) #we send this back to the generator 7 >>> g.next() #now it will yield 7 until we send something else 7
甚至可以替代文件删除,文件打开等功能,直接操作语言库。测试时,这是一个巨大的优势。我们不必将所有内容都包装在复杂的容器中。只需替换一个函数/方法就可以了。这也称为猴子修补。
>>> 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]']"是所创建列表的"秘密名称"。当正在构建的列表状态影响后续的构建决策时,此功能非常有用。
切片运算符中的step参数。例如:
a = [1,2,3,4,5] >>> a[::2] # iterate over the whole list in 2-increments [1,3,5]
特例x [::-1]是有用的成语,用于x反转。
>>> a[::-1] [5,4,3,2,1]
隐式串联:
>>> print "Hello " "World" Hello World
当我们想要使长文本适合脚本中的多行时很有用:
hello = "Greaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Hello " \ "Word"
或者
hello = ("Greaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Hello " "Word")
我们可以使用property使类接口更加严格。
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
要了解有关属性如何工作的更多信息,请参考描述符。
链接比较运算符:
>>> 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和x <10"," x <10和10 <x * 10和x * 10 <100",但是键入较少,每个术语只被评估一次。
一切都是动态的
"没有编译时间"。 Python中的所有内容都是运行时。就像脚本一样,通过自上而下执行模块的源代码来"定义"模块,并且结果命名空间就是模块的属性空间。同样,通过从上到下执行类主体来"定义"一个类,并且结果命名空间是该类的属性空间。类主体可以包含完全任意的代码-包括import语句,循环和其他类语句。有时会要求"动态地"创建类,函数甚至模块,这并不难。实际上,这是不可避免的,因为一切都是"动态的"。
重新引发例外:
# 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一样通过"回溯"模块完成打印。
就地价值交换
>>> a = 10 >>> b = 5 >>> a, b (10, 5) >>> a, b = b, a >>> a, b (5, 10)
赋值的右侧是创建新元组的表达式。作业的左侧立即将(未引用的)元组解压缩为名称" a"和" b"。
分配后,新的元组将不被引用并标记为垃圾回收,并且绑定到a
和b
的值已被交换。
如Python教程中有关数据结构的部分所述,
Note that multiple assignment is really just a combination of tuple packing and sequence unpacking.
描述符
它们是一大堆Python核心功能背后的魔力。
当我们使用点分访问来查找成员(例如x.y)时,Python首先在实例字典中查找该成员。如果找不到,它将在类字典中查找。如果它在类字典中找到它,并且该对象实现了描述符协议,而不是仅仅返回它,Python就会执行它。描述符是实现__get__
,__set__
或者__delete__
方法的任何类。
这是使用描述符实现自己的(只读)版本的属性的方法:
class Property(object): def __init__(self, fget): self.fget = fget def __get__(self, obj, type): if obj is None: return self return self.fget(obj)
我们将像内置的property()一样使用它:
class MyClass(object): @Property def foo(self): return "Foo!"
描述符在Python中用于实现属性,绑定方法,静态方法,类方法和插槽等。理解它们可以很容易地看出为什么以前看起来像Python的"怪癖"的很多东西都是它们的样子。
Raymond Hettinger的教程很棒,比我做得更好。
Doctest:同时进行文档编制和单元测试。
从Python文档中提取的示例:
def factorial(n): """Return the factorial of n, an exact integer >= 0. If the result is small enough to fit in an int, return an int. Else return a long. >>> [factorial(n) for n in range(6)] [1, 1, 2, 6, 24, 120] >>> factorial(-1) Traceback (most recent call last): ... ValueError: n must be >= 0 Factorials of floats are OK, but the float must be an exact integer: """ import math if not n >= 0: raise ValueError("n must be >= 0") if math.floor(n) != n: raise ValueError("n must be exact integer") if n+1 == n: # catch a value like 1e300 raise OverflowError("n too large") result = 1 factor = 2 while factor <= n: result *= factor factor += 1 return result def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test()
iter()可以接受可调用参数
例如:
def seek_next_line(f): for c in iter(lambda: f.read(1),'\n'): pass
iter(callable,until_value)函数反复调用callable并产生其结果,直到返回until_value为止。
懒得初始化字典中的每个字段?没问题:
在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解释器
>>>
也许并不是鲜为人知,但肯定是我最喜欢的Python功能之一。
一流的功能
它并不是真正的隐藏功能,但是功能是一流对象这一事实确实很棒。我们可以像其他任何变量一样传递它们。
>>> 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".'
以完全动态的方式创建新类型
>>> NewType = type("NewType", (object,), {"x": "hello"}) >>> n = NewType() >>> n.x "hello"
完全一样
>>> class NewType(object): >>> x = "hello" >>> n = NewType() >>> n.x "hello"
可能不是最有用的东西,但很高兴知道。
编辑:固定的新类型名称,应该是NewType
,与class
语句完全一样。
编辑:调整标题以更准确地描述功能。
列表推导中的" 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,我才意识到这一点。
上下文管理器和" with"语句
在PEP 343中引入的上下文管理器是一个对象,它充当一组语句的运行时上下文。
由于该功能使用了新的关键字,因此逐步引入了该功能:在Python 2.5中,可以通过__future__
指令使用该功能。 Python 2.6及更高版本(包括Python 3)默认情况下可用。
我经常使用" with"语句,因为我认为这是一个非常有用的构造,下面是一个快速演示:
from __future__ import with_statement with open('foo.txt', 'w') as f: f.write('hello!')
幕后发生的事情是," with"语句在文件对象上调用了特殊的__enter__和__exit__方法。如果在with语句主体中引发了任何异常,则异常详细信息也将传递给__exit__,从而允许在那里进行异常处理。
在这种特殊情况下,这可以为我们提供保证,当执行超出with
套件的范围时,无论该文件是否正常发生或者是否引发异常,它都将关闭文件。从根本上讲,它是一种抽象通用异常处理代码的方法。
其他常见用例包括使用线程锁定和数据库事务。
一些内置的收藏夹,map(),reduce()和filter()。一切都非常快速和强大。
函数参数解压缩
我们可以使用*
和**
将列表或者字典解压缩为函数参数。
例如:
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)
由于列表,元组和字典广泛用作容器,因此非常有用的快捷方式。
字典具有get()方法
字典有一个" get()"方法。如果执行d ['key']而键不存在,则会出现异常。如果执行d.get('key'),则如果'key'不存在,则返回None。我们可以添加第二个参数来取回该项目,而不是无,例如:d.get('key',0)。
这非常适合诸如加号之类的事情:
sum [value] = sum.get(value,0)+ 1
如果在函数中使用" exec",则变量查找规则将发生巨大变化。不再可能进行闭包,但是Python允许在函数中使用任意标识符。这为我们提供了"可修改的locals()",可用于星号导入标识符。不利的一面是,它会使每次查找变慢,因为变量最终以字典而不是帧中的插槽结尾:
>>> 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
从2.5开始,字典有一个特殊的方法__missing__,用于缺少项:
>>> class MyDict(dict): ... def __missing__(self, key): ... self[key] = rv = [] ... return rv ... >>> m = MyDict() >>> m["foo"].append(1) >>> m["foo"].append(2) >>> dict(m) {'foo': [1, 2]}
在collections中还有一个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来检查是否存在将向dict添加新项目的项目。
如果我们在类上使用描述符,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()的干净。
如果我们不喜欢使用空格来表示作用域,则可以通过发出以下命令来使用C样式的{}:
from __future__ import braces
__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}
Python的高级切片操作具有鲜为人知的语法元素,省略号:
>>> class C(object): ... def __getitem__(self, item): ... return item ... >>> C()[1:2, ..., 3] (slice(1, 2, None), Ellipsis, 3)
不幸的是,它几乎没有用,因为仅在涉及元组时才支持省略号。
内置方法或者函数未实现描述符协议,因此无法执行以下操作:
>>> 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
命名格式
%-formatting需要一个字典(也适用于%i /%s等。验证)。
>>> print "The %(foo)s is %(bar)i." % {'foo': 'answer', 'bar':42} The answer is 42. >>> foo, bar = 'question', 123 >>> print "The %(foo)s is %(bar)i." % locals() The question is 123.
并且由于locals()也是一个字典,因此我们可以简单地将其作为字典传递,并从本地变量中获取%替换。我认为这是不满意的,但是可以简化事情。
新样式格式
>>> print("The {foo} is {bar}".format(foo='answer', bar=42))
注意可变的默认参数
>>> 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]
元组拆包:
>>> (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)
解压缩Python中不需要的
有人在博客上发表关于Python没有zip()附带的解压缩功能的博客。解压缩很简单,因为:
>>> t1 = (0,1,2,3) >>> t2 = (7,6,5,4) >>> [t1,t2] == zip(*zip(t1,t2)) True
在反思时,我宁愿有一个显式的unzip()。
要添加更多的python模块(尤其是第三方模块),大多数人似乎使用PYTHONPATH环境变量,或者在其站点包目录中添加符号链接或者目录。另一种方法是使用* .pth文件。这是python官方文档的解释:
"The most convenient way [to modify python's search path] is to add a path configuration file to a directory that's already on Python's path, usually to the .../site-packages/ directory. Path configuration files have an extension of .pth, and each line must contain a single path that will be appended to sys.path. (Because the new paths are appended to sys.path, modules in the added directories will not override standard modules. This means you can't use this mechanism for installing fixed versions of standard modules.)"
例外else子句:
try: put_4000000000_volts_through_it(parrot) except Voom: print "'E's pining!" else: print "This parrot is no more!" finally: end_sketch()
使用else子句比向try子句添加其他代码更好,因为它避免了意外捕获由try ... except语句保护的代码未引发的异常。
参见http://docs.python.org/tut/node10.html
for ... else语法(请参阅http://docs.python.org/ref/for.html)
for i in foo: if i == 0: break else: print("i was never 0")
除非调用break,否则" else"块通常会在for循环的末尾执行。
上面的代码可以模拟如下:
found = False for i in foo: if i == 0: found = True break if not found: print("i was never 0")
许多人不了解" 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.
Access Dictionary elements as attributes (properties). so if an a1=AttrDict() has key 'name' -> instead of a1['name'] we can easily access name attribute of a1 using -> 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
Python sort函数可以正确地对元组进行排序(即使用熟悉的字典顺序):
a = [(2, "b"), (1, "a"), (2, "a"), (3, "c")] print sorted(a) #[(1, 'a'), (2, 'a'), (2, 'b'), (3, 'c')]
如果要对年龄后的人员列表进行排序,然后命名,则很有用。
条件分配
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是两个类。
- 下划线,包含解释器显示的最新输出值(在交互式会话中):
>>> (a for a in xrange(10000)) <generator object at 0x81a8fcc> >>> b = 'blah' >>> _ <generator object at 0x81a8fcc>
- 方便的Web浏览器控制器:
>>> import webbrowser >>> webbrowser.open_new_tab('http://www.stackoverflow.com')
- 内置的http服务器。要提供当前目录中的文件:
python -m SimpleHTTPServer 8000
- 退出
>>> import atexit
__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服务分派并传递回用户。
枚举
用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 >>>
参考:
- Python教程循环技术
- Python文档内置函数的枚举
- PEP 279
三元运算符
>>> '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'
我们可以从一组长度为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}
元组在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
"解包"到功能参数
def foo(a, b, c): print a, b, c bar = (3, 14, 15) foo(*bar)
执行时打印:
3 14 15
布尔上下文中的对象
空元组,列表,字典,字符串和许多其他对象在布尔上下文中等效于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
一切都是一流的("一切都是物体"),以及这可能造成的混乱。
>>> 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的两个方面,即``万物都是对象''的哲学到了极致,而通过不正确或者构思不当的语言语法使用这些方法,可能导致完全难以理解,无法维护的意大利面条式代码适合单个表达式。
分配和删除切片:
>>> 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]
)时,分配的可迭代对象的长度必须与切片相同。
dict的构造函数接受关键字参数:
>>> dict(foo=1, bar=2) {'foo': 1, 'bar': 2}
获取python regex解析树以调试regex。
正则表达式是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)
内置的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'
显然,反重力模块。
xkcd#353
发电机
我认为许多刚开始使用Python的开发人员会忽略生成器,而实际上并没有掌握它们的用途或者对其功能有任何了解。直到我阅读了David M. Beazley关于发电机的PyCon演示(可在此处获得)后,我才意识到它们的用途(本质上是非常有用)。该演示对我来说是一种全新的编程方式,我向不熟悉生成器的任何人推荐它。
交互式口译员选项卡完成
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环境变量。
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"
利用python的动态特性来开发应用
python语法中的配置文件。例如,如果我们有以下内容
在配置文件中:
{ "name1": "value1", "name2": "value2" }
然后,我们可以像下面这样简单地阅读它:
config = eval(open("filename").read())
嵌套函数参数重新绑定
def create_printers(n): for i in xrange(n): def printer(i=i): # Doesn't work without the i=i print i yield printer
python的一些功能不足。将字符串列表连接在一起的通常快速方法是:
''.join(list_of_strings)
进口反重力
私有方法和数据隐藏(封装)
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'
关键是privateMethod
和privateData
根本不是obj的真正属性,因此无法从外部访问它们,也不会显示在dir()
或者类似文件中。它们是构造函数中的局部变量,在__init__之外完全不可访问。但是,由于闭包的神奇之处,它们实际上是与实例关联的对象具有相同生命周期的按实例变量,即使没有其他方法可以通过调用privilegedMethod来从外部访问它们(在本示例中)
。通常,这种非常严格的封装是过大的,但有时对于保持API或者命名空间干净整洁的确非常有用。
在Python 2.x中,拥有可变私有状态的唯一方法是使用可变对象(例如本示例中的dict)。许多人评论说这有多烦人。 Python 3.x将通过引入PEP 3104中描述的nonlocal
关键字来消除此限制。
使用关键字参数作为赋值
有时,我们可能想根据一个或者多个参数来构建一系列功能。但是,这很容易导致闭包都引用相同的对象和值:
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
对象实例的方法替换
我们可以替换已经创建的对象实例的方法。它允许我们创建具有不同(例外)功能的对象实例:
>>> 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>
尽我们所能,在inst实例中,将C.a替换为fun2()(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>
节点:此解决方案不应该用作继承机制的常规替代!但这在某些特定情况(调试,模拟)中可能非常方便。
警告:此解决方案不适用于内置类型和使用插槽的新样式类。
正在构建列表时参考列表推导。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
我们可以引用列表理解,因为它是由符号" _ [1]"构建的。例如,以下函数通过引用元素列表理解来唯一化元素列表,而不更改其顺序。
def unique(my_list): return [x for x in my_list if x not in locals()['_[1]']]
固定/冻结
可能是一个容易被忽略的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
功能支持。
生成器和生成器表达式,特别是。
Ruby再次成为主流,但是Python也可以做到这一点。在库中没有在Ruby中那样普遍存在,这太糟糕了,但是我更喜欢语法,它更简单。
因为它们不那么普遍,所以我没有看到很多关于它们为什么有用的示例,但是它们使我能够编写更简洁,更有效的代码。
在调试复杂的数据结构时,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']
>>> 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]
不是真正的隐藏功能,而是部分功能对于后期评估功能非常有用。
我们可以在初始调用中将任意数量的参数绑定到partial,然后将其与其余所有参数一起调用(在本示例中,我已将begin / end args绑定到range,但是第二次使用步骤arg)
请参阅文档。
is_ok() and "Yes" or "No"
... 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
我们可以轻松地用zip转置数组。
a = [(1,2), (3,4), (5,6)] zip(*a) # [(1, 3, 5), (2, 4, 6)]
负回合
round()函数将浮点数四舍五入为给定的精度(以十进制数字表示),但精度可以为负:
>>> str(round(1234.5678, -2)) '1200.0' >>> str(round(1234.5678, 2)) '1234.57'
注意:round()
总是返回一个浮点数,在上面的示例中使用str()
,因为浮点数学不精确,在2.x下,第二个示例可以打印为" 1234.5700000000001"。另请参阅"十进制"模块。
口译员中的口译员
标准库的代码模块使我们可以在程序中包含自己的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'
对于要接受用户的脚本输入或者实时查询虚拟机状态的情况,这非常有用。
TurboGears具有一个WebConsole,可从中查询实时Web应用程序的状态,从而极大地发挥了作用。
内置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}
标准库参考中的更多详细信息:设置类型
我们可以使用元类覆盖类的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'>)
它可能被隐藏了,这是有充分的理由的。 :)
内置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!
pdb Python调试器
作为程序员,调试程序是进行认真的程序开发所需的第一件事。 Python具有一个内置模块,该模块可以作为名为pdb的模块使用(自然而然是" Python DeBugger"!)。
http://docs.python.org/library/pdb.html
小整数(-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 >>>
创建具有相关数据的两个序列的字典
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}
使用和和或者模拟第三级运算符。
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
检查模块也是一个很酷的功能。
标准Python中的spam
模块
它用于测试目的。
我从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>
内存管理
Python动态分配内存并使用垃圾回收来恢复未使用的空间。一旦对象超出范围,并且没有其他变量引用它,它将被恢复。我不必担心缓冲区溢出和服务器进程缓慢增长的问题。内存管理也是其他动态语言的功能,但是Python确实做到了。
当然,我们必须提防循环引用,并保留对不再需要的对象的引用,但是弱引用在这里有很大帮助。
每次与正则表达式匹配时都可以调用函数的事实非常方便。
在这里,我有一个示例,其中将每个" 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?'
我个人喜欢这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
一句话:IPython
制表符的自省,漂亮的打印,%debug
,历史记录管理,pylab
,...非常值得花时间学习。
重新加载模块可启用"实时编码"样式。但是类实例不会更新。这就是为什么,以及如何解决它。记住,一切都是,一切都是对象。
>>> 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
是朋友。不过,了解它为何起作用仍然有帮助。
不是很隐藏,但是函数具有属性:
def doNothing(): pass doNothing.monkeys = 4 print doNothing.monkeys 4
我们可以使用类来装饰函数,而用类实例代替函数:
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)
只需花费很少的工作,线程模块就变得非常易于使用。此修饰器更改了一个函数,使其在自己的线程中运行,返回一个占位符类实例,而不是其常规结果。我们可以通过检查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
当我们在代码文件的顶部使用正确的编码声明时,ROT13是源代码的有效编码:
#!/usr/bin/env python # -*- coding: rot13 -*- cevag "Uryyb fgnpxbiresybj!".rapbqr("rot13")
如果我们已在应用程序中重命名了一个类,该类将通过Pickle加载用户保存的文件,并且其中一个重命名的类存储在用户的旧保存中,则我们将无法加载该腌制的文件。
但是,只需添加对类定义的引用即可,一切都很好:
例如,之前:
class Bleh: pass
现在,
class Blah: pass
因此,用户的腌制保存文件包含对Bleh的引用,由于重命名该引用不存在。解决办法?
Bleh = Blah
简单的!
一切都是对象的事实,因此是可以扩展的。我可以将成员变量作为元数据添加到我定义的函数中:
>>> 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'] >>>
如果要根据上下文分派功能,则很有用。请参阅"深入Python"中的示例(此处)
测试键是否在字典中的简单方法:
>>> 'key' in { 'key' : 1 } True >>> d = dict(key=1, key2=2) >>> if 'key' in d: ... print 'Yup' ... Yup
将类作为一流对象(通过动态类定义显示)
还请注意使用闭包。如果此特定示例看起来像解决问题的"正确"方法,请仔细考虑……几次:)
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
暴露可变缓冲区
使用Python缓冲区协议在Python(2.5 / 2.6)中公开可变的面向字节的缓冲区。
(对不起,这里没有代码。要求使用低级C API或者现有的适配器模块)。
在子类中扩展属性(定义为描述符)
有时,扩展(修改)子类中的描述符"返回"的值很有用。可以通过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习惯用法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可以理解任何类型的unicode数字,而不仅仅是ASCII类型:
>>> s = u'10585' >>> s u'\uff11\uff10\uff15\uff18\uff15' >>> print s 10585 >>> int(s) 10585 >>> float(s) 10585.0
关于尼克·约翰逊(Nick Johnson)对Property类的实现(当然,只是对描述符的演示,不能替代内置函数),我将包括一个引发AttributeError的setter:
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) def __set__(self, obj, value): raise AttributeError, 'Read-only attribute'
包含setter使其成为数据描述符,而不是方法/非数据描述符。数据描述符的优先级高于实例字典。现在,实例无法将其他对象分配给属性名称,并且尝试分配给属性将引发属性错误。
如示例所示,拆包语法已在最新版本中升级。
>>> 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)
操纵递归限制
使用sys.getrecursionlimit()和sys.setrecursionlimit()获取或者设置最大递归深度。
我们可以对其进行限制,以防止无限递归引起的堆栈溢出。
乘以布尔值
我在Web开发中经常做的一件事是有选择地打印HTML参数。我们都已经用其他语言看到过这样的代码:
class='<% isSelected ? "selected" : "" %>'
在Python中,我们可以乘以一个布尔值,并且它可以完全满足期望:
class='<% "selected" * isSelected %>'
这是因为乘法将布尔值强制为整数(False表示0,True表示1),而在python中,将字符串乘以int会将字符串重复N次。
Mod可以正确地使用负数
-1%5应该是4,而不是其他语言(例如JavaScript)中的-1,而不是-1. 这使Python中的"环绕式窗口"更加干净,我们只需执行以下操作:
index = (index + increment) % WINDOW_SIZE
我们可以根据需要构造一个函数:
kwargs = {} kwargs[str("%s__icontains" % field)] = some_value some_function(**kwargs)
某种程度上需要str()调用,因为python否则会抱怨说它不是字符串。不知道为什么;)
我将其用于Django对象模型中的动态过滤器:
result = model_class.objects.filter(**kwargs)
猜测整数基数
>>> 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
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。
猴子修补对象
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中,我们可以执行以下操作以快速创建枚举:
>>> 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
操纵系统模块
我们可以直接操纵模块缓存,根据需要使模块可用或者不可用:
>>> 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中删除该条目将完全导致下一个import对该模块进行常规搜索,即使该模块之前已被导入。
Python中没有秘密;)
将元组传递给内置函数
许多Python函数都接受元组,但看起来却并非如此。例如,我们要测试变量是否为数字,可以执行以下操作:
if isinstance (number, float) or isinstance (number, int): print "yaay"
但是,如果我们通过我们的元组,则看起来会更干净:
if isinstance (number, (float, int)): print "yaay"
我们可以将多个变量分配给相同的值
>>> foo = bar = baz = 1 >>> foo, bar, baz (1, 1, 1)
以紧凑的方式将几个变量初始化为None很有用。
threading.enumerate()提供对系统中所有线程对象的访问权限,而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.
看
大括号
def g(): print 'hi!' def f(): ( g() ) >>> f() hi!
最高机密属性
>>> 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'
很好地处理字典中的无限递归:
>>> a = {} >>> b = {} >>> a['b'] = b >>> b['a'] = a >>> print a {'b': {'a': {...}}}
对迭代器的多个引用
我们可以使用列表乘法创建对同一迭代器的多个引用:
>>> 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)
我们可以通过查看其__module__属性来询问任何对象来自哪个模块。例如,如果我们在命令行上进行实验并且导入了很多东西,这将很有用。
同样,我们可以通过查看其__ file__属性来询问模块的来源。这在调试路径问题时很有用。
使用负步反转可迭代
>>> 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]
使用交互式外壳时," _"包含最后打印的项目的值:
>>> range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> _ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>>
切片和可变性
复制清单
>>> 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]
结合开箱与打印功能:
# 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 3.1(2.7)字典和set comprehensions开始支持:
{ a:a for a in range(10) } { a for a in range(10) }
如果在序列的最后一个元素之后找到,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,)
** Using sets to reference contents in sets of frozensets**
如我们所知,集合是可变的,因此不可散列,因此,如果要建立集合集(或者将集合用作字典键),则必须使用Frozensets:
>>> 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标准库文档:
Note, the elem argument to the __contains__(), remove(), and discard() methods may be a set. To support searching for an equivalent frozenset, the elem set is temporarily mutated during the search and then restored. During the search, the elem set should not be read or mutated since it does not have a meaningful value.
不幸的是,也许令人惊讶的是,字典并非如此:
>>> 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'
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)
切片为左值。这个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)
要工作,必须在左侧的切片上分配一个长度相同的列表。
零参数和可变参数Lambda
Lambda函数通常用于将一个值快速转换为另一个值,但也可以用于将值包装在函数中:
>>> f = lambda: 'foo' >>> f() 'foo'
他们也可以接受通常的* args
和** kwargs
语法:
>>> g = lambda *args, **kwargs: args[0], kwargs['thing'] >>> g(1, 2, 3, thing='stuff') (1, 'stuff')
多行字符串
一种方法是使用反斜杠:
>>> 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
注意行之间没有逗号(这不是元组),并且我们必须考虑字符串需要具有的尾随/前导空格。顺便说一下,所有这些都使用占位符(例如"我的名字是%s","%name")。
pow()也可以有效地计算(x ** y)%z。
内置pow()
函数的鲜为人知的第三个参数使我们比简单地执行(x ** y)%z
更有效地计算xy模z。
>>> x, y, z = 1234567890, 2345678901, 17 >>> pow(x, y, z) # almost instantaneous 6
相比之下,(x ** y)%z
在我的机器上一分钟内没有给出相同值的结果。
原始字符串中的反斜杠仍然可以转义引号。看到这个:
>>> print repr(r"aaa\"bbb") 'aaa\"bbb'
请注意,最后一个字符串中同时包含反斜杠和双引号。
因此,我们不能以反斜杠结束原始字符串:
>>> print repr(r"C:\") SyntaxError: EOL while scanning string literal >>> print repr(r"C:\"") 'C:\"'
发生这种情况是因为实现了原始字符串以帮助编写正则表达式,而不是编写Windows路径。在Windows文件名中的Gotcha反斜杠上阅读有关此问题的漫长讨论。
序列乘法和反射操作数
>>> '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
用不同的起始索引枚举
"答案"部分涵盖了"枚举",但是最近我发现了"枚举"的一个更隐藏的功能,我认为值得发表自己的文章,而不仅仅是发表评论。
从Python 2.6开始,我们可以在其第二个参数中指定一个起始索引来"枚举":
>>> l = ["spam", "ham", "eggs"] >>> list(enumerate(l)) >>> [(0, "spam"), (1, "ham"), (2, "eggs")] >>> list(enumerate(l, 1)) >>> [(1, "spam"), (2, "ham"), (3, "eggs")]
我发现它非常有用的一个地方是当我枚举对称矩阵的项时。由于矩阵是对称的,因此我可以仅通过对上三角进行迭代来节省时间,但是在那种情况下,我必须在内部for
循环中使用带有不同起始索引的enumerate
来跟踪行和列正确索引:
for ri, row in enumerate(matrix): for ci, column in enumerate(matrix[ri:], ri): # ci now refers to the proper column index
奇怪的是,这种"枚举"的行为并没有记录在"帮助(枚举)"中,只有在线文档中。
不是"隐藏"的,但非常有用且不常用
像这样快速创建字符串连接函数
comma_join = ",".join semi_join = ";".join print comma_join(["foo","bar","baz"]) 'foo,bar,baz
和
能够比引号,逗号混乱更优雅地创建字符串列表。
l = ["item1", "item2", "item3"]
取而代之
l = "item1 item2 item3".split()
对象数据模型
我们可以为自己的类覆盖该语言的任何运算符。请参阅此页面以获取完整列表。一些例子:
- 我们可以覆盖任何运算符(
* +-/ //%^ == <> <=> = .
等)。所有这些都是通过覆盖对象中的__mul ,add__等来完成的。我们甚至可以覆盖诸如__rmul__之类的东西,以分别处理your_object * something_else
和something_else * your_object
。.
是属性访问(a.b
),可以通过使用__getattr__
来覆盖它以处理任意的b
。这里还包括使用__call__
的a()
。 - 我们可以使用以下内容的任意组合来创建自己的切片语法(
a [stuff]
),该语法可能非常复杂,并且与列表中使用的标准语法(numpy在其数组中具有强大功能的一个很好的例子)完全不同。我们可以使用Slice对象来选择我们喜欢的,
,:
和``。 - 专门处理该语言中的许多关键字会发生的情况。包括
del
,in
,import
和not
。 - 处理当对象调用许多内置函数时发生的情况。标准的__int__,__str__等去了这里,len,reversed,abs__以及三个参数__pow(用于模幂)也是如此。