Это помогает, если вы разбиваете процесс на логические, повторно используемые части.
for new_product in products_and_articles['products']:
for old_product in products_for_update:
if new_product.article == old_product.article:
…
Например, вот что вы делаете, это найти продукт, который соответствует конкретной article
.Поскольку article
уникален, мы могли бы написать что-то вроде этого:
def find_products_by_article(products, article):
'''Find all products that match the given article. Returns
either a product or 'None' if it doesn't exist.'''
for products in products:
return product
Затем вызовите его:
for old_product in products_for_update:
new_products = find_products_by_article(
products_and_articles['products'],
old_product.article)
…
Но это может быть гораздо более эффективной, если мы могли бы воспользоваться структурой данных, оптимизирован для поиска, а именно: dict
(постоянный вместо линейной сложности). Так что мы могли бы сделать вместо этого:
# build a dictionary that stores products indexed by article
products_by_article = dict(product.article, product for product in
products_and_articles['products'])
for old_product in products_for_update:
try:
# look up product in the dictionary
new_product = products_by_article[old_product.article]
except KeyError:
# silently ignore products that don't exist
continue
…
Если вы делаете такие Lookups часто, было бы лучше, чтобы повторно использовать products_by_article
словарь в другом месте, а вместо того, чтобы строить с нуля каждый раз. Будьте осторожны:: если вы используете несколько представлений записей продукта, вам нужно заставить их всегда оставаться в синхронизации!
Для внутренних петель, обратите внимание, что new_field
здесь служит только в качестве проверки для того, существует поле:
…
for old_field in old_product._meta.get_all_field_names():
for new_field in new_product._meta.get_all_field_names():
if old_field == new_field and old_field != 'id' and old_field != 'slug':
setattr(old_product, old_field, getattr(new_product, old_field))
(Обратите внимание, что это немного подозрительно: новые поля, которые уже не существуют в old_product
отбрасываются: это намеренное)
Это можно упаковать следующим образом:
def transfer_fields(old, new, exclusions=('id', 'slug')):
'''Update all pre-existing fields in the old record to have
the same values as the new record. The 'exclusions' parameter
can be used to exclude certain fields from being updated.'''
# use a set here for efficiency reasons
fields = frozenset(old._meta.get_all_field_names())
fields.difference_update(new._meta.get_all_field_names())
fields.difference_update(exclusions)
for field in fields:
setattr(old, field, getattr(new, field))
Собираем все это вместе:
# dictionary of products indexed by article
products_by_article = dict(product.article, product for product in
products_and_articles['products'])
for old_product in products_for_update:
try:
new_product = products_by_article[old_product.article]
except KeyError:
continue # ignore non-existent products
transfer_fields(old_product, new_product)
Этот окончательный код имеет временную сложность O(n × k)
, где n
является количество продуктов и k
является количество полей.
Удалить петлю new_field? вы все равно не используете new_field. – lolopop
Также сортируйте два списка, и вы получите nlogn вместо n^2 – lolopop
Не могли бы вы привести быстрый пример двух входных списков и быстрый пример вывода? – Jivan