The North Sea Divine Skill, named after the Daoist Zhuangzi's "Free and Easy Wandering." Its main principles are summarized in the following points:
"People in the world practice martial arts, all start from the Cloud Gate and go to the Lesser Merchant. Our Free and Easy Sect, however, goes against this path, starting from the Lesser Merchant and reaching the Cloud Gate. When the thumb connects with another person, their internal strength enters my body and is stored in the Cloud Gate and other acupoints. However, if the enemy's internal strength is stronger than mine, then the sea water will flood back into the rivers, which is extremely dangerous. Be cautious, be cautious.
From the perspective of the cultivation mindset of the North Sea Divine Skill, it is completely opposite to the walking meridians of ordinary internal martial artists. Therefore, the North Sea Divine Skill has the function of encompassing all rivers, allowing it to absorb all internal martial arts in the world. Moreover, this absorption has no side effects, as it can also assimilate others' internal strength.
iterable techniques
▍1. Create a sequence of numbers (from 0 to 10, with an interval of 2)
>>> range(0,10,2)
[0, 2, 4, 6, 8]
▍2. Sum a series of numbers (from 0 to 10, with an interval of 2)
>>> l = range(0,10,2)
>>> sum(l)
20
▍3. Check if any element in the sequence is True
>>> any(a % 2 for a in range(0,10,2))
True
▍4. Check if all elements in the sequence are True
>>> all(a % 2 for a in range(0,10,2))
True
▍5. Cumulative sum of a series of numbers
>>> import numpy as np
>>> res = list(np.cumsum(range(0,10,2)))
>>> res
[ 0, 2, 6, 12, 20]
▍6. Construct a tuple by adding an index to each iterable
>>> a = ['Hello', 'world', '!']
>>> list(enumerate(a))
[(0, 'Hello'), (1, 'world'), (2, '!')]
▍7. Join iterables into a single string
>>> a = ["python","really", "rocks"]
>>> " ".join(a)
'python really rocks'
▍8. Combine two iterable tuples or pivot nested iterables
# Combining two iterables
>>> a = [1, 2, 3]
>>> b = ['a', 'b', 'c']
>>> z = zip(a, b)
>>> z
[(1, 'a'), (2, 'b'), (3, 'c')]
# Pivoting list of tuples
>>> zip(*z)
[(1, 2, 3), ('a', 'b', 'c')]
▍9. Get minimum/maximum from iterables (with/without specific function)
# Getting maximum from iterable
>>> a = [1, 2, -3]
>>> max(a)
2
# Getting minimum from iterable
>>> min(a)
1
# Both min/max have key value to allow getting maximum by applying function
>>> max(a,key=abs)
3
▍10. Sort iterables (can be sorted by "compare" function)
>>> a = [1, 2, -3]
>>> sorted(a)
[-3, 1, 2]
>>> sorted(a,key=abs)
[1, 2, -3]
▍11. Split a single string into a list
>>> s = "a,b,c"
>>> s.split(",")
["a", "b", "c"]
▍12. Initialize a list containing repeated numbers
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
▍13. Merge/insert two dictionaries
>>> a = {"a":1, "b":1}
>>> b = {"b":2, "c":1}
>>> a.update(b)
>>> a
{"a":1, "b":2, "c":1}
▍14. Name and save iterable slices
# 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]
▍15. Find the index of an item in a list
>>> a = ["foo", "bar", "baz"]
>>> a.index("bar")
1
▍16. Find the index of the minimum/maximum item in iterables
>>> a = [2, 3, 1]
>>> min(enumerate(a),key=lambda x: x[1])[0]
2
▍17. Rotate k elements of iterables
>>> a = [1, 2, 3, 4]
>>> k = 2
>>> a[-2:] + a[:-2]
[3, 4, 1, 2]
▍18. Remove unnecessary characters from the end/start/both ends of a string
>>> name = "//George//"
>>> name.strip("/")
'George'
>>> name.rstrip("/")
'//George'
>>> name.lstrip("/")
'George//'
▍19. Reverse the order of iterables (strings, lists, etc.)
# Reversing string
>>> s = "abc"
>>> s[::-1]
"cba"
# Reversing list
>>> l = ["a", "b", "c"]
>>> l[::-1]
["c", "b", "a"]
branching techniques
▍20. Multiple short-cuts
>>> n = 10
>>> 1 < n < 20
True
▍21. The for-else structure is useful when searching for something and finding it
for i in mylist:
if i == theflag:
break
process(i)
else:
raise ValueError("List argument missing terminal flag.")
▍22. Ternary operator
>>> "Python ROCK" if True else " I AM GRUMPY"
"Python ROCK"
▍23. Try-catch-else structure
try:
foo()
except Exception:
print("Exception occurred")
else:
print("Exception didn't occur")
finally:
print("Always gets here")
▍24. While-else structure
i = 5
while i > 1:
print("Whil-ing away!")
i -= 1
if i == 3:
break
else:
print("Finished up!")
comprehensions techniques
▍25. List comprehension
>>> m = [x ** 2 for x in range(5)]
>>> m
[0, 1, 4, 9, 16]
▍26. Set comprehension
>>> m = {x ** 2 for x in range(5)}
>>> m
{0, 1, 4, 9, 16}
▍27. Dict comprehension
>>> m = {x: x ** 2 for x in range(5)}
>>> m
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
▍28. Generator comprehension
# A generator comprehension is the lazy version of a list comprehension.
>>> m = (x ** 2 for x in range(5))
>>> m
<generator object <genexpr> at 0x108efe408>
>>> list(m)
[0, 1, 4, 9, 16]
>>> m = (x ** 2 for x in range(5))
>>> next(m)
0
>>> list(m)
[1, 4, 9, 16]
▍29. List comprehension using current and past values
>>> a = [1, 2, 4, 2]
>>> [y - x for x,y in zip(a,a[1:])]
[1, 2, -2]
unpacking techniques
▍30. Unpack variables from iterable
# One can unpack all iterables (tuples, list etc)
>>> a, b, c = 1, 2, 3
>>> a, b, c
(1, 2, 3)
>>> a, b, c = [1, 2, 3]
>>> a, b, c
(1, 2, 3)
▍31. Swap variable values
>>> a, b = 1, 2
>>> a, b = b, a
>>> a, b
(2, 1)
▍32. Unpack variables from iterable without indicating all elements
>>> a, *b, c = [1, 2, 3, 4, 5]
>>> a
1
>>> b
[2, 3, 4]
>>> c
5
▍33. Unpack variables using the splat operator
>>> def test(x, y, z):
>>> print(x, y, z)
>>> res = test(*[10, 20, 30])
10 20 30
>>> res = test(**{'x': 1, 'y': 2, 'z': 3} )
10 20 30
Itertools techniques
▍34. Flatten iterables
>>> a = [[1, 2], [3, 4], [5, 6]]
>>> list(itertools.chain.from_iterable(a))
[1, 2, 3, 4, 5, 6]
▍35. Create Cartesian product from iterables
>>> for p in itertools.product([1, 2, 3], [4, 5]):
>>> print(''.join(str(x) for x in p))
(1, 4)
(1, 5)
(2, 4)
(2, 5)
(3, 4)
(3, 5)
▍36. Create permutations from iterable
>>> for p in itertools.permutations([1, 2, 3, 4]):
>>> print(''.join(str(x) for x in p))
123
132
213
231
312
321
▍37. Create n-grams from iterable
>>> 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)]
▍38. Use padding to combine two iterators or pivot nested iterables
>>> import itertools as it
>>> x = [1, 2, 3, 4, 5]
>>> y = ['a', 'b', 'c']
>>> list(zip(x, y))
[(1, 'a'), (2, 'b'), (3, 'c')]
>>> list(it.zip_longest(x, y))
[(1, 'a'), (2, 'b'), (3, 'c'), (4, None), (5, None)]
▍39. Create k combinations from an iterable n
>>> import itertools
>>> bills = [20, 20, 20, 10, 10, 10, 10, 10, 5, 5, 1, 1, 1, 1, 1]
>>> list(itertools.combinations(bills, 3))
[(20, 20, 20), (20, 20, 10), (20, 20, 10), ... ]
▍40. Create an accumulating result from an iterable given a function
>>> import itertools
>>> list(itertools.accumulate([9, 21, 17, 5, 11, 12, 2, 6], min))
[9, 9, 9, 5, 5, 5, 2, 2]
▍41. Create an iterator that returns elements from an iterable as long as the predicate is True
>>> import itertools
>>> itertools.takewhile(lambda x: x < 3, [0, 1, 2, 3, 4])
[0, 1, 2]
>>> it.dropwhile(lambda x: x < 3, [0, 1, 2, 3, 4])
[3, 4]
▍42. Create an iterator that filters elements from an iterable, returning only those for which the predicate is False
>>> import itertools
# keeping only false values
>>> list(itertools.filterfalse(bool, [None, False, 1, 0, 10]))
[None, False, 0]
▍43. Create an iterator that computes a function using parameters obtained from iterating over the iterable
>>> import itertools
>>> import operator
>>> a = [(2, 6), (8, 4), (7, 3)]
>>> list(itertools.starmap(operator.mul, a))
[12, 32, 21]
collections techniques
▍44. Set basic 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
▍45. Counter data structure (unordered collection where elements are stored as dictionary keys and their counts as dictionary values)
import collections
>>> 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)]
▍46. Default dictionary structure (a subclass of dictionary that retrieves a default value when accessing a nonexistent key)
>>> import collections
>>> m = collections.defaultdict(int)
>>> m['a']
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]'
>>> m = collections.defaultdict(list)
>>> m['a']
[]
▍47. Ordered dict structure (a subclass of dictionary that maintains order)
>>> from collections import OrderedDict
>>> d = OrderedDict.fromkeys('abcde')
>>> d.move_to_end('b')
>>> ''.join(d.keys())
'acdeb'
>>> d.move_to_end('b', last=False)
>>> ''.join(d.keys())
'bacde'
▍48. Deques structure (Deques are a generalization of stacks and queues)
>>> import collections
>>> 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])
>>> last_three = collections.deque(maxlen=3)
>>> for i in range(4):
... 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
▍49. Named tuple structure (creates objects that are like tuples, with fields accessible via attribute lookup, as well as indexing and iteration)
>>> import collections
>>> 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
▍50. Use a dictionary to store Switch
>>> func_dict = {'sum': lambda x, y: x + y, 'subtract': lambda x, y: x - y}
>>> func_dict['sum'](9,3)
12
>>> func_dict['subtract'](9,3)
6
▍51. Data class structure
>>> from dataclasses import dataclass
>>> @dataclass
>>> class DataClassCard:
>>> rank: str
>>> suit: str
>>> queen_of_hearts = DataClassCard('Q', 'Hearts')
>>> queen_of_hearts.rank
'Q'
>>> queen_of_hearts
DataClassCard(rank='Q', suit='Hearts')
>>> queen_of_hearts == DataClassCard('Q', 'Hearts')
True
Other techniques
▍52. Generate UUID
# This creates a randomized
#128-bit number that will almost certainly be unique.
# In fact, there are over 2¹²² possible
#UUIDs that can be generated.
#That’s over five undecillion (or 5,000,000,000,000,000,000,000,000,000,000,000,000).
>>> import uuid
>>> user_id = uuid.uuid4()
>>> user_id
UUID('7c2faedd-805a-478e-bd6a-7b26210425c7')
▍53. Use LRU cache for memoization
import functools
@functools.lru_cache(maxsize=128)
def fibonacci(n):
if n == 0:
return 0
elif n == 1:
return 1
return fibonacci(n - 1) + fibonacci(n - 2)
▍54. Suppression of expressions
>>> from contextlib import suppress
>>> with contextlib.suppress(ZeroDivisionError):
>>> 10/0
# No exception raised
▍55. Create context management when setup and teardown are needed
class FileManager:
def __init__(self, filename):
self.filename = filename
self.file = None
def __enter__(self):
self.file = open(self.filename, 'r')
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
# Use context manager to read a file
with FileManager('example.txt') as file:
for line in file:
print(line)
▍56. An elegant way to handle file paths (3.4≥)
>>> from pathlib import Path
>>> data_folder = Path("source_data/text_files/)
# Path calculation and metadata
>>> file_to_open = data_folder / "raw_data.txt"
>>> file_to_open.name
"raw_data.txt"
>>> file_to_open.suffix
"txt"
>>>file_to_open.stem
"raw_data"
# Files functions
>>> f = open(file_to_open)
>>> f.read()
# content of the file
>>> file_to_open.exists()
True
▍57. Implement standard operators as functions of a class
@staticmethod
def add(x, y):
return x + y
@staticmethod
def subtract(x, y):
return x - y
@staticmethod
def multiply(x, y):
return x * y
@staticmethod
def divide(x, y):
return x / y
▍58. Create decorators to separate concerns
>>>from functools import wraps
>>>def add_sandwich(wrapped):
>>> @wraps(wrapped)
>>> def wrapper(*args, **kwargs):
>>> return wrapped(*args, **kwargs) + ' sandwich'
>>> return wrapper
>>>@add_sandwich
>>>def ham():
>>> return 'ham'
>>>ham()
'ham sandwich'
▍59. Create a simple iterator using yield
>>> def foo(lst):
>>> for x in lst:
>>> yield x
>>> yield x*2
>>> a = [1, 3]
>>> list(foo(a))
[1, 2, 3, 6]
▍60. Yield from use cases and tricks
def numbers():
yield from range(1, 4)
def letters():
yield from ('A', 'B', 'C')
def combine():
yield from numbers()
yield from letters()
for value in combine():
print(value)
# Output: 1 2 3 A B C
Easter eggs
▍61. Anti-gravity
import antigravity
antigravity.fly()
▍62. 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 not 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 are Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it is a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea let us do more of those!