我有一个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)
所以基本上-实例化该类后,它就会:
然后用于生成类似这样的页面:
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):
这看起来更简单,而我的逻辑则更少。
但是:我没有看到不利之处吗?是我的变革表现不佳,还是做事的好方法?
答案 0 :(得分:1)
只要每个项目仅调用一次fetch_output
,就不会对性能造成任何影响(显然,如果您在同一fetch_output
实例上两次调用SerializedContentItem
,则性能会受到影响)。而且,不执行无用的操作通常也是一件好事(您不希望open("/path/to/some/file.ext")
读取文件的内容,对吗?)
唯一的警告是,对于原始版本,如果item
在SerializedContentItem
的初始化与对fetch_output
的调用之间发生了突变,则更改不会反映在json输出(因为它是在初始化时创建的),而在您的“惰性”版本中,这些更改将反映在json中。这是一个可以解决的问题,一个潜在的问题,或者实际上是您想要的只是取决于上下文,因此只有您可以说出来。
编辑:
什么提示了我的问题:根据我对产量的(较差的)理解,在这里使用它是有道理的:我只需要对页面项目进行一次迭代,因此可以最大限度地减少内存占用。但是当前大部分工作尚未在yield函数中完成,而是在实例化该类时在其上方的一行中完成,从而使yield变得毫无意义。还是我误会了它的工作原理?
恐怕您确实误会了yield
。推迟json序列化直到yield json_item.fetch_output()
会将没什么(nada,零,zilch,shunya)更改为带有原始版本的内存消耗。
yield
不是函数,而是关键字。它的作用是将包含它的函数转换为“生成器函数”(generator function)-该函数返回生成器(惰性迭代器)对象,然后可以对其进行迭代。它将不会更改用于对项目进行json处理的内存的任何内容,并且此jsonification是否与yield
关键字“在同一行”发生完全无关。
生成器为您带来的(wrt /内存使用)是您不必一次创建完整的内容列表,即:
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个字符串,每个字符串使用的空间与第一个解决方案相同。区别在于,由于(在此示例中)您没有在这些字符串上保留任何引用,因此在每次迭代时都可以对其进行垃圾回收,而将它们存储在列表中则可以防止在列表本身上没有更多引用之前对其进行收集