程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

那些用起來很爽,但用不好可能會被人打的Python騷操作

編輯:Python

快速將兩個分別存放有key和value的列表合並成一個字典

>>> a = ["key1", "key2", "key3"]
>>> b = ["value1", "value2", "value3"]
>>> dict(zip(a, b))
{'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}

這個操作的用途對於爬蟲工程師而言挺常見的,比如說:

你需要采集一些鍵值對類型的信息,然後你可能會碰到一些平台的接口返回這些信息時是直接返回的一個只有value的列表,並且key都是寫死在代碼或請求參數裡的,這時候你就可以通過這個操作來將它們快速合並成一個字典,這樣就能比較方便地取值了,取值部分的可讀性也會好很多。

還有一種情況就是,你可能會碰到一些網站上的這種鍵值對信息在HTML中key和value的元素是平級關系,並且key和value也沒有能夠區分的標識,這些情況我們只能通過把整個鍵值對部分的元素都提取出來轉換成列表,然後再通過按列表下標間隔切片的方式分別取出key和value的列表,然後再將它們合並成一個字典。比如這樣:

>>> result = ["key1", "value1", "key2", "value2", "key3", "value3"]
>>> result[0::2]
['key1', 'key2', 'key3']
>>> result[1::2]
['value1', 'value2', 'value3']
>>> dict(zip(result[0::2], result[1::2]))
{'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}

需要注意的是,這個操作在碰到key和value數量不一致的時候,會自動忽略掉多的那部分列表尾部的值,你需要確保key和value是對得上的才行。

簡單來說就是,如果你的key列表比value列表多一個值,那麼最終出來的字典就會缺失掉key列表中的最後那一個多出來的值。

快速按頭、尾、中間部分切割元素,並將它們分別賦值給三個變量

>>> a = "123456789" # 也可以是list之類的
>>> a1, *a2, a3 = a
>>> a1
'1'
>>> a2
['2', '3', '4', '5', '6', '7', '8']
>>> a3
'9'

這個操作的用途,對於爬蟲工程師或者是一些會接觸到私有協議的後端工程師應該都挺常見的,比如說:

你可能會碰到一些基於TCP或UDP搞的私有協議,然後它們可能會定義一個由內容類型頭、通信內容、校驗碼之類的東西組成的結構,每次通信時都會給你返回這麼一個東西,然後你需要將它們切開並賦值給不同的變量,這時候你就可以通過這個操作來快速實現這個效果了。

當然,這個操作也不能亂用,你最好是能夠確保它的內容不會變化,像私有協議這種情況,你可以判斷一下協議版本號之類的,以確保它的內容一定是這個結構。

快速解壓內含嵌套列表的列表,並同時將嵌套列表內的值也賦值給不同的變量

>>> result = [1, 2, [3, 4], 5]
>>> [a, b, [c, d], e] = result
>>> a
1
>>> b
2
>>> c
3
>>> d
4
>>> e
5

這個操作也是偶爾會在處理一些私有協議時用到,你可以通過這個操作快速地將列表中的值賦值給不同的變量再進行處理,非常方便。

如果你只需要嵌套列表中的某個開頭的值,其余的不需要怎麼辦呢?可以像這樣:

>>> result = [1, 2, [3, 4, 5]]
>>> [a, b, [c, *_]] = result
>>> a
1
>>> b
2
>>> c
3

總之就是和前面的操作一樣,使用星號來處理後面的多個值,並將它們賦值給下劃線這種臨時變量拋棄。

注:下劃線變量的用途和含義可以自行通過搜索引擎搜索一下,網上有很多文章提到,這裡就不再贅述了。

遍歷嵌套且長短不一的列表時,按頭、尾切割,並將它們分別賦值給兩個變量

>>> result = [["items", "item1", "item2", "item3"], ["status", 1]]
>>> for key, *values in result:
... print(key)
... print(values)
...
>>> items
['item1', 'item2', 'item3']
>>> status
[1]

這個操作偶爾會在處理一些私有協議或者奇葩平台的接口時用到,就是對方給你返回的內容可能是這麼一個嵌套列表,子列表的第一個值是key、後面的部分是value,然後有些奇葩點的平台可能連子列表的順序都不相同。

這種時候如果用這個操作去取key和value的話,就會方便很多,你不需要管它的value到底有多少個,也不需要按下標0去取嵌套列表中的key,你只需要直接這麼for一下然後處理key和value就完事了。

>>> {key:values for key, *values in result}
{'items': ['item1', 'item2', 'item3'], 'status': [1]}

甚至你還可以直接把這個代碼寫成一行,直接將它們轉成字典再做後續的處理。

快速解壓一個字典,並將它裡面的key和value們分別賦值給不同的變量

>>> a = {"key1": "value1", "key2": "value2", "key3": "value3"}
>>> (key1, value1), (key2, value2), (key3, value3) = a.items()
>>> key1
'key1'
>>> value1
'value1'
>>> key2
'key2'
>>> value2
'value2'

這個操作的用途也是對於爬蟲工程師和後端工程師而言會比較常見,比如說:

你需要提取一個接口返回的帶有狀態碼、狀態信息和data的內容,並且你需要判斷一下狀態碼是不是代表請求成功的那個,這時候如果是一個一個通過key去取值、賦值的話就會很麻煩,但如果你用這個操作的話,就可以快速解決。

可能光是這麼說不太直觀,看一段樣例代碼吧:

>>> result = {"code": 200, "data": {"balabala": 111}, "msg": None}
>>> (_, code), (_, data), (_, msg) = result.items()
>>> code
200
>>> data
{'balabala': 111}
>>> msg
>>>

當然這個操作也不能亂用,在使用這個操作時,你需要確保字典中key的順序嚴格一致,否則就可能會出現提取到錯誤內容的情況,所以提取之前為了防止順序錯亂,可以先按key做個排序,以確保順序是嚴格一致的。

然後字典中的內容是否會有變化也是需要考慮的,如果你寫的代碼需要非常嚴謹,那就還是老老實實地按key一個一個取吧,畢竟如果這裡面多了一個key在中間位置,取到的東西就會完全不同。

動態創建函數

有時候你可能會碰到類似這樣的特殊情況:你有一些不同的值需要通過處理方式相同的函數進行處理,但由於條件限制你還不能將這個函數歸納為一個並通過傳參的方式進行處理,只能是寫出多個不同名稱的函數來分別處理。

或者你可能就是單純有個需求需要動態創建一個臨時函數來使用。

這時候如果使用這個操作的話,就可以很輕松地解決這個問題,只需要像這樣就可以動態地創建出一個函數了:

>>> from types import FunctionType
>>>
>>> func = FunctionType(compile(
... ("def func():\n"
... " print(1)\n"
... " return 2"),
... "<string>",
... "exec"
... ).co_consts[0], globals())
>>> print(func())
1
2

注:這裡面用括號包起來的字符串會自動進行拼接,屬於Python中多行字符串的寫法之一,優點是不會因為像三個引號那樣會因為縮進導致字符串內容受影響。如果想了解更多可以自行查看Python官方文檔,裡面的字符串部分有講這個小技巧。

我們還可以對裡面那個函數代碼字符串進行動態生成(比如用format),以實現對函數名和內容的修改,甚至我們還可以通過使用像Jinja這種模板渲染庫來實現更方便的函數代碼生成。

由於Django Admin的那個action函數的參數是固定的,並且如果需要傳參給action函數的話就需要通過中間頁來實現,而我既不想弄中間頁、又有好幾個不同的參數需要分別處理,於是就直接采用動態創建函數的方式來解決了。

動態導入

有時候你可能會有一些擴展代碼之類的需要在運行時動態地被導入,這時候你就可以用上這個操作了,比如說我們需要導入operator這個庫:

>>> import importlib
>>>
>>> module = importlib.import_module("operator")

然後這個module變量就是被導入後的模塊名稱了,我們可以直接和正常導入時一樣使用,比如我們要調用它的add函數:

>>> module.add(1, 1) # 加法運算符
2

動態調用

有時候你在動態導入之後,還會需要進行動態調用,這時候你就可以這樣:

>>> import importlib
>>>
>>> module = importlib.import_module("operator")
>>> func = getattr(module, "add")
>>> func(1, 1)
2

當然,在使用動態創建、動態導入、動態調用這種比較Hack的操作的時候,一定要注意安全問題,就是如果你在使用時有部分參數是需要用戶輸入的的話,就一定要對輸入內容進行檢查,以免被利用來直接執行危險代碼。

比如說你提供了一個動態創建函數的功能,如果沒有檢查內容的話,可能有些比較壞的人直接就通過os庫之類的來調用命令行把你的機子給黑了,非常危險。

那麼以上就是本次分享的全部內容了,關於這些騷操作其實還有很多,大家感興趣的話平時可以多留意一下別人寫的代碼以及各種論壇之類的地方,有時候會有些意外收獲。

 


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved