The Zen Of Python (Pythonic Style Coding)

2023-11-25

筆記 Pythonic Style Coding,藉由風格對比的方式,深化至自己的心中。

logo

說明

  1. Using List Comprehension
  • Unpythonic 😥
numbers = [1, 2, 3, 4, 5]
squares = []
for n in numbers:
    squares.append(n ** 2)
  • Pythonic 😎
numbers = [1, 2, 3, 4, 5]
squares = [n ** 2 for n in numbers]

  1. Iterating Over Dictionary Items
  • Unpythonic 😥
dictionary = {'a': 1, 'b': 2, 'c': 3}
for key in dictionary.keys():
    print(key, dictionary[key])
  • Pythonic 😎
dictionary = {'a': 1, 'b': 2, 'c': 3}
for key, value in dictionary.items():
    print(key, value)

  1. File Operations : with
  • Unpythonic 😥
file = open('file.txt', 'r')
content = file.read()
print(content)
file.close()
  • Pythonic 😎
with open('file.txt', 'r') as file:
    content = file.read()
    print(content)

  1. Swapping Variable Values
  • Unpythonic 😥
a = 1
b = 2
temp = a
a = b
b = temp
  • Pythonic 😎
a = 1
b = 2
a, b = b, a

  1. Conditional Expressions
  • Unpythonic 😥
if condition:
    x = 1
else:
    x = 0
  • Pythonic 😎
x = 1 if condition else 0

  1. 使用 enumerate (Using enumerate)
  • Unpythonic 😥
my_list = ['apple', 'banana', 'grape']
i = 0
for item in my_list:
    print(i, item)
    i += 1
  • Pythonic 😎
my_list = ['apple', 'banana', 'grape']
for i, item in enumerate(my_list):
    print(i, item)

  1. Using the Zip Function
  • Unpythonic 😥
keys = ['a', 'b', 'c']
values = [1, 2, 3]
my_dict = {}
for i in range(len(keys)):
    my_dict[keys[i]] = values[i]
  • Pythonic 😎
keys = ['a', 'b', 'c']
values = [1, 2, 3]
my_dict = dict(zip(keys, values))

  1. Defining Default Values in Functions
  • Unpythonic 😥
def foo(bar):
    if bar is None:
        bar = []
    # do something with bar
  • Pythonic 😎
def foo(bar = None):
    bar = bar or []
    # do something with bar

  1. Using Generator Expressions
  • Unpythonic 😥
numbers = [1, 2, 3, 4, 5]
sum_squares = sum([n ** 2 for n in numbers])
  • Pythonic 😎
numbers = [1, 2, 3, 4, 5]
sum_squares = sum(n ** 2 for n in numbers)

  1. Using *args and **kwargs
  • Unpythonic 😥
def foo(a, b, c, d):
    print(a, b, c, d)
foo(1, 2, 3, 4)
  • Pythonic 😎
def foo(*args, **kwargs):
    print(*args, **kwargs)
foo(1, 2, c=3, d=4)

  1. String Concatenation
  • Unpythonic 😥
strings = ['Hello', 'World']
result = ''
for s in strings:
    result += s
  • Pythonic 😎
strings = ['Hello', 'World']
result = ''.join(strings)

  1. Filtering Lists with Comprehension
  • Unpythonic 😥
numbers = [1, 2, 3, 4, 5, 6]
even = []
for n in numbers:
    if n % 2 == 0:
        even.append(n)
  • Pythonic 😎
numbers = [1, 2, 3, 4, 5, 6]
even = [n for n in numbers if n % 2 == 0]

  1. Multiple Return Values
  • Unpythonic 😥
def get_user():
    # ... some logic ...
    return name, age, email
result = get_user()
name = result[0]
age = result[1]
email = result[2]
  • Pythonic 😎
def get_user():
    # ... some logic ...
    return name, age, email
name, age, email = get_user()

  1. sing List's extend Method
  • Unpythonic 😥
