首頁 > 軟體

python使用數位與字串方法技巧

2022-03-18 13:01:56

1. 少使用數位字面量

下面的程式碼使用數位來作為判斷條件的語句,如果你從別人手裡接手過這部分程式碼,很難第一時間理解它的意義。

def mark_trip_as_featured(trip):
    """將某個旅程新增到推薦欄目
    """
    if trip.source == 11:
        do_some_thing(trip)
    elif trip.source == 12:
        do_some_other_thing(trip)
    ... ...
    return

我們可以使用列舉的方式,對這些數位部分做一些說明。

from enum import IntEnum

class TripSource(IntEnum):
    FROM_WEBSITE = 11
    FROM_IOS_CLIENT = 12


def mark_trip_as_featured(trip):
    if trip.source == TripSource.FROM_WEBSITE:
        do_some_thing(trip)
    elif trip.source == TripSource.FROM_IOS_CLIENT:
        do_some_other_thing(trip)
    ... ...
    return

將重複出現的數位定義成列舉型別,不僅改善了程式碼的可讀性,還降低了程式碼出現 Bug 的機率。

當然不是所有的數位都需要用到列舉說明,像常見數位下標 0 和 -1
就不需要。

2. 裸字串處理的問題

“ 裸字串處理 ” 這裡指只使用基本的加減乘除和迴圈、配合內建函數/方法來操作字串,獲得我們需要的結果。

def fetch_users(conn, min_level=None, gender=None, has_membership=False, sort_field="created"):
    """獲取使用者列表
   
    :param int min_level: 要求的最低使用者級別,預設為所有級別
    :param int gender: 篩選使用者性別,預設為所有性別
    :param int has_membership: 篩選所有會員/非會員使用者,預設非會員
    :param str sort_field: 排序欄位,預設為按 created "使用者建立日期"
    :returns: 列表:[(User ID, User Name), ...]
    """
    # 一種古老的 SQL 拼接技巧,使用 "WHERE 1=1" 來簡化字串拼接操作
    # 區分查詢 params 來避免 SQL 注入問題
    statement = "SELECT id, name FROM users WHERE 1=1"
    params = []
    if min_level is not None:
        statement += " AND level >= ?"
        params.append(min_level)
    if gender is not None:
        statement += " AND gender >= ?"
        params.append(gender)
    if has_membership:
        statement += " AND has_membership == true"
    else:
        statement += " AND has_membership == false"
    
    statement += " ORDER BY ?"
    params.append(sort_field)
    return list(conn.execute(statement, params))

這樣做雖然看起來簡單,符合直覺,但是隨著函數邏輯變得複雜,這段程式碼會變得容易出錯。

更好的選擇是利用一些開源的物件化模組來操作他們。
這裡使用了 SQLAlchemy

def fetch_users_v2(conn, min_level=None, gender=None, has_membership=False, sort_field="created"):
    """獲取使用者列表
    """
    query = select([users.c.id, users.c.name])
    if min_level is not None:
        query = query.where(users.c.level >= min_level)
    if gender is not None:
        query = query.where(users.c.gender == gender)
    query = query.where(users.c.has_membership == has_membership).order_by(users.c[sort_field])
    return list(conn.execute(query))

其它的替換思路:

Q: 目標/源字串是結構化的,遵循某種格式嗎?

其它的開源的物件化模組。

  • SQL:SQLAlchemy
  • XML:lxml
  • JSON、YAML …

嘗試使用模板引擎而不是複雜字串處理邏輯來達到目的。

  • Jinja2
  • Mako
  • Mustache

3. 展開復雜的計算字面量表示式

def f1(delta_seconds):
    # 如果時間已經過去了超過 11 天,不做任何事
    if delta_seconds > 950400:
        return 
    ...

“為什麼我們不直接把程式碼寫成 if delta_seconds < 11 * 24 * 3600: 呢?”

“效能”,答案一定會是“效能”。 Python 是一門直譯語言,所以預先計算出 950400 正是因為我們不想讓每次對函數 f1 的呼叫都帶上這部分的計算開銷。

