As mentioned, I wanted to quickly advance through Python practice problems and be able to see working solutions. The best method I was able to find is HackerRank. You can access previous contests, do coding and testing in many languages right in the browser, and then see others' code on the leaderboard, so it's great to see a variety of solutions and languages.
However, I'm starting to get diminishing returns from doing coding puzzles. Better to read a book on best practices, especially object oriented. Here are some more tidbits from today:
Pass by assignment. Every language seems to have a slightly different approach to passing parameters to a method. Python's is called pass by assignment. The best explanation I saw of this is that every assignment in Python is binding a name to an object. x = 1 means that x refers to the 1 object of type int. Every integer 1 is the same object. So here, when passing an integer, b is, within the function, being bound to a different object, 3, and a is not affected.
def f(a): b = a b += 2 a=1 f(a) print a # 1
However when you pass a list, the list that a and b point to is being modified, . With this understanding, there's no need for concepts of pass-by-value, pass-by-reference, or mutable/immutable.
def f(a): b = a b +=  a= f(a) print a # [1,2]
List comprehensions. In Ruby I used a lot of map, select, and inject, which correspond to map, filter, and reduce in Python. So I was surprised to read that map, filter, and reduce are discouraged by Python's creator. The alternative for map and filter are list comprehensions. For example, the list of squares of x, for 0 <= x < 20, where x is divisible by 3 or 5:
[x**2 for x in range(20) if x % 3 == 0 or x % 5 == 0] # [0, 9, 25, 36, 81, 100, 144, 225, 324]
Iterators can sometimes be advantageous over the typical for...in syntax in python. In the Make it Anagram problem on HackerRank, I used iterators to go through two sorted lists in order. An interesting point here is the use of the "ask for forgiveness not permission" principle because iterator has no "has_next":
num_chars(chars): try: chars.next() cnt += 1 except StopIteration: return cnt
Deque is Python's efficient queue implementation.
from collections import deque bfs(start): queue = deque([start]) while len(queue) > 0: p = queue.popleft() process_node(p) for q in unvisited_neighbors(p): queue.append(q)