list1 = [1, 2, 3]
list2 = [4, 5, 6]
for item in list2:
    list1.append(item)
  • Pythonic 😎
list1 = [1, 2, 3]
list2 = [4, 5, 6]
list1.extend(list2)

  1. 字典預設值 (Dictionary Default Values)
  • Unpythonic 😥
dict = {'a': 1, 'b': 2}
if 'c' in dict:
    value = dict['c']
else:
    value = 'default'
  • Pythonic 😎
dict = {'a': 1, 'b': 2}
value = dict.get('c', 'default')

  1. List Slicing
  • Unpythonic 😥
my_list = [1, 2, 3, 4, 5]
first_three = []
for i in range(3):
    first_three.append(my_list[i])
  • Pythonic 😎
my_list = [1, 2, 3, 4, 5]
first_three = my_list[:3]

  1. Using map and filter
  • Unpythonic 😥
numbers = [1, 2, 3, 4, 5]
squares = []
for n in numbers:
    squares.append(n ** 2)
even_squares = []
for square in squares:
    if square % 2 == 0:
        even_squares.append(square)
  • Pythonic 😎
numbers = [1, 2, 3, 4, 5]
even_squares = map(lambda n: n ** 2, filter(lambda n: n % 2 == 0, numbers))

  1. Using all or any
  • Unpythonic 😥
values = [True, True, False]
all_true = True
for v in values:
    if not v:
        all_true = False
        break
  • Pythonic 😎
values = [True, True, False]
all_true = all(values)

  1. Using Sets to Remove Duplicates
  • Unpythonic 😥
my_list = [1, 2, 2, 3, 3, 3]
unique_list = []
for item in my_list:
    if item not in unique_list:
        unique_list.append(item)
  • Pythonic 😎
my_list = [1, 2, 2, 3, 3, 3]
unique_list = list(set(my_list))

  1. Concise Function Definition
  • Unpythonic 😥
def square(x):
    return x * x
  • Pythonic 😎
square = lambda x: x * x

  1. Merging Dictionaries
  • Unpythonic 😥
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
merged = dict1.copy()
for key in dict2:
    merged[key] = dict2[key]
  • Pythonic 😎
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
merged = {**dict1, **dict2}

  1. Reversing Lists
  • Unpythonic 😥
my_list = [1, 2, 3, 4, 5]
reversed_list = []
for item in reversed(my_list):
    reversed_list.append(item)
  • Pythonic 😎
my_list = [1, 2, 3, 4, 5]
reversed_list = my_list[::-1]

  1. Set Operations
  • Unpythonic 😥
set1 = {1, 2, 3}
set2 = {3, 4, 5}
intersection = set()
for item in set1:
    if item in set2:
        intersection.add(item)
  • Pythonic 😎
set1 = {1, 2, 3}
set2 = {3, 4, 5}
intersection = set1 & set2

同場加映 🎬

union = set1 | set2

difference = set1 - set2

symmetric_difference = set1 ^ set2

is_subset = set1 <= set2

is_superset = set1 >= set2

is_disjoint = set1.isdisjoint(set2)

  1. 使用 defaultdict (Using defaultdict)
  • Unpythonic 😥
my_dict = {}
for key, value in pairs:
    if key not in my_dict:
        my_dict[key] = []
    my_dict[key].append(value)
  • Pythonic 😎
from collections import defaultdict
my_dict = defaultdict(list)
for key, value in pairs:
    my_dict[key].append(value)

  1. Logging Function Execution Time
  • Unpythonic 😥
import time

def my_function():
    start_time = time.time()
    # function logic
    end_time = time.time()
    print(f"Time taken: {end_time - start_time} seconds")
  • Pythonic 😎
import time
import functools

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        value = func(*args, **kwargs)
        end_time = time.time()
        print(f"{func.__name__} took {end_time - start_time} seconds")
        return value
    return wrapper

@timer
def my_function():
    # function logic

  1. 鏈式比較 (Chained Comparisons)
  • Unpythonic 😥
