iT邦幫忙

1

超實用的python小技巧

常常要用python做一件看起來很簡單的事,
卻不小心寫的很複雜?
本文嘗試收集開發python程式常用的小技巧,
方便查詢使用

使用須知:
為了將程式寫的精簡,
有時語法不是那麼易懂,
所以建議可以將功能包裝成函數增加可讀性

目錄:
這邊將文章收錄的功能整理出來,
可在此瀏覽是否有你想找的

<字串類>

  • 去除字串內不要的字元
  • 只保留英文字元,忽略大小寫、標號符號、空格
  • 判斷一個字串內的漢字(含簡體)
  • 字串全形半形字元互轉

<容器類>(list, set, dict, ...)

  • 查詢一個元素是否在list中
  • 去除列表中重複的元素,需保持列表元素原來的順序
  • 合併多個字典,若key值相同新的會覆蓋舊的
  • 給定一個整數陣列,回傳每個位置元素的排名(最小為1)
  • 將嵌套list拉平成一維
  • 將一個列表k個元素分成一組,變成兩維列表
  • 將兩個list組合成dict
  • dict的key-value互換

<實用內建函數類>

  • 計算每個元素出現次數
  • 將陣列中同性質的元素group在一起
  • 檢查一個容器是否為另一個的子集合
  • 二維矩陣之轉置(行列互換)、旋轉、翻轉操作

想看到更多python技巧的文章?歡迎點like追蹤
有更多值得收藏的技巧?歡迎留言分享
返回主頁: 系列篇章統整: 小馬的程式語法學習筆記(python為主)

字串類

去除字串內不要的字元

範例input: 
s = "apple pie pineapple pen"
delChars = 'aeiou' # 要刪除的字元
範例output: 'ppl p pnppl pn'

程式:

def removeChar(s, delChars):
    for c in delChars:
        s = s.replace(c,'')
    return s

s = "apple pie pineapple pen"
s = removeChar(s,'aeiou')
print(s)

只保留英文字元,忽略大小寫、標號符號、空格

範例input: 
s = "A man, a plan, a canal: Panama123987"
範例output: 'amanaplanacanalpanama'

程式:

def strAlpha(s):
    return ''.join([c.lower() for c in s if c.isalpha()])
    
s =  "A man, a plan, a canal: Panama"
s = strAlpha(s)
print(s)

判斷一個字串內的漢字(含簡體)

說明:
這邊示範統計一個字串由多少「漢字」、「英文」及「其它符號(含數字、空格、…)」組成
網路查詢資料說「漢字」(應該含簡體字)的範圍介於utf-8碼的[u'\u4e00', u'\u9fa5']之間,
注意isalpha()函數很特別,碰到漢字亦返回true,故需要先判斷漢字

def strComposition(string):    
    result = [0, 0, 0]   
    for char in string:        
        if u'\u4e00' <= char <= u'\u9fa5':    
            result[0] += 1        
        elif char.isalpha():
            result[1] += 1
        else:            
            result[2] += 1    
    return result 

result = strComposition("汉字test @*%")
print(result)  # [2, 4, 4]

字串全形半形字元互轉

說明:
全形字元: abcdefghijklmnopqrstuvwxyz123456789
半形字元: abcdefghijklmonpqrstuvwxyz123456789

爬文查到說
全形字元的unicode編碼從65281~65374 (十六進位制 0xFF01 ~ 0xFF5E),
對應到半形字元unicode編碼從33~126 (十六進位制 0x21~ 0x7E)

空格比較特殊,全形為 12288(0x3000),半形為 32(0x20)

因此,除了空格需特別處理外,
可以得到轉換公式為「半形+65248=全形」

def dict_key_val_reverse(D):
    # 將字典的鍵-值互換,例: {'a': 1, 'b': 2}-> {1: 'a', 2: 'b'}
    return dict(zip(D.values(), D.keys()))
    
