(Mis)了解发电机

2024-04-24 09:33:23 发布

您现在位置:Python中文网/ 问答频道 /正文

我想从一个每隔一段时间刷新自己的列表中一次提取一个代理,我对此没有任何问题。你知道吗

有些代理不好,我希望使用列表中的下一个代理。这就是我的生成器所在,但是,当我第一次调用a.next()时,第二次调用它时,我可以得到相同的值!你知道吗

很明显,我在理解发电机的工作原理时,肯定遗漏了一个关键部分。你知道吗

我的生成器代码位于ProxyHandler类中:

class ProxyHandler:

    def __init__(self):
        self.proxies = list()
        self.current = dict()

    def get_proxies(self):
        """ Retrieves proxies """

    def __len__(self):
        return len(self.proxies)

    def yield_proxy(self):
        if not self.proxies:
            print 'Created new proxy list'
            self.get_proxies()  # This populates self.proxies which is a list of tuples where the 0th element is the host and the 1st element is the port
        for p in self.proxies:
            try:
                proxy = {'http': 'http://%s:%s' % (p[0], p[1])}  # Formatted to python's request lib proxy format
                self.current = proxy
                yield proxy
            except StopIteration:
                print 'Reached end of proxy list'
                self.current = {}
                self.get_proxies()
                yield self.yield_proxy()

使用方法:

def get_response(self, url):
    proxy = self.proxy_handler.current
    if proxy == {}:
        proxy = self.proxy_handler.yield_proxy().next()
    print 'Current proxy -', proxy
    response = url_request(url, proxy=proxy)  # url_request() is basically a modified version of python's requests
    print response
    if response: # url_request() returns true if status code == 200
        return response, proxy
    gen = self.proxy_handler.yield_proxy()
    gen.next()
    return self.get_ebay_response(url)

Tags: theselfurlgetifisresponserequest
1条回答
网友
1楼 · 发布于 2024-04-24 09:33:23

每次都要重新创建生成器:

gen = self.proxy_handler.yield_proxy()
gen.next()

新的生成器从头开始;单独的生成器不共享状态。将生成器存储在某个位置,然后重用该对象以获取新值。你知道吗

您也许可以将生成器对象作为属性存储在self

proxy_generator = None

def get_response(self, url):
    if not self.proxy:
        if self.proxy_generator is None
            self.proxy_generator = self.proxy_handler.yield_proxy()
        self.proxy = next(self.proxy_generator)
    proxy = self.proxy

我使用^{} function使您的代码与python3保持向前兼容(您迟早要切换到python3,python2现在是一种遗留语言)。你知道吗

接下来,生成器将尝试捕获永远不会抛出的异常:

for p in self.proxies:
    try:
        proxy = {'http': 'http://%s:%s' % (p[0], p[1])}  # Formatted to python's request lib proxy format
        self.current = proxy
        yield proxy
    except StopIteration:
        print 'Reached end of proxy list'
        self.current = {}
        self.get_proxies()
        yield self.yield_proxy()

try中没有被访问的生成器;您将该作业交给了self.proxies上的for循环,for已经知道如何处理迭代器(它将捕获StopIterator来结束循环)。不管怎样,self.proxies只是一个列表。你知道吗

如果你想让循环在你的代理上循环,在一个无休止的while True循环中这样做:

while True:
    for p in self.proxies:
        proxy = {'http': 'http://%s:%s' % (p[0], p[1])}  # Formatted to python's request lib proxy format
        self.current = proxy
        yield proxy

    print 'Reached end of proxy list'
    self.current = {}
    self.get_proxies()

我不知道你为什么认为你需要清除那里的self.current,然后重新获取代理。生成器上的元组从未更改,所以为什么要重新获取?并且当前代理仍然有效,即使您确实从顶部再次启动循环。我会放弃最后三行。你知道吗

您可以进一步简化代码。生成器没有长度,因此不需要__len__方法。该方法最多只能生成错误的信息;在开始迭代之前,self.proxies属性是空的,因此对象的长度从0开始。完全放弃这个方法。你知道吗

接下来,可以为对象提供一个__iter__方法来生成生成器:

class ProxyHandler:
    def __init__(self):
        self.proxies = []
        self.current = {}

    def get_proxies(self):
        """ Retrieves proxies """

    def __iter__(self):
        if not self.proxies:
            print 'Created new proxy list'
            self.get_proxies()
        while True:
            for p in self.proxies:
                proxy = {'http': 'http://%s:%s' % (p[0], p[1])} 
                self.current = proxy
                yield proxy

这使得整个ProxyHandler实例成为一个iterable,只需使用iter(self.proxy_handler)而不是self.proxy_handler.yield_proxy()就可以让生成器生成所有这些值。你知道吗

最后但并非最不重要的一点是,您可以对整个过程使用生成器表达式,再加上^{}使迭代器无止境。但是,您必须删除current属性,但这并不重要,因为当生成器生成当前对象时,实际上并不需要该属性:

from itertools import cycle

class ProxyHandler:
    def __init__(self):
        self.proxies = []

    def get_proxies(self):
        """ Retrieves proxies """

    def __iter__(self):
        if not self.proxies:
            print 'Created new proxy list'
            self.get_proxies()
        return cycle({'http': 'http://%s:%s' % (p[0], p[1])} for p in self.proxies)

生成器表达式生成相同类型的对象。你知道吗

这一切仍然需要iter(self.proxy_generator);您可以通过让__iter__返回self,并添加一个next()方法,使实例迭代器(而不是iterable);在第一次调用时,将上面的生成器表达式移动到一个属性,然后将next()调用传递到该属性以生成值:

class ProxyHandler:
    def __init__(self):
        self.proxies = []
        self._gen = None

    def get_proxies(self):
        """ Retrieves proxies """

    def __iter__(self):
        return self

    def next(self):
        if not self._gen:
            self.get_proxies()
            self._gen = cycle({'http': 'http://%s:%s' % (p[0], p[1])} for p in self.proxies)
        return next(self._gen)

    __next__ = next  # Python 3 compatibility

现在可以使用“下一步”(self.proxy\u处理程序)每次:

def get_response(self, url):
    if not self.proxy:
        self.proxy = next(self.proxy_handler)
    proxy = self.proxy

相关问题 更多 >