揭秘,一些你可能不知道的 Python 小技巧
在本文中,我們來(lái)看一看日常工作中經(jīng)常使用的一些 Python 小技巧。
01.集合
開(kāi)發(fā)人員常常忘記 Python 也有集合數(shù)據(jù)類(lèi)型,大家都喜歡使用列表處理一切。
集合(set)是什么?簡(jiǎn)單來(lái)說(shuō)就是:集合是一組無(wú)序事物的匯集,不包含重復(fù)元素。
如果你熟練掌握集合及其邏輯,那么很多問(wèn)題都可以迎刃而解。舉個(gè)例子,如何獲取一個(gè)單詞中出現(xiàn)的字母?
- myword = "NanananaBatman"
 - set(myword)
 - {'N', 'm', 'n', 'B', 'a', 't'}
 
就這么簡(jiǎn)單,問(wèn)題解決了,這個(gè)例子就來(lái)自 Python 的官方文檔,大可不必過(guò)于驚訝。再舉一個(gè)例子,如何獲取一個(gè)列表的各個(gè)元素,且不重復(fù)?
- # first you can easily change set to list and other way around
 - mylist = ["a", "b", "c","c"]
 - # let's make a set out of it
 - myset = set(mylist)
 - # myset will be:
 - {'a', 'b', 'c'}
 - # and, it's already iterable so you can do:
 - for element in myset:
 - print(element)
 - # but you can also convert it to list again:
 - mynewlist = list(myset)
 - # and mynewlist will be:
 - ['a', 'b', 'c']
 
我們可以看到,“c”元素不再重復(fù)出現(xiàn)了。只有一個(gè)地方你需要注意,mylist 與 mynewlist 之間的元素順序可能會(huì)有所不同:
- mylist = ["c", "c", "a","b"]
 - mynewlist = list(set(mylist))
 - # mynewlist is:
 - ['a', 'b', 'c']
 
可以看出,兩個(gè)列表的元素順序不同。
下面,我們來(lái)進(jìn)一步深入。
假設(shè)某些實(shí)體之間有一對(duì)多的關(guān)系,舉個(gè)更加具體的例子:用戶與權(quán)限。通常,一個(gè)用戶可以擁有多個(gè)權(quán)限。現(xiàn)在假設(shè)某人想要修改多個(gè)權(quán)限,即同時(shí)添加和刪除某些權(quán)限,應(yīng)當(dāng)如何解決這個(gè)問(wèn)題?
- # this is the set of permissions before change;
 - original_permission_set = {"is_admin","can_post_entry", "can_edit_entry", "can_view_settings"}
 - # this is new set of permissions;
 - new_permission_set = {"can_edit_settings","is_member", "can_view_entry", "can_edit_entry"}
 - # now permissions to add will be:
 - new_permission_set.difference(original_permission_set)
 - # which will result:
 - {'can_edit_settings', 'can_view_entry', 'is_member'}
 - # As you can see can_edit_entry is in both sets; so we do notneed
 - # to worry about handling it
 - # now permissions to remove will be:
 - original_permission_set.difference(new_permission_set)
 - # which will result:
 - {'is_admin', 'can_view_settings', 'can_post_entry'}
 - # and basically it's also true; we switched admin to member, andadd
 - # more permission on settings; and removed the post_entrypermission
 
總的來(lái)說(shuō),不要害怕使用集合,它們能幫助你解決很多問(wèn)題,更多詳情,請(qǐng)參考 Python 官方文檔。
02.日歷
當(dāng)開(kāi)發(fā)與日期和時(shí)間有關(guān)的功能時(shí),有些信息可能非常重要,比如某一年的這個(gè)月有多少天。這個(gè)問(wèn)題看似簡(jiǎn)單,但是我相信日期和時(shí)間是一個(gè)非常有難度的話題,而且我覺(jué)得日歷的實(shí)現(xiàn)問(wèn)題非常多,簡(jiǎn)直就是噩夢(mèng),因?yàn)槟阈枰紤]大量的極端情況。
那么,究竟如何才能找出某個(gè)月有多少天呢?
- import calendar
 - calendar.monthrange(2020, 12)
 - # will result:
 - (1, 31)
 - # BUT! you need to be careful here, why? Let's read thedocumentation:
 - help(calendar.monthrange)
 - # Help on function monthrange in module calendar:
 - # monthrange(year, month)
 - # Return weekday (0-6~ Mon-Sun) and number of days (28-31) for
 - # year, month.
 - # As you can see the first value returned in tuple is a weekday,
 - # not the number of the first day for a given month; let's try
 - # to get the same for 2021
 - calendar.monthrange(2021, 12)
 - (2, 31)
 - # So this basically means that the first day of December 2021 isWed
 - # and the last day of December 2021 is 31 (which is obvious,cause
 - # December always has 31 days)
 - # let's play with February
 - calendar.monthrange(2021, 2)
 - (0, 28)
 - calendar.monthrange(2022, 2)
 - (1, 28)
 - calendar.monthrange(2023, 2)
 - (2, 28)
 - calendar.monthrange(2024, 2)
 - (3, 29)
 - calendar.monthrange(2025, 2)
 - (5, 28)
 - # as you can see it handled nicely the leap year;
 
