복붙노트

[PYTHON] 루프 및 반복자 동작을위한 Python

PYTHON

루프 및 반복자 동작을위한 Python

이터레이터에 대해 좀 더 이해하고 싶었 기 때문에 내가 틀렸다면 수정 해주십시오.

반복자는 다음 객체에 대한 포인터를 갖는 객체이며 버퍼 또는 스트림 (즉, 링크 된 목록)으로 읽혀집니다. 특히 색인을 사용하는 대신 참조 할 항목을 알려주는 것이 효과적입니다.

그러나 나는 왜 아직도 다음과 같은 행동이 일어나는지 이해하지 못한다.

In [1]: iter = (i for i in range(5))

In [2]: for _ in iter:
   ....:     print _
   ....:     
0
1
2
3
4

In [3]: for _ in iter:
   ....:     print _
   ....:     

In [4]: 

iterator (In [2])를 통해 첫 번째 루프가 소비 된 것처럼 비워 두 번째 루프 (In [3])는 아무 것도 출력하지 않도록합니다.

그러나 iter 변수에 새로운 값을 할당하지 않았습니다.

for 루프의 두드러기에서 실제로 일어나고있는 것은 무엇입니까?

해결법

  1. ==============================

    1.귀하의 의심은 정확합니다. 반복기가 사용되었습니다.

    귀하의 의심은 정확합니다. 반복기가 사용되었습니다.

    사실, 반복자는 생성자이며 한 번만 반복 할 수있는 객체입니다.

    type((i for i in range(5))) # says it's type generator 
    
    def another_generator():
        yield 1 # the yield expression makes it a generator, not a function
    
    type(another_generator()) # also a generator
    

    그들이 효율적이라는 이유는 다음에 "참조로"무엇을 말할지와 아무런 관련이 없습니다. 요청에 따라 다음 항목 만 생성하기 때문에 효율적입니다. 모든 항목이 한 번에 생성되지 않습니다. 사실 무한 생성기를 사용할 수 있습니다.

    def my_gen():
        while True:
            yield 1 # again: yield means it is a generator, not a function
    
    for _ in my_gen(): print(_) # hit ctl+c to stop this infinite loop!
    

    이해를 돕기위한 몇 가지 수정 사항 :

    당신은 "수동으로"파이썬에서 이런 식으로 for 루프를 구현할 수 있습니다 (완벽하지는 않지만 충분히 가깝습니다) :

    try:
        temp = iterable.__iter__()
    except AttributeError():
        raise TypeError("'{}' object is not iterable".format(type(iterable).__name__))
    else:
        while True:
            try:
                _ = temp.__next__()
            except StopIteration:
                break
            except AttributeError:
                raise TypeError("iter() returned non-iterator of type '{}'".format(type(temp).__name__))
            # this is the "body" of the for loop
            continue
    

    위와 예제 코드는 거의 차이가 없습니다.

    사실 for 루프의 흥미로운 부분은 for가 아니라 in이다. in을 사용하는 것은 in과는 다른 결과를 낳지 만, 인수를 사용하여 수행하는 작업을 이해하는 것은 매우 유용하다. 비슷한 행동.

    반복 할 고유 한 객체 유형을 만들려면 (즉, in 또는 in에 사용할 수 있음) 생성자에서 사용되는 yield 키워드에 대해 알고 있으면 유용합니다 (위에서 언급 한 바와 같이).

    class MyIterable():
        def __iter__(self):
            yield 1
    
    m = MyIterable()
    for _ in m: print(_) # 1
    1 in m # True    
    

    yield가 있으면 함수 또는 메소드가 일반 함수 / 메소드 대신 생성기로 바뀝니다. 생성기를 사용하는 경우 __next__ 메소드가 필요하지 않습니다 (자동으로 __next__를 가져옵니다).

    고유 한 컨테이너 객체 유형을 만들려면 (즉, in을 사용할 수 있지만 in은 사용할 수 없음) __contains__ 메소드가 필요합니다.

    class MyUselessContainer():
        def __contains__(self, obj):
            return True
    
    m = MyUselessContainer()
    1 in m # True
    'Foo' in m # True
    TypeError in m # True
    None in m # True
    

    1 iterator가 되려면 객체가 반복자 프로토콜을 구현해야합니다. 이는 __next__ 및 __iter__ 메소드가 모두 올바르게 구현되어야 함을 의미합니다 (생성자는 "무료"로이 기능을 제공하므로 사용시 걱정할 필요가 없습니다). 또한 ___next__ 메쏘드는 실제로 파이썬 2에서 다음 (밑줄 없음)임을주의하십시오.

    2 반복 가능한 클래스를 만드는 여러 가지 방법에 대해이 대답을 참조하십시오.

  2. ==============================

    2.For 루프는 기본적으로 Python 3에서 (__next__)에 적용된 객체의 다음 메소드를 호출합니다.

    For 루프는 기본적으로 Python 3에서 (__next__)에 적용된 객체의 다음 메소드를 호출합니다.

    다음과 같이 간단히 시뮬레이션 할 수 있습니다.

    iter = (i for i in range(5))
    
    print(next(iter))
    print(next(iter))  
    print(next(iter))  
    print(next(iter))  
    print(next(iter)) 
    
    # this prints 1 2 3 4 
    

    이 시점에서 입력 개체에는 다음 요소가 없습니다. 그래서 이렇게 :

    print(next(iter))  
    

    StopIteration 예외가 throw됩니다. 이 시점에서 멈출 것입니다. iterator는 next () 함수에 응답하고 더 이상의 요소가 없을 때 예외를 throw하는 객체 일 수 있습니다. 그것은 포인터 또는 참조 (C / C ++의 의미에서 파이썬에서는 그런 것들이 없습니다), 링크 된리스트 등일 필요는 없습니다.

  3. ==============================

    3.파이썬에는 for 문이리스트와 딕셔너리와 함께 동작하는 방법과 반복 될 수있는 다른 것들을 정의하는 반복자 프로토콜이있다.

    파이썬에는 for 문이리스트와 딕셔너리와 함께 동작하는 방법과 반복 될 수있는 다른 것들을 정의하는 반복자 프로토콜이있다.

    여기 파이썬 문서에 나와 있습니다.

    반복자 프로토콜이 일반적으로 작동하는 방식은 비단뱀 생성기의 형태입니다. 우리는 끝까지 도달 할 때까지 가치가있는 한 값을 내고 StopIteration을 올립니다.

    자 이제 우리 자신의 반복자를 작성해 보겠습니다.

    def my_iter():
        yield 1
        yield 2
        yield 3
        raise StopIteration()
    
    for i in my_iter():
        print i
    

    결과는 다음과 같습니다.

    1
    2
    3
    

    그것에 대해주의해야 할 몇 가지. my_iter는 함수입니다. my_iter ()는 반복자를 반환합니다.

    대신에 다음과 같이 iterator를 사용하여 작성했다면 :

    j = my_iter()    #j is the iterator that my_iter() returns
    for i in j:
        print i  #this loop runs until the iterator is exhausted
    
    for i in j:
        print i  #the iterator is exhausted so we never reach this line
    

    결과는 위와 같습니다. iter는 두 번째 for 루프에 들어갈 때가 지 소모됩니다.

    그러나 그것은 다소 단순한 무엇이 더 복잡합니까? 어쩌면 루프에 왜 없을까요?

    def capital_iter(name):
        for x in name:
            yield x.upper()
        raise StopIteration()
    
    for y in capital_iter('bobert'):
        print y
    

    그리고 실행될 때 iterator를 문자열 유형 (iter에 내장되어 있습니다)에서 사용합니다. 이것은 차례로 우리가 for 루프를 실행하고, 끝날 때까지 결과를 산출 할 수있게 해줍니다.

    B
    O
    B
    E
    R
    T
    

    이제는 이것이 질문을 던지므로 반복자의 수익률은 어떻게됩니까?

    j = capital_iter("bobert")
    print i.next()
    print i.next()
    print i.next()
    
    print("Hey there!")
    
    print i.next()
    print i.next()
    print i.next()
    
    print i.next()  #Raises StopIteration
    

    그 대답은 next ()에 대한 다음 호출을 기다리는 yield에서 함수가 일시 중지되었다는 것입니다.

    B
    O
    B
    Hey There!
    E
    R
    T
    Traceback (most recent call last):
      File "", line 13, in 
        StopIteration
    
  4. ==============================

    4.자체 __iter__ 메소드가없는 __getitem__ 클래스가있는 iter ()의 동작에 대한 몇 가지 추가 세부 정보입니다.

    자체 __iter__ 메소드가없는 __getitem__ 클래스가있는 iter ()의 동작에 대한 몇 가지 추가 세부 정보입니다.

    __iter__ 전에는 __getitem__이있었습니다. __getitem__이 0 - len (obj) -1의 int로 작동하면 iter ()는 이러한 객체를 지원합니다. StopIteration으로 변환되는 IndexError를 가져올 때까지 0, 1, 2, ...와 반복하여 __getitem__을 호출하는 새 반복기를 생성합니다.

    이터레이터를 만드는 다양한 방법에 대한 자세한 내용은이 대답을 참조하십시오.

  5. ==============================

    5.개념 1

    개념 1

    개념 2

    개념 3

    귀하의 경우

    >>> it = (i for i in range(5))
    >>> type(it)
    <type 'generator'>
    >>> callable(getattr(it, 'iter', None))
    False
    >>> callable(getattr(it, 'next', None))
    True
    
  6. ==============================

    6.Python Practice 책에서 발췌 :

    Python Practice 책에서 발췌 :

    우리는 목록을 반복하는 문장을 사용합니다.

    >>> for i in [1, 2, 3, 4]:
    ...     print i,
    ...
    1
    2
    3
    4
    

    문자열과 함께 사용하면 문자 위로 반복됩니다.

    >>> for c in "python":
    ...     print c
    ...
    p
    y
    t
    h
    o
    n
    

    사전과 함께 사용하면 키를 반복합니다.

    >>> for k in {"x": 1, "y": 2}:
    ...     print k
    ...
    y
    x
    

    파일과 함께 사용하면 파일의 행을 반복합니다.

    >>> for line in open("a.txt"):
    ...     print line,
    ...
    first line
    second line
    

    따라서 for 루프와 함께 사용할 수있는 많은 유형의 객체가 있습니다. 이를 반복 가능한 객체라고합니다.

    이러한 반복 가능한 함수를 사용하는 많은 함수가 있습니다.

    >>> ",".join(["a", "b", "c"])
    'a,b,c'
    >>> ",".join({"x": 1, "y": 2})
    'y,x'
    >>> list("python")
    ['p', 'y', 't', 'h', 'o', 'n']
    >>> list({"x": 1, "y": 2})
    ['y', 'x']
    

    내장 함수 iter는 반복 가능한 객체를 사용하여 반복자를 반환합니다.

        >>> x = iter([1, 2, 3])
    >>> x
    <listiterator object at 0x1004ca850>
    >>> x.next()
    1
    >>> x.next()
    2
    >>> x.next()
    3
    >>> x.next()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    

    반복자에서 다음 메소드를 호출 할 때마다 우리에게 다음 요소가 제공됩니다. 요소가 없으면 StopIteration을 발생시킵니다.

    반복자는 클래스로서 구현됩니다. 다음은 내장 xrange 함수처럼 작동하는 반복자입니다.

    class yrange:
        def __init__(self, n):
            self.i = 0
            self.n = n
    
        def __iter__(self):
            return self
    
        def next(self):
            if self.i < self.n:
                i = self.i
                self.i += 1
                return i
            else:
                raise StopIteration()
    

    iter 메소드는 객체를 반복 가능하게 만드는 것입니다. 그 뒤에서 iter 함수는 주어진 객체에 대해 iter 메소드를 호출합니다.

    iter의 반환 값은 반복자입니다. 다음 메소드가 있어야하며 더 이상 요소가 없으면 StopIteration을 발생시킵니다.

    그것을 시도하자 :

    >>> y = yrange(3)
    >>> y.next()
    0
    >>> y.next()
    1
    >>> y.next()
    2
    >>> y.next()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 14, in next
    

    많은 내장 함수는 반복자를 인수로 허용합니다.

    >>> list(yrange(5))
    [0, 1, 2, 3, 4]
    >>> sum(yrange(5))
    10
    

    위의 경우 iterator와 iterator는 모두 같은 객체입니다. iter 메서드는 self를 반환합니다. 항상 그럴 필요는 없습니다.

    class zrange:
        def __init__(self, n):
            self.n = n
    
        def __iter__(self):
            return zrange_iter(self.n)
    
    class zrange_iter:
        def __init__(self, n):
            self.i = 0
            self.n = n
    
        def __iter__(self):
            # Iterators are iterables too.
            # Adding this functions to make them so.
            return self
    
        def next(self):
            if self.i < self.n:
                i = self.i
                self.i += 1
                return i
            else:
                raise StopIteration()
    

    iterable과 iterator가 모두 같은 객체라면, 그것은 하나의 반복에서 소비된다.

    >>> y = yrange(5)
    >>> list(y)
    [0, 1, 2, 3, 4]
    >>> list(y)
    []
    >>> z = zrange(5)
    >>> list(z)
    [0, 1, 2, 3, 4]
    >>> list(z)
    [0, 1, 2, 3, 4]
    

    생성자는 반복자 생성을 단순화합니다. 생성기는 단일 값 대신 일련의 결과를 생성하는 함수입니다.

    def yrange(n):
       i = 0
        while i < n:
            yield i
            i += 1
    

    yield 문이 실행될 때마다이 함수는 새로운 값을 생성합니다.

    >>> y = yrange(3)
    >>> y
    <generator object yrange at 0x401f30>
    >>> y.next()
    0
    >>> y.next()
    1
    >>> y.next()
    2
    >>> y.next()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    

    따라서 생성자는 반복자이기도합니다. 반복자 프로토콜에 대해 걱정할 필요가 없습니다.

    "generator"라는 단어는 생성하는 함수와 생성하는 함수를 혼동하는 의미로 사용됩니다. 이 장에서는 생성자를 의미하는 "생성자"와 생성자를 생성하는 함수를 의미하는 "생성자 함수"를 사용합니다.

    내부적으로 어떻게 작동하는지 생각해 볼 수 있습니까?

    생성기 함수가 호출되면 함수 실행을 시작하지 않고 생성기 객체를 반환합니다. 다음 메서드가 처음 호출 될 때 함수는 yield 문에 도달 할 때까지 실행을 시작합니다. 반환 된 값은 다음 호출에서 반환됩니다.

    다음 예제에서는 생성기 개체에서 yield와 next 메서드 호출 간의 상호 작용을 보여줍니다.

    >>> def foo():
    ...     print "begin"
    ...     for i in range(3):
    ...         print "before yield", i
    ...         yield i
    ...         print "after yield", i
    ...     print "end"
    ...
    >>> f = foo()
    >>> f.next()
    begin
    before yield 0
    0
    >>> f.next()
    after yield 0
    before yield 1
    1
    >>> f.next()
    after yield 1
    before yield 2
    2
    >>> f.next()
    after yield 2
    end
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    

    중지

    예제를 보자.

    def integers():
        """Infinite sequence of integers."""
        i = 1
        while True:
            yield i
            i = i + 1
    
    def squares():
        for i in integers():
            yield i * i
    
    def take(n, seq):
        """Returns first n values from the given sequence."""
        seq = iter(seq)
        result = []
        try:
            for i in range(n):
                result.append(seq.next())
        except StopIteration:
            pass
        return result
    
    print take(5, squares()) # prints [1, 4, 9, 16, 25]
    
  7. from https://stackoverflow.com/questions/29403401/python-for-loop-and-iterator-behavior by cc-by-sa and MIT license