哪个更有效:将对象用作 class 属性?或者在需要时重新初始化?

Which is more efficient: to use object as class attribute? Or to reinitialize when needed?

我正在用 Scrapy 构建一个蜘蛛/抓取器,我想知道哪个更有效:将一个 API 包装器对象初始化一次作为 class 属性?或者重新初始化每个 URL 请求?我想知道整体效率和内存(泄漏)的情况,因为这将是一个相当大的项目(数百万个请求)。

案例 1:

# init API wrapper ONCE as class attribute
class ScrapySpider():

    api = SomeAPIWrapper()

    urls = [
        'https://website.com',
        # ... +1mil URLs
    ]

    def request(self):
        for url in urls:
            yield Request(url)

    def parse(self, response):
        yield self.api.get_meta(response.url)

案例二:

# init new API wrapper on EACH request
class ScrapySpider():

    urls = [
        'https://website.com',
        # ... +1mil URLs
    ]

    def request(self):
        for url in urls:
            yield Request(url)

    def parse(self, response):
        api = SomeAPIWrapper()
        yield api.get_meta(response.url)

在示例代码中,使用 class 属性(案例 1)应该更有效。

这个问题没有通用的、放之四海而皆准的答案——这取决于对象实例化的成本、在最佳/平均/最坏情况下最终实例化它的频率,以及您的使用 class 属性(而不是实例属性)的示例,在主机 class.

的所有实例之间共享此对象是否安全

请注意,还有两个替代项:

1/ 在初始化程序中创建的每个实例属性:

class ScrapySpider():

    def __init__(self, *args, **kw):
        super().__init__(*args, **kw)
        self.api = SomeAPIWrapper()

这避免了使用 class 属性可能遇到的并发访问问题,并且

2/个缓存属性

class ScrapySpider():

    @property
    def api(self):
        if not hasattr(self, "_cached_api"):
            self._cached_api = ApiWrapper()
        return self._cached_api

这也会阻止在需要之前创建 ApiWrapper 实例(如果创建它的成本很高并且并不总是需要它,则可能会有用)但会增加属性访问的少量开销。