83122


### <後日談>

回答の指摘で気づけましたが、単なる勘違いでした。`__next` となっていたのを見逃していたのが原因でした。経験の浅い言語のため、原因を
`__next__` に限定できていなかったのが根本原因だと受け止めます。

* * *

問題は解決しましたが、その解決方法の理屈が分からないので質問します。  
よろしくお願いします。

### <質問>

自作のクラスをjinja2に渡して繰り返し処理を組んでいます。  
この時__iter__を使って繰り返し処理を実現しますが、次の前者ではエラーになります。

  * ［NG］`__next__` を使った `__iter__`（※通常はこれで間にあうと思っている）
  * ［OK］yieldを使った `__iter__`（※エラーのため、調べて見つけた対処方法）

エラーになる/ならないの違いは何でしょうか。

### <確認したこと>

  * サンプル（※後述）ではどちらも問題なく表示されます。
  * エラーが発生しているコードに対して、doctestを利用した再現コードを書いてエラーが発生することを確認しました。（※後述）
  * `__iter__` と `__getitem__`, `__setitem__` それぞれで検索しましたが、特に該当しそうな情報は見当たらず。
  * （追加）怪しいと思っていた特殊メソッドを使う形でサンプルを作りましたが、サンプルでは再現しませんでした。

### <予想>

エラーが出ているオブジェクトは他のクラスを継承しており、また特殊メソッドを定義しています。  
この辺りが怪しいと思っています。（※サンプルでは再現しません）

実際のコードはコード量が多いので、使っている特殊メソッドを列挙します。

  * `__init__`
  * `__len__`
  * `__getitem__`（※これがあるせい？）
  * `__setitem__`
  * `__eq__`
  * `__str__`
  * `__repr__`
  * `__iter__`
  * `__next__`（※現在は未使用）

### <エラー、doctest>

doctestがパスするコード

[code]

     401     def __iter__(self):
     402         #jinja2用
     403         for isruby, value in self.asruby():
     404             yield (isruby, value)
     405         #self._iterator = self.asruby()
     406         #self._iter_counter = -1
     407         #return self
    
[/code]

doctestがエラーになるコード

[code]

     401     def __iter__(self):
     402         #jinja2用
     403         self._iterator = self.asruby()
     404         self._iter_counter = -1
     405         return self
     406         #for isruby, value in self.asruby():
     407         #    yield (isruby, value)
    
[/code]

エラー

[code]

    $ python src/__init__.py
    **********************************************************************
    File "src/__init__.py", line 564, in __main__.debug_kana_text
    Failed example:
        debug_kana_text()
    Exception raised:
        Traceback (most recent call last):
          File "/usr/lib/python3.8/doctest.py", line 1336, in __run
            exec(compile(example.source, filename, "single",
          File "<doctest __main__.debug_kana_text[0]>", line 1, in <module>
            debug_kana_text()
          File "src/__init__.py", line 570, in debug_kana_text
            for e in term:
        TypeError: iter() returned non-iterator of type 'KanaText'
    **********************************************************************
    1 items had failures:
       1 of   1 in __main__.debug_kana_text
    ***Test Failed*** 1 failures.
    
    $
    
[/code]

doctest

[code]

     561
     562 def debug_kana_text():
     563     """
     564     >>> debug_kana_text()
     565     (True, ('壱', 'い'))
     566     (True, ('弐', 'ろ'))
     567     (True, ('参', 'は'))
     568     """
     569     term = KanaText('いろは|壱弐参^111')
     570     for e in term:
     571         print(e)
     572
    
[/code]

### <サンプル>

[code]

    #!/usr/bin/python
    from jinja2 import Template
    
    class cls1(object):
        def __iter__(self):
            self._counter = -1
            self._list = ["a", "b", "c"]
            return self
        def __next__(self):
            self._counter += 1
            try:
                return self._list[self._counter]
            except IndexError:
                raise StopIteration
        def __len__(self):
            return len(self._list)
        def __getitem__(self, key):
            return self._list[key]
        def __setitem__(self, key, value):
            self._list[key] = value
        def __str__(self):
            rtn = ""
            for d in self._list:
                rtn += d
        def __repr__(self):
            rtn = ""
            for d in self._list:
                rtn += d+","
    
    class cls2(object):
        def __iter__(self):
            for e in ["a", "b", "c"]:
                yield e
    
    tpl_text = """{%- for val in value -%}
    a = {{ val }}
    {% endfor -%}"""
    template = Template(tpl_text)
    
    a = cls1()
    data = {'value': a}
    text = template.render(data)
    print(text)
    
    b = cls2()
    data = {'value': b}
    text = template.render(data)
    print(text)
    
[/code]

実行結果

[code]

    $ ./iter.py
    a = a
    a = b
    a = c
    
    a = a
    a = b
    a = c
    
    
    $
    
[/code]


CC BY-SA 4.0
2021-10-15T07:13:01.047
83112
2021-10-16T20:27:35.477
2021-10-16T20:27:35.477
4236
48452
question
python python3 sphinx
__next__を使った__iter__はエラーになって、yieldを使った__iter__はエラーにならない


問題となるコードが提示できないようなので、これは推測になるのですが、[イテレータ型](https://docs.python.org/ja/3/library/stdtypes.html#iterator-
types)として振る舞うためには、`__iter__`メソッドと`__next__`メソッドが必要です。

doctestがエラーになるコードを見ると、`__iter__`メソッドではselfを返していますが、このときのselfは`__next__`メソッドを実装していないのだと思います。（※現在は未使用と書いてたため推測）  
そのため、selfで返されるKanaTextオブジェクトは、イテレータ型扱いにならずにエラーになるのかと思います。

[code]

    def __iter__(self):
        #jinja2用
        self._iterator = self.asruby()
        self._iter_counter = -1
        return self   # selfがイテレータオブジェクトでしょうか？？
    
[/code]

`asruby()`関数がイテレータを返すメソッドになっているようなので、単純にそれを返せばdoctestがパスするコードと同じようになると思います。

[code]

    def __iter__(self):
        #jinja2用
        return self.asruby()   # asruby()がイテレータオブジェクトを返してくれるので
                               # 自身はイテレータでなくてよい。(__next__不要)
    
[/code]

サンプルコードのcls1は、`__iter__`メソッドと`__next__`メソッドを実装しており、selfで返される自分自身がイテレータとして振る舞うようになっています。したがってこれは正しく動作します。


CC BY-SA 4.0
2021-10-16T00:36:19.607
83122
2021-10-16T00:36:19.607
28626
83112
answer