不過事實是:即使我們把程式碼改成 if delta_seconds < 11 * 24 * 3600:,函數也不會多出任何額外的開銷。

當我們的程式碼中需要出現複雜計算的字面量時,請保留整個算式吧。它對效能沒有任何影響,而且會增加程式碼的可讀性。

def f1(delta_seconds):
    if delta_seconds < 11 * 24 * 3600:
        return

4.實用技巧

4.1布林值也是數位

True False 可以當成 1 和 0 使用

>>> True + 1
2
>>> 1 / False
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

計數簡化操作。

>>> l = [1, 2, 4, 5, 7]
>>> sum(i % 2 == 0 for i in l)
2

如果將某個布林值表示式作為列表的下標使用,可以實現類似三元表示式的目的:

# 類似的三元表示式:"Javascript" if 2 > 1 else "Python"
>>> ["Python", "Javascript"][2 > 1]
'Javascript'

4.2改善字串的可讀性。

對於字串我們常使用 和 + 來講字串拆分成好幾段。

還有一種簡單的方法是用 ()。
用 ()括起來就可以隨意拆行了。

s = (
    "There is something really bad happened during the process. "
    "Please contact your administrator."
)

對於多級縮排字串:

可以呼叫其他的標準庫來達到簡化效果。

from textwrap import dedent

def main():
    if user.is_active:
        # dedent 將會縮排掉整段文字最左邊的空字串
        message = dedent("""
            Welcome, today's movie list:
            - Jaw (1975)
            - The Shining (1980)
            - Saw (2004)""")

大數位也可以變得更加可閱讀:

在數位之間加入下劃線。

>>> 10_000_000.0  # 以「千」為單位劃分數位
10000000.0
>>> 0xCAFE_F00D  # 16進位制數位同樣有效,4個一組更易讀
3405705229
>>> 0b_0011_1111_0100_1110  # 二進位制也有效
16206
>>> int('0b_1111_0000', 2)  # 處理字串的時候也會正確處理下劃線
240

4.3以 r 開頭的內建字串函數。

例如 : .split() 和 .rsplit() 的區別是,一個從左到右分割字串,另一個是從右到左處理字串。

合理使用一些現成 string 操作函數可以讓工作事半功倍。

4.4 float (" inf ")

float ( " inf " ) float ( " -inf ") ,對應著無窮大和無窮小。

float( " -inf ") < 任意數值 < float( " inf ")

一些可以用上的場合。

# A. 根據年齡升序排序,沒有提供年齡放在最後邊
>>> users = {"tom": 19, "jenny": 13, "jack": None, "andrew": 43}
>>> sorted(users.keys(), key=lambda user: users.get(user) or float('inf'))
['jenny', 'tom', 'andrew', 'jack']

# B. 作為迴圈初始值,簡化第一次判斷邏輯
>>> max_num = float('-inf')
>>> # 找到列表中最大的數位
>>> for i in [23, 71, 3, 21, 8]:
...:    if i > max_num:
...:         max_num = i
...:
>>> max_num
71

5.常見誤區

5.1“value += 1” 並非執行緒安全

如下:這個操作並不是執行緒安全的。

這個簡單的累加語句,會被編譯成包括取值和儲存在內的好幾個不同步驟。

而在多執行緒環境下,任意一個其他執行緒都有可能在其中某個步驟切入進來,阻礙你獲得正確的結果。

def incr(value):
    value += 1


# 使用 dis 模組檢視位元組碼
import dis

dis.dis(incr)
      0 LOAD_FAST                0 (value)
      2 LOAD_CONST               1 (1)
      4 INPLACE_ADD
      6 STORE_FAST               0 (value)
      8 LOAD_CONST               0 (None)
     10 RETURN_VALUE

常用 dis 模組去驗證自己的操作,有時候,結果和我們預想的並不一樣。

5.2字串拼接並不慢

Python 的字串拼接(+=)在 2.2 以及之前的版本確實很慢。
但之後的版本做了更新,效率已經大大提升,所有字串的拼接還是可以使用的。

到此這篇關於python使用數位與字串方法技巧的文章就介紹到這了,更多相關python數位與字串內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com