某個(gè)月的第一天當(dāng)然非常簡(jiǎn)單,就是 1 號(hào)。但是,“某個(gè)月的第一天是周X”,如何使用這條信息呢?你可以很容易地查到某一天是周幾:
- calendar.monthrange(2024, 2)
 - (3, 29)
 - # means that February 2024 starts on Thursday
 - # let's define simple helper:
 - weekdays = ["Monday", "Tuesday","Wednesday", "Thursday", "Friday","Saturday", "Sunday"]
 - # now we can do something like:
 - weekdays[3]
 - # will result in:
 - 'Thursday'
 - # now simple math to tell what day is 15th of February 2020:
 - offset = 3 # it's thefirst value from monthrange
 - for day in range(1, 29):
 - print(day,weekdays[(day + offset - 1) % 7])
 - 1 Thursday
 - 2 Friday
 - 3 Saturday
 - 4 Sunday
 - ...
 - 18 Sunday
 - 19 Monday
 - 20 Tuesday
 - 21 Wednesday
 - 22 Thursday
 - 23 Friday
 - 24 Saturday
 - ...
 - 28 Wednesday
 - 29 Thursday
 - # which basically makes sense;
 
也許這段代碼不適合直接用于生產(chǎn),因?yàn)槟憧梢允褂?datetime 更容易地查找星期:
- from datetime import datetime
 - mydate = datetime(2024, 2, 15)
 - datetime.weekday(mydate)
 - # will result:
 - 3
 - # or:
 - datetime.strftime(mydate, "%A")
 - 'Thursday'
 
總的來(lái)說(shuō),日歷模塊有很多有意思的地方,值得慢慢學(xué)習(xí):
- # checking if year is leap:
 - calendar.isleap(2021) #False
 - calendar.isleap(2024) #True
 - # or checking how many days will be leap days for given yearspan:
 - calendar.leapdays(2021, 2026) # 1
 - calendar.leapdays(2020, 2026) # 2
 - # read the help here, as range is: [y1, y2), meaning that second
 - # year is not included;
 - calendar.leapdays(2020, 2024) # 1
 
03.枚舉有第二個(gè)參數(shù)
是的,枚舉有第二個(gè)參數(shù),可能很多有經(jīng)驗(yàn)的開(kāi)發(fā)人員都不知道。下面我們來(lái)看一個(gè)例子:
- mylist = ['a', 'b', 'd', 'c', 'g', 'e']
 - for i, item in enumerate(mylist):
 - print(i, item)
 - # Will give:
 - 0 a
 - 1 b
 - 2 d
 - 3 c
 - 4 g
 - 5 e
 - # but, you can add a start for enumeration:
 - for i, item in enumerate(mylist, 16):
 - print(i, item)
 - # and now you will get:
 - 16 a
 - 17 b
 - 18 d
 - 19 c
 - 20 g
 - 21 e
 
第二個(gè)參數(shù)可以指定枚舉開(kāi)始的地方,比如上述代碼中的 enumerate(mylist,16)。如果你需要處理偏移量,則可以考慮這個(gè)參數(shù)。
04.if-else 邏輯
你經(jīng)常需要根據(jù)不同的條件,處理不同的邏輯,經(jīng)驗(yàn)不足的開(kāi)發(fā)人員可能會(huì)編寫(xiě)出類(lèi)似下面的代碼:
- OPEN = 1
 - IN_PROGRESS = 2
 - CLOSED = 3
 - def handle_open_status():
 - print('Handling openstatus')
 - def handle_in_progress_status():
 - print('Handling inprogress status')
 - def handle_closed_status():
 - print('Handling closedstatus')
 - def handle_status_change(status):
 - if status == OPEN:
 - handle_open_status()
 - elif status ==IN_PROGRESS:
 - handle_in_progress_status()
 - elif status == CLOSED:
 - handle_closed_status()
 - handle_status_change(1) #Handling open status
 - handle_status_change(2) #Handling in progress status
 - handle_status_change(3) #Handling closed status
 