if x > 5 and x < 10:
    pass
  • Pythonic 😎
if 5 < x < 10:
    pass

  1. 使用 reversed 函数 (Using reversed)
  • Unpythonic 😥
for i in range(len(my_list) - 1, -1, -1):
    print(my_list[i])
  • Pythonic 😎
for item in reversed(my_list):
    print(item)

  1. 字典推導式(Dictionary Comprehension)
  • Unpythonic 😥
keys = ['a', 'b', 'c']
values = [1, 2, 3]
my_dict = {}
for i in range(len(keys)):
    my_dict[keys[i]] = values[i]
  • Pythonic 😎
keys = ['a', 'b', 'c']
values = [1, 2, 3]
my_dict = {k: v for k, v in zip(keys, values)}

  1. 同時獲取索引和值(Getting Index and Value Simultaneously)
  • Unpythonic 😥
my_list = ['a', 'b', 'c']
for i in range(len(my_list)):
    print(i, my_list[i])
  • Pythonic 😎
my_list = ['a', 'b', 'c']
for index, value in enumerate(my_list):
    print(index, value)

  1. 使用生成器表達式(Using Generator Expressions)
  • Unpythonic 😥
def square_numbers(nums):
    for num in nums:
        yield num * num
  • Pythonic 😎
nums = [1, 2, 3, 4]
square_gen = (num * num for num in nums)

  1. 函數參數解包 (Function Parameter Unpacking)
  • Unpythonic 😥
def my_func(a, b, c):
    print(a, b, c)

args = [1, 2, 3]
my_func(args[0], args[1], args[2])
  • Pythonic 😎
args = [1, 2, 3]
my_func(*args)

  1. 使用 itertools.chain 合并迭代器 (Using itertools.chain)
  • Unpythonic 😥
list1 = [1, 2, 3]
list2 = [4, 5, 6]
for list in (list1, list2):
    for item in list:
        process(item)
  • Pythonic 😎
import itertools
list1 = [1, 2, 3]
list2 = [4, 5, 6]
for item in itertools.chain(list1, list2):
    process(item)
  1. 使用 _ 忽略特定值 (Using _ to Ignore Specific Values)
  • Unpythonic 😥
a, b, c = (1, 2, 3)
print(a, b)
  • Pythonic 😎
a, b, _ = (1, 2, 3)
print(a, b)

  1. 使用 := 賦值表達式(自 Python 3.8 起) (Using := Assignment Expression)
  • Unpythonic 😥
n = len(my_list)
if n > 4:
    print(f"List is too long ({n} elements, expected <= 4)")
  • Pythonic 😎
if (n := len(my_list)) > 4:
    print(f"List is too long ({n} elements, expected <= 4)")

  1. 使用 pathlib 處理路徑 (Using pathlib for Path Handling)
  • Unpythonic 😥
import os
filepath = os.path.join('dir', 'file.txt')
  • 語法糖寫法(使用 pathlib):
from pathlib import Path
filepath = Path('dir') / 'file.txt'

  1. 使用 or 簡化賦值 (Simplifying Assignment with or)
  • Unpythonic 😥
name = ''
if user_name:
    name = user_name
else:
    name = 'Guest'
  • Pythonic 😎
name = user_name or 'Guest'

  1. Using Set Comprehension
  • Unpythonic 😥
my_list = [1, 2, 2, 3, 3, 3]
my_set = set()
for item in my_list:
    my_set.add(item)
  • Pythonic 😎
my_list = [1, 2, 2, 3, 3, 3]
my_set = {item for item in my_list}

  1. Using f-string
  • Unpythonic 😥
name = "Alice"
greeting = "Hello, " + name + "!"
  • Pythonic 😎
name = "Alice"
greeting = f"Hello, {name}!"

  1. 以優雅方式進行異常處理 (Concise Exception Handling)
  • Unpythonic 😥
try:
    result = 10 / number
