我现在正在使用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()}
,我真的很难理解什么语法是合法的,该语法的规则是什么(即什么时候可以使用变量,什么时候不能使用变量)以及如何构造这样的事情。通常,构建它的思想过程是什么?如果我有一个正常的正常循环解决方案,我将如何应用它来构建这种理解?
答案 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
在这里定义,并且可以在此理解中(或嵌套在其中的任何其他理解中的任何其他地方)使用。
在整个示例中,g
和vc
在外部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
)。您宁愿面对该功能的哪个版本?速记理解结构,我最初使用的完全写完的代码还是中间版本之一?