如何在Python 3

时间:2019-05-08 19:35:56

标签: python list dictionary list-comprehension

我现在正在使用Python上课,并且很难理解列表/集合/字典的理解。我希望有人可以帮助我理解提供的答案,也许可以帮助我理解构成该答案的基本规则,并为我提供一些资源,以便我可以更好地了解这些规则

问题如下:

  

学校注册商使用dict,其中键为课程(即'PHYS12''PHYS13'),其关联值为set中的2-tuples 。每个tuple都有一个学生姓名和一个字母等级(可能后跟+/-。示例注册商如下所示:

db = {'PHYS12': {('Bob', 'A'), ('David', 'C'), ('Carol', 'B')}, 'PHYS13': {('Bob',
'B'), ('Alice', 'A')}}
  

我们还可以使用以下dict将成绩与成绩点相关联:

SCHOOL = {'A+': 4.0, 'A': 4.0, 'A-': 3.7,
 'B+': 3.3, 'B': 3.0, 'B-': 2.7,
 'C+': 2.3, 'C': 2.0, 'C-': 1.7,
 'D+': 1.3, 'D': 1.0, 'D-': 0.7,
 'F' : 0.0}
  

鉴于此信息,定义一个函数:gpa_ex,该函数接受一个int参数,并返回一个dict,其中键是课程名称,值是{{1} }个学生姓名,按年级降序排列,其年级等于或超过lists参数。如果多个学生的gpa是相同的,则这些学生将按字母升序显示。例如,如果我们调用gpa,它将返回:

gpa_ex(2.7)

因此,我了解如何通过在{'PHYS12': ['Bob', 'Carol'], 'PHYS13': ['Alice', 'Bob']} 上进行迭代,然后在元组上进行迭代,然后将db.items()与传入的grade进行比较来实现此目的。我的问题是答案提供如下:

gpa

有人可以帮我分解这个答案,并向我解释发生了什么吗?鉴于我们只介绍了基本的列表理解,即。 return {c: [s for _,s in sorted((-SCHOOL[g],s) for (s,g) in grades if SCHOOL[g] >= gpa)] for c,grades in db.items()} ,我真的很难理解什么语法是合法的,该语法的规则是什么(即什么时候可以使用变量,什么时候不能使用变量)以及如何构造这样的事情。通常,构建它的思想过程是什么?如果我有一个正常的正常循环解决方案,我将如何应用它来构建这种理解?

2 个答案:

答案 0 :(得分:2)

似乎我们在dict理解中嵌入了一个列表理解。所以换句话说,我们有一个列表的字典,看起来像

list_comprehension = [s for _,s in sorted((-SCHOOL[g],s) for (s,g) in grades if SCHOOL[g] >= gpa)]
{c:[list_comprehension] for c,grades in db.items()}

这很简单。现在,让我们看一下[something]

[s for _,s in sorted((-SCHOOL[g],s) for (s,g) in grades if SCHOOL[g] >= gpa)]

本身就是元组理解的列表理解:

tuple_comprehension = ((-SCHOOL[g], s) for (s,g) in grades if SCHOOL[g] >= gpa)
[s for s in sorted(tuple_comprehension)]

让我们看一下tuple_comprehension

((-SCHOOL[g], s) for (s,g) in grades if SCHOOL[g] >= gpa)

这里的条件语句意味着(-SCHOOL[g], s)仅在{em> {em}评估为SCHOOL[g] >= gpa的情况下才添加到输出。否则,该元素将被跳过。


总的来说,该语句是

True

对于return {c: # key:value, where the value is a list comprehension: [s for _,s in # list comprehension, where the source-list is... sorted( # ...the sorted version of the comprehension: (-SCHOOL[g],s) for (s,g) in grades if SCHOOL[g] >= gpa comprehension ) ] # end list comprehension for c,grades in db.items()} # end dict comprehension 这样的方法,它可以接受任意数量的变量,您可以传递一个理解,而不必使用通常需要的方括号或圆括号。上面,我称其为“元组理解”,尽管这并不是真正正确的术语。

至于“何时在列表理解中定义变量”:在定义要迭代的内容时,它位于sorted()子句中。然后,您可以在定义(要添加的对象/键值对)或 condition for语句中)中使用这些变量。最后)),例如,在

if

... for (s,g) in grades ... s在这里定义,并且可以在此理解中(或嵌套在其中的任何其他理解中的任何其他地方)使用。

在整个示例中,gvc在外部dict理解中定义,并在定义中使用,其中包括嵌套列表理解。然后,grades_在内部列表理解中定义,并在定义 s中使用(即s被忽略)。最后,_和另一个g(与外部定义的对象无关)在最内层的理解中定义,并在定义 s中使用和 condition (-SCHOOL[g], s))。

答案 1 :(得分:2)

@GreenCloakGuy已经给出了great explanation答案代码的作用。我将尝试解释如何从使用for循环和中间变量的代码到这种理解构造。

  

这将是一个冗长的

在不使用理解的情况下,您必须编写类似以下内容的内容才能获得所需的结果:

def gpa_ex(gpa):
    result = {}

    for className, grades in db.items():
        scores = []

        for studentName, gradeLetter in grades:
            score = SCHOOL[gradeLetter]

            if score >= gpa:
                scores.append((-score, studentName))

        sortedScores = sorted(scores)

        result[className] = []

        for _, studentName in sortedScores:
            result[className].append(studentName)

    return result

理解是缩短代码的好工具,所以让我们看看是否可以使用它来简化代码。

第一步,我们可以使用列表理解来简化result[className]的构建:

