1 Introduction
Since I started learning Python, I decided to maintain an often visited list of "tricks". Any time I saw a piece of code (in an example, on Stack Overflow, in open source software, etc.) that made me think "Cool! I didn't know you could do that!" I experimented with it until I understood it and then added it to the list. This post is part of that list, after some cleaning up. If you are an experienced Python programmer, chances are you already know most of these, though you might still find a few that you didn't know about. If you are a C, C++ or Java programmer who is learning Python, or just brand new to programming, then you might find quite a few of them surprisingly useful, like I did.linux
Each trick or language feature is demonstrated only through examples, with no explanation. While I tried my best to make the examples clear, some of them might still appear cryptic depending on your familiarity level. So if something still doesn't make sense after looking at the examples, the title should be clear enough to allow you to use Google for more information on it.c++
The list is very roughly ordered by difficulty, with the easier and more commonly known language features and tricks appearing first.git
A table of contents is given at the end.程序員
- Update - April 9th, 2014
- As you can see the article has been growing with currently 38 items in it, mostly thanks to comments from readers. As such the number 30 in the title is no longer accurate. However, I chose to leave it as is since that's the original title the article was shared as, making it more recognizable and easier to find.
- Update - March 14th, 2014
- Roy Keyes made a great suggestion of turning this article into a GitHub repository to allow readers to make improvements or additions through pull requests. The repository is now at https://github.com/sahands/python-by-example. Feel free to fork, add improvements or additions and submit pull requests. I will update this page periodically with the new additions.
- Update - March 8th, 2014
-
This article generated a lot of good discussion on Reddit (http://redd.it/1zv3q3), Hacker News (https://news.ycombinator.com/item?id=7365410), and in the comments below, with many readers suggesting great alternatives and improvements. I have updated the list below to include many of the improvements suggested, and added a few new items based on suggestions that made me have one of those "Cool! I didn't know you could do that!" moments. In particular, I did not know about itertools.chain.from_iterable, and dictionary comprehensions.github
There was also a very interesting discussion about the possibility of some of the techniques below leading to harder to debug code. My say on it is that as far as I can see, none of the items below are inherently harder to debug. But I can definitely see how they can be taken too far, resulting in hard to debug, maintain and understand code. Use your best judgment and if it feels like how short and smart your code is is outweighing how readable and maintainable it is, then break it down and simplify it. For example, I think list comprehensions can be very readable and rather easy to debug and maintain. But a list comprehension inside another list comprehension that is then passed to map and then toitertools.chain? Probably not the best idea!web
1.1 Unpacking
>>> a, b, c = 1, 2, 3 >>> a, b, c (1, 2, 3) >>> a, b, c = [1, 2, 3] >>> a, b, c (1, 2, 3) >>> a, b, c = (2 * i + 1 for i in range(3)) >>> a, b, c (1, 3, 5) >>> a, (b, c), d = [1, (2, 3), 4] >>> a 1 >>> b 2 >>> c 3 >>> d 4
1.2 Unpacking for swapping variables
>>> a, b = 1, 2 >>> a, b = b, a >>> a, b (2, 1)
1.3 Extended unpacking (Python 3 only)
>>> a, *b, c = [1, 2, 3, 4, 5] >>> a 1 >>> b [2, 3, 4] >>> c 5
1.4 Negative indexing
>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> a[-1] 10 >>> a[-3] 8
1.5 List slices (a[start:end])
>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> a[2:8] [2, 3, 4, 5, 6, 7]
1.6 List slices with negative indexing
>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> a[-4:-2] [7, 8]
1.7 List slices with step (a[start:end:step])
>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> a[::2] [0, 2, 4, 6, 8, 10] >>> a[::3] [0, 3, 6, 9] >>> a[2:8:2] [2, 4, 6]
1.8 List slices with negative step
>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> a[::-1] [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] >>> a[::-2] [10, 8, 6, 4, 2, 0]
1.9 List slice assignment
>>> a = [1, 2, 3, 4, 5] >>> a[2:3] = [0, 0] >>> a [1, 2, 0, 0, 4, 5] >>> a[1:1] = [8, 9] >>> a [1, 8, 9, 2, 0, 0, 4, 5] >>> a[1:-1] = [] >>> a [1, 5]
1.10 Naming slices (slice(start, end, step))
>>> a = [0, 1, 2, 3, 4, 5] >>> LASTTHREE = slice(-3, None) >>> LASTTHREE slice(-3, None, None) >>> a[LASTTHREE] [3, 4, 5]
1.11 Iterating over list index and value pairs (enumerate)
>>> a = ['Hello', 'world', '!'] >>> for i, x in enumerate(a): ... print '{}: {}'.format(i, x) ... 0: Hello 1: world 2: !
1.12 Iterating over dictionary key and value pairs (dict.iteritems)
>>> m = {'a': 1, 'b': 2, 'c': 3, 'd': 4} >>> for k, v in m.iteritems(): ... print '{}: {}'.format(k, v) ... a: 1 c: 3 b: 2 d: 4
Note: use dict.items in Python 3.算法
1.13 Zipping and unzipping lists and iterables
>>> a = [1, 2, 3] >>> b = ['a', 'b', 'c'] >>> z = zip(a, b) >>> z [(1, 'a'), (2, 'b'), (3, 'c')] >>> zip(*z) [(1, 2, 3), ('a', 'b', 'c')]
1.14 Grouping adjacent list items using zip
>>> a = [1, 2, 3, 4, 5, 6] >>> # Using iterators >>> group_adjacent = lambda a, k: zip(*([iter(a)] * k)) >>> group_adjacent(a, 3) [(1, 2, 3), (4, 5, 6)] >>> group_adjacent(a, 2) [(1, 2), (3, 4), (5, 6)] >>> group_adjacent(a, 1) [(1,), (2,), (3,), (4,), (5,), (6,)] >>> # Using slices >>> from itertools import islice >>> group_adjacent = lambda a, k: zip(*(islice(a, i, None, k) for i in range(k))) >>> group_adjacent(a, 3) [(1, 2, 3), (4, 5, 6)] >>> group_adjacent(a, 2) [(1, 2), (3, 4), (5, 6)] >>> group_adjacent(a, 1) [(1,), (2,), (3,), (4,), (5,), (6,)]
1.15 Sliding windows (\(n\)-grams) using zip and iterators
>>> from itertools import islice >>> def n_grams(a, n): ... z = (islice(a, i, None) for i in range(n)) ... return zip(*z) ... >>> a = [1, 2, 3, 4, 5, 6] >>> n_grams(a, 3) [(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6)] >>> n_grams(a, 2) [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)] >>> n_grams(a, 4) [(1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 6)]
1.16 Inverting a dictionary using zip
>>> m = {'a': 1, 'b': 2, 'c': 3, 'd': 4} >>> m.items() [('a', 1), ('c', 3), ('b', 2), ('d', 4)] >>> zip(m.values(), m.keys()) [(1, 'a'), (3, 'c'), (2, 'b'), (4, 'd')] >>> mi = dict(zip(m.values(), m.keys())) >>> mi {1: 'a', 2: 'b', 3: 'c', 4: 'd'}
1.17 Flattening lists:
>>> a = [[1, 2], [3, 4], [5, 6]] >>> list(itertools.chain.from_iterable(a)) [1, 2, 3, 4, 5, 6] >>> sum(a, []) [1, 2, 3, 4, 5, 6] >>> [x for l in a for x in l] [1, 2, 3, 4, 5, 6] >>> a = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] >>> [x for l1 in a for l2 in l1 for x in l2] [1, 2, 3, 4, 5, 6, 7, 8] >>> a = [1, 2, [3, 4], [[5, 6], [7, 8]]] >>> flatten = lambda x: [y for l in x for y in flatten(l)] if type(x) is list else [x] >>> flatten(a) [1, 2, 3, 4, 5, 6, 7, 8]
Note: according to Python's documentation on sum, itertools.chain.from_iterable is the preferred method for this.sql
1.18 Generator expressions
>>> g = (x ** 2 for x in xrange(10)) >>> next(g) 0 >>> next(g) 1 >>> next(g) 4 >>> next(g) 9 >>> sum(x ** 3 for x in xrange(10)) 2025 >>> sum(x ** 3 for x in xrange(10) if x % 3 == 1) 408
1.19 Dictionary comprehensions
>>> m = {x: x ** 2 for x in range(5)} >>> m {0: 0, 1: 1, 2: 4, 3: 9, 4: 16} >>> m = {x: 'A' + str(x) for x in range(10)} >>> m {0: 'A0', 1: 'A1', 2: 'A2', 3: 'A3', 4: 'A4', 5: 'A5', 6: 'A6', 7: 'A7', 8: 'A8', 9: 'A9'}
1.20 Inverting a dictionary using a dictionary comprehension
>>> m = {'a': 1, 'b': 2, 'c': 3, 'd': 4} >>> m {'d': 4, 'a': 1, 'b': 2, 'c': 3} >>> {v: k for k, v in m.items()} {1: 'a', 2: 'b', 3: 'c', 4: 'd'}
1.21 Named tuples (collections.namedtuple)
>>> Point = collections.namedtuple('Point', ['x', 'y']) >>> p = Point(x=1.0, y=2.0) >>> p Point(x=1.0, y=2.0) >>> p.x 1.0 >>> p.y 2.0
1.22 Inheriting from named tuples:
>>> class Point(collections.namedtuple('PointBase', ['x', 'y'])): ... __slots__ = () ... def __add__(self, other): ... return Point(x=self.x + other.x, y=self.y + other.y) ... >>> p = Point(x=1.0, y=2.0) >>> q = Point(x=2.0, y=3.0) >>> p + q Point(x=3.0, y=5.0)
1.23 Sets and set operations
>>> A = {1, 2, 3, 3} >>> A set([1, 2, 3]) >>> B = {3, 4, 5, 6, 7} >>> B set([3, 4, 5, 6, 7]) >>> A | B set([1, 2, 3, 4, 5, 6, 7]) >>> A & B set([3]) >>> A - B set([1, 2]) >>> B - A set([4, 5, 6, 7]) >>> A ^ B set([1, 2, 4, 5, 6, 7]) >>> (A ^ B) == ((A - B) | (B - A)) True
1.24 Multisets and multiset operations (collections.Counter)
>>> A = collections.Counter([1, 2, 2]) >>> B = collections.Counter([2, 2, 3]) >>> A Counter({2: 2, 1: 1}) >>> B Counter({2: 2, 3: 1}) >>> A | B Counter({2: 2, 1: 1, 3: 1}) >>> A & B Counter({2: 2}) >>> A + B Counter({2: 4, 1: 1, 3: 1}) >>> A - B Counter({1: 1}) >>> B - A Counter({3: 1})
1.25 Most common elements in an iterable (collections.Counter)
>>> A = collections.Counter([1, 1, 2, 2, 3, 3, 3, 3, 4, 5, 6, 7]) >>> A Counter({3: 4, 1: 2, 2: 2, 4: 1, 5: 1, 6: 1, 7: 1}) >>> A.most_common(1) [(3, 4)] >>> A.most_common(3) [(3, 4), (1, 2), (2, 2)]
1.26 Double-ended queue (collections.deque)
>>> Q = collections.deque() >>> Q.append(1) >>> Q.appendleft(2) >>> Q.extend([3, 4]) >>> Q.extendleft([5, 6]) >>> Q deque([6, 5, 2, 1, 3, 4]) >>> Q.pop() 4 >>> Q.popleft() 6 >>> Q deque([5, 2, 1, 3]) >>> Q.rotate(3) >>> Q deque([2, 1, 3, 5]) >>> Q.rotate(-3) >>> Q deque([5, 2, 1, 3])
1.27 Double-ended queue with maximum length (collections.deque)
>>> last_three = collections.deque(maxlen=3) >>> for i in xrange(10): ... last_three.append(i) ... print ', '.join(str(x) for x in last_three) ... 0 0, 1 0, 1, 2 1, 2, 3 2, 3, 4 3, 4, 5 4, 5, 6 5, 6, 7 6, 7, 8 7, 8, 9
1.28 Ordered dictionaries (collections.OrderedDict)
>>> m = dict((str(x), x) for x in range(10)) >>> print ', '.join(m.keys()) 1, 0, 3, 2, 5, 4, 7, 6, 9, 8 >>> m = collections.OrderedDict((str(x), x) for x in range(10)) >>> print ', '.join(m.keys()) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 >>> m = collections.OrderedDict((str(x), x) for x in range(10, 0, -1)) >>> print ', '.join(m.keys()) 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
1.29 Default dictionaries (collections.defaultdict)
>>> m = dict() >>> m['a'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'a' >>> >>> m = collections.defaultdict(int) >>> m['a'] 0 >>> m['b'] 0 >>> m = collections.defaultdict(str) >>> m['a'] '' >>> m['b'] += 'a' >>> m['b'] 'a' >>> m = collections.defaultdict(lambda: '[default value]') >>> m['a'] '[default value]' >>> m['b'] '[default value]'
1.30 Using default dictionaries to represent simple trees
>>> import json >>> tree = lambda: collections.defaultdict(tree) >>> root = tree() >>> root['menu']['id'] = 'file' >>> root['menu']['value'] = 'File' >>> root['menu']['menuitems']['new']['value'] = 'New' >>> root['menu']['menuitems']['new']['onclick'] = 'new();' >>> root['menu']['menuitems']['open']['value'] = 'Open' >>> root['menu']['menuitems']['open']['onclick'] = 'open();' >>> root['menu']['menuitems']['close']['value'] = 'Close' >>> root['menu']['menuitems']['close']['onclick'] = 'close();' >>> print json.dumps(root, sort_keys=True, indent=4, separators=(',', ': ')) { "menu": { "id": "file", "menuitems": { "close": { "onclick": "close();", "value": "Close" }, "new": { "onclick": "new();", "value": "New" }, "open": { "onclick": "open();", "value": "Open" } }, "value": "File" } }
(See https://gist.github.com/hrldcpr/2012250 for more on this.)
1.31 Mapping objects to unique counting numbers (collections.defaultdict)
>>> import itertools, collections >>> value_to_numeric_map = collections.defaultdict(itertools.count().next) >>> value_to_numeric_map['a'] 0 >>> value_to_numeric_map['b'] 1 >>> value_to_numeric_map['c'] 2 >>> value_to_numeric_map['a'] 0 >>> value_to_numeric_map['b'] 1
1.32 Largest and smallest elements (heapq.nlargest andheapq.nsmallest)
>>> a = [random.randint(0, 100) for __ in xrange(100)] >>> heapq.nsmallest(5, a) [3, 3, 5, 6, 8] >>> heapq.nlargest(5, a) [100, 100, 99, 98, 98]
1.33 Cartesian products (itertools.product)
>>> for p in itertools.product([1, 2, 3], [4, 5]): (1, 4) (1, 5) (2, 4) (2, 5) (3, 4) (3, 5) >>> for p in itertools.product([0, 1], repeat=4): ... print ''.join(str(x) for x in p) ... 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
1.34 Combinations and combinations with replacement (itertools.combinations anditertools.combinations_with_replacement)
>>> for c in itertools.combinations([1, 2, 3, 4, 5], 3): ... print ''.join(str(x) for x in c) ... 123 124 125 134 135 145 234 235 245 345 >>> for c in itertools.combinations_with_replacement([1, 2, 3], 2): ... print ''.join(str(x) for x in c) ... 11 12 13 22 23 33
1.35 Permutations (itertools.permutations)
>>> for p in itertools.permutations([1, 2, 3, 4]): ... print ''.join(str(x) for x in p) ... 1234 1243 1324 1342 1423 1432 2134 2143 2314 2341 2413 2431 3124 3142 3214 3241 3412 3421 4123 4132 4213 4231 4312 4321
1.36 Chaining iterables (itertools.chain)
>>> a = [1, 2, 3, 4] >>> for p in itertools.chain(itertools.combinations(a, 2), itertools.combinations(a, 3)): ... print p ... (1, 2) (1, 3) (1, 4) (2, 3) (2, 4) (3, 4) (1, 2, 3) (1, 2, 4) (1, 3, 4) (2, 3, 4) >>> for subset in itertools.chain.from_iterable(itertools.combinations(a, n) for n in range(len(a) + 1)) ... print subset ... () (1,) (2,) (3,) (4,) (1, 2) (1, 3) (1, 4) (2, 3) (2, 4) (3, 4) (1, 2, 3) (1, 2, 4) (1, 3, 4) (2, 3, 4) (1, 2, 3, 4)
1.37 Grouping rows by a given key (itertools.groupby)
>>> from operator import itemgetter >>> import itertools >>> with open('contactlenses.csv', 'r') as infile: ... data = [line.strip().split(',') for line in infile] ... >>> data = data[1:] >>> def print_data(rows): ... print '\n'.join('\t'.join('{: <16}'.format(s) for s in row) for row in rows) ... >>> print_data(data) young myope no reduced none young myope no normal soft young myope yes reduced none young myope yes normal hard young hypermetrope no reduced none young hypermetrope no normal soft young hypermetrope yes reduced none young hypermetrope yes normal hard pre-presbyopic myope no reduced none pre-presbyopic myope no normal soft pre-presbyopic myope yes reduced none pre-presbyopic myope yes normal hard pre-presbyopic hypermetrope no reduced none pre-presbyopic hypermetrope no normal soft pre-presbyopic hypermetrope yes reduced none pre-presbyopic hypermetrope yes normal none presbyopic myope no reduced none presbyopic myope no normal none presbyopic myope yes reduced none presbyopic myope yes normal hard presbyopic hypermetrope no reduced none presbyopic hypermetrope no normal soft presbyopic hypermetrope yes reduced none presbyopic hypermetrope yes normal none >>> data.sort(key=itemgetter(-1)) >>> for value, group in itertools.groupby(data, lambda r: r[-1]): ... print '-----------' ... print 'Group: ' + value ... print_data(group) ... ----------- Group: hard young myope yes normal hard young hypermetrope yes normal hard pre-presbyopic myope yes normal hard presbyopic myope yes normal hard ----------- Group: none young myope no reduced none young myope yes reduced none young hypermetrope no reduced none young hypermetrope yes reduced none pre-presbyopic myope no reduced none pre-presbyopic myope yes reduced none pre-presbyopic hypermetrope no reduced none pre-presbyopic hypermetrope yes reduced none pre-presbyopic hypermetrope yes normal none presbyopic myope no reduced none presbyopic myope no normal none presbyopic myope yes reduced none presbyopic hypermetrope no reduced none presbyopic hypermetrope yes reduced none presbyopic hypermetrope yes normal none ----------- Group: soft young myope no normal soft young hypermetrope no normal soft pre-presbyopic myope no normal soft pre-presbyopic hypermetrope no normal soft presbyopic hypermetrope no normal soft
1.38 Start a static HTTP server in any directory
[10:26] $ python -m SimpleHTTPServer 5000 Serving HTTP on 0.0.0.0 port 5000 ...
1.39 Learn the Zen of 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!
1.40 Use C-Style Braces Instead of Indentation to Denote Scopes
>>> from __future__ import braces
2 Table of contents
List of language features and tricks in this article:
- 1 Introduction
- 1.1 Unpacking
- 1.2 Unpacking for swapping variables
- 1.3 Extended unpacking (Python 3 only)
- 1.4 Negative indexing
- 1.5 List slices (a[start:end])
- 1.6 List slices with negative indexing
- 1.7 List slices with step (a[start:end:step])
- 1.8 List slices with negative step
- 1.9 List slice assignment
- 1.10 Naming slices (slice(start, end, step))
- 1.11 Iterating over list index and value pairs (enumerate)
- 1.12 Iterating over dictionary key and value pairs (dict.iteritems)
- 1.13 Zipping and unzipping lists and iterables
- 1.14 Grouping adjacent list items using zip
- 1.15 Sliding windows (\(n\)-grams) using zip and iterators
- 1.16 Inverting a dictionary using zip
- 1.17 Flattening lists:
- 1.18 Generator expressions
- 1.19 Dictionary comprehensions
- 1.20 Inverting a dictionary using a dictionary comprehension
- 1.21 Named tuples (collections.namedtuple)
- 1.22 Inheriting from named tuples:
- 1.23 Sets and set operations
- 1.24 Multisets and multiset operations (collections.Counter)
- 1.25 Most common elements in an iterable (collections.Counter)
- 1.26 Double-ended queue (collections.deque)
- 1.27 Double-ended queue with maximum length (collections.deque)
- 1.28 Ordered dictionaries (collections.OrderedDict)
- 1.29 Default dictionaries (collections.defaultdict)
- 1.30 Using default dictionaries to represent simple trees
- 1.31 Mapping objects to unique counting numbers (collections.defaultdict)
- 1.32 Largest and smallest elements (heapq.nlargest and heapq.nsmallest)
- 1.33 Cartesian products (itertools.product)
- 1.34 Combinations and combinations with replacement (itertools.combinations anditertools.combinations_with_replacement)
- 1.35 Permutations (itertools.permutations)
- 1.36 Chaining iterables (itertools.chain)
- 1.37 Grouping rows by a given key (itertools.groupby)
- 1.38 Start a static HTTP server in any directory
- 1.39 Learn the Zen of Python
- 1.40 Use C-Style Braces Instead of Indentation to Denote Scopes
B、http://ozkatz.github.io/improving-your-python-productivity.html
I've been programming in python for a few years now, and I'm still often amazed by how clear and DRY well written Python code can be. There are many small tips and tricks I've learned over time, mostly by reading the code of popular open source projects, such as Django, Flask, Requests and others.
Here are a few things I've picked up that are sometimes overlooked, but can really help with everyday work.
1. Dictionary and Set comprehensions
Most Python developers know and use list comprehensions. For those of you unfamiliar with the concept, a list comprehension is a shorter, more concise way of creating lists.
>>> some_list = [1, 2, 3, 4, 5] >>> another_list = [ x + 1 for x in some_list ] >>> another_list [2, 3, 4, 5, 6]
Since python 3.1 (and also back-ported to Python 2.7), we can now use the same idiom to create sets and dictionaries:
>>> # Set Comprehensions >>> some_list = [1, 2, 3, 4, 5, 2, 5, 1, 4, 8] >>> even_set = { x for x in some_list if x % 2 == 0 } >>> even_set set([8, 2, 4]) >>> # Dict Comprehensions >>> d = { x: x % 2 == 0 for x in range(1, 11) } >>> d {1: False, 2: True, 3: False, 4: True, 5: False, 6: True, 7: False, 8: True, 9: False, 10: True}
In the first example we create a set of unique items from some_list
, but only ones that are even numbers. The Dictionary example shows the creation of a dictionary, where the keys are integers from 1 to 10 (inclusive), and the values are booleans, indicating whether or not the key is an even number.
Another feature worth mentioning here is the availability of set literals. We can create a set simply by doing:
>>> my_set = {1, 2, 1, 2, 3, 4} >>> my_set set([1, 2, 3, 4])
Instead of using the set()
built-in function.
2. Using a Counter object for, well, counting.
This is a pretty obvious one, but it's often forgotten. Counting stuff is a pretty common task for most developers, and while it isn't a very challenging thing to do (In most cases) - there are ways to make it even easier.
Python's collections library includes a subclass of the built-in dict
, created specifically for this task:
>>> from collections import Counter >>> c = Counter('hello world') >>> c Counter({'l': 3, 'o': 2, ' ': 1, 'e': 1, 'd': 1, 'h': 1, 'r': 1, 'w': 1}) >>> c.most_common(2) [('l', 3), ('o', 2)]
3. Pretty printing JSON
JSON is a great serialization format, heavily used today by APIs and web-services. While it's somewhat human-readable, when working with the built-in json
module, It's very hard to make sense of larger data structures with the naked eye, as they appear as one long, continuous line.
To make things a bit easier we can use the indent
argument to "pretty print" our JSON data. this is especially useful when working interactively from the REPL or for logging:
>>> import json >>> print(json.dumps(data)) # No indention {"status": "OK", "count": 2, "results": [{"age": 27, "name": "Oz", "lactose_intolerant": true}, {"age": 29, "name": "Joe", "lactose_intolerant": false}]} >>> print(json.dumps(data, indent=2)) # With indention { "status": "OK", "count": 2, "results": [ { "age": 27, "name": "Oz", "lactose_intolerant": true }, { "age": 29, "name": "Joe", "lactose_intolerant": false } ] }
Also, be sure to checkout the built-in pprint module for pretty-printing anything else.
4. Create a one-off, quick and dirty web service
Sometimes we need a very basic and quick way of doing some sort of RPC between services or machines. All we want is a simple way to call a method of program A by program B, possibly on another machine - for internal use.
While I wouldn't advocate this technique for creating anything other than internal one-off hacks, we can use a protocol called XML-RPC (with its corresponding Python libraries) for this sort of work.
Here is an example of a quick and dirty file-reading server, using theSimpleXMLRPCServer
module:
from SimpleXMLRPCServer import SimpleXMLRPCServer def file_reader(file_name): with open(file_name, 'r') as f: return f.read() server = SimpleXMLRPCServer(('localhost', 8000)) server.register_introspection_functions() server.register_function(file_reader) server.serve_forever()
With the corresponding client:
import xmlrpclib proxy = xmlrpclib.ServerProxy('http://localhost:8000/') proxy.file_reader('/tmp/secret.txt')
And we now have some sort of a remote file reader with no external dependencies and only a little code (And, of course, no security of any kind, so try this only at home).
5. Python's awesome open source community has you covered
Well, I've only covered things available in Python's standard library, which, if you have Python installed, is already included with your distribution. for most other tasks, there are a ton of community maintained 3rd party libraries at your disposal.
Here is my checklist for choosing a great reliable open source library:
A Great open source library must...
- Contain a clear license that suites your particular case.
- Be Actively developed and maintained (Or, your willingness to step in and maintain it).
- Be installable using
pip
for easy, repeatable deployment. - have a test suite with at least decent coverage.
If you do find a good library that fits your needs, don't be shy - most open source projects welcome contributions and assistance, even if you are not a Python veteran. Helping hands (and heads) are almost always welcome.
As always, I'd be happy to learn from your experience and of course discuss the tips I presented! Hit me up in the comments here below, or on Twitter at ozkatz100.
C、http://blog.jobbole.com/61171/
本文由 伯樂在線 - 熊崽Kevin 翻譯。未經許可,禁止轉載!
英文出處:pypix。歡迎加入翻譯組。
譯註:本文的英文題目是《Advanced Design Patterns in Python》,可直譯爲《Python中的高級設計模式》。不過本人在通讀徹底文以後發現文章的內容與咱們一般理解的設計模式有很大出入,原文後的評論中也有其餘讀者提出了這一點。所以,根據文章內容我將題目譯爲「Python高級編程技巧」,若有不一樣意見請留言指出,很是感謝。
正文:
本文展現一些高級的Python設計結構和它們的使用方法。在平常工做中,你能夠根據須要選擇合適的數據結構,例如對快速查找性的要求、對數據一致性的要求或是對索引的要求等,同時也能夠將各類數據結構合適地結合在一塊兒,從而生成具備邏輯性並易於理解的數據模型。Python的數據結構從句法上來看很是直觀,而且提供了大量的可選操做。這篇指南嘗試將大部分經常使用的數據結構知識放到一塊兒,而且提供對其最佳用法的探討。
推導式(Comprehensions)
若是你已經使用了很長時間的Python,那麼你至少應該據說過列表推導(list comprehensions)。這是一種將for循環、if表達式以及賦值語句放到單一語句中的一種方法。換句話說,你可以經過一個表達式對一個列表作映射或過濾操做。
一個列表推導式包含如下幾個部分:
- 一個輸入序列
- 一個表示輸入序列成員的變量
- 一個可選的斷言表達式
- 一個將輸入序列中知足斷言表達式的成員變換成輸出列表成員的輸出表達式
舉個例子,咱們須要從一個輸入列表中將全部大於0的整數平方生成一個新的序列,你也許會這麼寫:
1
2
3
4
5
6
7
8
9
|
num = [1, 4, -5, 10, -7, 2, 3, -1]
filtered_and_squared = []
for number in num:
if number > 0:
filtered_and_squared.append(number ** 2)
print filtered_and_squared
# [1, 16, 100, 4, 9]
|
很簡單是吧?可是這就會有4行代碼,兩層嵌套外加一個徹底沒必要要的append操做。而若是使用filter、lambda和map函數,則可以將代碼大大簡化:
1
2
3
4
5
|
num = [1, 4, -5, 10, -7, 2, 3, -1]
filtered_and_squared = map(lambda x: x ** 2, filter(lambda x: x > 0, num))
print filtered_and_squared
# [1, 16, 100, 4, 9]
|
嗯,這麼一來代碼就會在水平方向上展開。那麼是否可以繼續簡化代碼呢?列表推導可以給咱們答案:
1
2
3
4
5
|
num = [1, 4, -5, 10, -7, 2, 3, -1]
filtered_and_squared = [ x**2 for x in num if x > 0]
print filtered_and_squared
# [1, 16, 100, 4, 9]
|
- 迭代器(iterator)遍歷輸入序列num的每一個成員x
- 斷言式判斷每一個成員是否大於零
- 若是成員大於零,則被交給輸出表達式,平方以後成爲輸出列表的成員。
列表推導式被封裝在一個列表中,因此很明顯它可以當即生成一個新列表。這裏只有一個type函數調用而沒有隱式調用lambda函數,列表推導式正是使用了一個常規的迭代器、一個表達式和一個if表達式來控制可選的參數。
另外一方面,列表推導也可能會有一些負面效應,那就是整個列表必須一次性加載於內存之中,這對上面舉的例子而言不是問題,甚至擴大若干倍以後也都不是問題。可是總會達到極限,內存總會被用完。
針對上面的問題,生成器(Generator)可以很好的解決。生成器表達式不會一次將整個列表加載到內存之中,而是生成一個生成器對象(Generator objector),因此一次只加載一個列表元素。
生成器表達式同列表推導式有着幾乎相同的語法結構,區別在於生成器表達式是被圓括號包圍,而不是方括號:
1
2
3
4
5
6
7
8
9
10
|
num = [1, 4, -5, 10, -7, 2, 3, -1]
filtered_and_squared = ( x**2 for x in num if x > 0 )
print filtered_and_squared
# <generator object <genexpr> at 0x00583E18>
for item in filtered_and_squared:
print item
# 1, 16, 100 4,9
|
這比列表推導效率稍微提升一些,讓咱們再一次改造一下代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
num = [1, 4, -5, 10, -7, 2, 3, -1]
def square_generator(optional_parameter):
return (x ** 2 for x in num if x > optional_parameter)
print square_generator(0)
# <generator object <genexpr> at 0x004E6418>
# Option I
for k in square_generator(0):
print k
# 1, 16, 100, 4, 9
# Option II
g = list(square_generator(0))
print g
# [1, 16, 100, 4, 9]
|
除非特殊的緣由,應該常常在代碼中使用生成器表達式。但除非是面對很是大的列表,不然是不會看出明顯區別的。
下例使用zip()函數一次處理兩個或多個列表中的元素:
1
2
3
4
5
6
7
8
9
|
alist = ['a1', 'a2', 'a3']
blist = ['1', '2', '3']
for a, b in zip(alist, blist):
print a, b
# a1 1
# a2 2
# a3 3
|
再來看一個經過兩階列表推導式遍歷目錄的例子:
1
2
3
4
5
6
7
8
|
import os
def tree(top):
for path, names, fnames in os.walk(top):
for fname in fnames:
yield os.path.join(path, fname)
for name in tree('C:\Users\XXX\Downloads\Test'):
print name
|
裝飾器(Decorators)
裝飾器爲咱們提供了一個增長已有函數或類的功能的有效方法。聽起來是否是很像Java中的面向切面編程(Aspect-Oriented Programming)概念?二者都很簡單,而且裝飾器有着更爲強大的功能。舉個例子,假定你但願在一個函數的入口和退出點作一些特別的操做(好比一些安全、追蹤以及鎖定等操做)就可使用裝飾器。
裝飾器是一個包裝了另外一個函數的特殊函數:主函數被調用,而且其返回值將會被傳給裝飾器,接下來裝飾器將返回一個包裝了主函數的替代函數,程序的其餘部分看到的將是這個包裝函數。
1
2
3
4
5
6
7
8
9
10
|
def timethis(func):
'''
Decorator that reports the execution time.
'''
pass
@timethis
def countdown(n):
while n > 0:
n -= 1
|
語法糖@標識了裝飾器。
好了,讓咱們回到剛纔的例子。咱們將用裝飾器作一些更典型的操做:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
import time
from functools import wraps
def timethis(func):
'''
Decorator that reports the execution time.
'''
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(func.__name__, end-start)
return result
return wrapper
@timethis
def countdown(n):
while n > 0:
n -= 1
countdown(100000)
# ('countdown', 0.006999969482421875)
|
當你寫下以下代碼時:
1
2
|
@timethis
def countdown(n):
|
意味着你分開執行了如下步驟:
1
2
3
|
def countdown(n):
...
countdown = timethis(countdown)
|
裝飾器函數中的代碼建立了一個新的函數(正如此例中的wrapper函數),它用 *args 和 **kwargs 接收任意的輸入參數,而且在此函數內調用原函數而且返回其結果。你能夠根據本身的須要放置任何額外的代碼(例如本例中的計時操做),新建立的包裝函數將做爲結果返回並取代原函數。
1
2
3
|
@decorator
def function():
print("inside function")
|
當編譯器查看以上代碼時,function()函數將會被編譯,而且函數返回對象將會被傳給裝飾器代碼,裝飾器將會在作完相關操做以後用一個新的函數對象代替原函數。
裝飾器代碼是什麼樣的?大部分的例子都是將裝飾器定義爲函數,而我發覺將裝飾器定義成類更容易理解其功能,而且這樣更能發揮裝飾器機制的威力。
對裝飾器的類實現惟一要求是它必須能如函數通常使用,也就是說它必須是可調用的。因此,若是想這麼作這個類必須實現__call__方法。
這樣的裝飾器應該用來作些什麼?它能夠作任何事,但一般它用在當你想在一些特殊的地方使用原函數時,但這不是必須的,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class decorator(object):
def __init__(self, f):
print("inside decorator.__init__()")
f() # Prove that function definition has completed
def __call__(self):
print("inside decorator.__call__()")
@decorator
def function():
print("inside function()")
print("Finished decorating function()")
function()
# inside decorator.__init__()
# inside function()
# Finished decorating function()
# inside decorator.__call__()
|
譯者注:
1. 語法糖@decorator至關於function=decorator(function),在此調用decorator的__init__打印「inside decorator.__init__()」
2. 隨後執行f()打印「inside function()」
3. 隨後執行「print(「Finished decorating function()」)」
4. 最後在調用function函數時,因爲使用裝飾器包裝,所以執行decorator的__call__打印 「inside decorator.__call__()」。
一個更實際的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
def decorator(func):
def modify(*args, **kwargs):
variable = kwargs.pop('variable', None)
print variable
x,y=func(*args, **kwargs)
return x,y
return modify
@decorator
def func(a,b):
print a**2,b**2
return a**2,b**2
func(a=4, b=5, variable="hi")
func(a=4, b=5)
# hi
# 16 25
# None
# 16 25
|
上下文管理庫(ContextLib)
contextlib模塊包含了與上下文管理器和with聲明相關的工具。一般若是你想寫一個上下文管理器,則你須要定義一個類包含__enter__方法以及__exit__方法,例如:
1
2
3
4
5
6
7
8
9
10
11
|
import time
class demo:
def __init__(self, label):
self.label = label
def __enter__(self):
self.start = time.time()
def __exit__(self, exc_ty, exc_val, exc_tb):
end = time.time()
print('{}: {}'.format(self.label, end - self.start))
|
完整的例子在此:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import time
class demo:
def __init__(self, label):
self.label = label
def __enter__(self):
self.start = time.time()
def __exit__(self, exc_ty, exc_val, exc_tb):
end = time.time()
print('{}: {}'.format(self.label, end - self.start))
with demo('counting'):
n = 10000000
while n > 0:
n -= 1
# counting: 1.36000013351
|
上下文管理器被with聲明所激活,這個API涉及到兩個方法。
1. __enter__方法,當執行流進入with代碼塊時,__enter__方法將執行。而且它將返回一個可供上下文使用的對象。
2. 當執行流離開with代碼塊時,__exit__方法被調用,它將清理被使用的資源。
利用@contextmanager裝飾器改寫上面那個例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
from contextlib import contextmanager
import time
@contextmanager
def demo(label):
start = time.time()
try:
yield
finally:
end = time.time()
print('{}: {}'.format(label, end - start))
with demo('counting'):
n = 10000000
while n > 0:
n -= 1
# counting: 1.32399988174
|
看上面這個例子,函數中yield以前的全部代碼都相似於上下文管理器中__enter__方法的內容。而yield以後的全部代碼都如__exit__方法的內容。若是執行過程當中發生了異常,則會在yield語句觸發。
描述器(Descriptors)
描述器決定了對象屬性是如何被訪問的。描述器的做用是定製當你想引用一個屬性時所發生的操做。
構建描述器的方法是至少定義如下三個方法中的一個。須要注意,下文中的instance是包含被訪問屬性的對象實例,而owner則是被描述器修辭的類。
- __get__(self, instance, owner) – 這個方法是當屬性被經過(value = obj.attr)的方式獲取時調用,這個方法的返回值將被賦給請求此屬性值的代碼部分。
- __set__(self, instance, value) – 這個方法是當但願設置屬性的值(obj.attr = ‘value’)時被調用,該方法不會返回任何值。
- __delete__(self, instance) – 當從一個對象中刪除一個屬性時(del obj.attr),調用此方法。
譯者注:對於instance和owner的理解,考慮如下代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class Celsius(object):
def __init__(self, value=0.0):
self.value = float(value)
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = float(value)
class Temperature(object):
celsius = Celsius()
temp=Temperature()
temp.celsius #calls Celsius.__get__
|
上例中,instance指的是temp,而owner則是Temperature。
LazyLoading Properties例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
import weakref
class lazyattribute(object):
def __init__(self, f):
self.data = weakref.WeakKeyDictionary()
self.f = f
def __get__(self, obj, cls):
if obj not in self.data:
self.data[obj] = self.f(obj)
return self.data[obj]
class Foo(object):
@lazyattribute
def bar(self):
print "Being lazy"
return 42
f = Foo()
print f.bar
# Being lazy
# 42
print f.bar
# 42
|
描述器很好的總結了Python中的綁定方法(bound method)這個概念,綁定方法是經典類(classic classes)的實現核心。在經典類中,當在一個對象實例的字典中沒有找到某個屬性時,會繼續到類的字典中查找,而後再到基類的字典中,就這麼一直遞歸的查找下去。若是在類字典中找到這個屬性,解釋器會檢查找到的對象是否是一個Python函數對象。若是是,則返回的並非這個對象自己,而是返回一個柯里化(currying function)的包裝器對象。當調用這個包裝器時,它會首先在參數列表以前插入實例,而後再調用原函數。
譯者注:
1. 柯里化 – http://zh.wikipedia.org/wiki/%E6%9F%AF%E9%87%8C%E5%8C%96
2. function,method,bound method及unbound method的區別。首先,函數(function)是由def或lambda建立的。當一個函數在class語句塊中定義或是由type來建立時,它會轉成一個非綁定方法(unbound method),而當經過類實例(instance)來訪問此方法的時候,它將轉成綁定方法(bound method),綁定方法會自動將實例做爲第一個參數傳入方法。綜上所述,方法是出如今類中的函數,綁定方法是一個綁定了具體實例的方法,反之則是非綁定方法。
綜上,描述器被賦值給類,而這些特殊的方法就在屬性被訪問的時候根據具體的訪問類型自動地調用。
元類(MetaClasses)
元類提供了一個改變Python類行爲的有效方式。
元類的定義是「一個類的類」。任何實例是它本身的類都是元類。
1
2
3
4
5
6
7
8
9
10
|
class demo(object):
pass
obj = demo()
print "Class of obj is {0}".format(obj.__class__)
print "Class of obj is {0}".format(demo.__class__)
# Class of obj is <class '__main__.demo'>
# Class of obj is <type 'type'>
|
在上例中,咱們定義了一個類demo,而且生成了一個該類的對象obj。首先,能夠看到obj的__class__是demo。有意思的來了,那麼demo的class又是什麼呢?能夠看到demo的__class__是type。
因此說type是python類的類,換句話說,上例中的obj是一個demo的對象,而demo自己又是type的一個對象。
因此說type就是一個元類,並且是python中最多見的元類,由於它使python中全部類的默認元類。
由於元類是類的類,因此它被用來建立類(正如類是被用來建立對象的同樣)。可是,難道咱們不是經過一個標準的類定義來建立類的麼?的確是這樣,可是python內部的運做機制以下:
- 當看見一個類定義,python會收集全部屬性到一個字典中。
- 當類定義結束,python將決定類的元類,咱們就稱它爲Meta吧。
- 最後,python執行Meta(name, bases, dct),其中:
a. Meta是元類,因此這個調用是實例化它。
b. name是新建類的類名。
c. bases是新建類的基類元組
d. dct將屬性名映射到對象,列出全部的類屬性。
那麼如何肯定一個類(A)的元類呢?簡單來講,若是一個類(A)自身或其基類(Base_A)之一有__metaclass__屬性存在,則這個類(A/Base_A)就是類(A)的元類。不然type就將是類(A)的元類。
模式(Patterns)
「請求寬恕比請求許可更容易(EFAP)」
這個Python設計原則是這麼說的「請求寬恕比請求許可更容易(EFAP)」。不提倡深思熟慮的設計思路,這個原則是說應該儘可能去嘗試,若是遇到錯誤,則給予妥善的處理。Python有着強大的異常處理機制能夠支持這種嘗試,這些機制幫助程序員開發出更爲穩定,容錯性更高的程序。
單例
單例是指只能同時存在一個的實例對象。Python提供了不少方法來實現單例。
Null對象
Null對象可以用來代替None類型以免對None的測試。
觀察者
觀察者模式容許多個對象訪問同一份數據。
構造函數
構造函數的參數常常被賦值給實例的變量。這種模式可以用一行代碼替代多個手動賦值語句。
總結
謝謝閱讀,若有疑問,請留言討論。
D、http://www.cnblogs.com/vamei/archive/2012/11/06/2755503.html
Python補充02 Python小技巧
做者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!
在這裏列舉一些我使用Python時積累的小技巧。這些技巧是我在使用Python過程當中常用的。以前很零碎的記在筆記本中,如今整理出來,和你們分享,也做爲Python快速教程的一個補充。
import模塊
在Python常用import聲明,以使用其餘模塊(也就是其它.py文件)中定義的對象。
1) 使用__name__
當咱們編寫Python庫模塊的時候,咱們每每運行一些測試語句。當這個程序做爲庫被import的時候,咱們並不須要運行這些測試語句。一種解決方法是在import以前,將模塊中的測試語句註釋掉。Python有一種更優美的解決方法,就是使用__name__。
下面是一個簡單的庫程序TestLib.py。當直接運行TestLib.py時,__name__爲"__main__"。若是被import的話,__name__爲"TestLib"。
def lib_func(a): return a + 10
def lib_func_another(b): return b + 20
if __name__ == '__main__': test = 101 print(lib_func(test))
咱們在user.py中import上面的TestLib。
import TestLib print(TestLib.lib_func(120))
你能夠嘗試不在TestLib.py中使用if __name__=='__main__', 並對比運行結果。
2) 更多import使用方式
import TestLib as test # 引用TestLib模塊,並將它更名爲t
好比:
import TestLib as t print(t.lib_func(120))
from TestLib import lib_func # 只引用TestLib中的lib_func對象,並跳過TestLib引用字段
這樣的好處是減少所引用模塊的內存佔用。
好比:
from TestLib import lib_func print(lib_func(120))
from TestLib import * # 引用全部TestLib中的對象,並跳過TestLib引用字段
好比:
from TestLib import * print(lib_func(120))
查詢
1) 查詢函數的參數
當咱們想要知道某個函數會接收哪些參數的時候,可使用下面方法查詢。
import inspect print(inspect.getargspec(func))
2) 查詢對象的屬性
除了使用dir()來查詢對象的屬性以外,咱們可使用下面內置(built-in)函數來確認一個對象是否具備某個屬性:
hasattr(obj, attr_name) # attr_name是一個字符串
例如:
a = [1,2,3] print(hasattr(a,'append'))
2) 查詢對象所屬的類和類名稱
a = [1, 2, 3] print a.__class__ print a.__class__.__name__
3) 查詢父類
咱們能夠用__base__屬性來查詢某個類的父類:
cls.__base__
例如:
print(list.__base__)
使用中文(以及其它非ASCII編碼)
在Python程序的第一行加入#coding=utf8,例如:
#coding=utf8 print("你好嗎?")
也能用如下方式:
#-*- coding: UTF-8 -*- print("你好嗎?")
表示2進制,8進制和16進制數字
在2.6以上版本,以以下方式表示
print(0b1110) # 二進制,以0b開頭 print(0o10) # 八進制,以0o開頭 print(0x2A) # 十六進制,以0x開頭
若是是更早版本,能夠用以下方式:
print(int("1110", 2))
print(int("10", 8))
print(int("2A", 16))
註釋
一行內的註釋能夠以#開始
多行的註釋能夠以'''開始,以'''結束,好比
''' This is demo ''' def func(): # print something print("Hello world!") # use print() function # main func()
註釋應該和所在的程序塊對齊。
搜索路徑
當咱們import的時候,Python會在搜索路徑中查找模塊(module)。好比上面import TestLib,就要求TestLib.py在搜索路徑中。
咱們能夠經過下面方法來查看搜索路徑:
import sys print(sys.path)
咱們能夠在Python運行的時候增長或者刪除sys.path中的元素。另外一方面,咱們能夠經過在shell中增長PYTHONPATH環境變量,來爲Python增長搜索路徑。
下面咱們增長/home/vamei/mylib到搜索路徑中:
$export PYTHONPATH=$PYTHONPATH:/home/vamei/mylib
你能夠將正面的這行命令加入到~/.bashrc中。這樣,咱們就長期的改變了搜索路徑。
腳本與命令行結合
可使用下面方法運行一個Python腳本,在腳本運行結束後,直接進入Python命令行。這樣作的好處是腳本的對象不會被清空,能夠經過命令行直接調用。
$python -i script.py
安裝非標準包
Python的標準庫隨着Python一塊兒安裝。當咱們須要非標準包時,就要先安裝。
1) 使用Linux repository (Linux環境)
這是安裝Python附加包的一個好的起點。你能夠在Linux repository中查找可能存在的Python包 (好比在Ubuntu Software Center中搜索matplot)。
2) 使用pip。pip是Python自帶的包管理程序,它鏈接Python repository,並查找其中可能存在的包。
好比使用以下方法來安裝、卸載或者升級web.py:
$pip install web.py
$pip uninstall web.py
$pip install --upgrade web.py
若是你的Python安裝在一個非標準的路徑(使用$which python來確認python可執行文件的路徑)中,好比/home/vamei/util/python/bin中,你可使用下面方法設置pip的安裝包的路徑:
$pip install --install-option="--prefix=/home/vamei/util/" web.py
3) 從源碼編譯
若是上面方法都無法找到你想要的庫,你可能須要從源碼開始編譯。Google每每是最好的起點。
之後若是有新的收穫,會補充到這篇博文中。
E、http://python.jobbole.com/32748/
本文由 伯樂在線 - 劉志軍 翻譯。未經許可,禁止轉載!
英文出處:Max Burstein。歡迎加入翻譯組。
如下是我近些年收集的一些Python實用技巧和工具,但願能對你有所幫助。
交換變量
1
2
3
4
5
6
7
8
9
|
x = 6
y = 5
x, y = y, x
print x
>>> 5
print y
>>> 6
|
if 語句在行內
1
2
|
print "Hello" if True else "World"
>>> Hello
|
鏈接
下面的最後一種方式在綁定兩個不一樣類型的對象時顯得很cool。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
nfc = ["Packers", "49ers"]
afc = ["Ravens", "Patriots"]
print nfc + afc
>>> ['Packers', '49ers', 'Ravens', 'Patriots']
print str(1) + " world"
>>> 1 world
print `1` + " world"
>>> 1 world
print 1, "world"
>>> 1 world
print nfc, 1
>>> ['Packers', '49ers'] 1
|
數字技巧
1
2
3
4
5
6
|
#除後向下取整
print 5.0//2
>>> 2
# 2的5次方
print 2**5
>> 32
|
注意浮點數的除法
1
2
3
4
|
print .3/.1
>>> 2.9999999999999996
print .3//.1
>>> 2.0
|
數值比較
這是我見過諸多語言中不多有的如此棒的簡便法
1
2
3
4
5
6
7
|
x = 2
if 3 > x > 1:
print x
>>> 2
if 1 < x > 0:
print x
>>> 2
|
同時迭代兩個列表
1
2
3
4
5
6
|
nfc = ["Packers", "49ers"]
afc = ["Ravens", "Patriots"]
for teama, teamb in zip(nfc, afc):
print teama + " vs. " + teamb
>>> Packers vs. Ravens
>>> 49ers vs. Patriots
|
帶索引的列表迭代
1
2
3
4
5
6
7
|
teams = ["Packers", "49ers", "Ravens", "Patriots"]
for index, team in enumerate(teams):
print index, team
>>> 0 Packers
>>> 1 49ers
>>> 2 Ravens
>>> 3 Patriots
|
列表推導式
已知一個列表,咱們能夠刷選出偶數列表方法:
1
2
3
4
5
|
numbers = [1,2,3,4,5,6]
even = []
for number in numbers:
if number%2 == 0:
even.append(number)
|
轉變成以下:
1
2
|
numbers = [1,2,3,4,5,6]
even = [number for number in numbers if number%2 == 0]
|
是否是很牛呢,哈哈。
字典推導
和列表推導相似,字典能夠作一樣的工做:
1
2
3
|
teams = ["Packers", "49ers", "Ravens", "Patriots"]
print {key: value for value, key in enumerate(teams)}
>>> {'49ers': 1, 'Ravens': 2, 'Patriots': 3, 'Packers': 0}
|
初始化列表的值
1
2
3
|
items = [0]*3
print items
>>> [0,0,0]
|
列表轉換爲字符串
1
2
3
|
teams = ["Packers", "49ers", "Ravens", "Patriots"]
print ", ".join(teams)
>>> 'Packers, 49ers, Ravens, Patriots'
|
從字典中獲取元素
我認可try/except代碼並不雅緻,不過這裏有一種簡單方法,嘗試在字典中查找key,若是沒有找到對應的alue將用第二個參數設爲其變量值。
1
2
3
4
5
|
data = {'user': 1, 'name': 'Max', 'three': 4}
try:
is_admin = data['admin']
except KeyError:
is_admin = False
|
1
|
替換誠這樣:
|
1
2
|
data = {'user': 1, 'name': 'Max', 'three': 4}
is_admin = data.get('admin', False)
|
獲取列表的子集
有時,你只須要列表中的部分元素,這裏是一些獲取列表子集的方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
x = [1,2,3,4,5,6]
#前3個
print x[:3]
>>> [1,2,3]
#中間4個
print x[1:5]
>>> [2,3,4,5]
#最後3個
print x[-3:]
>>> [4,5,6]
#奇數項
print x[::2]
>>> [1,3,5]
#偶數項
print x[1::2]
>>> [2,4,6]
|
60個字符解決FizzBuzz
前段時間Jeff Atwood 推廣了一個簡單的編程練習叫FizzBuzz,問題引用以下:
寫一個程序,打印數字1到100,3的倍數打印「Fizz」來替換這個數,5的倍數打印「Buzz」,對於既是3的倍數又是5的倍數的數字打印「FizzBuzz」。
這裏就是一個簡短的,有意思的方法解決這個問題:
1
|
for x in range(101):print"fizz"[x%3*4::]+"buzz"[x%5*4::]or x
|
集合
除了python內置的數據類型外,在collection模塊一樣還包括一些特別的用例,在有些場合Counter很是實用。若是你參加過在這一年的Facebook HackerCup,你甚至也能找到他的實用之處。
1
2
3
|
from collections import Counter
print Counter("hello")
>>> Counter({'l': 2, 'h': 1, 'e': 1, 'o': 1})
|
迭代工具
和collections庫同樣,還有一個庫叫itertools,對某些問題真能高效地解決。其中一個用例是查找全部組合,他能告訴你在一個組中元素的全部不能的組合方式
1
2
3
4
5
6
7
8
9
10
|
from itertools import combinations
teams = ["Packers", "49ers", "Ravens", "Patriots"]
for game in combinations(teams, 2):
print game
>>> ('Packers', '49ers')
>>> ('Packers', 'Ravens')
>>> ('Packers', 'Patriots')
>>> ('49ers', 'Ravens')
>>> ('49ers', 'Patriots')
>>> ('Ravens', 'Patriots')
|
False == True
比起實用技術來講這是一個頗有趣的事,在python中,True和False是全局變量,所以:
1
2
3
4
5
6
|
False = True
if False:
print "Hello"
else:
print "World"
>>> Hello
|
若是你還有任何很酷的奇技淫巧,能夠在下面留言,感謝閱讀
F、http://www.cnblogs.com/JohnABC/p/4673649.html
0.推薦的擴展
requests urilib的替代品
BeautifulSoup4 解析HTML代碼
rq 任務隊列
selenium 自動化測試工具,模擬瀏覽器
1.sys.modules, 根據已加載的模塊建立對象, 其中 page 爲類名
if hasattr(sys.modules[__name__], page): setattr(self, page, getattr(sys.modules[__name__], page)(self))
2.hasattr, setattr, locals(), globals() 動態建立變量
3.綁定、未綁定
未綁定的類方法: 沒有self
經過類來引用方法返回一個未綁定方法對象, 要調用它, 你必須顯示地提供一個實例做爲第一個參數
綁定的實例方法: 有self
經過實例訪問方法返回一個綁定的方法對象, Python自動地給方法綁定一個實例, 因此咱們調用它時不用再傳一個實例參數
class Test: def func(self,message): print message object1 = Test() x = object1.func x('綁定方法對象,實例是隱含的') t=Test.func t(object1,'未綁定的方法對象,須要傳遞一個實例') #t('未綁定的方法對象,須要傳遞一個實例') #錯誤的調用
object1 = Test()生成一個實例, object1.func返回一個綁定的方法, 把實例object1和方法func綁定, 而Test.func是用類去引用方法, 咱們獲得一個未綁定的方法對象, 要調用它就得傳一個實例參數, 如t(object1,'未綁定的方法對象,須要傳遞一個實例')
class A(object): def f(self): print "f" def ff(): print "ff" a = A() a.f() xf = a.f #xf爲綁定的方法, 故不用傳入第一個值 xf() a.f = ff a.f()
4.MRO(method resolution order, 多繼承時判斷調的屬性的類)
MRO要先肯定一個線性序列, 而後查找路徑由由序列中類的順序決定, 因此MRO的算法就是生成一個線性序列
Python前後有三種不一樣的MRO: 經典方式、Python2.2 新式算法、Python2.3 新式算法(C3), Python 3中只保留了最後一種, 即C3算法
經典方式: 很是簡單, 深度優先, 按定義從左到右
新式算法: 仍是經典方式, 但出現重複的, 只保留最後一個
C3算法: 最先被提出是用於Lisp的, 應用在Python中是爲了解決原來基於深度優先搜索算法不知足本地優先級, 和單調性的問題:
本地優先級: 指聲明時父類的順序, 好比C(A, B), 若是訪問C類對象屬性時, 應該根據聲明順序, 優先查找A類, 而後再查找B類
單調性: 若是在C的解析順序中, A排在B的前面, 那麼在C的全部子類裏, 也必須知足這個順序
class B(A) 這時B的mro序列爲[B,A]
class B(A1,A2,A3 ...) 這時B的mro序列 mro(B) = [B] + merge(mro(A1), mro(A2), mro(A3) ..., [A1,A2,A3])
merge操做就是C3算法的核心, 遍歷執行merge操做的序列, 若是一個序列的第一個元素, 是其餘序列中的第一個元素, 或不在其餘序列出現, 則從全部執行merge操做序列中刪除這個元素, 合併到當前的mro中, merge操做後的序列, 繼續執行merge操做, 直到merge操做的序列爲空, 若是merge操做的序列沒法爲空, 則說明不合法
class a:pass class b(a):pass class c(a):pass class d(b, c):pass
好比此菱形繼承結構
按經典方式, d類MRO爲dbaca, 缺點是若是c類重寫了a類中得方法, c類的方法將不會被調用到(即本地優先順序問題)
按新式算法, d類MRO爲dbca, 缺點是單調性, 好比d繼承b、c, 且b在c的前面, 若是f繼承d, 那麼f的MRO中也應該和d的同樣b在c的前面, 單調性即繼承時要保持順序, 如今e繼承c、b, 且c在b的前面, f繼承d, e時, bc的順序就無法決定了, 不管怎樣排都違反了單調性
class A(O):pass class B(O):pass class C(O):pass class E(A,B):pass class F(B,C):pass class G(E,F):pass
按C3算法, A、B、C都繼承至一個基類, 因此mro序列依次爲[A,O]、[B,O]、[C,O]
mro(E) = [E] + merge(mro(A), mro(B), [A,B])
= [E] + merge([A,O], [B,O], [A,B])
此時須要執行merge操做的序列爲[A,O]、[B,O]、[A,B]
A是序列[A,O]中的第一個元素, 在序列[B,O]中不出現, 在序列[A,B]中也是第一個元素, 因此從執行merge操做的序列([A,O]、[B,O]、[A,B])中刪除A, 合併到當前mro, [E]中
mro(E) = [E,A] + merge([O], [B,O], [B])
再執行merge操做, O是序列[O]中的第一個元素, 但O在序列[B,O]中出現而且不是其中第一個元素, 繼續查看[B,O]的第一個元素B, B知足條件, 因此從執行merge操做的序列中刪除B, 合併到[E, A]中
mro(E) = [E,A,B] + merge([O], [O])
= [E,A,B,O]
實現C3算法的代碼:
#-*- encoding:GBK -*-# def mro_C3(*cls): if len(cls)==1: if not cls[0].__bases__: return cls else: return cls+ mro_C3(*cls[0].__bases__) else: seqs = [list(mro_C3(C)) for C in cls ] +[list(cls)] res = [] while True: non_empty = list(filter(None, seqs)) if not non_empty: return tuple(res) for seq in non_empty: candidate = seq[0] not_head = [s for s in non_empty if candidate in s[1:]] if not_head: candidate = None else: break if not candidate: raise TypeError("inconsistent hierarchy, no C3 MRO is possible") res.append(candidate) for seq in non_empty: if seq[0] == candidate: del seq[0]
查看一個類的MRO列表, 可使用 classname.__mro__
5.super 避免屢次調用基類
# -*- coding:utf-8 -*- class D(object): def foo(self): print "class D" class B(D): pass class C(D): def foo(self): print "class C" class A(B, C): pass f = A() f.foo() #A的實例對象f在調用foo函數的時候, 根據廣度優先搜索原則, 調用的是C類裏面的foo函數, 上面的代碼輸出class C; 若是定義D類的時候直接class D, 而不是class D(object), 那麼上述代碼就該輸出class D了
1). super並非一個函數, 是一個類名, 形如super(B, self)事實上調用了super類的初始化函數產生了一個super對象;
2). super類的初始化函數並無作什麼特殊的操做, 只是簡單記錄了類類型和具體實例;
3). super(B, self).func的調用並非用於調用當前類的父類的func函數;
4). Python的多繼承類是經過mro的方式來保證各個父類的函數被逐一調用, 並且保證每一個父類函數只調用一次(若是每一個類都使用super);
5). 混用super類和非綁定的函數是一個危險行爲, 這可能致使應該調用的父類函數沒有調用或者一個父類函數被調用屢次
6).super並非像咱們想象中同樣直接找到當前類的父類, 而是沿着mro順藤摸瓜
參考:http://blog.csdn.net/johnsonguo/article/details/585193
6.模塊即模塊對象
7.利用 __new__ 實現單例
class Singleton(object): def __new__(cls, *args, **kwargs): # 關鍵在於這,每一次實例化的時候,咱們都只會返回這同一個instance對象 if not hasattr(cls, "instance"): cls.instance = super(Singleton, cls).__new__(cls, *args, **kwargs) return cls.instance
8.__new__ 執行順序
class A(object): def __new__(cls): Object = super(A, cls).__new__(cls) print "in New" return Object #若是把此行註釋掉, 則不會執行 __init__ 方法 def __init__(self): print "in init" class B(A): def __init__(self): print "in B's init" B()
9.判斷一個變量是否存在
1).
'var' in locals().keys()
2).
try: print var except NameError: print 'var not defined'
3).
'var' in dir()
10.根據類名建立實例, 主要是得到類的 class 對象
1).類名cls爲class類型, 直接 cls() 便可
2).類名cls爲str類型
m = __import__(clsstr所在模塊名) cls = getattr(m, clsstr) cls()
若是myclass並不在mymodule的自動導出列表中(__all__), 則必須顯式地導入, __import__('mymodule', globals(), locals(), ['myclass'])
11.獲取當前模塊名
locals()/globals().get("__name__")
12.import, reload, __import__
del sys.modules[modulename] 便可實現 unimport
import 調用的 __import__, 好比 import sys => sys = __import__("sys")
reload 對已經加載的模塊進行從新加載, 通常用於原模塊有變化等特殊狀況, reload前該模塊必須已經import過, 但原來已經使用的實例仍是會使用舊的模塊, 新生產的實例會使用新的模塊
import sys #引用sys模塊進來,並非進行sys的第一次加載 reload(sys) #從新加載sys sys.setdefaultencoding('utf8') ##調用setdefaultencoding函數 #若是去掉reload(sys), 會執行失敗, 由於這裏的import語句其實並非sys的第一次導入語句, 也就是說這裏其實多是第2、三次進行sys模塊的import, 這裏只是一個對sys的引用, 只能reload才能進行從新加載; 那麼爲何要從新加載, 而直接引用過來則不能調用該函數呢?由於setdefaultencoding函數在被系統調用後被刪除了, 因此經過import引用進來時其實已經沒有了, 因此必須reload一次sys模塊, 這樣setdefaultencoding纔會爲可用, 才能在代碼裏修改解釋器當前的字符編碼
__import__, 返回模塊實例
__import__(module_name[, globals[, locals[, fromlist]]]) #可選參數默認爲globals(),locals(),[] __import__('os') __import__('os',globals(),locals(),['path','pip']) #等價於from os import path, pip
13.迭代器
基本原理:
實現了__iter__方法的對象是可迭代的, 實現了next()方法的對象是迭代器(迭代器就是一個有next()方法的對象), 因此iter(實現了__iter__方法的對象)會調用此對象的__iter__方法, 返回一個實現了next()方法的迭代器對象, 不斷調用此迭代器對象的next()方法, 實現遍歷, 直到遇到StopIteration異常, 使用迭代器一個顯而易見的好處就是每次只從對象中讀取一條數據, 不會形成內存的過大開銷
序列、字典、文件中當使用for x in y的結構時, 其實質就是迭代器, 迭代器是和實際對象綁定在一塊兒的, 因此在使用迭代器時或者上述3者時不能修改可變對象的值, 這會產生錯誤
迭代器不要求你事先準備好整個迭代過程當中全部的元素, 迭代器僅僅在迭代至某個元素時才計算該元素, 而在這以前或以後, 元素能夠不存在或者被銷燬, 這個特色使得它特別適合用於遍歷一些巨大的或是無限的集合, 好比幾個G的文件, 或是斐波那契數列等等, 這個特色被稱爲延遲計算或惰性求值(Lazy evaluation)
建立迭代器的方法: iter(object)和iter(func, sentinel)兩種, 一種使用的是序列, 另外一種使用類來建立, 迭代器更大的功勞是提供了一個統一的訪問集合的接口, 只要是實現了__iter__()方法的對象, 就可使用迭代器進行訪問, 返回一個對象, 這個對象擁有一個next()方法, 這個方法能在恰當的時候拋出StopIteration異常便可
for i in seq: do_something_to(i)
實際爲:
fetch = iter(seq) while True: try: i = fetch.next() except StopIteration: break do_something_to(i)
字典中, iterkeys(), itervalues(), iteritems() 比 keys(), values(), items() 更省內存
open("test.txt").readlines() 返回的是列表, open("test.txt") 返回的是迭代器
14.生成器
若是一個函數返回的列表很是大, 僅僅建立這個列表就會用完系統全部內存, 由於在咱們的觀念中函數只有一次返回結果的機會, 於是必須一次返回全部的結果, 此類問題能夠用生成器解決
生成器是特定的函數, 容許你返回一個值, 而後「暫停」代碼的執行, 稍後恢復, 生成器使用了「延遲計算」, 因此在內存上面更加有效, 生成器函數不能有返回值, 由於 yield 的值就是返回值, 生成器就是一類特殊的迭代器
1).調用生成器函數將返回一個生成器
>>> generator = get_0_1_2() >>> generator <generator object get_0_1_2 at 0x00B1C7D8>
2).第一次調用生成器的next方法時, 生成器纔開始執行生成器函數(而不是構建生成器時), 直到遇到yield時暫停執行(掛起), 而且yield的參數將做爲這次next方法的返回值
>>> generator.next() 0
3).以後每次調用生成器的next方法, 生成器將從上次暫停執行的位置恢復執行生成器函數, 直到再次遇到yield時暫停, 而且一樣的, yield的參數將做爲next方法的返回值
>>> generator.next() 1 >>> generator.next() 2
4).若是當調用next方法時生成器函數結束(遇到空的return語句或是到達函數體末尾), 則此次next方法的調用將拋出StopIteration異常(即for循環的終止條件)
>>> generator.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
5).生成器函數在每次暫停執行時, 函數體內的全部變量都將被封存(freeze)在生成器中, 並將在恢復執行時還原, 而且相似於閉包, 即便是同一個生成器函數返回的生成器, 封存的變量也是互相獨立的, 咱們的小例子中並無用到變量, 因此這裏另外定義一個生成器來展現這個特色
>>> def fibonacci(): ... a = b = 1 ... yield a ... yield b ... while True: ... a, b = b, a+b ... yield b ... >>> for num in fibonacci(): ... if num > 100: break ... print num, ... 1 1 2 3 5 8 13 21 34 55 89
看到while True可別太吃驚, 由於生成器能夠掛起, 因此是延遲計算的, 無限循環並無關係, 這個例子中咱們定義了一個生成器用於獲取斐波那契數列
若是生成器函數調用了return, 或者執行到函數的末尾, 會出現一個StopIteration異常
有一篇好文章:點擊進入
15.列表解析器
也能夠直接生成 dict
{x: 1 for x in ["name", "sex"]} #返回 {"name": 1, "sex" 1}
列表解析一次生成一個列表, 所佔內存較大
(x+1 for x in lst) #生成器表達式,返回迭代器。外部的括號可在用於參數時省略。 [x+1 for x in lst] #列表解析,返回list
因爲返回迭代器時, 並非在一開始就計算全部的元素, 這樣能獲得更多的靈活性而且能夠避開不少沒必要要的計算, 因此除非你明確但願返回列表, 不然應該始終使用生成器表達式
能夠在Python Shell中試一下一下兩個語句的執行時間
(for x in range(1000000)) #返回生成器 [for x in range(1000000)] #返回整個列表
或者提供多條for子句進行嵌套循環, 嵌套次序就是for子句的順序:
((x, y) for x in range(3) for y in range(x))
一樣外部也可使用 if
(x for x in (y.doSomething() for y in lst) if x>0)
16.在Python裏, 函數的默認值實在函數定義的時候實例化的, 而不是在調用的時候, 若是在調用函數的時候重寫了默認值, 那麼這個存儲的值就不會被使用, 當你不重寫默認值的時候, 那麼Python就會讓默認值引用存儲的值(這個例子裏的numbers)
def abc(numbers = []): numbers.append(9) print numbers
abc(): [9] #第一次執行
abc(): [9, 9] #第二次執行
abc([1, 2]): [1, 2, 9] #第三次執行
abc(): [9, 9, 9] #第四次執行
def print_now(now = time.time()): print now #若是不加參數, 則每次都會返回相同的時間
默認參數最好指向不變對象!
17.當def這個聲明執行的時候, Python會靜態地從函數的局部做用域裏獲取信息, 當來到 xxx = yyy 這行的時候(不是執行到這行代碼, 而是當Python解釋器讀到這行代碼的時候), 它會把xxx這個變量加入到函數的局部變量列表裏
18.__builtin__模塊, 在Python啓動後、且沒有執行程序員所寫的任何代碼前, Python會首先加載該內建模塊到內存, 另外, 該內建模塊中的功能能夠直接使用, 不用在其前添加內建模塊前綴, 導入僅僅是讓__builitin__標識符在該做用域內可見
19.from __future__ import xxx, 必須是模塊或程序的第一個語句, 此外,'__ future__' 模塊中存在的特性最終將成爲Python語言標準的一部分, 到那時, 將再也不須要使用 '__future__' 模塊
20.序列解包
a, b, c = 1, 2, 3 #賦值 a, b = b, a #轉換a, b的值 a = 1, #聲明元組
21.最好了解下二進制文件和文本文件的區別以及編碼問題, ASCII(固定一個字節)->Unicode(固定兩個字節)->UTF-8(變長), 在計算機內存中, 統一使用Unicode編碼, 當須要保存到硬盤或者須要傳輸的時候, 就轉換爲UTF-8編碼, 用記事本編輯的時候, 從文件讀取的UTF-8字符被轉換爲Unicode字符到內存裏, 編輯完成後, 保存的時候再把Unicode轉換爲UTF-8保存到文件, len()函數計算的是str(Unicode編碼)的字符數, 若是換成bytes(UTF-8等編碼), len()函數就計算字節數:
參考自:廖雪峯Python
22.dict的key必須是不可變對象
23.Python函數參數順序
定義: (arg, kwarg = <value>, *args, **kwargs)
調用:
第一, 按順序把"arg"這種形式的實參給對應的形參
第二, 把"arg=<value>"這種形式的實參賦值給形式kwarg
第三, 把多出來的"arg"這種形式的實參組成一個tuple給帶一個星號的形參args
第四, 把多出來的"key=value"這種形式的實參轉爲一個dictionary給帶兩個星號的形參kwargs
def test(x, y = 5, *a, **b): print x, y, a, b
test(1) ===> 1 5 () {}
test(1,2) ===> 1 2 () {}
test(1,2,3) ===> 1 2 (3,) {}
test(1,2,3,4) ===> 1 2 (3,4)
test(x=1) ===> 1 5 () {}
test(x=1,y=1) ===> 1 1 () {}
test(x=1,y=1,a=1) ===> 1 1 () {'a':1}
test(x=1,y=1,a=1,b=1) ===> 1 1 () {'a':1,'b':1}
test(1,y=1) ===> 1 1 () {}
test(1,2,y=1) ===> 出錯, 說y給賦了多個值
test(1, y = 2, 3, a = 4) ===> 出錯, non-keyword arg after keyword arg
test(1,2,3,4,a=1) ===> 1 2 (3,4) {'a':1}
24.限制 **kwargs 參數個數
#只接收city和job做爲關鍵字參數 def person(name, age, *, city, job): print(name, age, city, job)
25.functools 模塊
wraps: 用來裝飾返回func的函數, 以同步func與被裝飾的函數的屬性, 好比__name__等 @functools.wraps(func)
partial: 變化函數的參數建立偏函數, 固定住原函數的部分參數, 好比 int2 = functools.partial(x, base = 2), 此時 int2 是一個把二進制字符串轉爲十進制數字的函數, int2('10010')
26.一個.py文件稱爲一個模塊, 爲了不模塊名衝突, Python按目錄來組織模塊的方法, 稱爲包(Package), 包內必須有__init__.py, 由於__init__.py自己就是一個模塊, 它的模塊名就是包名(目錄名)
27.第三方庫都會在 Python 官方的 pypi.python.org 網站註冊, 要安裝一個第三方庫, 必須先知道該庫的名稱, 能夠在官網或者 pypi 上搜索
28.各類包管理工具區別:
distribute是setuptools的取代, pip是easy_install的取代
distribute被建立是由於setuptools包再也不維護了
29.Python的實例屬性必須在__init__(self) 方法中定義, 直接跟在類名後邊定義的屬性都默認是類屬性(相似於c++的static變量), 類的屬性一改, 只要沒有覆蓋此屬性的對象的此屬性都會改
main.py
#-*- coding: utf-8 -*- class Model(): model_path = "online_model" model_dict = {} def __init__(self, path = None): self.reload(path) def reload(self, path = None): if not path: return self.model_dict.clear() fp = file(path, "r") for line in fp: cols = line.split() self.model_dict[cols[0]] = cols[1] self.model_path = path def main(): m1 = Model() m2 = Model() m1.reload("aa_model") m2.reload("bb_model") print m1.model_path print m2.model_path print m1.model_dict print m2.model_dict return 0 if __name__ == "__main__": main()
aa_model
1 a
2 b
bb_model
x 1 y 2 z 3
結果如圖:
aa_model, bb_model: 因爲reload方法中self.model_path = path, 實例重寫了類的model_path屬性(第一次賦值視爲變量的定義), 故輸出本實例的model_path
{‘y': 2, 'x': '1', 'z': '3'}, {‘y': 2, 'x': '1', 'z': '3'}: 因爲self.model_dict.clear(), 第一次使用此變量非賦值而是直接使用(相似PHP), 相似函數中表示使用已有的值, 根據MRO, 搜索到Model.model_dict, 故兩次會覆蓋同一個變量
30.類的特殊屬性
C.__name__: 類的名字
C.__doc__: 類的文檔描述
C.__bases__: 類的基類組成的元組
C.__dict__: 類的屬性
C.__module__: 類所在模塊
C.__class__: 類的類名
__getattribute__: 無論屬性是否存在都會調用
__getattr__: 若是屬性不存在則調用, 若是__getattribute__被定義會先被調用, 而後調用此方法, 也能夠返回函數
class A(object): def __getattr__(self, attr): if attr == "age": return lambda : 25 a = A() a.age()
__setattr__
__delattr__
__slots__: 限制實例能夠添加到屬性
hasattr(), getattr(), setattr(), delattr()
31.實例的特殊屬性
i.__class__: 實例的類名
i.__dict__: 實例的屬性, __dict__分層存儲屬性, 每一層的__dict__只存儲該層新增的屬性, 子類不須要重複存儲父類中的屬性
32.property()能夠即時生成屬性
33.類中私有屬性__xx, 實現爲Python自動爲__xx改變變量名
34.當咱們定義一個class的時候, 咱們實際上就定義了一種數據類型, 咱們定義的數據類型和Python自帶的數據類型, 好比str、list、dict沒什麼兩樣, type(instance), 返回instance對應的Class類型, 好比
class A(object): pass a = A() b = type(a)() #此時b爲A的實例 print type(A) #<type 'type'> print type(a) #<class '__main__.A'> print isinstance(b, A) #True
type()函數能夠查看一個類型或變量的類型, A是一個class(類對象), 它的類型就是type, 而h是一個實例, 它的類型就是class A
class的定義是運行時動態建立的, 而建立class的方法就是使用type()函數, type()函數既能夠返回一個對象的類型, 又能夠建立出新的類型, 好比, 咱們能夠經過type()函數建立出A類, 而無需經過class A(object)...的定義,
def fn(self, name = "John"): self.name = name A = type('A', (object,), dict(hello=fn)) a = A() print type(A) #<type 'type'> print type(a) #<class '__main__.A'>
type() 建立類對象的參數以下:
class的名稱
繼承的父類集合, 注意Python支持多重繼承, 若是隻有一個父類, 別忘了tuple的單元素寫法
class的方法名稱與函數綁定, 這裏咱們把函數fn綁定到方法名hello上
經過type()函數建立的類和直接寫class是徹底同樣的, 由於Python解釋器遇到class定義時, 僅僅是掃描一下class定義的語法, 而後調用type()函數建立出class
除了使用type()動態建立類對象, 還可使用元類, 即 metaclass, 流程爲先定義metaclass, 就能夠建立類, 最後建立實例
# metaclass是建立類,因此必須從`type`類型派生: class ListMetaclass(type): def __new__(cls, name, bases, attrs): attrs['add'] = lambda self, value: self.append(value) return type.__new__(cls, name, bases, attrs) class MyList(list): __metaclass__ = ListMetaclass # 指示使用ListMetaclass來定製類, 表示建立MyList時要經過ListMetaclass.__new__()來建立, 在此, 咱們能夠修改類的定義, 好比, 加上新的方法, 而後, 返回修改後的定義
__new__()方法接收到的參數依次是:當前準備建立的類的對象, 類的名字, 類繼承的父類集合, 類的方法集合
35.types模塊, 能夠查看全部的type, type(abs) == types.BuiltinFunctionType
36.把方法變成屬性 @property
class Student(object): #@property 會建立一個另一個裝飾器 @score.setter, 負責把一個方法編程屬性賦值 @property def score(self): return self._score @score.setter def score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value s = Student() s.score = 60 s.score = 101 #ValueError: Score must between 0 ~ 100!
37.Linux下用Python實現多進程(此處不是講線程), Unix/Linux操做系統提供了一個fork()系統調用, 它返回兩次, 由於操做系統自動把當前進程(稱爲父進程)複製了一份(稱爲子進程), 而後, 分別在父進程和子進程內返回, 子進程永遠返回0, 而父進程返回子進程的ID, 這樣作的理由是, 一個父進程能夠fork出不少子進程, 因此, 父進程要記下每一個子進程的ID, 而子進程只須要調用getppid()就能夠拿到父進程的ID; Windows系統不支持fork()函數; 有了fork調用, 一個進程在接到新任務時就能夠複製出一個子進程來處理新任務, 常見的Apache服務器就是由父進程監聽端口, 每當有新的http請求時, 就fork出子進程來處理新的http請求
import os print('Process (%s) start...' % os.getpid()) # Only works on Unix/Linux/Mac: pid = os.fork() if pid == 0: print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid())) else: print('I (%s) just created a child process (%s).' % (os.getpid(), pid))
38.multiprocessing模塊是跨平臺的多進程模塊
建立一個進程運行某個函數
from multiprocessing import Process import os # 子進程要執行的代碼 def run_proc(name): print('Run child process %s (%s)...' % (name, os.getpid())) if __name__=='__main__': print('Parent process %s.' % os.getpid()) p = Process(target=run_proc, args=('test',)) print('Child process will start.') p.start() p.join() print('Child process end.')
結果爲:
Parent process 67416. Child process will start. Run child process test (70388)... Child process end.
join()方法能夠等待子進程結束後再繼續往下運行, 一般用於進程間的同步
39.佔位
40.timestamp與時區毫無關係, timestamp一旦肯定, 其UTC時間就肯定了, 轉換到任意時區的時間也是徹底肯定的, 全球各地的計算機在任意時刻的timestamp都是徹底相同的, datetime是有時區的, 會在timestamp與本地時間之間作轉換
>>> from datetime import datetime >>> t = 1429417200.0 >>> print(datetime.fromtimestamp(t)) # 本地時間 2015-04-19 12:20:00 >>> print(datetime.utcfromtimestamp(t)) # UTC時間 2015-04-19 04:20:00
41.struct模塊, 瞭解c語言的人, 必定會知道struct結構體在c語言中的做用, 它定義了一種結構, 裏面包含不一樣類型的數據(int, char, bool等等), 方便對某一結構對象進行處理, 而在網絡通訊當中, 大多傳遞的數據是以二進制流(binary data)存在的, 當傳遞字符串時, 沒必要擔憂太多的問題, 而當傳遞諸如int、char之類的基本數據的時候, 就須要有一種機制將某些特定的結構體類型打包成二進制流的字符串而後再網絡傳輸, 而接收端也應該能夠經過某種機制進行解包還原出原始的結構體數據, python中的struct模塊就提供了這樣的機制, 該模塊的主要做用就是對python基本類型值與用python字符串格式表示的C struct類型間的轉化.
>>>import struct >>>ss = struct.pack("!H4s2I", 20, "abcd", 6, 7) >>>ss "\x00\x14abcd\x00\x00\x00\x06\x00\x00\x00\x07" #ss是一個字符串 相似c結構體的字節流(二進制)的字符串表示 >>>struct.unpack("!H4s2I", ss) (20, 'abcd', 6, 7)
H表示 一個unsigned short的id, 4s表示4字節長的字符串, 2I表示有兩個unsigned int類型的數據
struct中支持的格式以下表
Format | C Type | Python | 字節數 |
---|---|---|---|
x | pad byte | no value | 1 |
c | char | string of length 1 | 1 |
b | signed char | integer | 1 |
B | unsigned char | integer | 1 |
? | _Bool | bool | 1 |
h | short | integer | 2 |
H | unsigned short | integer | 2 |
i | int | integer | 4 |
I | unsigned int | integer or long | 4 |
l | long | integer | 4 |
L | unsigned long | long | 4 |
q | long long | long | 8 |
Q | unsigned long long | long | 8 |
f | float | float | 4 |
d | double | float | 8 |
s | char[] | string | 1 |
p | char[] | string | 1 |
P | void * | long |
另外一方面, 打包的後的字節順序默認上是由操做系統的決定的, 固然struct模塊也提供了自定義字節順序的功能, 能夠指定大端存儲、小端存儲等特定的字節順序, 對於底層通訊的字節順序是十分重要的, 不一樣的字節順序和存儲方式也會致使字節大小的不一樣; 在format字符串前面加上特定的符號便可以表示不一樣的字節順序存儲方式, 例如採用小端存儲 s = struct.Struct(‘<I3sf’)就能夠了, 官方api library 也提供了相應的對照列表:
Character | Byte order | Size and alignment |
---|---|---|
@ | native | native 湊夠4個字節 |
= | native | standard 按原字節數 |
< | little-endian | standard 按原字節數 |
> | big-endian | standard 按原字節數 |
! | network (= big-endian) | standard 按原字節數 |
利用struct解析BMP文件頭, 首先找一個bmp文件, 沒有的話用「畫圖」畫一個, 讀入前30個字節來分析:
>>> s = b'\x42\x4d\x38\x8c\x0a\x00\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00\x00\x00\x80\x02\x00\x00\x68\x01\x00\x00\x01\x00\x18\x00'
BMP格式採用小端方式存儲數據,文件頭的結構按順序以下: 兩個字節:'BM'表示Windows位圖,'BA'表示OS/2位圖; 一個4字節整數:表示位圖大小; 一個4字節整數:保留位,始終爲0; 一個4字節整數:實際圖像的偏移量; 一個4字節整數:Header的字節數; 一個4字節整數:圖像寬度; 一個4字節整數:圖像高度; 一個2字節整數:始終爲1; 一個2字節整數:顏色數。 因此,組合起來用unpack讀取:
>>> struct.unpack('<ccIIIIIIHH', s) (b'B', b'M', 691256, 0, 54, 40, 640, 360, 1, 24)
結果顯示,b'B'、b'M'說明是Windows位圖,位圖大小爲640x360,顏色數爲24。
42.快速建立字典
{}.from_keys(list, defaultValue)
dict((key1, value1), (key2, value2), (key3, value3)...)
zip([key1, key2, key3...], [value1, value2, value3...])
dict(name = "John", "age" = 26)
43.判斷字典中鍵名是否存在的兩種方法
#第一種 d.has_key() #第二種 "" in d.keys()
44.判斷對象中屬性/方法是否存在
hasattr(object, attr)
attr in dir(object)
45.動態調用方法
def _execute(self, sql, params, isMany): func = "executemany" if isMany else "execute" func = getattr(self._db["cur"], func) return func(sql, params)
46.False, 0, '', (), [], {}均可以視爲假
47.列表內數據惟一(相似PHP中array_unique)
set([1, 2, 1]) -- set([1, 2])
48.根據一個對象建立另外一個對象
obj.__class__(...)
49.collections模塊
collections.namedtuple 建立可命名的tuple
import collections as cs Point = cs.namedtuple("Point", ["x", "y", "z"]) p = Point(1, 2, 3) print p.x, p.y, p.z
collections.deque 爲了高效實現插入和刪除操做的雙向列表,增刪效率高於 list,也可使用 deque(maxlen = 10) 來限制列表長度,超出時添加會把頭尾的元素擠出。
import collections as cs q = cs.deque(['a', 'b', 'c']) q.append('x') q.appendleft('y') print q >>>deque(['y', 'a', 'b', 'c', 'x'])
collections.defaultdict 使用dict時,若是引用的Key不存在,就會拋出KeyError。若是但願key不存在時,返回一個默認值,就能夠用defaultdict,也能夠直接建立多維字典。
import collections as cs tree = lambda: collections.defaultdict(tree) d = tree() d["names"]["John"] = "ABC"
collections.OrderedDict 建立按key值遍歷時有序的dict(普通dict無序排列) 使用
50.禁止建立對象屬性字典。每一個類都有實例屬性,默認用一個字典保存一個對象的實例屬性,容許咱們設置任意屬性,可是可能會浪費不少內存。使用__slots__高速Python不要使用字典,並且只給一個固定集合的屬性分配空間。
class MyClass(obj ect) : __slots__ = [ ' name' , ' identifier' ] def __init__(self, name, identifier) : self. name = name self. identifier = identifier self. set_up()
51.文件遍歷
with open("foo.txt", "r") as f: for line in f: # do_something(line) for line in open("foo.txt", "r"): # do_something(line)
52.打印格式化的json
import json print json.dumps({"name": "John"}, indent = 2)
53.格式化輸出字典
print("I'm %(name)s. I'm %(age)d year old" % {'name':'Vamei', 'age':99})
54.字典和集合也有列表推導式,都使用{}包圍,分別返回字典和集合
#快速兌換字典鍵—值 mca={"a":1, "b":2, "c":3, "d":4} dicts={v:k for k,v in mca.items()} #{1: 'a', 2: 'b', 3: 'c', 4: 'd'}
55.
G、http://python.jobbole.com/85668/
原文出處: 張 穎
使用 pdb 進行調試
pdb 是 python 自帶的一個包,爲 python 程序提供了一種交互的源代碼調試功能,主要特性包括設置斷點、單步調試、進入函數調試、查看當前代碼、查看棧片斷、動態改變變量的值等。pdb 提供了一些經常使用的調試命令,詳情見表 1。
表 1. pdb 經常使用命令
命令 | 解釋 |
---|---|
break 或 b 設置斷點 | 設置斷點 |
continue 或 c | 繼續執行程序 |
list 或 l | 查看當前行的代碼段 |
step 或 s | 進入函數 |
return 或 r | 執行代碼直到從當前函數返回 |
exit 或 q | 停止並退出 |
next 或 n | 執行下一行 |
pp | 打印變量的值 |
help | 幫助 |
下面結合具體的實例講述如何使用 pdb 進行調試。
清單 1. 測試代碼示例
1
2
3
4
5
6
7
|
import pdb
a = "aaa"
pdb.set_trace()
b = "bbb"
c = "ccc"
final = a + b + c
print final
|
開始調試:直接運行腳本,會停留在 pdb.set_trace() 處,選擇 n+enter 能夠執行當前的 statement。在第一次按下了 n+enter 以後能夠直接按 enter 表示重複執行上一條 debug 命令。
清單 2. 利用 pdb 調試
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
[root@rcc-pok-idg-2255 ~]# python epdb1.py
> /root/epdb1.py(4)?()
-> b = "bbb"
(Pdb) n
> /root/epdb1.py(5)?()
-> c = "ccc"
(Pdb)
> /root/epdb1.py(6)?()
-> final = a + b + c
(Pdb) list
1 import pdb
2 a = "aaa"
3 pdb.set_trace()
4 b = "bbb"
5 c = "ccc"
6 -> final = a + b + c
7 print final
[EOF]
(Pdb)
[EOF]
(Pdb) n
> /root/epdb1.py(7)?()
-> print final
(Pdb)
|
退出 debug:使用 quit 或者 q 能夠退出當前的 debug,可是 quit 會以一種很是粗魯的方式退出程序,其結果是直接 crash。
清單 3. 退出 debug
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
[root@rcc-pok-idg-2255 ~]# python epdb1.py
> /root/epdb1.py(4)?()
-> b = "bbb"
(Pdb) n
> /root/epdb1.py(5)?()
-> c = "ccc"
(Pdb) q
Traceback (most recent call last):
File "epdb1.py", line 5, in ?
c = "ccc"
File "epdb1.py", line 5, in ?
c = "ccc"
File "/usr/lib64/python2.4/bdb.py", line 48, in trace_dispatch
return self.dispatch_line(frame)
File "/usr/lib64/python2.4/bdb.py", line 67, in dispatch_line
if self.quitting: raise BdbQuit
bdb.BdbQuit
|
打印變量的值:若是須要在調試過程當中打印變量的值,能夠直接使用 p 加上變量名,可是須要注意的是打印僅僅在當前的 statement 已經被執行了以後才能看到具體的值,不然會報 NameError: 錯誤。
清單 4. debug 過程當中打印變量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
[root@rcc-pok-idg-2255 ~]# python epdb1.py
> /root/epdb1.py(4)?()
-> b = "bbb"
(Pdb) n
> /root/epdb1.py(5)?()
-> c = "ccc"
(Pdb) p b
'bbb'
(Pdb)
'bbb'
(Pdb) n
> /root/epdb1.py(6)?()
-> final = a + b + c
(Pdb) p c
'ccc'
(Pdb) p final
*** NameError:
(Pdb) n
> /root/epdb1.py(7)?()
-> print final
(Pdb) p final
'aaabbbccc'
(Pdb)
|
使用 c 能夠中止當前的 debug 使程序繼續執行。若是在下面的程序中繼續有 set_statement() 的申明,則又會從新進入到 debug 的狀態,讀者能夠在代碼 print final 以前再加上 set_trace() 驗證。
清單 5. 中止 debug 繼續執行程序
1
2
3
4
5
6
7
8
|
[root@rcc-pok-idg-2255 ~]# python epdb1.py
> /root/epdb1.py(4)?()
-> b = "bbb"
(Pdb) n
> /root/epdb1.py(5)?()
-> c = "ccc"
(Pdb) c
aaabbbccc
|
顯示代碼:在 debug 的時候不必定能記住當前的代碼塊,如要要查看具體的代碼塊,則能夠經過使用 list 或者 l 命令顯示。list 會用箭頭 -> 指向當前 debug 的語句。
清單 6. debug 過程當中顯示代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
[root@rcc-pok-idg-2255 ~]# python epdb1.py
> /root/epdb1.py(4)?()
-> b = "bbb"
(Pdb) list
1 import pdb
2 a = "aaa"
3 pdb.set_trace()
4 -> b = "bbb"
5 c = "ccc"
6 final = a + b + c
7 pdb.set_trace()
8 print final
[EOF]
(Pdb) c
> /root/epdb1.py(8)?()
-> print final
(Pdb) list
3 pdb.set_trace()
4 b = "bbb"
5 c = "ccc"
6 final = a + b + c
7 pdb.set_trace()
8 -> print final
[EOF]
(Pdb)
|
在使用函數的狀況下進行 debug
清單 7. 使用函數的例子
1
2
3
4
5
6
7
8
9
10
11
|
import pdb
def combine(s1,s2): # define subroutine combine, which...
s3 = s1 + s2 + s1 # sandwiches s2 between copies of s1, ...
s3 = '"' + s3 +'"' # encloses it in double quotes,...
return s3 # and returns it.
a = "aaa"
pdb.set_trace()
b = "bbb"
c = "ccc"
final = combine(a,b)
print final
|
若是直接使用 n 進行 debug 則到 final=combine(a,b) 這句的時候會將其當作普通的賦值語句處理,進入到 print final。若是想要對函數進行 debug 如何處理呢 ? 能夠直接使用 s 進入函數塊。函數裏面的單步調試與上面的介紹相似。若是不想在函數裏單步調試能夠在斷點處直接按 r 退出到調用的地方。
清單 8. 對函數進行 debug
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
[root@rcc-pok-idg-2255 ~]# python epdb2.py
> /root/epdb2.py(10)?()
-> b = "bbb"
(Pdb) n
> /root/epdb2.py(11)?()
-> c = "ccc"
(Pdb) n
> /root/epdb2.py(12)?()
-> final = combine(a,b)
(Pdb) s
--Call--
> /root/epdb2.py(3)combine()
-> def combine(s1,s2): # define subroutine combine, which...
(Pdb) n
> /root/epdb2.py(4)combine()
-> s3 = s1 + s2 + s1 # sandwiches s2 between copies of s1, ...
(Pdb) list
1 import pdb
2
3 def combine(s1,s2): # define subroutine combine, which...
4 -> s3 = s1 + s2 + s1 # sandwiches s2 between copies of s1, ...
5 s3 = '"' + s3 +'"' # encloses it in double quotes,...
6 return s3 # and returns it.
7
8 a = "aaa"
9 pdb.set_trace()
10 b = "bbb"
11 c = "ccc"
(Pdb) n
> /root/epdb2.py(5)combine()
-> s3 = '"' + s3 +'"' # encloses it in double quotes,...
(Pdb) n
> /root/epdb2.py(6)combine()
-> return s3 # and returns it.
(Pdb) n
--Return--
> /root/epdb2.py(6)combine()->'"aaabbbaaa"'
-> return s3 # and returns it.
(Pdb) n
> /root/epdb2.py(13)?()
-> print final
(Pdb)
|
在調試的時候動態改變值 。在調試的時候能夠動態改變變量的值,具體以下實例。須要注意的是下面有個錯誤,緣由是 b 已經被賦值了,若是想從新改變 b 的賦值,則應該使用! B。
清單 9. 在調試的時候動態改變值
1
2
3
4
5
6
7
8
9
|
[root@rcc-pok-idg-2255 ~]# python epdb2.py
> /root/epdb2.py(10)?()
-> b = "bbb"
(Pdb) var = "1234"
(Pdb) b = "avfe"
*** The specified object '= "avfe"' is not a function
or was not found along sys.path.
(Pdb) !b="afdfd"
(Pdb)
|
pdb 調試有個明顯的缺陷就是對於多線程,遠程調試等支持得不夠好,同時沒有較爲直觀的界面顯示,不太適合大型的 python 項目。而在較大的 python 項目中,這些調試需求比較常見,所以須要使用更爲高級的調試工具。接下來將介紹 PyCharm IDE 的調試方法 .
使用 PyCharm 進行調試
PyCharm 是由 JetBrains 打造的一款 Python IDE,具備語法高亮、Project 管理、代碼跳轉、智能提示、自動完成、單元測試、版本控制等功能,同時提供了對 Django 開發以及 Google App Engine 的支持。分爲我的獨立版和商業版,須要 license 支持,也能夠獲取免費的 30 天試用。試用版本的 Pycharm 能夠在官網上下載,下載地址爲:http://www.jetbrains.com/pycharm/download/index.html。 PyCharm 同時提供了較爲完善的調試功能,支持多線程,遠程調試等,能夠支持斷點設置,單步模式,表達式求值,變量查看等一系列功能。PyCharm IDE 的調試窗口布局如圖 1 所示。
圖 1. PyCharm IDE 窗口布局
下面結合實例講述如何利用 PyCharm 進行多線程調試。具體調試所用的代碼實例見清單 10。
清單 10. PyCharm 調試代碼實例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
__author__ = 'zhangying'
#!/usr/bin/python
import thread
import time
# Define a function for the thread
def print_time( threadName, delay):
count = 0
while count 0 and valueB>0:
return valueA+valueB
def readFile(threadName, filename):
file = open(filename)
for line in file.xreadlines():
print line
try:
thread.start_new_thread( print_time, ("Thread-1", 2, ) )
thread.start_new_thread( check_sum, ("Thread-2", 4,5, ) )
thread.start_new_thread( readFile, ("Thread-3","test.txt",))
except:
print "Error: unable to start thread"
while 1:
# print "end"
pass
|
在調試以前一般須要設置斷點,斷點能夠設置在循環或者條件判斷的表達式處或者程序的關鍵點。設置斷點的方法很是簡單:在代碼編輯框中將光標移動到須要設置斷點的行,而後直接按 Ctrl+F8 或者選擇菜單」Run」->」Toggle Line Break Point」,更爲直接的方法是雙擊代碼編輯處左側邊緣,能夠看到出現紅色的小圓點(如圖 2)。當調試開始的時候,當前正在執行的代碼會直接顯示爲藍色。下圖中設置了三個斷點,藍色高亮顯示的爲正在執行的代碼。
圖 2. 斷點設置
表達式求值:在調試過程當中有的時候須要追蹤一些表達式的值來發現程序中的問題,Pycharm 支持表達式求值,能夠經過選中該表達式,而後選擇「Run」->」Evaluate Expression」,在出現的窗口中直接選擇 Evaluate 即可以查看。
Pychar 同時提供了 Variables 和 Watches 窗口,其中調試步驟中所涉及的具體變量的值能夠直接在 variable 一欄中查看。
圖 3. 變量查看
若是要動態的監測某個變量能夠直接選中該變量並選擇菜單」Run」->」Add Watch」添加到 watches 欄中。當調試進行到該變量所在的語句時,在該窗口中能夠直接看到該變量的具體值。
圖 4. 監測變量
對於多線程程序來講,一般會有多個線程,當須要 debug 的斷點分別設置在不一樣線程對應的線程體中的時候,一般須要 IDE 有良好的多線程調試功能的支持。 Pycharm 中在主線程啓動子線程的時候會自動產生一個 Dummy 開頭的名字的虛擬線程,每個 frame 對應各自的調試幀。如圖 5,本實例中一共有四個線程,其中主線程生成了三個線程,分別爲 Dummy-4,Dummy-5,Dummy-6. 其中 Dummy-4 對應線程 1,其他分別對應線程 2 和線程 3。
圖 5. 多線程窗口
當調試進入到各個線程的子程序時,Frame 會自動切換到其所對應的 frame,相應的變量欄中也會顯示與該過程對應的相關變量,如圖 6,直接控制調試按鈕,如 setp in,step over 即可以方便的進行調試。
圖 6. 子線程調試
查看大圖。
使用 PyDev 進行調試
PyDev 是一個開源的的 plugin,它能夠方便的和 Eclipse 集成,提供方便強大的調試功能。同時做爲一個優秀的 Python IDE 還提供語法錯誤提示、源代碼編輯助手、Quick Outline、Globals Browser、Hierarchy View、運行等強大功能。下面講述如何將 PyDev 和 Eclipse 集成。在安裝 PyDev 以前,須要先安裝 Java 1.4 或更高版本、Eclipse 以及 Python。 第一步:啓動 Eclipse,在 Eclipse 菜單欄中找到 Help 欄,選擇 Help > Install New Software,並選擇 Add button,添加 Ptdev 的下載站點 http://pydev.org/updates。選擇 PyDev 以後完成餘下的步驟即可以安裝 PyDev。
圖 7. 安裝 PyDev
安裝完成以後須要配置 Python 解釋器,在 Eclipse 菜單欄中,選擇 Window > Preferences > Pydev > Interpreter – Python。Python 安裝在 C:Python27 路徑下。單擊 New,選擇 Python 解釋器 python.exe,打開後顯示出一個包含不少複選框的窗口,選擇須要加入系統 PYTHONPATH 的路徑,單擊 OK。
圖 8. 配置 PyDev
在配置完 Pydev 以後,能夠經過在 Eclipse 菜單欄中,選擇 File > New > Project > Pydev >Pydev Project,單擊 Next 建立 Python 項目,下面的內容假設 python 項目已經建立,而且有個須要調試的腳本 remote.py(具體內容以下),它是一個登錄到遠程機器上去執行一些命令的腳本,在運行的時候須要傳入一些參數,下面將詳細講述如何在調試過程當中傳入參數 .
清單 11. Pydev 調試示例代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
#!/usr/bin/env python
import os
def telnetdo(HOST=None, USER=None, PASS=None, COMMAND=None): #define a function
import telnetlib, sys
if not HOST:
try:
HOST = sys.argv[1]
USER = sys.argv[2]
PASS = sys.argv[3]
COMMAND = sys.argv[4]
except:
print "Usage: remote.py host user pass command"
return
tn = telnetlib.Telnet() #
try:
tn.open(HOST)
except:
print "Cannot open host"
return
tn.read_until("login:")
tn.write(USER + 'n')
if PASS:
tn.read_until("Password:")
tn.write(PASS + 'n')
tn.write(COMMAND + 'n')
tn.write("exitn")
tmp = tn.read_all()
tn.close()
del tn
return tmp
if __name__ == '__main__':
print telnetdo()
|
在調試的時候有些狀況須要傳入一些參數,在調試以前須要進行相應的配置以便接收所須要的參數,選擇須要調試的程序(本例 remote.py),該腳本在 debug 的過程當中須要輸入四個參數:host,user,password 以及命令。在 eclipse 的工程目錄下選擇須要 debug 的程序,單擊右鍵,選擇「Debug As」->「Debug Configurations」,在 Arguments Tab 頁中選擇「Variables」。以下 圖 9 所示 .
圖 9. 配置變量
在窗口」Select Variable」以後選擇「Edit Varuables」 ,出現以下窗口,在下圖中選擇」New」 並在彈出的窗口中輸入對應的變量名和值。特別須要注意的是在值的後面必定要有空格,否則全部的參數都會被當作第一個參數讀入。
圖 10. 添加具體變量
按照以上方式依次配置完全部參數,而後在」select variable「窗口中安裝參數所須要的順序依次選擇對應的變量。配置完成以後狀態以下圖 11 所示。
圖 11. 完成配置
選擇 Debug 即可以開始程序的調試,調試方法與 eclipse 內置的調試功能的使用類似,而且支持多線程的 debug,這方面的文章已經有不少,讀者能夠自行搜索閱讀,或者參考」使用 Eclipse 平臺進行調試「一文。
使用日誌功能達到調試的目的
日誌信息是軟件開發過程當中進行調試的一種很是有用的方式,特別是在大型軟件開發過程須要不少相關人員進行協做的狀況下。開發人員經過在代碼中加入一些特定的可以記錄軟件運行過程當中的各類事件信息可以有利於甄別代碼中存在的問題。這些信息可能包括時間,描述信息以及錯誤或者異常發生時候的特定上下文信息。 最原始的 debug 方法是經過在代碼中嵌入 print 語句,經過輸出一些相關的信息來定位程序的問題。但這種方法有必定的缺陷,正常的程序輸出和 debug 信息混合在一塊兒,給分析帶來必定困難,當程序調試結束再也不須要 debug 輸出的時候,一般沒有很簡單的方法將 print 的信息屏蔽掉或者定位到文件。python 中自帶的 logging 模塊能夠比較方便的解決這些問題,它提供日誌功能,將 logger 的 level 分爲五個級別,能夠經過 Logger.setLevel(lvl) 來設置。默認的級別爲 warning。
表 2. 日誌的級別
Level | 使用情形 |
---|---|
DEBUG | 詳細的信息,在追蹤問題的時候使用 |
INFO | 正常的信息 |
WARNING | 一些不可預見的問題發生,或者將要發生,如磁盤空間低等,但不影響程序的運行 |
ERROR | 因爲某些嚴重的問題,程序中的一些功能受到影響 |
CRITICAL | 嚴重的錯誤,或者程序自己不可以繼續運行 |
logging lib 包含 4 個主要對象
- logger:logger 是程序信息輸出的接口。它分散在不一樣的代碼中使得程序能夠在運行的時候記錄相應的信息,並根據設置的日誌級別或 filter 來決定哪些信息須要輸出並將這些信息分發到其關聯的 handler。經常使用的方法有 Logger.setLevel(),Logger.addHandler() ,Logger.removeHandler() ,Logger.addFilter() ,Logger.debug(), Logger.info(), Logger.warning(), Logger.error(),getLogger() 等。logger 支持層次繼承關係,子 logger 的名稱一般是父 logger.name 的方式。若是不建立 logger 的實例,則使用默認的 root logger,經過 logging.getLogger() 或者 logging.getLogger(「」) 獲得 root logger 實例。
- Handler:Handler 用來處理信息的輸出,能夠將信息輸出到控制檯,文件或者網絡。能夠經過 Logger.addHandler() 來給 logger 對象添加 handler,經常使用的 handler 有 StreamHandler 和 FileHandler 類。StreamHandler 發送錯誤信息到流,而 FileHandler 類用於向文件輸出日誌信息,這兩個 handler 定義在 logging 的核心模塊中。其餘的 hander 定義在 logging.handles 模塊中,如 HTTPHandler,SocketHandler。
- Formatter:Formatter 則決定了 log 信息的格式 , 格式使用相似於 %()s 的形式來定義,如’%(asctime)s – %(levelname)s – %(message)s’,支持的 key 能夠在 python 自帶的文檔 LogRecord attributes 中查看。
- Filter:Filter 用來決定哪些信息須要輸出。能夠被 handler 和 logger 使用,支持層次關係,好比若是設置了 filter 爲名稱爲 A.B 的 logger,則該 logger 和其子 logger 的信息會被輸出,如 A.B,A.B.C.
清單 12. 日誌使用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import logging
LOG1=logging.getLogger('b.c')
LOG2=logging.getLogger('d.e')
filehandler = logging.FileHandler('test.log','a')
formatter = logging.Formatter('%(name)s %(asctime)s %(levelname)s %(message)s')
filehandler.setFormatter(formatter)
filter=logging.Filter('b')
filehandler.addFilter(filter)
LOG1.addHandler(filehandler)
LOG2.addHandler(filehandler)
LOG1.setLevel(logging.INFO)
LOG2.setLevel(logging.DEBUG)
LOG1.debug('it is a debug info for log1')
LOG1.info('normal infor for log1')
LOG1.warning('warning info for log1:b.c')
LOG1.error('error info for log1:abcd')
LOG1.critical('critical info for log1:not worked')
LOG2.debug('debug info for log2')
LOG2.info('normal info for log2')
LOG2.warning('warning info for log2')
LOG2.error('error:b.c')
LOG2.critical('critical')
|
上例設置了 filter b,則 b.c 爲 b 的子 logger,所以知足過濾條件該 logger 相關的日誌信息會 被輸出,而其餘不知足條件的 logger(這裏是 d.e)會被過濾掉。
清單 13. 輸出結果
1
2
3
4
|
b.c 2011-11-25 11:07:29,733 INFO normal infor for log1
b.c 2011-11-25 11:07:29,733 WARNING warning info for log1:b.c
b.c 2011-11-25 11:07:29,733 ERROR error info for log1:abcd
b.c 2011-11-25 11:07:29,733 CRITICAL critical info for log1:not worked
|
logging 的使用很是簡單,同時它是線程安全的,下面結合多線程的例子講述如何使用 logging 進行 debug。
清單 14. 多線程使用 logging
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
logging.conf
[loggers]
keys=root,simpleExample
[handlers]
keys=consoleHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler
[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
code example:
#!/usr/bin/python
import thread
import time
import logging
import logging.config
logging.config.fileConfig('logging.conf')
# create logger
logger = logging.getLogger('simpleExample')
# Define a function for the thread
def print_time( threadName, delay):
logger.debug('thread 1 call print_time function body')
count = 0
logger.debug('count:%s',count)
|
總結
全文介紹了 python 中 debug 的幾種不一樣的方式,包括 pdb 模塊、利用 PyDev 和 Eclipse 集成進行調試、PyCharm 以及 Debug 日誌進行調試,但願能給相關 python 使用者一點參考。更多關於 python debugger 的資料能夠參見參考資料。
參考資料
學習
- 參考 Tutorial Python查看 Python 的官方文檔。
- 參考 Python Logging文檔獲取更多的 logging 使用信息