' ''' " """ print """I wish that I'd never heard him say, '''She said, "He said, 'Give me five dollars'"'''"""
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'
结论:如果只是想判断对象是否为空,真的没必要去检查它的长度或者是否相等。
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!'
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.
默认是整除,想要得到小数请看如下的几种方法
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
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
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]
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]
恩,列表解析再一次给了我们更短、更简洁、更容易理解的代码。
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]
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).
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.
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.
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.
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!'
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)]
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).
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
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.
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)]
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.
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': '[email protected]', 'Jane': '[email protected]', 'Stou': '[email protected]'} 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.
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.
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.
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.
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
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
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)
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)'
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)'.
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
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.
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.
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.
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.
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 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