Effective Python Summary — Python 3 — Part 1

Chapter 1: Pythonic Thinking

  1. Know which Python version you are running: Python 2 or 3
$ python —version
Python 2.7.13
  • Python 2 and 3 are both maintained by Python Community so this is not a big issue for the older version Python 2.
  • There are a variety of packages/libraries that use Python 2. They are also compatible with Python 3.
  • There are, of course, some new features of P3 that cannot be added to P2. So you should to move to Python 3 if you have time.

2. Follow PEP8 as style guide for Python code

  • This is a guide for Python coding style. Following this brings more benefits and is worth your learning effort.

3. Differences between bytes, str and Unicode

  • In Python 3: bytes contain 8-bit values, str contains Unicode characters
  • In python 2: str contain 8-bit values, Unicode contain Unicode characters

4. Writing helper functions for complex logic/expressions

  • For complex logic, we should write util/helper functions in order to make code clearer.
  • Python is good at ONE-LINE function
  • If/else is clearer than OR or AND in expressions
>>> from urllib.parse import parse_qs
>>> values = parse_qs("red=4&blue=0&gree=")
>>> print(repr(values))
{'red': ['5'], 'blue'= ['0'], 'green': ['']}
>>> red = values.get('red', [''])
>>> red = int(red[0]) if red[0] else 0
We should do like
>>> def get_first_int(value, key, default=0):
found = values.get(key, [''])
return int(found[0]) if found[0] else default

I myself find utility/helper functions helpful. Therefore I start writing some small functions such as: 
1. Gmail helpers: send, receive emails
2. String helpers: find occurences of substring, turn string to JSON(and reversed)…
3. Object helpers: print objects beautifully, turn dictionary to an object
4. Validator functions: validate email, phone, country…
There are more than that when you work on some projects like Django, Flask

5. Slice Sequences

  • Slice has default value for start and end indexes
>>> a = list(xrange(1, 6))
>>> a[:-1]
[1, 2, 3, 4]
>>> a[-4:]
[4, 5]
>>> a[2:-2]
[3]
>>> a[-10:]
[1, 2, 3, 4, 5]
>>> a[1:-10]
[]
>>> a[-0:]
[1, 2, 3, 4, 5] => A deep copy of a
>>> a[::2] # odd values
[1, 3, 5]
>>> a[1::2] # even values
[2, 4]
>>> a[::-2] # odd values reversed
[5, 3, 1]
>>> a[-2::2] # even values
[4]
>>> a[-2::-2] # even values reversed
[4, 2]
This is extremely crazily when I go through some tricky tests, but, I figure it out somehow by only focusing on START, END indexes and STRIDE. Write it down for list[START:END] then use STRIDE to return the correct result.

6. Use list comprehension instead of map and filter

  • List comprehensive is better/clearer/more pythonic than map/filter
  • Dictionary/set also support comprehension expressions
>>> a = [1, 2, 3, 4, 5]
>>> odds = [i for i in a if i % 2]
>>> odds
[1, 3, 5]
The above is better (more pythonic) than the below
>>> odds = map(lambda i: i, filter(lambda x: x % 2, a))
>>> odds
[1, 3, 5]
  • Avoid more than 2 expressions in list comprehensions
>>> a = [[1, 2, 3], [4, 5, 6]]
>>> odds = [[i for i in item if i % 2] for item in a]
>>> odds
[[1, 3], [5]]
This is not good and unclear. Make things difficult to read.
I use this like all files and functions that helps me to make them clearer and easier to maintain.

7. Consider Generator Expressions for Large Comprehensions

  • List comprehension is great and powerful, however, for large input data, this will devastate our memory
  • Generator does work extremely well with large input, it generate output one at a time as an iterator.
>>> lines = [line for line in open('file.txt')]
>>> print lines
...
This will hold all lines in the memory for the list, if the file is a big one, this will consume a lot of memory.
-> solution
>>> lines = (line for line in open('file.txt'))
>>> print lines
<generator object ...>
>>> print next(lines)
Hello Joe
>>> print next(lines)
Hello Snow
Generators are only used fews in my projects since there are not many memory restrictions on real projects except processing image, video, pdf. The rest is mostly about string, number and small objects.

8. Enumerate

  • Looping through a list is simple: using IN or xrange(len(list))
  • Enumerate is better than the above
>>> a = [1, 2, 3, 4, 5]
>>> for index, element in enumerate(a):
print index, element
0, 1
1, 2
2, 3
3, 4
4, 5
This looping provide both index and value of the element. This is much clearer and cleaner.

9. Zip — izip and zip_longest — izip_longest

  • Using zip built-in function to iterate through multiple iterators
  • if two iterators have different length => zip will truncate the result
  • Or we can use zip_longest with fill-value
  • Use IZIP and IZIP_LONGEST for better memory management.
>>> a = [10, 20, 30, 40, 50]
>>> b = [1, 2, 3, 4, 5]
>>> result = []
>>> for index, element in enumerate(b):
result.append(element * 5 + a[index])
>>> result
[15, 30, 45, 60, 75]
There is a better solution for this
>>> result = []
>>> for ai, bi in zip(a, b):
result.append(bi * 5 + ai)
>>> result
[15, 30, 45, 60, 75]

10. Do NOT use else after for and while loops

>>> a = [10, 20, 30, 40, 50]
>>> for i in a:
print i
else:
print 'the loop does not run'
10
20
30
40
50
the loop does not run... # Why??? this seems wrong

This explains the reason

In short

Both `for` and `while` loops take an optional `else` suite, which executes if the loop completes normally, including the case of the loop not executing at all (e.g. if the `for` sequence is empty). If the loop is exited with a `break` statement, the `else` suite is skipped. (Naturally if the loop is exited with a return or by raising an exception, the `else` suite never gets a
chance to execute.)

11. Use try/except/else/finally to make things completed.

  • Using this full command results in better handling exceptions.
  • This example is from the boook EFFECTIVE PYTHON
UNDEFINED = object()
def divide_json(path):
handle = open(path, ‘r+’) # May raise IOError
try:
data = handle.read() # May raise UnicodeDecodeError
op = json.loads(data) # May raise ValueError
value = (
op[‘numerator’] / op[‘denominator’]
) # May raise ZeroDivisionError
except ZeroDivisionError as e:
return UNDEFINED
else:
op[‘result’] = value
result = json.dumps(op)
handle.seek(0)
handle.write(result) # May raise IOError
return value
finally:
handle.close() # Always runs

Part 2 Function

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.