The translation of sequence
If there’s a downside to grace, it’s that you need hard work to get it and a good education to appreciate it.
– Edsger Wybe Dijkstra
The Culture of the Python community has evolved a unique code style to guide the proper use of Python, often referred to as Pythonic. Idiomatic Python code is generally said to be pythonic. Python’s syntax and standard library design are everywhere pythonic. Furthermore, the Python community is very concerned with the consistency of the coding style, and they are pushing and practicing Pythonic everywhere. So it’s not uncommon to see discussions based on some code P vs NP (Pythonic vs non-Pythonic). Pythonic code is concise, unambiguous, elegant and, for the most part, efficient. Reading Pythonic code is a pleasant experience: “The code was written for people, just to make the machine run.”
But what is pythonic, like what is native Chinese, is real but vague. Import this See The Zen of Python by Tim Peters, which provides guidance. Many beginners have read it and agree with its ideas, but are at a loss to put it into practice. PEP 8 is nothing more than a coding specification, not enough to practice Pythonic. If you are having trouble writing Pythonic code, perhaps this note will help you.
Raymond Hettinger is a core Python developer who developed many of the features mentioned in this article. He is also an enthusiastic evangelist for the Python community, devoting himself to teaching pythonic. This post was compiled by Jeff Paine from his presentation at PyCon in 2013.
Terminology clarification: All collections referred to in this article are collections, not sets.
The original link: https://gist.github.com/JeffPaine/6213790. The following is the text.
Here are notes (video, slides) from Raymond Hettinger’s 2013 PyCon talk.
Sample code and quotes are from Raymond’s talk. This is sorted out according to my understanding, I hope you understand as smooth as ME!
Iterate over a range of numbers
for i in [0, 1, 2, 3, 4, 5]:
print i ** 2
for i in range(6):
print i ** 2
Copy the code
A better way
for i in xrange(6):
print i ** 2
Copy the code
Xrange returns an iterator that iterates through a range one value at a time. This method will save more than the Range. Xrange has been renamed range in Python 3.
Iterate over a set
colors = ['red', 'green', 'blue', 'yellow']
for i in range(len(colors)):
print colors[i]
Copy the code
A better way
for color in colors:
print color
Copy the code
Reverse traversal
colors = ['red', 'green', 'blue', 'yellow']
for i in range(len(colors)-1, -1, -1):
print colors[i]
Copy the code
A better way
for color in reversed(colors):
print color
Copy the code
Iterate over a set and its subscripts
colors = ['red', 'green', 'blue', 'yellow']
for i in range(len(colors)):
print i, '--->', colors[i]
Copy the code
A better way
for i, color in enumerate(colors):
print i, '--->', color
Copy the code
This is efficient, elegant, and saves you from creating and adding subscripts yourself.
When you find yourself manipulating subscripts in a set, you’re probably doing something wrong.
Iterate over two sets
names = ['raymond', 'rachel', 'matthew']
colors = ['red', 'green', 'blue', 'yellow']
n = min(len(names), len(colors))
for i in range(n):
print names[i], '--->', colors[i]
for name, color in zip(names, colors):
print name, '--->', color
Copy the code
A better way
for name, color in izip(names, colors):
print name, '--->', color
Copy the code
Zip generates a new list in memory that requires more memory. Izip is more efficient than ZIP.
Note: In Python 3, izip was renamed zip and replaced the original zip as the built-in function.
Traversal in order
Color = ['red', 'green', 'blue', 'yellow'] Sorted (colors, reverse=True): print colorsCopy the code
Custom sort order
colors = ['red', 'green', 'blue', 'yellow']
def compare_length(c1, c2):
if len(c1) < len(c2): return -1
if len(c1) > len(c2): return 1
return 0
print sorted(colors, cmp=compare_length)
Copy the code
A better way
print sorted(colors, key=len)
Copy the code
The first method is inefficient and uncomfortable to write. In addition, Python 3 no longer supports comparison functions.
Call a function until a token value is encountered
blocks = []
while True:
block = f.read(32)
if block == '':
break
blocks.append(block)
Copy the code
A better way
blocks = []
for block in iter(partial(f.read, 32), ''):
blocks.append(block)
Copy the code
Iter takes two arguments. The first is the function you call over and over again, and the second is the tag value.
In this example, it’s not obvious that partial makes the code less readable. The advantage of method 2 is that the return value of iter is an iterator. Iterators can be used in a variety of places: set, sorted, min, Max, heapq, sum…
Identify multiple exit points within the loop
def find(seq, target):
found = False
for i, value in enumerate(seq):
if value == target:
found = True
break
if not found:
return -1
return i
Copy the code
A better way
def find(seq, target):
for i, value in enumerate(seq):
if value == target:
break
else:
return -1
return i
Copy the code
For executes all the loops and else is executed.
If you’re new to the for-else syntax, you’ll be confused as to when else is executed. There are two ways to think about the else. The traditional approach is to treat for as if and else when the condition following for is False. When the condition is False, the for loop has not been broken, and all loops have been completed. So another way to do it is to call the else nobreak, and when for doesn’t get broken, then the loop ends with an else.
Iterate over the dictionary key
d = {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}
for k in d:
print k
for k in d.keys():
if k.startswith('r'):
del d[k]
Copy the code
When should you use the second method rather than the first? When you need to change your dictionary.
If you change something while iterating on it, you are risking the world and deserve what happens next.
D.keyys () copies all the keys in the dictionary into a list. Then you can modify the dictionary.
Note: If iterating over a dictionary in Python 3 you have to explicitly write: list(d.keys()), because d.keys() returns a “dictionary view” (an iterator that provides a dynamic view of the dictionary key). See the documentation for details.
Iterate over the keys and values of a dictionary
For k in d: print k, '-- >', d[k] # print a large list for k, v in d.items(): print k, '-- >', vCopy the code
A better way
for k, v in d.iteritems():
print k, '--->', v
Copy the code
Iteritems () is better because it returns an iterator.
Note: Python 3 no longer has iteritems(), items() behaves very similar to iteritems(). See the documentation for details.
Build a dictionary with key-value pairs
names = ['raymond', 'rachel', 'matthew']
colors = ['red', 'green', 'blue']
d = dict(izip(names, colors))
# {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}
Copy the code
Python 3: d = dict(zip(names, colors))
Count with a dictionary
Colors = [' red ', 'green', 'red' and 'blue', 'green', 'red'] # simple and basic method of counting. Suitable for beginners to start learning. d = {} for color in colors: if color not in d: d[color] = 0 d[color] += 1 # {'blue': 1, 'green': 2, 'red': 3}Copy the code
A better way
D = {} for color in colors: d[color] = d.net (color, 0) + 1 # d = defaultdict(int) for color in colors: d[color] += 1Copy the code
Group by dictionary – Parts I and II
Names = [' Raymond ', 'Rachel ',' Matthew ', 'Roger ',' Betty ', 'Melissa ',' Judith ', 'Charlie '] # In this example, D = {} for name in names: key = len(name) if key not in d: d[key] = [] d[key].append(name) # {5: ['roger', 'betty'], 6: ['rachel', 'judith'], 7: ['raymond', 'matthew', 'melissa', 'charlie']} d = {} for name in names: key = len(name) d.setdefault(key, []).append(name)Copy the code
A better way
d = defaultdict(list)
for name in names:
key = len(name)
d[key].append(name)
Copy the code
A dictionary ofpopitem()
Is it atomic?
d = {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}
while d:
key, value = d.popitem()
print key, '-->', value
Copy the code
Popitem is atomic, so there is no need to wrap a lock around it when multithreading.
Connect the dictionary
defaults = {'color': 'red', 'user': 'guest'} parser = argparse.ArgumentParser() parser.add_argument('-u', '--user') parser.add_argument('-c', '--color') namespace = parser.parse_args([]) command_line_args = {k: V for k, v in vars(namespace).items() if v} # Here is the usual approach, which defaults to using the first dictionary, overwriting it with environment variables, and finally overwriting it with command-line arguments. # Unfortunately, copying data in this way is crazy. d = defaults.copy() d.update(os.environ) d.update(command_line_args)Copy the code
A better way
d = ChainMap(command_line_args, os.environ, defaults)
Copy the code
ChainMap was added in Python 3. Efficient and elegant.
Improve readability
- Positional parameters and subscripts are nice
- But keywords and names are better
- The first method is convenient for computers
- The second approach is consistent with the way humans think
Use keyword arguments to improve readability of function calls
twitter_search('@obama', False, 20, True)
Copy the code
A better way
twitter_search('@obama', retweets=False, numtweets=20, popular=True)
Copy the code
The second method is slightly slower (in microseconds), but worth it for the readability and development time of the code.
withnamedtuple
Improved readability of multiple return values
Doctest.testmod () # (0, 4) # test result is good or bad? You can't tell because the return value is not clear.Copy the code
A better way
(Failed =0, CHS =4)Copy the code
Namedtuple is a subclass of tuple, so it still works for normal tuple operations, but it’s friendlier.
Create a nametuple
TestResults = namedTuple('TestResults', ['failed', 'attempted'])
Copy the code
Unpack the sequence
P = 'Raymond', 'Hettinger', 0x30, '[email protected]' # Fname = p[0] lname = p[1] age = p[2] email = p[3]Copy the code
A better way
fname, lname, age, email = p
Copy the code
The second method uses unpack tuples, which are faster and more readable.
Update the state of multiple variables
def fibonacci(n):
x = 0
y = 1
for i in range(n):
print x
t = y
y = x + y
x = t
Copy the code
A better way
def fibonacci(n):
x, y = 0, 1
for i in range(n):
print x
x, y = y, x + y
Copy the code
Problem with the first method
- X and y are states, and the states should be updated in a single operation, and the states can be out of sync over several lines, which is often a source of bugs.
- Operations have sequential requirements
- Too low-level, too detailed
The second method has a higher level of abstraction, no risk of ordering errors and is more efficient.
Simultaneous status update
tmp_x = x + dx * t
tmp_y = y + dy * t
tmp_dx = influence(m, x, y, dx, dy, partial='x')
tmp_dy = influence(m, x, y, dx, dy, partial='y')
x = tmp_x
y = tmp_y
dx = tmp_dx
dy = tmp_dy
Copy the code
A better way
x, y, dx, dy = (x + dx * t,
y + dy * t,
influence(m, x, y, dx, dy, partial='x'),
influence(m, x, y, dx, dy, partial='y'))
Copy the code
The efficiency of
- Basic principles of optimization
- Don’t move data unless you have to
- Notice a little bit about replacing the O(n**2) operation with a linear operation
In general, don’t move data without reason
Connection string
names = ['raymond', 'rachel', 'matthew', 'roger',
'betty', 'melissa', 'judith', 'charlie']
s = names[0]
for name in names[1:]:
s += ', ' + name
print s
Copy the code
A better way
print ', '.join(names)
Copy the code
Update the sequence
names = ['raymond', 'rachel', 'matthew', 'roger', 'betty', 'melissa', 'judith', 'Charlie '] del names[0] # The following code indicates you are using the wrong data structure names.pop(0) names.insert(0, 'mark')Copy the code
A better way
names = deque(['raymond', 'rachel', 'matthew', 'roger', 'betty', 'melissa', 'judith', [0] names.popleft() names.appendleft('mark')Copy the code
Decorator and context management
- Logic used to separate business and management
- A clean and elegant tool for shredding code and improving code reuse
- Having a good name is key
- Remember the motto of Spider-Man: With great power comes great responsibility
Use decorators to separate out administrative logic
Def web_lookup(url, saved={}): if url in saved: return saved[url] page = urllib.urlopen(url).read() saved[url] = page return pageCopy the code
A better way
@cache
def web_lookup(url):
return urllib.urlopen(url).read()
Copy the code
Note: Functools. lru_cache was introduced in Python 3.2 to solve this problem.
Detach temporary context
# Save the old, Copy () getContext ().prec = 50 print Decimal(355)/Decimal(113) setContext (old_context)Copy the code
A better way
with localcontext(Context(prec=50)):
print Decimal(355) / Decimal(113)
Copy the code
The sample code is using the standard library Decimal, which already implements the LocalContext.
How do I open and close a file
f = open('data.txt')
try:
data = f.read()
finally:
f.close()
Copy the code
A better way
with open('data.txt') as f:
data = f.read()
Copy the code
How to use the lock
Lock.acquire () = threading.lock () print 'Critical section 1' print 'Critical section 2' finally: lock.release()Copy the code
A better way
Print 'Critical section 1' print 'Critical section 2'Copy the code
Isolate the temporary context
try:
os.remove('somefile.tmp')
except OSError:
pass
Copy the code
A better way
with ignored(OSError):
os.remove('somefile.tmp')
Copy the code
Ignored is a documentation added to Python 3.4.
Note: Ignored is actually called suppress in the standard library.
Try creating your own ignored context manager.
@contextmanager
def ignored(*exceptions):
try:
yield
except exceptions:
pass
Copy the code
Put it in your tools directory and you can ignore exceptions as well
__enter__ and __exit__ are written to the contextmanager by decorating the generator function in contextlib. See the documentation for details.
Detach temporary context
With open('help.txt', 'w') as f: oldstdout = sys.stdout = f try: help(pow) finally: sys.stdout = oldstdoutCopy the code
A better way to write it
with open('help.txt', 'w') as f:
with redirect_stdout(f):
help(pow)
Copy the code
Redirect_stdout was added in Python 3.4, bug feedback.
Implement your own redirect_stdout context manager.
@contextmanager
def redirect_stdout(fileobj):
oldstdout = sys.stdout
sys.stdout = fileobj
try:
yield fieldobj
finally:
sys.stdout = oldstdout
Copy the code
Concise one-sentence expression
Two conflicting principles:
- Don’t have too much logic on one line
- Don’t break a single idea into multiple parts
Raymond’s principles:
- The logic of a line of code is equivalent to a sentence of natural language
List parsing and generators
result = []
for i in range(10):
s = i ** 2
result.append(s)
print sum(result)
Copy the code
A better way
print sum(i**2 for i in xrange(10))
Copy the code
The first way is about what you are doing, and the second way is about what you want.