有人能看完这段Python代码并给我解释一下吗

有人能看完这段Python代码并给我解释一下吗,python,Python,上面是python代码,它创建了一个月的列表,基本上打印用户输入的内容,如果它是有效月还是无效月。上面的代码很好 下面是与上面类似的另一个版本,但我想更好或者更方便用户。但我不确定它到底是如何工作的,我还没有看到python字典以这种方式出现。有人能看完这段代码并向我解释一下吗?非常感谢 months = ['January', 'February', 'March', 'April', 'May',

上面是python代码,它创建了一个月的列表,基本上打印用户输入的内容,如果它是有效月还是无效月。上面的代码很好

下面是与上面类似的另一个版本,但我想更好或者更方便用户。但我不确定它到底是如何工作的,我还没有看到python字典以这种方式出现。有人能看完这段代码并向我解释一下吗?非常感谢

months = ['January',
          'February',
          'March',
          'April',
          'May',
          'June',
          'July',
          'August',
          'September',
          'October',
          'November',
          'December']

def valid_month(month):
    if month:
        cap_month = month.capitalize()
        if cap_month in months:
            return cap_month
这更有效的原因是
dict
使用哈希集作为其内部表示,因此查找其中是否存在键是一个(分期)O(1)操作,而对大小为n的列表执行相同操作是最坏情况下的O(n)操作


根据@abarnert的评论,这也使得编写代码更加方便用户,因为您只需执行
dict.get(key)
,而不必担心代码中的迭代逻辑。条件变成了“真/假”问题,而不是“此集合中的任何一种情况都是真的[…]?”

它可能有助于打印出您从该额外步骤中得到的信息:

months_abbvs = dict((m[:3].lower(), m) for m in months)
# months_abbvs = { 'jan':'January', 'feb':'February',... }
# the actual operation is two-step:
#     1. [(m[:3].lower(),m) for m in months] list comprehension over the "months" list which:
#     1.1. [m[:3].lower()] take first three letters of each item in the list, apply lowercase()
#     1.2. [(m[:3].lower,m)] return a tuple of (1.1, item)
#     2. [dict(...)] build a dictionary from the list comprehension
#     2.2. for each tuple returned from (1.2), create a key:value pair in the dict

def valid_month(month):
    if month:
        short_month = month[:3].lower()
        # get first three letters as lowercase string
        return month_abbvs.get(short_month)
        # 1. [month_abbvs.get(short_month)] perform dict.get(key)
        # 2. return result of 1 (None if no such key exists)
因此,您有一个小写缩写到完整月份名称的映射

这是怎么回事?首先,让我们看一下表达式的作用:

>>> months_abbvs
{'apr': 'April',
 'aug': 'August',
 'dec': 'December',
 'feb': 'February',
 'jan': 'January',
 'jul': 'July',
 'jun': 'June',
 'mar': 'March',
 'may': 'May',
 'nov': 'November',
 'oct': 'October',
 'sep': 'September'}

因此,每个月的理解都是这样的:

>>> m = 'December'
>>> m[:3].lower()
'dec'
>>> m[:3].lower(), m
('dec', 'December')
如中更详细的解释,理解基本上是循环的简写。特别是,这:

>>> [(m[:3].lower(), m) for m in months]
[('jan', 'January'),
 ('feb', 'February'),
 ('mar', 'March'),
 ('apr', 'April'),
 ('may', 'May'),
 ('jun', 'June'),
 ('jul', 'July'),
 ('aug', 'August'),
 ('sep', 'September'),
 ('oct', 'October'),
 ('nov', 'November'),
 ('dec', 'December')]
更好的是,不要反复编写
m[:3].lower()
,给它起个好名字并使用它:

months_abbvs = {m[:3].lower(): m for m in months}
然后:

def abbreviate(m):
    return m[:3].lower()
months_abbvs = {abbreviate(m): m for m in months}

现在,您对新版本中的输入所做的是:

def valid_month(month):
    if month:
        short_month = abbreviate(month)
        return month_abbvs.get(short_month)
由于
month\u abvs
是一个
dict
(您可以通过打印出来,或者仅仅从它是通过调用
dict
在某个东西上创建的这一事实来判断),因此
get
方法是有效的。因此,正如链接文档中所解释的,
month\u abvs.get(short\u month)
months\u abvs[short\u month]
相同,只是如果未找到键
short\u month
,您将获得
None
,而不是引发异常

因此,如果给定
“十月”
,您将
short\u month
设置为
“十月”
。然后在缩写词典中查找,它返回
“十月”
。如果给定了
'OCT'
'octor'
'octal digital string'
,您也将返回相同的内容。由于任何非空字符串都是真实的,如果您执行了类似于
if valid_month('十月'):
的操作,那么它将是真实的

但是,如果给定,比如说,
'Muhammed'
,您将
short\u month
设置为
'muh'
。然后你查了一下,它不在那里。如上所述,对于未知键,
get
方法返回
None
,因此您将返回
None
。因为
None
是错误的,如果你做了像
if valid\u month('Muhammed'):
这样的事情,它就不会是正确的


换言之,它使函数更加宽松,这可能是一种改进,也可能是一件坏事(或者两者兼而有之,也许你希望
'OCT'
工作,但不是
'octal digital string'
)。

其他两个问题详细解释了代码的工作原理,但我想提请您注意代码设计的一个特殊方面:它有避免异常的测试,如果这些测试失败,代码将停止执行,这样执行将从函数文本的末尾开始,并返回
None

编写代码的一种更简单的方法是消除检查并处理异常:

short_month = month[:3].lower()
return month_abbvs.get(short_month)
变成:

def valid_month(month):
    if month:
        # this will raise an exception if month is not a string
        short_month = month[:3].lower() 
        return month_abbvs.get(short_month)
def valid_month(month):
    if month:
        cap_month = month.capitalize()
        if cap_month in months:
            return cap_month
这给了我们一条主线去理解,在这种情况下更容易理解。显然,检查越多,这给我们的简单性就越高


变成:

def valid_month(month):
    if month:
        # this will raise an exception if month is not a string
        short_month = month[:3].lower() 
        return month_abbvs.get(short_month)
def valid_month(month):
    if month:
        cap_month = month.capitalize()
        if cap_month in months:
            return cap_month

在这种情况下,这并不能给我们带来很多好处。能够同时使用这两种样式很好。

在第一段代码中,月份是一个列表,而不是一个字典。我不认为效率与此相关。这一变化是对函数语义的一个重大变化,其目的显然是使其“更加用户友好”,因此语义而不是性能才是最重要的。此外,如果性能才是最重要的,那么它会简单得多,速度也会快得多,也不能仅仅将
months
更改为
set
,方法是用大括号替换方括号,其他内容保持不变。@abarnert同意。我猜问题中的代码来自某个在线课程。。。我还猜测(可能是错误的?)问题是问“这段代码中发生了什么?”而不是为什么,因此代码部分出现了故障。它很粗糙,但我认为它准确地描述了过程本身。你的新编辑说“你只需做一个
dict.get(key)
,而不必担心迭代逻辑”…但他的原始版本也不担心迭代逻辑;它只是在几个月内做了一次
cap\u月检查。所以,这个评论是错误的。谢谢。这实际上非常有用。这是不对的
month_abvs.get(…)
永远不会引发
keyrerror
。除非
month
不是一个序列,否则任何东西都不会产生
AttributeError
,这与
if month:
的测试完全不同,后者测试(除其他外)空字符串。@abarnert try
month=['foo';month.capitalize()
并告诉我您得到了什么。@Marcin:是的,除了跳过他以前做的一些测试之外,您还为他最初的代码没有测试的完全不同的问题添加了测试。在month\u abvs.get(短\u month)中get方法从何而来另外,你能简单地解释一下你不熟悉的整个理解概念是如何运作的吗here@muhammed:我将编辑答案进行解释,并添加一些链接。谢谢你澄清了你需要解释的部分。
def valid_month(month):
    try:
        return month_abbvs.get(month[:3].lower())
    except KeyError, AttributeError: # or Exception to catch everything
        return None
def valid_month(month):
    if month:
        cap_month = month.capitalize()
        if cap_month in months:
            return cap_month
def valid_month(month):
    try:
        cap_month = month.capitalize()
        if cap_month and cap_month in months:
            return cap_month
    except AttributeError:
        return None