def gpa_ex(gpa):
    result = {}

    for className, grades in db.items():
        scores = []

        for studentName, gradeLetter in grades:
            score = SCHOOL[gradeLetter]

            if score >= gpa:
                scores.append((-score, studentName))

        sortedScores = sorted(scores)

        # result[className] = []
        # 
        # for _, studentName in sortedScores:
        #     result[className].append(studentName)
        result[className] = [studentName for _, studentName in sortedScores]

    return result

sortedScores只是sorted(scores)的结果,我们不需要单独的变量,因为我们可以内联地进行:

def gpa_ex(gpa):
    result = {}

    for className, grades in db.items():
        scores = []

        for studentName, gradeLetter in grades:
            score = SCHOOL[gradeLetter]

            if score >= gpa:
                scores.append((-score, studentName))

        # sortedScores = sorted(scores)
        result[className] = [studentName for _, studentName in sorted(scores)]

    return result

在将scores的创建转换为理解之前,请务必注意,我们需要将等级字母“ A”转换为等级分数“ 4.0”,以便对其进行排序。或者实际上是“ -4.0”,以便按降序排序。

现在,我为此使用了一个单独的变量score。让我们看看如果我们只删除该变量并使用两次SCHOOL[gradeLetter]会发生什么:

def gpa_ex(gpa):
    result = {}

    for className, grades in db.items():
        scores = []

        for studentName, gradeLetter in grades:
            # score = SCHOOL[gradeLetter]
            if SCHOOL[gradeLetter] >= gpa:
                scores.append((-SCHOOL[gradeLetter], studentName))

        result[className] = [studentName for _, studentName in sorted(scores)]

    return result

看起来可能是条件列表理解,不是吗?

def gpa_ex(gpa):
    result = {}

    for className, grades in db.items():
        # scores = []
        # 
        # for studentName, gradeLetter in grades:
        #     if SCHOOL[gradeLetter] >= gpa:
        #         scores.append((-SCHOOL[gradeLetter], studentName))
        scores = [(-SCHOOL[gradeLetter], studentName) for studentName, gradeLetter in grades if SCHOOL[gradeLetter] >= gpa]

        result[className] = [studentName for _, studentName in sorted(scores)]

    return result

好吧,它可能并不漂亮,但是如果我们只使用一次,创建变量实际上没有意义,因此我们可以在其中创建scores我们使用它的地方:

def gpa_ex(gpa):
    result = {}

    for className, grades in db.items():
        # scores = [(-SCHOOL[gradeLetter], studentName) for studentName, gradeLetter in grades if SCHOOL[gradeLetter] >= gpa]
        result[className] = [studentName for _, studentName in sorted(
            [(-SCHOOL[gradeLetter], studentName) for studentName, gradeLetter in grades if SCHOOL[gradeLetter] >= gpa]
        )]

    return result

尽管此步骤是可选的,但由于它现在是sorted()函数的函数参数,因此我们有可能在理解中放弃[]。代替列表理解,这将创建一个可迭代的 generator 。对于这个特殊的用例,没有任何区别,因为sorted()可以处理那些情况。我个人不会打扰,因为它节省了两个字符,但增加了一些开销,但这是您的老师提供的答案:

def gpa_ex(gpa):
    result = {}

    for className, grades in db.items():
        result[className] = [studentName for _, studentName in sorted(
            (-SCHOOL[gradeLetter], studentName) for studentName, gradeLetter in grades if SCHOOL[gradeLetter] >= gpa
        )]

    return result

现在我们已经精简了一下,我们可以看到result字典的创建可以简化为对字典的理解:

def gpa_ex(gpa):
    # result = {}
    # 
    # for className, grades in db.items():
    #     result[className] = [studentName for _, studentName in sorted(
    #         (-SCHOOL[gradeLetter], studentName) for studentName, gradeLetter in grades if SCHOOL[gradeLetter] >= gpa
    #     )]
    result = {
        className: [studentName for _, studentName in sorted(
            (-SCHOOL[gradeLetter], studentName) for studentName, gradeLetter in grades if SCHOOL[gradeLetter] >= gpa
        )] for className, grades in db.items()
    }

    return result

现在,我们再次创建了仅使用一次的变量,我们不妨完全删除该变量:

def gpa_ex(gpa):
    return {
        className: [studentName for _, studentName in sorted(
            (-SCHOOL[gradeLetter], studentName) for studentName, gradeLetter in grades if SCHOOL[gradeLetter] >= gpa
        )] for className, grades in db.items()
    }

如果您缩短变量名以简化它(我个人不愿意这样做,因为我希望变量名告诉我它们的用途),那么您会得到原来的答案:

def gpa_ex(gpa):
    return {
        c: [s for _, s in sorted(
            (-SCHOOL[g], s) for s, g in grades if SCHOOL[g] >= gpa
        )] for c, grades in db.items()
    }

需要一些练习来识别可以将创建列表或字典简化为理解的情况。通过删除单独的scores变量并仅使用score两次,可能不会立即发现SCHOOL[gradeLetter]可能是条件列表理解。特别是因为这是违反直觉的(我们开发人员不喜欢重复自己,所以如果我们多次执行相同的操作,我们倾向于将其粘贴到变量或函数中,以便我们可以重复使用)。这是实践和经验带来的。

就我个人而言,我认为这个答案有点过分了(除非问题的全部是要教你如何使用理解力)。较短的代码并不总是更好的代码,始终牢记可维护性。试想一下,在实际的应用程序中使用此代码,两年后,由于业务逻辑的更改,您需要对gpa_ex函数进行一些调整(也许您需要更改排序顺序,或者更改{{1} }需要成为>= gpa)。您宁愿面对该功能的哪个版本?速记理解结构,我最初使用的完全写完的代码还是中间版本之一?