3
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

所以上面是创建月份列表的python代码,如果它是有效月份,基本上打印用户输入的内容。上面的代码很好。

下面是与上面类似的另一个版本,但我想更好或更人性化。但我不确定这到底是如何工作的

months_abbvs = dict((m[:3].lower(), m) for m in months)

def valid_month(month):
    if month:
        short_month = month[:3].lower()
        return month_abbvs.get(short_month)
4

3 回答 3

5

打印出您从该额外步骤中获得的信息可能会有所帮助:

>>> 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')]

正如本教程中更详细地解释的那样,推导式基本上是循环的简写。特别是,这:

>>> m2 = [<expression with m> for m in months]

… 相当于:

>>> m2 = []
>>> for m in months:
...     m2.append(<expression with m>)

使用生成器表达式而不是列表推导式仅意味着将序列构建为惰性迭代器而不是列表。

然后将结果(无论哪种方式)传递给dict构建一个字典,将每个元组的第一个值映射到第二个值。


你可以把这一切写得更易读,作为字典理解:

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

更好的是m[:3].lower(),与其重复编写,不如给它起一个好听的名字并使用它:

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)

现在,您在新版本中输入的内容是:

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

因为month_abbvsis a dict(你可以通过打印出来,或者仅仅从它是通过调用dict某些东西创建的事实来判断),get方法是dict.get. 因此,如链接文档中所述,month_abbvs.get(short_month)与 相同months_abbvs[short_month],只是如果short_month找不到密钥,您将得到None,而不是引发异常。

因此,如果给定'October',您将设置short_month'oct'。然后你在缩写字典中查找它,它返回'October'. 如果给定'OCT'or'october''octal digit string',你也会返回同样的东西。由于任何非空字符串都是真实的,如果你做了类似的事情if valid_month('October'):,那将是真实的。

但是,如果给定,比如说,,'Muhammed'你将设置short_month'muh'。然后你查了一下,它不存在。如上所述,该get方法返回None未知键,因此您将返回None. 既然None是假的,如果你做过类似的事情if valid_month('Muhammed'):,那就不是真的。

换句话说,它使函数更宽松——这可能是一种改进,也可能是一件坏事(或者可能两者兼而有之——也许你想'OCT'工作,但不是'octal digit string')。

于 2013-09-05T19:44:31.637 回答
2
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)

这更有效的原因是dict使用哈希集作为其内部表示,因此查找其中是否存在密钥是(摊销的)O(1)操作,而对列表执行相同操作是最坏的情况 -对于大小为 n 的列表的场景 O(n) 操作。

根据@abarnert 的评论,这也使代码对用户更加友好,因为您只需执行 adict.get(key)而不必担心代码中的迭代逻辑。条件变成“真/假”问题,而不是“对于这组中的任何一种情况是否为真 [...]?”

于 2013-09-05T19:43:47.457 回答
0

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

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

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):
    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

在这种情况下,这对我们来说并不买账。能够同时使用这两种风格是很好的。

于 2013-09-05T20:10:10.490 回答