2017-04-30 9 views
2

간격을두고 새로 고치는 목록에서 한 번에 하나씩 프록시를 가져 오려고합니다. 그 문제는 없습니다.(Mis) Generators 이해

프록시 중 일부는 좋지 않으므로 목록에서 다음 프록시를 사용하고 싶습니다. 이것은 제 생성기가 들어오는 곳입니다. 그러나 처음으로 .next()를 호출하여 생성기 롤링을 얻을 수는 있지만, 두 번째 호출에서는 동일한 값을 얻습니다!

확실히 발전기 작동 방식을 이해하는 데 중요한 부분을 놓치고 있어야합니다.

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() 

및 사용 :

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

새로운 발전기가 시작 :

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) 

답변

2

당신은 발전기 새롭게마다 만드는

내 발전기 코드는 ProxyHandler 클래스 내에 처음부터; 별도의 발전기는 상태를 공유하지 않습니다. 생성기를 어딘가에 저장 한 다음 해당 객체를 다시 사용하여 새 값을 가져옵니다.

당신은 아마도 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 

난 당신이 조만간 전환해야합니다 파이썬 3 (포워드 호환 코드를 유지하기 위해 next() function를 사용, 파이썬 2는 이제 레거시 언어입니다.)

다음으로, 발전기는 발생하지 않을 것이다 예외 잡으려고 시도 :에서

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으로 이미은 루프를 끝내기 위해 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부터 시작합니다. 메소드를 모두 삭제하십시오.

이는 반복 가능한 전체 ProxyHandler 인스턴스를 만드는
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 

, 단지 모든 값을 생성하는 발전기를 얻기 위해 대신 self.proxy_handler.yield_proxy()iter(self.proxy_handler)를 사용

다음으로, 당신은 당신의 객체를 발전기를 생산하는 __iter__ 방법을 제공 할 수 있습니다 .

마지막으로, 반복자를 무한하게 만들기 위해 전체 표현식을 itertools.cycle()과 함께 사용할 수 있습니다.

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) 

발전기의 표현이 생성 발전기 그냥 어쨌든 현재 객체 를 산출 할 때 그러나, current 속성을 삭제해야하지만 실제로 속성을 필요로하지 않는 한 그건 정말 문제가되지해야 같은 종류의 물건.

이 모두가 여전히 필요합니다. iter(self.proxy_generator); __iter__ return self을 가지고 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_handler)를 사용할 수 있습니다 때마다 :

def get_response(self, url): 
    if not self.proxy: 
     self.proxy = next(self.proxy_handler) 
    proxy = self.proxy 
첫 번째 호출에 속성에 위의 발전기 식을 이동 한 후 값을 생성하기 위해 그에게 next() 호출 전달
+0

그래서이 문제를 해결하기 위해 내 메서드 외부에서 생성자를 만들고 필요할 때 호출해야합니다. –

+0

@galalmighty : 정확하게. 생성기는 상태를 유지하는 단일 객체입니다. 값을 필요로 할 때마다 교체하지 말고 주변에 두십시오. –

+0

통찰력 있고 자세하며 매우 명확합니다. 고맙습니다! 약 10 분 만에 많은 것을 배웠습니다. –