文档章节

Python Tips, Tricks, and Hacks

大数据之路
 大数据之路
发布于 2013/07/23 01:56
字数 6497
阅读 576
收藏 15

一、快速技巧

1.1、4 种引号

' 
''' 
" 
""" 

print """I wish that I'd never heard him say, '''She said, "He said, 'Give me five dollars'"'''"""

1.2、对象/变量的真与假

my_object = 'Test' # True example
# my_object = '' # False example


if len(my_object) > 0:
    print 'my_object is not empty'


if len(my_object):  # 0 等价于 false
    print 'my_object is not empty'


if my_object != '':
    print 'my_object is not empty'


if my_object: # 空字符串等价于 false
    print 'my_object is not empty'


结论:如果只是想判断对象是否为空,真的没必要去检查它的长度或者是否相等。

1.3、子串检测

string = 'Hi there' # True example
# string = 'Good bye' # False example
if string.find('Hi') != -1:
    print 'Success!'

上面的代码有点丑陋,下面的功能与之完全等价:

string = 'Hi there' # True example
# string = 'Good bye' # False example
if 'Hi' in string:
    print 'Success!'

1.4、优雅的打印列表

join 会将每个元素转换成字符串并用指定字符连接,其聪明之处在于不会在末尾元素添加连接符。
更重要的是它的优雅和高效,它的时间是线性的。

recent_presidents = ['George Bush', 'Bill Clinton', 'George W. Bush']
print 'The three most recent presidents were: %s.' % ', '.join(recent_presidents)
# prints 'The three most recent presidents were: George Bush, Bill Clinton, George W. Bush.

1.5、整除与浮点除法

默认是整除,想要得到小数请看如下的几种方法

5/2        # Returns 2
5.0/2      # Returns 2.5
float(5)/2 # Returns 2.5
5//2       # Returns 2


from __future__ import division
5/2        # Returns 2.5
5.0/2      # Returns 2.5
float(5)/2 # Returns 2.5
5//2       # Returns 2

1.6、Lambda 表达式

Lambda 就是一个单行的匿名函数,你可以把它转换成一个正常的函数,但活用 Lambda 可以让我们的代码更加优雅。

def add(a,b): return a+b
add2 = lambda a,b: a+b
print add2(2, 3)

squares = map(lambda a: a*a, [1,2,3,4,5])
# squares is now [1,4,9,16,25]


这里如果没有 lambda,你必须定义一个函数。

Lambda Functions 的语法:
lambda variable(s) : expression

二、列表

2.1、列表解析

2.1.1、映射列表

numbers = [1,2,3,4,5]
squares = []
for number in numbers:
    squares.append(number*number)
# Now, squares should have [1,4,9,16,25]

使用 map 映射

numbers = [1,2,3,4,5]
squares = map(lambda x: x*x, numbers)
# Now, squares should have [1,4,9,16,25]

虽然 3 行代码变成了 1 行代码,但仍然比较丑陋,下面看看列表解析:

numbers = [1,2,3,4,5]
squares = [number*number for number in numbers]
# Now, squares should have [1,4,9,16,25]

2.1.2、过滤列表

numbers = [1,2,3,4,5]
numbers_under_4 = []
for number in numbers:
    if number < 4:
        numbers_under_4.append(number)
# Now, numbers_under_4 contains [1,4,9]


numbers = [1,2,3,4,5]
numbers_under_4 = filter(lambda x: x < 4, numbers)
# Now, numbers_under_4 contains [1,2,3]


numbers = [1,2,3,4,5]
numbers_under_4 = [number for number in numbers if number < 4]
# Now, numbers_under_4 contains [1,2,3]

恩,列表解析再一次给了我们更短、更简洁、更容易理解的代码。

2.1.3、Map and Filter at Once

numbers = [1,2,3,4,5]
squares = []
for number in numbers:
    if number < 4:
        squares.append(number*number)
# squares is now [1,4,9]


numbers = [1,2,3,4,5]
squares = map(lambda x: x*x, filter(lambda x: x < 4, numbers))
# squares is now [1,4,9]


numbers = [1,2,3,4,5]
squares = [number*number for number in numbers if number < 4]
# square is now [1,4,9]

2.1.4、生成器表达式

Generator expressions are newish in Python 2.4, and possibly the least publicized Cool Thing About Python ever. As in, I just found out about them. Generator expressions do not load the whole list into memory at once, but instead create a 'generator object' so only one list element has to be loaded at any time.

Of course, if you actually need to use the entire list for something, this doesn't really help much. But if you're just passing it off to something that takes any iterable object -- like a for loop -- you might as well use a generator function.

numbers = (1,2,3,4,5) # Since we're going for efficiency, I'm using a tuple instead of a list ;)
squares_under_10 = (number*number for number in numbers if number*number < 10)
print type(squares_under_10) # squares_under_10 现在是一个生成器对象
# squares_under_10 is now a generator object, from which each successive value can be gotten by calling .next()

for square in squares_under_10:
    print square,
# prints '1 4 9'

这会比列表解析更加高效

So, you want to use generator expressions for large numbers of items. You want to always use list comprehensions if you need the entire list at once for some reason. If neither of these is true, just do whatever you want. It's probably good practice to use generator expressions unless there's some reason not to, but you're not going to see any real difference in efficiency unless the list is very large.

As a final note, generator expressions only need to be surrounded by one set of parentheses. So, if you're calling a function with only a generator expression, you only need one set of parentheses. This is valid Python: some_function(item for item in list).

2.1.5、嵌套 for 语句

List comprehensions and generator expressions can be used for more than just mapping and filtering; you can create rather complex lists of lists with them [1]. Not only can you map and filter, you can nest the for expressions. A python neophyte might write something like:

for x in (0,1,2,3):
    for y in (0,1,2,3):
        if x < y:
            print (x, y, x*y),

# prints (0, 1, 0) (0, 2, 0) (0, 3, 0) (1, 2, 2) (1, 3, 3) (2, 3, 6)


You can see that this code is pretty crazy. With a list comprehension, though, you can do this more quickly:

print [(x, y, x * y) for x in (0,1,2,3) for y in (0,1,2,3) if x < y]
# prints [(0, 1, 0), (0, 2, 0), (0, 3, 0), (1, 2, 2), (1, 3, 3), (2, 3, 6)]


As you can see, this code iterates over four values of y, and for each of those values, iterates over four values of x and then filters and maps. Each list item then, is itself a list of x, y, x * y.

Note that xrange(4) is a bit cleaner than (0,1,2,3), especially for longer lists, but we haven't gotten there yet.

2.2 reduce化简一个列表

numbers = [1,2,3,4,5]
result = 1
for number in numbers:
    result *= number
# result is now 120
Or you could use the built-in function reduce, which accepts a function that takes two arguments, and a list:

numbers = [1,2,3,4,5]
result = reduce(lambda a,b: a*b, numbers)
# result is now 120


Now it's not as pretty as a list comprehension, but it is shorter than a for loop. Definitely worth keeping in mind.

2.3 迭代一个列表: range, xrange and enumerate

strings = ['a', 'b', 'c', 'd', 'e']
for index in xrange(len(strings)):
    print index,
# prints '0 1 2 3 4'

The problem here is that usually you end up needing the list elements anyways. What's the use of just having the index values? Python has a really awesome built-in function called enumerate that will give you both. enumerate-ing a list will return an iterator of index, value pairs:

strings = ['a', 'b', 'c', 'd', 'e']
for index, string in enumerate(strings):
    print index, string,
# prints '0 a 1 b 2 c 3 d 4 e'

As an added advantage, enumerate is quite a bit cleaner and more readable than xrange(len()). Because of this, range and xrange are probably only useful if you need to create a list of values from scratch for some reason, instead of from an existing list.

2.4 检查列表中的每个元素

Say you want to check to see if any element in a list satisfies a condition (say, it's below 10). Before Python 2.5, you could do something like this:

numbers = [1,10,100,1000,10000]
if [number for number in numbers if number < 10]:
    print 'At least one element is over 10'
# Output: 'At least one element is over 10'

With the new built-in any function introduced in Python 2.5, you can do the same thing cleanly and efficiently. 

numbers = [1,10,100,1000,10000]
if any(number < 10 for number in numbers):
    print 'Success'
# Output: 'Success!'

Similarly, you can check if every element satisfies a condition. Without Python 2.5, you'll have to do something like this:

numbers = [1,2,3,4,5,6,7,8,9]
if len(numbers) == len([number for number in numbers if number < 10]):
    print 'Success!'
# Output: 'Success!'


With Python 2.5, there's of course an easier way: the built-in all function. As you might expect, it's smart enough to bail after the first element that doesn't match, returning False. This method works just like the any method described above.

numbers = [1,2,3,4,5,6,7,8,9]
if all(number < 10 for number in numbers):
    print 'Success!'
# Output: 'Success!'

2.5 按元素位置合并列表: zip

letters = ['a', 'b', 'c']
numbers = [1, 2, 3]
squares = [1, 4, 9]
zipped_list = zip(letters, numbers, squares)
# zipped_list contains [('a', 1, 1), ('b', 2, 4), ('c', 3, 9)]

2.6 更多的列表操作

The following are all built-in functions that can be called on any list or iterable.

max
Returns the largest element in the list
min
Returns the smallest element in the list
sum
Returns the sum of all elements in the list. Accepts an optional second argument, the value to start with when summing (defaults to 0).

2.7 利用 set 唯一性判断 list 是否元素重复

numbers = [1,2,3,3,4,1]
set(numbers)
# returns set([1,2,3,4])

if len(numbers) == len(set(numbers)):
    print 'List is unique!'
# In this case, doesn't print anything

三、字典

3.1  字典结构

dict(a=1, b=2, c=3)
# returns {'a': 1, 'b': 2, 'c': 3}

相比 a = {'a': 1, 'b': 2, 'c': 3} 的方式更加简洁明了。

This might be a bit cleaner than a 'regular' dictionary creation depending on your code; there are less quotes floating around. I use it often.

3.2 字典转换成列表

Turning a dictionary into a list or an iterator is easy. To get a list of keys, you can just cast the dict into a list. It's cleaner, though to call .keys() on the dictionary to get a list of the keys, or .iterkeys() to get an iterator. Similarly, you can call .values() or .itervalues() to get a list or iterator of dictionary values. Remember though, that dicts are inherently unordered and so these values won't be in any meaningful order.

To preserve both keys and values, you can turn a dict into a list or iterator of 2-item tuples by using .items() or .iteritems(). This is something that you'll probably do a lot, and isn't very exciting:

dictionary = {'a': 1, 'b': 2, 'c': 3}
dict_as_list = dictionary.items()
#dict_as_list now contains [('a', 1), ('b', 2), ('c', 3)]

3.3 列表转换成字典

You can reverse the process, turning a list of 2-element lists or tuples into a dict:

dict_as_list = [['a', 1], ['b', 2], ['c', 3]]
dictionary = dict(dict_as_list)
# dictionary now contains {'a': 1, 'b': 2, 'c': 3}

You can also combine this with the 'keyword arguments' method of creating a dictionary discussed above:

dict_as_list = [['a', 1], ['b', 2], ['c', 3]]
dictionary = dict(dict_as_list, d=4, e=5)
# dictionary now contains {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

Being able to convert a dict to a list is kind of handy, I guess. But what really makes it awesome is the next trick.

3.4 字典解析

Although Python doesn't have built-in dictionary comprehensions, you can do something pretty close with little mess or code. Just use .iteritems() to turn your dict into a list, throw it in a generator expression (or list comprehension), and then cast that list back into a dict.

For example, say I have a dictionary of name:email pairs, and I want to create a dictionary of name:is_email_at_a_dot_com pairs:

emails = {'Dick': 'bob@example.com', 'Jane': 'jane@example.com', 'Stou': 'stou@example.net'}
email_at_dotcom = dict( [name, '.com' in email] for name, email in emails.iteritems() )
# email_at_dotcom now is {'Dick': True, 'Jane': True, 'Stou': False}

Damn straight. Of course, you don't have to start and end with a dict, you can throw some lists in there too.
While this is a little less readable than a straight list comprehension, I'd argue it's still better than a massive for loop.

四、条件表达式

4.1  正确的方式

While writing this article, I stumbled upon the right way to select values inline, new in Python 2.5 (you'd think there would have been more fanfare!). Python now supports the syntax 'value_if_true if test else value_if_false'. So, you can do simple selection of values in one line, with no weird syntax or major caveats:

test = True
# test = False
result = 'Test is True' if test else 'Test is False'
# result is now 'Test is True'

Okay, it's a bit ugly still. Alas. You can also chain multiple tests in one line:

test1 = False
test2 = True
result = 'Test1 is True' if test1 else 'Test1 is False, test2 is True' if test2 else 'Test1 and Test2 are both False'

The first if/else is evaluated first, and if test1 is false the second if/else is evaluated. You can do more complicated things too, especially if you throw in some parentheses.

Personal Note:
This is pretty new on the field, and my reaction is mixed. It really is the Right Way, it's cleaner, and I like it... but it's still ugly especially if you have multiple nested if/else's.
Of course, the syntax for all of the value selection tricks is ugly.
I have soft spot for the and/or trick below, I actually find it very intuitive, now that I understand how it works. Also, it's not any less efficient than doing things the Right Way.

What do you think? Feel free to comment below.
Although the inline if/else is the new, more correct way, you'd better still check out the tricks below. Even if you only plan on programming in Python 2.5, you're still going to run into these in older code. Of course, if you need backwards compatibility or don't have Python 2.5, you'd really better check out the tricks below.

4.2   The and/or Trick:慎用!

In Python, 'and' and 'or' are complex creatures. and-ing two expressions together doesn't just return True if both are true and False if both are false. Instead, 'and' returns the first false value, or the last value if all are true. In other words, if the first value is false it is returned, otherwise the last value is returned. The result of this is something you would expect: if both are true, the last value is returned, which is true and will evaluate to True in a boolean test (eg, an 'if' statement). If one is false, that one is returned and will evaluate to False in a boolean test.
or-ing two expressions together is similar. 'or' returns the first true value, or the last value if all are false. In other words, if the first value is true it is returned, otherwise the last value is returned. So, if both are false, the last value is returned, which is false and will evaluate to False in a boolean test. If one is true, that one is returned and will evaluate to True in a boolean test.

This doesn't help you much when we're just testing for truthfulness. But you can use 'and' and 'or' for other purposes in Python; my favorite is to select between values in a manner akin to C's ternary conditional assignment operator 'test ? value_if_true : value_if_false':

test = True
# test = False
result = test and 'Test is True' or 'Test is False'
# result is now 'Test is True'


How does this work? If test is true, the and statement skips over it and returns its right half, here 'Test is True' or 'Test is False'. As processing continues left to right, the or statement returns the first true value, 'Test is True'.

If test is false, the and statement returns test. As processing continues left to right, the remaining statement is test or 'Test is False'. Since test is false, the or statement skips over it and returns its right half, 'Test is False'.

Warning

Be careful that the middle (if_true) value is never false. If it is, the 'or' statement will always skip over it and always return the rightmost (if_false) value, no matter what the test value is.
Having gotten used to this method, 'The Right Way' (above) actually seems less intuitive to me. If you're not worried about backwards compatibility, I suggest you try both and see which one you like better. It's pretty easy to nest and/or tricks or to throw on extra and's or or's once you understand the logic behind it. If you can't decide or don't feel like learning both, then don't use and/or. Do things the Right Way, and be done with it.

Of course, if you need to support Python versions under 2.5, 'The Right Way' won't work. (I was tempted to say that it 'is The Wrong Way'). In that case the and/or trick is definitely your best bet for most situations.

Hopefully this all makes sense; it's hard to explain. It might seem complicated now, but if you use it a few times and play with 'and' and 'or' it will shortly make sense and you'll be able to come up with more complicated 'and' and 'or' tricks on your own.

4.3  使用 true 和 false 作为索引

Another way to select values is to use True and False as list indexes, taking advantage of the fact that False == 0 and True == 1:

test = True
# test = False
result = ['Test is False','Test is True'][test]
# result is now 'Test is True'

This is more straightforward than the and/or trick, and free of the problem where the value_if_true must itself be true.
However, it also suffers from a significant flaw: both list items are evaluated before truthfulness is checked. For strings or other simple items, this is not a big deal. But if each item involves significant computation or I/O, you really don't want to do twice the work that you have to. For this reason I usually prefer the 'Right Way' or the and/or trick.
Also note that the index method only works when you know that test is False or True (or 0 or 1, but not any other integer or an arbitrary object). Otherwise you should write bool(test) instead of test to get the same behavior as the and/or trick expression above.

五、 函数

5.1 默认值仅仅被计算一次

def function(item, stuff = []):
    stuff.append(item)
    print stuff

function(1)
# prints '[1]'
function(2)
# prints '[1,2]' !!!

在Python里,函数的默认值实在函数定义的时候实例化的,而不是在调用的时候。

The default value for a function argument is only evaluated once, when the function is defined. Python simply assigns this value to the correct variable name when the function is called.

def foo(numbers=[]):
    numbers.append(9)
    print numbers
    
>>> foo()
[9]
>>> foo(numbers=[1,2])
[1, 2, 9]
>>> foo(numbers=[1,2,3])
[1, 2, 3, 9]

>>> foo() # first time, like before
[9]
>>> foo() # second time
[9, 9]
>>> foo() # third time...
[9, 9, 9]
>>> foo() # WHAT IS THIS BLACK MAGIC?!
[9, 9, 9, 9]

我们仍然会问,为什么在调用函数的时候这个默认值却被赋予了不同的值?因为在你每次给函数指定一个默认值的时候,Python都会存储这个值。如果在调用函数的时候重写了默认值,那么这个存储的值就不会被使用。当你不重写默认值的时候,那么Python就会让默认值引用存储的值(这个例子里的numbers)。它并不是将存储的值拷贝来为这个变量赋值。这个概念可能对初学者来说,理解起来会比较吃力,所以可以这样来理解:有两个变量,一个是内部的,一个是当前运行时的变量。现实就是我们有两个变量来用相同的值进行交互,所以一旦 numbers 的值发生变化,也会改变Python里面保存的初始值的记录。

Python doesn't check if that value (that location in memory) was changed. It just continues to assign that value to any caller that needs it. So, if the value is changed, the change will persist across function calls. Above, when we appended a value to the list represented by stuff, we actually changed the default value for all eternity. When we called function again looking for a default value, the modified default was given to us.

The solution: don't use mutable objects as function defaults. You might be able to get away with it if you don't modify them, but it's still not a good idea.

解决方案:使用不可变对象,例如 None。

A better way to write the above code would be:

def function(item, stuff = None):
    if stuff is None:
        stuff = []
    stuff.append(item)
    print stuff

function(1)
# prints '[1]'
function(2)
# prints '[2]', as expected


None is immutable (and we're not trying to change it anyways), so we're safe from accidently changing value of the default.

On the plus side, a clever programmer could probably turn this into a trick, in effect creating C-style 'static variables'.

通常,当人们听到这里,大家会问另一个关于默认值的问题。思考下面的程序:

def foo(count=0):
    count += 1
    print count

>>> foo()
1
>>> foo()
1
>>> foo(2)
3
>>> foo(3)
4
>>> foo()
1

当我们运行它的时候,其结果完全是我们期望的:

这又是为啥呢?其秘密不在与默认值被赋值的时候,而是这个默认值本身。整型是一种不可变的变量。跟 list 类型不同,在函数执行的过程中,整型变量是不能被改变的。当我们执行 count+=1 这句话时,我们并没有改变 count 这个变量原有的值。而是让 count 指向了不同的值。可是,当我们执行 numbers.append(9) 的时候,我们改变了原有的 list 。因而导致了这种结果。

下面是在函数里使用默认值时会碰到的另一种相同问题:

def print_now(now=time.time()):
    print now
    
>>> print_now()
1373121487.91
>>> print_now()
1373121487.91
>>> print_now()
1373121487.91

跟前面一样,time.time() 的值是可变的,那么它只会在函数定义的时候计算,所以无论调用多少次,都会返回相同的事件 — 这里输出的事件是程序被Python解释运行的时间。

* 这个问题和它的解决方案在 Python 2.x 和 3.x 里都是类似的,在Python 3.x 里面唯一的不同,是里面的print 表达式应该是函数调用的方式(print(numbers))。

其实这里的知识点在于你要认清 python 中赋值、引用和复制、拷贝的关系,请参考:

http://my.oschina.net/leejun2005/blog/145911

5.1.1 强制使默认参数每次被拷贝复制

If you prefer less cluttered functions at the cost of some clarity, you can forcefully re-evaluate the default arguments before each function call. The following decorator stores the original values of the default arguments. It can be used to wrap a function and reset the default arguments before each call. [3]

from copy import deepcopy
def resetDefaults(f):
    defaults = f.func_defaults
    def resetter(*args, **kwds):
        f.func_defaults = deepcopy(defaults)
        return f(*args, **kwds)
    resetter.__name__ = f.__name__
    return resetter


Simply apply this decorator to your function to get the expected results.

@resetDefaults # This is how you apply a decorator
def function(item, stuff = []):
    stuff.append(item)
    print stuff

function(1)
# prints '[1]'
function(2)
# prints '[2]', as expected


5.2 接受任意的数字或参数

Python lets you have arbitrary numbers of arguments in your functions. First define any required arguments (if any), then use a variable with a '*' prepended to it. Python will take the rest of the non-keyword arguments, put them in a list or tuple, and assign them to this variable:

def do_something(a, b, c, *args):
    print a, b, c, args
do_something(1,2,3,4,5,6,7,8,9)
# prints '1, 2, 3, (4, 5, 6, 7, 8, 9)'

Why would you want to do this? A common reason is that your function accepts a number of items and does the same thing with all of them (say, sums them up). You could force the user to pass a list: sum_all([1,2,3]) or you could allow them to use an arbitrary number of arguments, which makes for cleaner code: sum_all(1,2,3).
You can also have arbitrary numbers of keyword arguments. After you've defined all other arguments, use a variable with '**' prepended to it. Python will take the rest of the keyword arguments, put them in a dictionary, and assign them to this variable:

def do_something_else(a, b, c, *args, **kwargs):
    print a, b, c, args, kwargs
do_something_else(1,2,3,4,5,6,7,8,9, timeout=1.5)
# prints '1, 2, 3, (4, 5, 6, 7, 8, 9), {"timeout": 1.5}'

Why would you want to do this? I think the most common reason is if your function is a wrapper for some other function or functions, any keyword arguments that you use can be popped off the dictionary and the remainder of the keyword arguments can be passed to the other function(s) (see Passing a List or Dictionary as Arguments, below)

5.2.1  警告

Passing both arbitrary non-keyword arguments and named (non-arbitrary) keyword arguments in one function is seemingly impossible. This is because named keyword arguments must be defined before the '*' parameter in the function definition, and are filled before that parameter is filled. For example, imagine a function:

def do_something(a, b, c, actually_print = True, *args):
    if actually_print:
        print a, b, c, args

We now have a problem: there is no way to specify 'actually_print' as a named keyword argument while simultaneously providing arbitrary non-keyword arguments. Both of the following will error:

do_something(1, 2, 3, 4, 5, actually_print = True)
# actually_print is initially set to 4 (see why?) and then re-set,
# causing a TypeError ('got multiple values for keyword argument')

do_something(1, 2, 3, actually_print = True, 4, 5, 6)
# This is not allowed as keyword arguments may not precede non-keyword arguments.  A SyntaxError is raised.


The only way to pass 'actually_print' in this situation is to pass it as a non-keyword argument:

do_something(1, 2, 3, True, 4, 5, 6)
# Result is '1, 2, 3, (4, 5, 6)'

5.3  传递一个列表或者字典作参数

Since you can receive arguments as a list or dictionary, it's not terribly surprising, I suppose, that you can send arguments to a function from a list or dictionary. The syntax is exactly the same as above.

To send a list as non-keyword arguments, just prepend it with a '*':

args = [5,2]
pow(*args)
# returns pow(5,2), meaning 5^2 which is 25

And, of course, to send a dictionary as keyword arguments (this is probably more common), prepend it with '**':

def do_something(actually_do_something=True, print_a_bunch_of_numbers=False):
    if actually_do_something:
        print 'Something has been done'
        #
        if print_a_bunch_of_numbers:
            print range(10)
kwargs = {'actually_do_something': True, 'print_a_bunch_of_numbers': True}
do_something(**kwargs)

# prints 'Something has been done', then '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]'

Historical footnote: In older versions of Python (pre-2.3) you called functions with arbitrary arguments using the built-in apply (function, arg_list, keyword_arg_dict)'.

5.4 装饰器

Function decorators are fairly simple, but if you've never seen them before you'll have no idea what's going on, as unlike most of Python the syntax isn't very clear. A decorator is a function that wraps another function: the main function is called and its return value is passed to the decorator. The decorator then returns a function that replaces the wrapped function as far as the rest of the program is concerned.

Without further delay, here is the syntax:

def decorator1(func):
    return lambda: func() + 1

def decorator2(func):
    def print_func():
        print func()
    return print_func

@decorator2
@decorator1
def function():
    return 41

function()
# prints '42'

关于装饰器的理解,请参考:

http://my.oschina.net/leejun2005/blog/146025

In this example, 'function' is passed to 'decorator1'. 'decorator1' returns a function that calls 'function' and adds 1. This function is then passed to 'decorator2', which returns a function that calls the function returned by 'decorator1' and prints the result. This last function is the function you are actually calling when you call 'function'. Whew.


This example does the exact same thing, but more verbosely and without decorators:

def decorator1(func):
    return lambda: func() + 1

def decorator2(func):
    def print_func():
        print func()
    return print_func

def function():
    return 41

function = decorator2(decorator1(function))

function()
# prints '42'


Typically decorators are used to add abilities to your functions (see Creating Class Methods, below). But even more typically, they're not used at all. But it's good to know what you're looking at.

For more information, check out my article Python Decorators Don't Have to be (that) Scary, Python Decorators on Dr. Dobbs or "Function Definitions" in the Python Docs

5.5  使用字典函数模拟 switch 语句

Ever miss the switch statement? As you probably know, Python doesn't really have a syntactical equivalent, unless you count repeated elif's. What you might not know, though, is that you can replicate the behavior (if not the cleanliness) of the switch statement by creating a dictionary of functions keyed by the value you want to switch on.

For example, say you're handling keystrokes and you need to call a different function for each keystroke. Also say you've already defined these three functions:

def key_1_pressed():
    print 'Key 1 Pressed'

def key_2_pressed():
    print 'Key 2 Pressed'

def key_3_pressed():
    print 'Key 3 Pressed'

def unknown_key_pressed():
    print 'Unknown Key Pressed'

In Python, you would typically use elif's to choose a function:

keycode = 2
if keycode == 1:
   key_1_pressed()
elif keycode == 2:
   key_2_pressed()
elif number == 3:
   key_3_pressed()
else:
   unknown_key_pressed()
# prints 'Key 2 Pressed'

But you could also throw all the functions in a dictionary, and key them to the value you're switching on. You could even check see if the key exists and run some code if it doesn't:

keycode = 2
functions = {1: key_1_pressed, 2: key_2_pressed, 3: key_3_pressed}
functions.get(keycode, unknown_key_pressed)()

You can see that this could be a lot cleaner than the elif example for large numbers of functions.

六、类

6.1 手动传递 self

Methods are just regular functions that when called from an instance are passed that instance as the first argument (usually called 'self'). If for some reason you're not calling the function from an instance, you can always pass the instance manually as the first argument. For example:

class Class:
    def a_method(self):
        print 'Hey a method'

instance = Class()

instance.a_method()
# prints 'Hey a method', somewhat unsuprisingly.  You can also do:

Class.a_method(instance)
# prints 'Hey a method'

Internally, these statements are exactly the same.

6.2  检查存在的方法或属性

Need to know if a particular class or instance has a particular property or method? You can use the built-in 'hasattr' function to check; it accepts the object and the attribute (as a string) to check for. You use similarly to the dict 'has_key' method (although it works completely differently):

class Class:
    answer = 42
hasattr(Class, 'answer')
# returns True
hasattr(Class, 'question')
# returns False


You can also check for existence of and access the property in one step using the built-in function 'getattr'. getattr also accepts the object and the attribute, as a string, to check for. It has an optional third argument, giving the default if the attribute is not found. Unlike the dict's 'get' method that you might be more familiar with, if the default is not given and the attribute is not found, an AttributeError is raised:

class Class:
    answer = 42
getattr(Class, 'answer')
# returns 42
getattr(Class, 'question', 'What is six times nine?')
# returns 'What is six times nine?'
getattr(Class, 'question')
# raises AttributeError

Don't overuse hasattr and getattr. If you've written your class in manner where you need to keep checking to see if a property exists, you've written it wrong. Just always have the value exist and set it to None (or whatever) if it's not being used. These functions are best used for handling polymorphism, that is, allowing your function/class/whatever to support different kinds of objects.

6.3  创建后再修改类

You can add, modify, or delete a class property or method long after the class has been created, and even after it has been instantiated. Just access the property or method as Class.attribute. No matter when they were created, instances of the class will respect these changes:

class Class:
   def method(self):
        print 'Hey a method'

instance = Class()
instance.method()
# prints 'Hey a method'

def new_method(self):
    print 'New method wins!'

Class.method = new_method
instance.method()
# prints 'New method wins!'

Pretty awesome. But don't get carried away with modifying preexisting methods, it's bad form and can confuse the crap out of any objects using that class. On the other hand, adding methods is a lot less (but still somewhat) dangerous.

6.4  创建类方法

Occasionally when writing a class you want to include a function that is called from the class, not the instance. Perhaps this method creates new instances, or perhaps it is independent of any properties of any individual instance. Python actually gives you two ways to do this, depending if your method needs to (or should) know about which class called it. Both involve applying decorators to your methods.

A 'class method' receives the class as the first argument, just as a regular instance method receives the instance as the first argument. So, the method is aware if it is being called from its own class or from a subclass.

A 'static method' receives no information about where it is called; it is essentially a regular function, just in a different scope.

Class and static methods can be called straight from the class, as Class.method(), or from an instance as Class().method(). The instance is ignored except for its class. Here's an example of each, along with a regular instance method:

class Class:
    @classmethod
    def a_class_method(cls):
        print 'I was called from class %s' % cls
    #
    @staticmethod
    def a_static_method():
        print 'I have no idea where I was called from'
    #
    def an_instance_method(self):
        print 'I was called from the instance %s' % self

instance = Class()

Class.a_class_method()
instance.a_class_method()
# both print 'I was called from class __main__.Class'

Class.a_static_method()
instance.a_static_method()
# both print 'I have no idea where I was called from'

Class.an_instance_method()

# raises TypeError
instance.an_instance_method()
# prints something like 'I was called from the instance <__main__.Class instance at 0x2e80d0>'

七、 总结

Need more inspiration? One good place to look is the Python Built-in Functions page. There's a lot of cool functions that you've probably never heard of.

If you come up with any good tricks or need-to-knows, feel free to add them to this article. (You can get a Siafoo account at http://www.siafoo.net/new/user)

Happy coding.

八、我写的一些其他 python 博文

Python Decorators Don't Have to be (that) Scary

Type Checking in Python

Python __Underscore__ Methods

Python Debugging Tips

Use setup.py to Deploy Your Python App with Style

九、引用和参考

1、Python Tips, Tricks, and Hacks

http://www.siafoo.net/article/52

2、Python 入门指南(中文版)

http://www.pythondoc.com/pythontutorial27/index.html

3、Python 新手常犯错误(第一部分):用一个可变值作为函数参数默认值

http://blog.jobbole.com/42706/

4、Python 新手常犯错误(第二部分):作用域(还可以参考《learning python》P332)

http://blog.jobbole.com/43826/

5、冲刺豆瓣(13):python 面试必读

http://www.cnblogs.com/BeginMan/p/3218164.html

6、30 Python Language Features and Tricks You May Not Know About

http://sahandsaba.com/thirty-python-language-features-and-tricks-you-may-not-know.html

7、Python编程中需要注意的一些事

http://blog.jobbole.com/19835/

8、Python技巧和陷阱

http://python.jobbole.com/81486/

9、分享一个准备给公司讲python高级编程的slide

http://www.dongwm.com/archives/fen-xiang-%5B%3F%5D-ge-zhun-bei-gei-gong-si-jiang-pythongao-ji-bian-cheng-de-slide/

http://dongweiming.github.io/Expert-Python/#1


© 著作权归作者所有

共有 人打赏支持
大数据之路
粉丝 1492
博文 516
码字总数 342415
作品 0
武汉
架构师
Free Download Top 100 Hacking Books

Kali Linux – Assuring Security by Penetration Testing Network Analysis Using Wireshark Cookbook Computer Security Handbook, 6th Edition iPad and iPhone Kung Fu: Tips, Tricks, ......

markGao
2014/06/10
0
1
Python系列教程:必看的好文

  文章都是英文的,学好Python要尝试读英文资料,都是熟练的过程(可以先收藏以后慢慢看)   PEP8 Style Guide for Python Code (必须烂熟于心)   https://www.python.org/dev/peps...

Python火火火
2017/05/02
0
0
如何在Linux的终端测试网速

学习如何在 Linux 终端使用命令行工具 测试网速,或者仅用一条 python 命令立刻获得网速的测试结果。 我们都会在连接到一个新的网络或者 WIFI 的时候去测试网络带宽。 为什么不用我们自己的服...

作者: Shrikant Lavhate
04/19
0
0
Ubuntu Server 18.04 通过 nvm 安装 node

安装 python(node 很多地方依赖 python2,而 Ubuntu Server 18.04 默认没装 Python2) sudo apt install python nvm 官方仓库:https://github.com/creationix/nvm 安装 curl -o- https://r......

RQSLT
05/29
0
0
转-How to Use UTF-8 with Python

from: http://www.evanjones.ca/python-utf8.html Tim Bray describes why Unicode and UTF-8 are wonderful much better than I could, so go read that for an overview of what Unicode i......

玉龙
2012/03/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

qduoj~前端~二次开发~打包docker镜像并上传到阿里云容器镜像仓库

上一篇文章https://my.oschina.net/finchxu/blog/1930017记录了怎么在本地修改前端,现在我要把我的修改添加到部署到本地的前端的docker容器中,然后打包这个容器成为一个本地镜像,然后把这...

虚拟世界的懒猫
今天
1
0
UML中 的各种符号含义

Class Notation A class notation consists of three parts: Class Name The name of the class appears in the first partition. Class Attributes Attributes are shown in the second par......

hutaishi
今天
1
0
20180818 上课截图

小丑鱼00
今天
1
0
Springsecurity之SecurityContextHolderStrategy

注:下面分析的版本是spring-security-4.2.x,源码的github地址是: https://github.com/spring-projects/spring-security/tree/4.2.x 先上一张图: 图1 SecurityContextHolderStrategy的三个......

汉斯-冯-拉特
今天
1
0
LNMP架构(Nginx负载均衡、ssl原理、生成ssl密钥对、Nginx配置ssl)

Nginx负载均衡 网站的访问量越来越大,服务器的服务模式也得进行相应的升级,比如分离出数据库服务器、分离出图片作为单独服务,这些是简单的数据的负载均衡,将压力分散到不同的机器上。有时...

蛋黄_Yolks
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部