揭秘,一些你可能不知道的 Python 小技巧

开发 后端
在本文中,我们来看一看日常工作中经常使用的一些 Python 小技巧。

[[432537]]

 在本文中,我们来看一看日常工作中经常使用的一些 Python 小技巧。

01.集合

开发人员常常忘记 Python 也有集合数据类型,大家都喜欢使用列表处理一切。

集合(set)是什么?简单来说就是:集合是一组无序事物的汇集,不包含重复元素。

如果你熟练掌握集合及其逻辑,那么很多问题都可以迎刃而解。举个例子,如何获取一个单词中出现的字母? 

  1. myword = "NanananaBatman"  
  2. set(myword)  
  3. {'N', 'm', 'n', 'B', 'a', 't'} 

就这么简单,问题解决了,这个例子就来自 Python 的官方文档,大可不必过于惊讶。再举一个例子,如何获取一个列表的各个元素,且不重复? 

  1. # first you can easily change set to list and other way around  
  2. mylist = ["a", "b", "c","c"]  
  3. # let's make a set out of it  
  4. myset = set(mylist)  
  5. # myset will be:  
  6. {'a', 'b', 'c'}  
  7. # and, it's already iterable so you can do:  
  8. for element in myset:  
  9.     print(element) 
  10. # but you can also convert it to list again:  
  11. mynewlist = list(myset)  
  12. # and mynewlist will be:  
  13. ['a', 'b', 'c'] 

我们可以看到,“c”元素不再重复出现了。只有一个地方你需要注意,mylist 与 mynewlist 之间的元素顺序可能会有所不同: 

  1. mylist = ["c", "c", "a","b"]  
  2. mynewlist = list(set(mylist))  
  3. # mynewlist is:  
  4. ['a', 'b', 'c'] 

可以看出,两个列表的元素顺序不同。

下面,我们来进一步深入。

假设某些实体之间有一对多的关系,举个更加具体的例子:用户与权限。通常,一个用户可以拥有多个权限。现在假设某人想要修改多个权限,即同时添加和删除某些权限,应当如何解决这个问题? 

  1. # this is the set of permissions before change;  
  2. original_permission_set = {"is_admin","can_post_entry", "can_edit_entry", "can_view_settings"}  
  3. # this is new set of permissions;  
  4. new_permission_set = {"can_edit_settings","is_member", "can_view_entry", "can_edit_entry"}  
  5. # now permissions to add will be:  
  6. new_permission_set.difference(original_permission_set)  
  7. # which will result:  
  8. {'can_edit_settings', 'can_view_entry', 'is_member'}  
  9. # As you can see can_edit_entry is in both sets; so we do notneed  
  10. # to worry about handling it  
  11. # now permissions to remove will be:  
  12. original_permission_set.difference(new_permission_set)  
  13. # which will result:  
  14. {'is_admin', 'can_view_settings', 'can_post_entry'}  
  15. # and basically it's also true; we switched admin to member, andadd  
  16. # more permission on settings; and removed the post_entrypermission 

总的来说,不要害怕使用集合,它们能帮助你解决很多问题,更多详情,请参考 Python 官方文档。

02.日历

当开发与日期和时间有关的功能时,有些信息可能非常重要,比如某一年的这个月有多少天。这个问题看似简单,但是我相信日期和时间是一个非常有难度的话题,而且我觉得日历的实现问题非常多,简直就是噩梦,因为你需要考虑大量的极端情况。

那么,究竟如何才能找出某个月有多少天呢? 

  1. import calendar  
  2. calendar.monthrange(2020, 12)  
  3. # will result:  
  4. (1, 31)  
  5. # BUT! you need to be careful here, why? Let's read thedocumentation:  
  6. help(calendar.monthrange)  
  7. # Help on function monthrange in module calendar:  
  8. # monthrange(year, month)  
  9. #     Return weekday (0-6~ Mon-Sun) and number of days (28-31) for  
  10. #    year, month.  
  11. # As you can see the first value returned in tuple is a weekday,  
  12. # not the number of the first day for a given month; let's try  
  13. # to get the same for 2021  
  14. calendar.monthrange(2021, 12)  
  15. (2, 31)  
  16. # So this basically means that the first day of December 2021 isWed  
  17. # and the last day of December 2021 is 31 (which is obvious,cause  
  18. # December always has 31 days)  
  19. # let's play with February  
  20. calendar.monthrange(2021, 2)  
  21. (0, 28)  
  22. calendar.monthrange(2022, 2)  
  23. (1, 28)  
  24. calendar.monthrange(2023, 2)  
  25. (2, 28) 
  26. calendar.monthrange(2024, 2)  
  27. (3, 29)  
  28. calendar.monthrange(2025, 2)  
  29. (5, 28)  
  30. # as you can see it handled nicely the leap year; 