雖然這段代碼看上去也沒(méi)有那么糟,但是如果有 20 多個(gè)條件呢?
那么,究竟應(yīng)該怎樣處理呢?
- from enum import IntEnum
 - class StatusE(IntEnum):
 - OPEN = 1
 - IN_PROGRESS = 2
 - CLOSED = 3
 - def handle_open_status():
 - print('Handling openstatus')
 - def handle_in_progress_status():
 - print('Handling inprogress status')
 - def handle_closed_status():
 - print('Handling closedstatus')
 - handlers = {
 - StatusE.OPEN.value:handle_open_status,
 - StatusE.IN_PROGRESS.value: handle_in_progress_status,
 - StatusE.CLOSED.value:handle_closed_status
 - }
 - def handle_status_change(status):
 - if status not inhandlers:
 - raiseException(f'No handler found for status: {status}')
 - handler =handlers[status]
 - handler()
 - handle_status_change(StatusE.OPEN.value) # Handling open status
 - handle_status_change(StatusE.IN_PROGRESS.value) # Handling in progress status
 - handle_status_change(StatusE.CLOSED.value) # Handling closed status
 - handle_status_change(4) #Will raise the exception
 
在 Python 中這種模式很常見(jiàn),它可以讓代碼看起來(lái)更加整潔,尤其是當(dāng)方法非常龐大,而且需要處理大量條件時(shí)。
05.enum 模塊
enum 模塊提供了一系列處理枚舉的工具函數(shù),最有意思的是 Enum 和 IntEnum。我們來(lái)看個(gè)例子:
- from enum import Enum, IntEnum, Flag, IntFlag
 - class MyEnum(Enum):
 - FIRST ="first"
 - SECOND ="second"
 - THIRD ="third"
 - class MyIntEnum(IntEnum):
 - ONE = 1
 - TWO = 2
 - THREE = 3
 - # Now we can do things like:
 - MyEnum.FIRST #
 - # it has value and name attributes, which are handy:
 - MyEnum.FIRST.value #'first'
 - MyEnum.FIRST.name #'FIRST'
 - # additionally we can do things like:
 - MyEnum('first') #, get enum by value
 - MyEnum['FIRST'] #, get enum by name
 
使用 IntEnum 編寫(xiě)的代碼也差不多,但是有幾個(gè)不同之處:
- MyEnum.FIRST == "first" # False
 - # but
 - MyIntEnum.ONE == 1 # True
 - # to make first example to work:
 - MyEnum.FIRST.value == "first" # True
 
在中等規(guī)模的代碼庫(kù)中,enum 模塊在管理常量方面可以提供很大的幫助。
enum 的本地化可能有點(diǎn)棘手,但也可以實(shí)現(xiàn),我用django快速演示一下:
- from enum import Enum
 - from django.utils.translation import gettext_lazy as _
 - class MyEnum(Enum):
 - FIRST ="first"
 - SECOND ="second"
 - THIRD ="third"
 - @classmethod
 - def choices(cls):
 - return [
 - (cls.FIRST.value, _('first')),
 - (cls.SECOND.value, _('second')),
 - (cls.THIRD.value, _('third'))
 - ]
 - # And later in eg. model definiton:
 - some_field = models.CharField(max_length=10,choices=MyEnum.choices())
 
06.iPython
iPython 就是交互式 Python,它是一個(gè)交互式的命令行 shell,有點(diǎn)像 Python 解釋器。
首先,你需要安裝 iPython:
- pip install ipython
 
接下來(lái),你只需要在輸入命令的時(shí)候,將 Python 換成 ipython:
- # you should see something like this after you start:
 - Python 3.8.5 (default, Jul 28 2020, 12:59:40)
 - Type 'copyright', 'credits' or 'license' for more information
 - IPython 7.18.1 -- An enhanced Interactive Python. Type '?' forhelp.
 - In [1]:
 
ipython 支持很多系統(tǒng)命令,比如 ls 或 cat,tab 鍵可以顯示提示,而且你還可以使用上下鍵查找前面用過(guò)的命令。更多具體信息,請(qǐng)參見(jiàn)官方文檔。















 
 
 







 
 
 
 