2018年6月27日 星期三

[Python] 學習Pythonic的Python - (2) (effective Python)


如果第一個學習的不是Python語言, 常常依照其他語言的語法來寫Python, 這裡紀錄常見語法的Python寫法本篇參考網路上的文章, 一些作者整理了Effective Python中相當多值得學習的地方, 完整連結於文章最下方

多個list一起使用
for a1, a2 in zip(arr1, arr2):
    print(a1, a2)

注意:zip 會在最短的 list 結束,想要執行到最長的 list,可以用 itertools 的 zip_longest

Generator

概括式遇到大型資料時,可能會因為創立一個過於龐大的 list,造成程式當掉,例如我要計算文字檔每一行的字數

lengths = [len(x) for x in open('/PATH/INFO.txt')]
print(lengths)
# > [32, 19, 1, 12, 26]

如果檔案 size 非常大,或是他是一個不會停止的 input,你應該改用 generator 來產生迭代器,按需要產出內容

lengths = (len(x) for x in open('/PATH/INFO.txt'))
print(it)
# > <generator object <genexpr> at 0x0000017BF5C939E8>

然後搭配 next 來一次次觸發 iterator

print(next(lengths))
print(next(lengths))
# > 32
# > 19


Python 3.7 開始會內建 Data Classes,之後 Python 3.6 也能透過 pip 安裝。

Data Class 支援預設值 和 更多樣化的型別定義,例如

from dataclasses import dataclass, field
from typing import Any, List

@dataclass
class Node:
    x: int
    y: int
    radius: float = 0.0
    name: str = 'none'
    child: Any = None
    test: List[int] = field(default_factory=list)

這段 code 應該很好理解,名稱後用 : 宣告型態,型態在 typing 套件中更多支援(例如這邊的 Any),在型態後面用 = 賦予預設值。

暫時忽略最後一行的那個 field,這段 code 可以像是正常使用 class 一樣,用順序或是 keywords 輸入數值

print(Node(2, 4))
# Node(x=2, y=4, radius=0.0, name='none', child=None, test=[])
print(Node(2, 4, name='end', radius=1.5))
# Node(x=2, y=4, radius=1.5, name='end', child=None, test=[])
print(Node(4, 6, 5.3, 'hi', test=[1, 2, 3]))
# Node(x=4, y=6, radius=5.3, name='hi', child=None, test=[1, 2, 3])

if statment

取代 == Bool
if valid():
    print 'valid'
if not valid():
    print 'invalid'
if not users:
    print 'No users available'

tuple
list to tuple
def get_error_details():
    return (2, 'details')


(errnum, errstr) = get_error_details()
my_list = ['Alice', 12, 'Python']
(name, age, skill) = my_list
(x, y) = (y, x)

邏輯很短的if 表示法
def foo(logging):
    level = (1 if logging else 0)

enumerate
不用額外宣告index 然後 index = index + 1
names = ['Alice', 'Bob', 'Cindy']
for index, element in enumerate(names):
    print '%d %s' % (index, element)

使用[:]計算陣列
不用len(account)再去計算
def get_check_number(account):
    return account[-4:]

文字過長用括號不用 \
sql = (
    "SELECT name, product, price "
    "FROM production.product "
    "WHERE description = 'TEST' "
    "AND days < 10 "
    "ORDER BY name DESC"
)

取代 if 中還需要temp變數紀錄是否滿足
for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0:
            print n, 'equals', x, '*', n/x
            break
    else:
        print n, 'is a prime number'

string合併
name_list = ['Alice', 'Bob', 'Cindy']
name_formatted = ', '.join(name_list)

判斷name是否在dict當中,若沒有回傳Bob
name = some_dict.get('name', 'Bob')

getter,setter
class Egg(object):

    def __init__(self, price):
        self._price = price

    @property
    def price(self):
        return self._price * RATE
    
    @price.setter
    def price(self, value):
        self._price = value

取代手動關閉, try catch必須手動關閉, 使用with才是好的寫法
with open('tmp.txt', 'w') as f:
    f.write('TEST')
f = open('tmp.txt', 'w')
try:
    f.write('TEST')
finally:
    f.close()

第一種方式最簡短, 也不會像map,filter難理解
imgs = [f.upper() for f in glob.iglob('*.gif') if os.stat(f).st_size > 2000]
imgs = []
for f in glob.iglob('*.gif'):
    if os.stat(f).st_size > 2000:
        imgs.append(f.upper())
imgs = map(lambda x: x.upper(),
           filter(lambda f: os.stat(f).st_size > 2000, glob.iglob('*.gif'))

使用內建函數all(條件必須全都符合), any (條件只要一個符合), sum取代自己寫
要記得不要去蓋掉他
def is_valid(file_names):
    return all('py' in name for name in file_names)
def is_dangerous(sql):
    actions = ('update', 'delete', 'replace')
    return any(sql.startswith(action) for action in actions)
prices = [100, 300, 50, 600]
total = sum(prices)

defaultdict 若key不存在會回傳[] , 不會有exception
import collections

the_dict = collections.defaultdict(list)
for author, book in books:
    the_dict[author].append(book)

code style
TypeExample
Modulemodule_name.py
Packagepackage_name
Classclass ClassName(object):
Exceptionclass ExceptionName(Exception):
Functiondef function_name():
Methoddef method_name(self):
ConstantGLOBAL_CONSTANT_NAME = 1
Variablevar_name = 1

import 順序

{{ Python 內建的 modules }}
\n
{{ 第三方 modules }}
\n
{{ 本地 modules }}

不要使用隱性import
from animals.dogs import base
import base

Ref:

沒有留言:

張貼留言