def strConvert(s, mode='Q2B'):
    """
    參數說明: 
    s: input string
    mode: 
        'Q2B'-> 字串全形轉半形
        'B2Q'-> 字串半形轉全形
    """
    assert mode in {'Q2B', 'B2Q'}, "請輸入合法的模式(e.g. Q2B, B2Q)"
    rstring = ""
    convertDict = dict(zip(range(33,127),range(65281, 65375)))
    convertDict.update({32:12288})
    if mode=='Q2B':
        convertDict = dict_key_val_reverse(convertDict)
    for uchar in s:
        u_code = ord(uchar)
        if u_code in convertDict:
            u_code = convertDict[u_code]
        rstring += chr(u_code)
    return rstring

if __name__ == '__main__':
    s = "你好pythonabdalduizxcvb nm"
    print(strConvert(s, mode='B2Q'))

容器類(list, set, dict, ...)

查詢一個元素是否在list中

範例input: 
List = [30,23,'a',6,97]
target = 6
範例output: True

說明: 程式很簡單,但是你若一開始學其它程式語言而轉換到python的話,
邏輯很容易寫成用for迴圈檢查每個元素是否等於目標值,
其實python有in運算子,可讀性高又簡潔

程式:

def BAD_find_in(List, target):
    for e in List:
        if e==target:
            return True
    return False

def find_in(List, target):
    return target in List
    
List = [30,23,'a',6,97]
target = 6
print(find_in(List, target)) # True

去除列表中重複的元素,需保持列表元素原來的順序

範例input: ["a", "b", "a", "c", "c"]
範例output: ["a", "b", "c"]

程式:

def removeDuplicate(x):
    """
    自python3.6版以後,
    亦可寫 return list(dict.fromkeys(x))
    因為python3.6版以後的字典是有序的(3.6版前的字典無序就會錯)
    """
    return sorted(set(seq), key = seq.index)

mylist = removeDuplicate(["a", "b", "a", "c", "c"])
print(mylist) #["a", "b", "c"]

合併多個字典,若key值相同新的會覆蓋舊的

範例input: 
x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}
z = {'e': 10}
範例output: {'a': 1, 'b': 3, 'c': 4, 'e': 10}

程式:

def merge_dicts(*dict_args):
    result = {}
    for dictionary in dict_args:
        result.update(dictionary)
    return result

x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}
z = {'e': 10}
print(merge_dicts(x,y,z))

給定一個整數陣列,回傳每個位置元素的排名(最小為1)

範例input: [1,1,2,5,-6,0,2]
範例output: [3, 3, 4, 5, 1, 2, 4]

說明: 原陣列由小到大排序為[-6, 0, 1, 2, 5],
第一個元素1第三小,
第二個元素1第三小,
第三個元素2第四小,

程式:

def get_rank(X):
    x_rank = dict((x, i+1) for i, x in enumerate(sorted(set(X))))
    return [x_rank[x] for x in X]

print(get_rank([1,1,2,5,-6,0,2]))

將嵌套list拉平成一維

範例input: [[1,2,3],[4,5],[6,7]]
範例output: [1, 2, 3, 4, 5, 6, 7]

程式:

#最簡單的攤平列表的方法,適用普通的二維列表
def singleFlat(arr):
    return sum(arr, [])

# 將一個多重嵌套的list轉為一維list,一行解 (是容器就拆,否則成list)
def flat(arr):
    return [x for sub in arr for x in flat(sub)] if isinstance(arr, (list,tuple,set,dict)) else [arr]

L=[{1,2},{1:'A'},(1,"H",3),[5,[2,1],(3,4)],50,(1,['54',0])]
print(flat(L))

將一個列表k個元素分成一組,變成兩維列表

範例input: 
List = [1,2,3,4,5,6,7,8,9,10,11,20,66]
step = 3
範例output: 
(正常分組)[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 20], [66]]
(從尾端分組)[[1], [2, 3, 4], [5, 6, 7], [8, 9, 10], [11, 20, 66]]

程式:

def sepList(L,step):
    return [L[i:i+step] for i in range(0,len(L),step)]

#從後面分組
def backSepList(L,step):
    return [L[max(0,i-step+1):i+1] for i in range(len(L)-1,-1,-step)][::-1]
    # <另解> return list(map(lambda x: list(reversed(x)),sepList(L[::-1],step)))[::-1]

a = [1,2,3,4,5,6,7,8,9,10,11,20,66]
step = 3
print(sepList(a,step)) #[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 20], [66]]
print(backSepList(a,step)) #[[1], [2, 3, 4], [5, 6, 7], [8, 9, 10], [11, 20, 66]]