某个月的第一天当然非常简单,就是 1 号。但是,“某个月的第一天是周X”,如何使用这条信息呢?你可以很容易地查到某一天是周几: 

  1. calendar.monthrange(2024, 2)  
  2. (3, 29)  
  3. # means that February 2024 starts on Thursday  
  4. # let's define simple helper:  
  5. weekdays = ["Monday", "Tuesday","Wednesday", "Thursday", "Friday","Saturday", "Sunday"]  
  6. # now we can do something like:  
  7. weekdays[3]  
  8. # will result in:  
  9. 'Thursday'  
  10. # now simple math to tell what day is 15th of February 2020:  
  11. offset = 3  # it's thefirst value from monthrange  
  12. for day in range(1, 29):  
  13.     print(day,weekdays[(day + offset - 1) % 7])  
  14. 1 Thursday  
  15. 2 Friday  
  16. 3 Saturday  
  17. 4 Sunday  
  18. ...  
  19. 18 Sunday  
  20. 19 Monday  
  21. 20 Tuesday  
  22. 21 Wednesday  
  23. 22 Thursday  
  24. 23 Friday  
  25. 24 Saturday  
  26. ...  
  27. 28 Wednesday  
  28. 29 Thursday  
  29. # which basically makes sense; 

也许这段代码不适合直接用于生产,因为你可以使用 datetime 更容易地查找星期: 

  1. from datetime import datetime  
  2. mydate = datetime(2024, 2, 15)  
  3. datetime.weekday(mydate)  
  4. # will result:  
  5.  
  6. # or:  
  7. datetime.strftime(mydate, "%A") 
  8. 'Thursday' 

总的来说,日历模块有很多有意思的地方,值得慢慢学习: 

  1. # checking if year is leap:  
  2. calendar.isleap(2021)  #False  
  3. calendar.isleap(2024)  #True  
  4. # or checking how many days will be leap days for given yearspan:  
  5. calendar.leapdays(2021, 2026) # 1  
  6. calendar.leapdays(2020, 2026) # 2  
  7. # read the help here, as range is: [y1, y2), meaning that second  
  8. # year is not included;  
  9. calendar.leapdays(2020, 2024) # 1 

03.枚举有第二个参数

是的,枚举有第二个参数,可能很多有经验的开发人员都不知道。下面我们来看一个例子: 

  1. mylist = ['a', 'b', 'd', 'c', 'g', 'e']  
  2. for i, item in enumerate(mylist):  
  3.     print(i, item)  
  4. # Will give:  
  5. 0 a  
  6. 1 b  
  7. 2 d  
  8. 3 c  
  9. 4 g  
  10. 5 e  
  11. # but, you can add a start for enumeration:  
  12. for i, item in enumerate(mylist, 16):  
  13.     print(i, item)  
  14. # and now you will get:  
  15. 16 a  
  16. 17 b  
  17. 18 d  
  18. 19 c  
  19. 20 g  
  20. 21 e 

第二个参数可以指定枚举开始的地方,比如上述代码中的 enumerate(mylist,16)。如果你需要处理偏移量,则可以考虑这个参数。

04.if-else 逻辑

你经常需要根据不同的条件,处理不同的逻辑,经验不足的开发人员可能会编写出类似下面的代码: 

  1. OPEN = 1  
  2. IN_PROGRESS = 2  
  3. CLOSED = 3  
  4. def handle_open_status():  
  5.     print('Handling openstatus')  
  6. def handle_in_progress_status():  
  7.     print('Handling inprogress status')  
  8. def handle_closed_status():  
  9.     print('Handling closedstatus')  
  10. def handle_status_change(status):  
  11.     if status == OPEN:  
  12.        handle_open_status()  
  13.     elif status ==IN_PROGRESS:  
  14.         handle_in_progress_status()  
  15.     elif status == CLOSED:  
  16.        handle_closed_status()  
  17. handle_status_change(1)  #Handling open status  
  18. handle_status_change(2)  #Handling in progress status  
  19. handle_status_change(3)  #Handling closed status 

虽然这段代码看上去也没有那么糟,但是如果有 20 多个条件呢?