except ZeroDivisionError:
    result = "Infinity"
  • Pythonic 😎
result = 10 / number if number else "Infinity"

  1. 使用 itertools (Using itertools)
  • Unpythonic 😥
pairs = []
for a in range(3):
    for b in range(3):
        if a != b:
            pairs.append((a, b))
  • Pythonic 😎
import itertools
pairs = [(a, b) for a, b in itertools.permutations(range(3), 2)]

  1. 使用 functools.reduce (Using functools.reduce)
  • Unpythonic 😥
numbers = [1, 2, 3, 4]
result = 0
for num in numbers:
    result += num
  • Pythonic 😎
from functools import reduce
numbers = [1, 2, 3, 4]
result = reduce(lambda x, y: x + y, numbers)

  1. 使用 next 與迭代器 (Using next with Iterators)
  • Unpythonic 😥
for item in my_iterable:
    first_item = item
    break
  • Pythonic 😎
first_item = next(iter(my_iterable))

  1. Using List's pop
  • Unpythonic 😥
last_item = my_list[len(my_list) - 1]
my_list = my_list[:-1]
  • Pythonic 😎
last_item = my_list.pop()

  1. 迴圈中使用 else (Using else in Loops)
  • Unpythonic 😥
found = False
for item in my_list:
    if condition(item):
        process(item)
        found = True
        break
if not found:
    handle_not_found()
  • Pythonic 😎
for item in my_list:
    if condition(item):
        process(item)
        break
else:
    handle_not_found()

  1. 使用 contextlib.suppress (Using contextlib.suppress)
  • Unpythonic 😥
try:
    os.remove(filename)
except FileNotFoundError:
    pass
  • Pythonic 😎
from contextlib import suppress
with suppress(FileNotFoundError):
    os.remove(filename)

  1. Optimizing Code with Assignment Expressions
  • Unpythonic 😥
file = open('test.txt')
data = file.readline()
while data:
    process(data)
    data = file.readline()
  • Pythonic 😎
with open('test.txt') as file:
    while (data := file.readline()):
        process(data)

  1. Using List's clear Method
  • Unpythonic 😥
my_list = [1, 2, 3, 4, 5]
del my_list[:]
  • Pythonic 😎
my_list = [1, 2, 3, 4, 5]
my_list.clear()

  1. Using zip to Iterate Over Multiple Sequences Simultaneously
  • Unpythonic 😥
list1 = [1, 2, 3]
list2 = ['a', 'b', 'c']
for i in range(len(list1)):
    print(list1[i], list2[i])
  • Pythonic 😎
list1 = [1, 2, 3]
list2 = ['a', 'b', 'c']
for number, letter in zip(list1, list2):
    print(number, letter)

  1. 使用 divmod 函數 (Using divmod Function)
  • Unpythonic 😥
dividend, divisor = 20, 7
quotient = dividend // divisor
remainder = dividend % divisor
  • Pythonic 😎
dividend, divisor = 20, 7
quotient, remainder = divmod(dividend, divisor)

  1. 條件判斷 (Optimizing Conditional Statements)
  • Unpythonic 😥
if x == 'a' or x == 'b' or x == 'c':
    pass
  • Pythonic 😎
if x in ('a', 'b', 'c'):
    pass

  1. 多重賦值 (Optimizing Multiple Assignments)
  • Unpythonic 😥
x = 5
y = 5
z = 5
  • Pythonic 😎
x = y = z = 5

  1. Membership Check with in
  • Unpythonic 😥
if my_list.count(x) > 0:
    pass
  • Pythonic 😎
if x in my_list:
    pass

  1. Using collections.Counter for Element Counting
  • Unpythonic 😥
my_list = ['a', 'b', 'c', 'a', 'b', 'b']
count = {}
for item in my_list:
    if item in count:
        count[item] += 1
    else:
        count[item] = 1
  • Pythonic 😎
from collections import Counter
my_list = ['a', 'b', 'c', 'a', 'b', 'b']
count = Counter(my_list)