python - заполнить генератор класса init или defer? - PullRequest
0 голосов
/ 03 октября 2018

У меня есть приложение django, которое на очень псевдокодийном уровне делает что-то вроде этого:

class SerialisedContentItem():

    def __init__(self, item),
        self.__output = self.__jsonify(item)

    def fetch_output(self):
        return self.__output

    def __jsonify(self, item):
        serialized = do_a_bunch_of_serialisey_stuff()
        return json.dumps(serialized)

Так что в основном - как только создается экземпляр класса, оно:

  • запускает внутреннюю функцию для генерации выходной строки JSON
  • сохраняет ее во внутренней переменной
  • предоставляет открытую функцию, которая может быть вызвана позже для получения JSON

Затем он используется для создания страницы, похожей на эту:

for item in page.items:
    json_item = SerialisedContentItem(item)
    yield json_item.fetch_output()

Мне это кажется немного бессмысленным.И это также вызывает проблемы с некоторыми изменениями бизнес-логики, которые мы должны сделать.

Что я предпочитаю делать, так это откладывать вызов функции "jsonify" до тех пор, пока я на самом деле не хочу этого.Грубо говоря, изменив вышеперечисленное на:

class SerialisedContentItem():

    def __init__(self, item),
        self.__item = item

    def fetch_output(self):
        return self.__jsonify(self.__item):

Это кажется более простым и немного отвлекает меня от моей логики.

Но: есть ли обратная сторона, которую я не вижу?Являются ли мои изменения менее эффективными, или это не очень хороший способ делать вещи?

1 Ответ

0 голосов
/ 03 октября 2018

До тех пор, пока вы вызываете fetch_output один раз для каждого предмета, производительности не будет (очевидно, будет один, если вы дважды вызываете fetch_output в одном и том же экземпляре SerializedContentItem).И не делать бесполезных операций, как правило, тоже хорошо (вы не ожидаете, что open("/path/to/some/file.ext") прочитает содержимое файла, не так ли?)

Единственное предостережение заключается в том, что с исходной версией, если item мутирует между инициализацией SerializedContentItem и вызовом fetch_output, изменение не будет отражено в выводе json (так как оно создано прямо во время инициализации), в то время как в вашей "ленивой" версии эти изменения будут отражатьв JSON.Независимо от контекста это зависит от того, является ли это проблемой, потенциальной проблемой или просто тем, что вы хотите.

РЕДАКТИРОВАТЬ:

что вызвало мой вопрос:в соответствии с моим (плохим) пониманием yield, его использование здесь имеет смысл: мне нужно только итерировать элементы страницы один раз, поэтому делайте это таким образом, чтобы минимизировать использование памяти.Но основная часть работы в настоящее время не выполняется в функции yield, она выполняется в строке над ней, когда создается экземпляр класса, что делает yield немного бессмысленным.Или я неправильно понимаю, как это работает?

Боюсь, вы действительно неправильно понимаете yield.Откладывание json-серализации до тех пор, пока yield json_item.fetch_output() не изменит ничего (nada, zero, zilch, shunya) на потребление памяти по сравнению с оригинальной версией.

yield это не функция, это ключевое слово.Он превращает содержащую ее функцию в «функцию генератора» - функцию, которая возвращает объект генератора (ленивый итератор), который затем можно перебрать.Это ничего не изменит в памяти, используемой для jsonify элемента, и то, происходит ли это jsonification «в той же строке», что и ключевое слово yield, или нет, совершенно не имеет значения.

Что приносит вам генератор (по сравнению с/ Использование памяти) заключается в том, что вам не нужно создавать целый список содержимого сразу, например:

def eager():
   result = []
   for i in range(1000):
       result.append("foo {}\n".format(i))
   return result

with open("file.txt", "w") as outfile:
    for item in eager():
        outfile.write(item)

Этот FIRST создает список из 1000 элементов в памяти, а затем перебирает его.

vs

def lazy():
   result = []
   for i in range(1000):
       yield "foo {}\n".format(i)

with open("file.txt", "w") as outfile:
    for item in lazy():
        outfile.write(item)

это лениво генерирует строку за строкой на каждой итерации, поэтому вы не получите список из 1000 элементов в памяти - НО вы по-прежнему генерируете 1000 строк, каждая изони используют то же количество места, что и в первом решении.Разница в том, что, поскольку (в этом примере) вы не сохраняете никаких ссылок на эти строки, они могут собираться мусором на каждой итерации, в то время как хранение их в списке не позволяет собирать их до тех пор, пока в самом списке больше не будет ссылок..

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...