Использование генераторов Python
Я уже какое-то время использую генераторы Python, и мне приходится иметь дело с большими наборами запросов Django Querysets
, большими файлами Excel и т.д. Возможность экономить память с помощью генераторов, что стало частью моей повседневной работы.
Предположим, вы хотите проверить, зарегистрирован ли данный объект user_email
в любой из следующих социальных сетей: Facebook, Github или Twitter.
def has_facebook_account(user_email):
print('calling Facebook service')
return False
def has_github_account(user_email):
print('calling Github service')
return True
def has_twitter_account(user_email):
print('calling Twitter service')
return True
def has_social_media_account(user_email):
print('Checking social media apps...')
# Python's `any` receives an Iterable
# and returns True when the first truthy clause is found.
response = any([
has_facebook_account(user_email), # This is False
has_github_account(user_email), # This is True
has_twitter_account(user_email), # This is True
])
print('Done!')
return response
Если вы запустите этот код локально, вы увидите следующий результат:
>>> has_social_media_account('fake@email.com')
Checking social media apps...
calling Facebook service # This is False, keep going...
calling Github service # This is True! I can stop now...
calling Twitter service # Oh no!
Done!
Проблема в том, что a list
оценивается сразу после создания. Таким образом, если это список вызовов методов, все вызовы будут выполнены.
На данный момент я думал, что проблема в том, что я действительно вызываю методы при инициализации списка. Так что, возможно, сохранение только ссылок на методы и использование списка может помочь:
def has_social_account(user_email):
calls = [has_facebook_account, has_github_account, has_twitter_account] # Refs
return any([call(user_email) for call in calls])
В приведенном выше примере он сначала обязательно вызовет все методы, чтобы список был полностью построен , и все его элементы были оценены, что, в свою очередь, означает, что все методы вызываются. Понимание списка также не знает своего контекста, т. е. не знает, что он используется в качестве аргумента any(...)
.
Пример решения, используя генератор.
def has_social_account(user_email):
calls = [has_facebook_account, has_github_account, has_twitter_account]
return any((call(user_email) for call in calls)) # Note the ( ) instead of [ ]
>>> has_social_account('fake@email.com')
calling Facebook service # This is False, keep going...
calling Github service # Aw yeah!
(call(user_email) for call in calls)
оценивается в generator
(не полностью построенный список), который затем используется any
. any(...)
, что будет перебирать элементы генератора, оценивая по одному . Таким образом, никакие дополнительные вызовы не выполняются, если один раз any
оценивает второй элемент ( has_github_account(user_email) -> True
), any
функция оценивается сама, возвращая True
и не вызывая третий метод службы ( has_twitter_account
).