將兩個list組合成dict

範例input: ['A', 'B', 'C'], [1, 2, 3]
範例output: {'A': 1, 'B': 2, 'C': 3}

程式:

def mergeListToDict(list1, list2):
    return dict(zip(list1, list2))

A = ['A', 'B', 'C']
B = [1, 2, 3]
print(mergeListToDict(A,B))

dict的key-value互換(需要是一對一的關係)

範例input: {'a': 1, 'b': 2, 'c': 3, 'd': 4}
範例output: {1: 'a', 2: 'b', 3: 'c', 4: 'd'}

程式:

def dict_key_val_reverse(D):
    # 將字典的鍵-值互換,例: {'a': 1, 'b': 2}-> {1: 'a', 2: 'b'}
    return dict(zip(D.values(), D.keys())) #另解: return {v: k for k, v in m.items()}

實用內建函數類

計算每個元素出現次數

範例input: myList = ['A','B','A','A','B','B','A','C','C','C']
範例output: Counter({'A': 4, 'B': 3, 'C': 3})

程式:

from collections import Counter
myList = ['A','B','A','A','B','B','A','C','C','C']
count = Counter(myList)
print(count) # Counter({'A': 4, 'B': 3, 'C': 3})
print(count['A']) # 4 
print(count['D']) #對於不存在的值默認為0
print(count.most_common(2)) #出現次數最高的兩個元素: [('A', 4), ('B', 3)]

將陣列中同性質的元素group在一起

函數功能:
利用模組itertools的groupby將相鄰的相同性質的元素排序,
input需要事先用key排序過。
以本例來說,
定義重組字(anagram)為同性質

from itertools import groupby
def grouping(arr, KEY_FUNC):
    A = sorted(arr, key = KEY_FUNC)
    return [(k, list(g)) for k, g in groupby(A,key=KEY_FUNC)]

A = ["eat", "tea", "tan", "ate", "nat", "bat"]
group = grouping(A, lambda x: ''.join(sorted(x)))
print(group) # [('abt', ['bat']), ('aet', ['eat', 'tea', 'ate']), ('ant', ['tan', 'nat'])]

檢查一個容器是否為另一個的子集合(元素可能重複)

程式功能: 檢查容器A的是否包含容器B(元素可重複)
思路:
利用Counter減法的特性,不會使元素個數減至負數。
計算元素個數,用B去減掉A的所有元素,如果A不包含B,
則一定會有一些東西剩下來。

from collections import Counter
def doesAcontainB(A, B):
    return len((Counter(B)-Counter(A)).keys())==0

print(doesAcontainB("ABCD","ACD")) # True
print(doesAcontainB([1,2,2],[1,2,3])) # False

二維矩陣之轉置(行列互換)、旋轉、翻轉操作

程式功能:
對二維矩陣做轉置、旋轉、翻轉的基本操作,
以二維矩陣表示影像時,同時也是影像的基本操作。
相關:
(LeetCode 48題) Rotate Image
(LeetCode 832題) Flipping an Image

目前包裝成一個class

class Image():
    def __init__(self, arr):
        self.arr = arr
    
    def show(self):
        for a in self.arr:
            print(a)
        print()
            
    #垂直翻轉
    def flipVertical(self):
        self.arr = self.arr[::-1]
    
    #水平翻轉
    def flipHorizontal(self):
        self.arr = [a[::-1] for a in self.arr]
    
    #轉置,即行列互換(沿左上- 右下對角線翻轉)
    def T(self):
        self.arr = list(map(list,(zip(*self.arr))))
        
    def rotateRight(self):
        self.flipVertical()
        self.T()
    
    def rotateLeft(self):
        self.flipHorizontal()
        self.T()
        
# 以下程式供測試
if __name__ == '__main__': 
    A = [list(range(i,i+4)) for i in range(1,13,4)]
    arr= Image(A)
    arr.show()
    arr.rotateLeft()
    arr.show()
    arr.rotateRight()
    arr.show()
    arr.flipVertical()
    arr.show()
    arr.flipHorizontal()
    arr.show()
    arr.T()
    arr.show()

尚未有邦友留言

立即登入留言