@ GreenCloakGuy уже дал отличное объяснение того, что делает код ответа.Я попытаюсь объяснить, как вы могли бы получить от фрагмента кода, использующего циклы 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
в понимание, важно отметить, что мынеобходимо преобразовать букву «А» в оценку «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()
, мы могли бы опустить []
в понимании.Вместо понимания списка это создаст итеративный генератор .Для этого конкретного случая использования это не имеет никакого значения, поскольку 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
dict может быть упрощено до понимания dict:
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
из-за изменения бизнес-логики (возможно, вам нужно изменить порядок сортировки, или >= gpa
должен стать> gpa
).С какой версией этой функции вы бы предпочли столкнуться?Сокращенная конструкция понимания, полностью написанный код, с которого я начал, или одна из промежуточных версий?