那么,究竟应该怎样处理呢? 

  1. from enum import IntEnum  
  2. class StatusE(IntEnum):  
  3.     OPEN = 1  
  4.     IN_PROGRESS = 2  
  5.     CLOSED = 3  
  6. def handle_open_status():  
  7.     print('Handling openstatus')  
  8. def handle_in_progress_status():  
  9.     print('Handling inprogress status')  
  10. def handle_closed_status():  
  11.     print('Handling closedstatus')  
  12. handlers = {  
  13.     StatusE.OPEN.value:handle_open_status,  
  14.    StatusE.IN_PROGRESS.value: handle_in_progress_status,  
  15.     StatusE.CLOSED.value:handle_closed_status  
  16.  
  17. def handle_status_change(status):  
  18.     if status not inhandlers:  
  19.          raiseException(f'No handler found for status: {status}')  
  20.     handler =handlers[status]  
  21.     handler()  
  22. handle_status_change(StatusE.OPEN.value)  # Handling open status  
  23. handle_status_change(StatusE.IN_PROGRESS.value)  # Handling in progress status  
  24. handle_status_change(StatusE.CLOSED.value)  # Handling closed status  
  25. handle_status_change(4)  #Will raise the exception 

在 Python 中这种模式很常见,它可以让代码看起来更加整洁,尤其是当方法非常庞大,而且需要处理大量条件时。

05.enum 模块

enum 模块提供了一系列处理枚举的工具函数,最有意思的是 Enum 和 IntEnum。我们来看个例子: 

  1. from enum import Enum, IntEnum, Flag, IntFlag  
  2. class MyEnum(Enum):  
  3.     FIRST ="first"  
  4.     SECOND ="second"  
  5.     THIRD ="third"  
  6. class MyIntEnum(IntEnum):  
  7.     ONE = 1  
  8.     TWO = 2  
  9.     THREE = 3  
  10. # Now we can do things like:  
  11. MyEnum.FIRST  #  
  12. # it has value and name attributes, which are handy:  
  13. MyEnum.FIRST.value  #'first'  
  14. MyEnum.FIRST.name  #'FIRST'  
  15. # additionally we can do things like: 
  16. MyEnum('first')  #, get enum by value  
  17. MyEnum['FIRST']  #, get enum by name 

使用 IntEnum 编写的代码也差不多,但是有几个不同之处: 

  1. MyEnum.FIRST == "first"  # False  
  2. # but  
  3. MyIntEnum.ONE == 1  # True  
  4. # to make first example to work:  
  5. MyEnum.FIRST.value == "first"  # True 

在中等规模的代码库中,enum 模块在管理常量方面可以提供很大的帮助。

enum 的本地化可能有点棘手,但也可以实现,我用django快速演示一下: 

  1. from enum import Enum  
  2. from django.utils.translation import gettext_lazy as _  
  3. class MyEnum(Enum):  
  4.     FIRST ="first"  
  5.     SECOND ="second"  
  6.     THIRD ="third"  
  7.     @classmethod  
  8.     def choices(cls):  
  9.         return [  
  10.             (cls.FIRST.value, _('first')),  
  11.             (cls.SECOND.value, _('second')), 
  12.             (cls.THIRD.value, _('third'))  
  13.          ]  
  14. # And later in eg. model definiton:  
  15. some_field = models.CharField(max_length=10,choices=MyEnum.choices()) 

06.iPython

iPython 就是交互式 Python,它是一个交互式的命令行 shell,有点像 Python 解释器。

首先,你需要安装 iPython:

  1. pip install ipython 

接下来,你只需要在输入命令的时候,将 Python 换成 ipython: 

  1. # you should see something like this after you start:  
  2. Python 3.8.5 (default, Jul 28 2020, 12:59:40)  
  3. Type 'copyright', 'credits' or 'license' for more information  
  4. IPython 7.18.1 -- An enhanced Interactive Python. Type '?' forhelp.  
  5. In [1]: 

ipython 支持很多系统命令,比如 ls 或 cat,tab 键可以显示提示,而且你还可以使用上下键查找前面用过的命令。更多具体信息,请参见官方文档。 

 

责任编辑:庞桂玉 来源: 马哥Linux运维
相关推荐

2021-02-16 09:02:59

Python代码技巧

2015-05-07 13:13:27

JavaScript JavaScript

2021-01-05 11:22:58

Python字符串代码

2020-01-29 19:40:36

Python美好,一直在身边Line

2021-07-05 05:34:10

Typescript语言开发

2015-08-13 09:03:14

调试技巧

2015-04-13 13:21:45

JavaScript JavaScript

2017-02-23 19:42:55

AS Android代码

2012-11-23 10:57:44

Shell

2024-03-04 00:00:00

Kubernetes技巧API

2022-09-20 11:58:27

NpmNode.js

2019-11-25 14:05:47

Python装饰器数据

2022-12-09 15:06:26

字符串Intl字符串分割

2022-12-21 08:05:04

字符串分割技巧

2020-11-03 09:51:04

JavaScript开发 技巧

2021-02-28 08:34:14

CSS outline-off负值技巧

2017-11-07 21:58:25

前端JavaScript调试技巧

2019-11-20 10:25:06

sudoLinux

2023-02-27 09:20:24

绝对定位CSS

2023-01-29 09:46:47

Dialog弹窗模态
点赞
收藏

51CTO技术栈公众号