कंटेनर ऑब्जेक्ट (पायथन) के लिए __iter __ (स्व) को कैसे लागू करें


115

मैंने एक कस्टम कंटेनर ऑब्जेक्ट लिखा है।

इस पृष्ठ के अनुसार , मुझे इस विधि को अपनी वस्तु पर लागू करने की आवश्यकता है:

__iter__(self)

हालाँकि, पायथन संदर्भ पुस्तिका में Iterator प्रकार के लिंक का अनुसरण करने पर, अपने स्वयं के कार्यान्वयन के तरीके के बारे में कोई उदाहरण नहीं दिया गया है।

क्या कोई स्निपेट (या किसी संसाधन से लिंक) पोस्ट कर सकता है, जिससे पता चलता है कि यह कैसे करना है?

मैं जो कंटेनर लिख रहा हूं, वह एक नक्शा है (यानी अद्वितीय कुंजियों द्वारा मूल्यों को संग्रहीत करता है)। dicts को इस तरह प्रसारित किया जा सकता है:

for k, v in mydict.items()

इस मामले में मुझे पुनरावृत्त में दो तत्वों (एक ट्यूपल?) को वापस करने में सक्षम होने की आवश्यकता है। यह अभी भी स्पष्ट नहीं है कि इस तरह के पुनरावृत्ति को कैसे लागू किया जाए (कई उत्तरों के बावजूद जो कृपया प्रदान किए गए हैं)। क्या कोई कृपया मानचित्र-जैसे कंटेनर वस्तु के लिए एक पुनरावृत्ति को लागू करने के बारे में कुछ और प्रकाश डाल सकता है? (यानी एक कस्टम वर्ग जो एक तानाशाह की तरह काम करता है)?

जवाबों:


120

मैं आमतौर पर एक जनरेटर फ़ंक्शन का उपयोग करता हूं। हर बार जब आप एक उपज कथन का उपयोग करते हैं, तो यह एक आइटम को अनुक्रम में जोड़ देगा।

निम्नलिखित एक पुनरावृत्ति पैदा करेगा जो पाँच उपज देता है, और फिर some_list में प्रत्येक आइटम।

def __iter__(self):
   yield 5
   yield from some_list

Pre-3.3, yield fromमौजूद नहीं था, इसलिए आपको करना होगा:

def __iter__(self):
   yield 5
   for x in some_list:
      yield x

क्या किसी को StopIteration बढ़ाने की आवश्यकता है? जब रोकना है तो यह कैसे भेद करता है?
— जोनाथन

1
@JonathanLeaders जब सभी तत्वों को उपज some_listदिया गया है।
— laike9m

वास्तव में - इस उपयोग के मामले के साथ - आपको स्टॉपइंटरेशन बढ़ाने की आवश्यकता है यदि आप थकने से पहले उपज मूल्यों को रोकना चाहते हैं some_list।
— टिम पीपल्स

21
StopIterationपाइथन द्वारा स्वचालित रूप से उठाया जाता है जब जनरेटर फ़ंक्शन वापस आता है, या तो आप स्पष्ट रूप से कॉल returnकरके या फ़ंक्शन के अंत तक पहुंचते हैं (जो सभी कार्यों की तरह return Noneअंत में निहित है )। स्पष्ट रूप से उठाना StopIterationआवश्यक नहीं है और जैसा कि पायथन 3.5 वास्तव में काम नहीं करेगा ( पीईपी 479 देखें ): जैसे जनरेटर चालू returnकरते हैं StopIteration, वे स्पष्ट रूप raise StopIterationसे एक में बदल जाते हैं RuntimeError।
— आर्थर टाका

28

एक अन्य विकल्प यहां संग्रहित दस्तावेज के रूप में `संग्रह मॉड्यूल से उपयुक्त सार आधार वर्ग से प्राप्त करना है ।

यदि कंटेनर अपना स्वयं का पुनरावृत्त है, तो आप इससे विरासत में प्राप्त कर सकते हैं collections.Iterator। आपको केवल nextविधि को लागू करने की आवश्यकता है ।

एक उदाहरण है:

>>> from collections import Iterator
>>> class MyContainer(Iterator):
...     def __init__(self, *data):
...         self.data = list(data)
...     def next(self):
...         if not self.data:
...             raise StopIteration
...         return self.data.pop()
...         
...     
... 
>>> c = MyContainer(1, "two", 3, 4.0)
>>> for i in c:
...     print i
...     
... 
4.0
3
two
1

जब आप collectionsमॉड्यूल को देख रहे हैं , तो उस से विरासत में मिला है Sequence, Mappingया एक अन्य सार आधार वर्ग पर विचार करें यदि यह अधिक उपयुक्त है। यहाँ एक Sequenceउपवर्ग के लिए एक उदाहरण दिया गया है :

>>> from collections import Sequence
>>> class MyContainer(Sequence):
...     def __init__(self, *data):
...         self.data = list(data)
...     def __getitem__(self, index):
...         return self.data[index]
...     def __len__(self):
...         return len(self.data)
...         
...     
... 
>>> c = MyContainer(1, "two", 3, 4.0)
>>> for i in c:
...     print i
...     
... 
1
two
3
4.0

NB : ग्लेन मेनार्ड के लिए धन्यवाद, एक तरफ पुनरावृत्तियों और कंटेनरों के बीच अंतर को स्पष्ट करने की आवश्यकता पर मेरा ध्यान आकर्षित करने के लिए जो दूसरे पर पुनरावृत्तियों के बजाय पुनरावृत्त होते हैं।


13
पुनरावृत्त वस्तुओं और पुनरावृत्तियों को न मिलाएं - आप पुनरावृत्त वस्तु के लिए Iterator से विरासत में प्राप्त नहीं करना चाहते हैं जो कि एक पुनरावृत्ति नहीं है (उदाहरण के लिए एक कंटेनर)।
— ग्लेन मेयार्ड

@ ग्लेन: आप सही हैं कि एक विशिष्ट कंटेनर एक पुनरावृत्ति नहीं है। मैंने सिर्फ उस प्रश्न का अनुसरण किया, जिसमें पुनरावृत्त प्रकारों का उल्लेख है। मुझे लगता है कि उत्तर के अंत में जैसा कि मैंने कहा था, अधिक उपयुक्त विकल्प से विरासत में प्राप्त करना अधिक उपयुक्त है। मैं जवाब में इस बिंदु को स्पष्ट करूंगा।
— मुहम्मद अलकरौरी

13

__iter__()यदि आप पहले से ही अगले () विधि (जनरेटर वस्तु) को परिभाषित कर चुके हैं तो आमतौर पर स्व वापस लौटें:

यहाँ एक जनरेटर का एक डमी उदाहरण है:

class Test(object):

    def __init__(self, data):
       self.data = data

    def next(self):
        if not self.data:
           raise StopIteration
        return self.data.pop()

    def __iter__(self):
        return self

लेकिन __iter__()इसका उपयोग इस तरह भी किया जा सकता है: http://mail.python.org/pipermail/tutor/2006-Januge/044525.html


7
यही आप इटेरेटर वर्गों के लिए करते हैं, लेकिन सवाल कंटेनर ऑब्जेक्ट्स के बारे में है।
— ग्लेन मेनार्ड

11

यदि आपकी वस्तु में डेटा का एक सेट है, जिसे आप अपनी वस्तु के लिए करना चाहते हैं, तो आप धोखा दे सकते हैं और ऐसा कर सकते हैं:

>>> class foo:
    def __init__(self, *params):
           self.data = params
    def __iter__(self):
        if hasattr(self.data[0], "__iter__"):
            return self.data[0].__iter__()
        return self.data.__iter__()
>>> d=foo(6,7,3,8, "ads", 6)
>>> for i in d:
    print i
6
7
3
8
ads
6

2
जाँच करने के बजाय hasattr, उपयोग करेंtry/except AttributeError
— IceArdor

9

अजगर में "चलने योग्य इंटरफ़ेस" में दो तरीके हैं __next__()और __iter__()। __next__समारोह सबसे महत्वपूर्ण है, के रूप में यह इटरेटर व्यवहार को परिभाषित करता - यह है कि, समारोह निर्धारित करता है क्या मूल्य अगले किया जाना चाहिये। __iter__()विधि यात्रा के प्रारंभिक बिंदु पुनर्स्थापित करने के लिए प्रयोग किया जाता है। अक्सर, आप पाएंगे कि __iter__()जब __init__()शुरुआती बिंदु को सेट करने के लिए उपयोग किया जाता है तो बस स्व वापस कर सकते हैं ।

एक क्लास रिवर्स को परिभाषित करने के लिए निम्नलिखित कोड देखें जो "चलने योग्य इंटरफ़ेस" को लागू करता है और किसी भी अनुक्रम वर्ग से किसी भी उदाहरण पर एक पुनरावृत्ति को परिभाषित करता है। __next__()विधि अनुक्रम के विपरीत क्रम में अनुक्रम और रिटर्न मूल्यों के अंत में शुरू होता है। ध्यान दें कि "अनुक्रम इंटरफ़ेस" को लागू करने वाले एक वर्ग के उदाहरणों को एक __len__()और __getitem__()विधि को परिभाषित करना चाहिए ।

class Reverse:
    """Iterator for looping over a sequence backwards."""
    def __init__(self, seq):
        self.data = seq
        self.index = len(seq)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]

>>> rev = Reverse('spam')
>>> next(rev)   # note no need to call iter()
'm'
>>> nums = Reverse(range(1,10))
>>> next(nums)
9

7

मैपिंग के बारे में प्रश्न का उत्तर देने के लिए : आपके द्वारा प्रदान की गई मैपिंग__iter__ की कुंजियों पर चलना चाहिए । निम्नलिखित एक सरल उदाहरण है जो मैपिंग बनाता है x -> x * xऔर एबीसी मैपिंग का विस्तार पायथन 3 पर काम करता है।

import collections.abc

class MyMap(collections.abc.Mapping):
    def __init__(self, n):
        self.n = n

    def __getitem__(self, key): # given a key, return it's value
        if 0 <= key < self.n:
            return key * key
        else:
            raise KeyError('Invalid key')

    def __iter__(self): # iterate over all keys
        for x in range(self.n):
            yield x

    def __len__(self):
        return self.n

m = MyMap(5)
for k, v in m.items():
    print(k, '->', v)
# 0 -> 0
# 1 -> 1
# 2 -> 4
# 3 -> 9
# 4 -> 16

4

यदि आप dictदूसरों के सुझाव के अनुसार वारिस नहीं बनना चाहते हैं , तो इस सवाल का सीधा जवाब है कि __iter__कस्टम डिक्टेट के क्रूड उदाहरण के लिए कैसे लागू किया जाए :

class Attribute:
    def __init__(self, key, value):
        self.key = key
        self.value = value

class Node(collections.Mapping):
    def __init__(self):
        self.type  = ""
        self.attrs = [] # List of Attributes

    def __iter__(self):
        for attr in self.attrs:
            yield attr.key

यह एक जनरेटर का उपयोग करता है, जिसे यहां अच्छी तरह से वर्णित किया गया है ।

चूंकि हम विरासत में प्राप्त कर रहे हैं Mapping, इसलिए आपको भी लागू करने की आवश्यकता है __getitem__और __len__:

    def __getitem__(self, key):
        for attr in self.attrs:
            if key == attr.key:
                return attr.value
        raise KeyError

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

2

एक विकल्प है कि कुछ मामलों के लिए काम कर सकते हैं अपने कस्टम वर्ग बनाने के लिए है इनहेरिट से dict। यह एक तार्किक विकल्प की तरह लगता है अगर यह एक तानाशाह की तरह काम करता है; शायद यह चाहिए होना एक dict। इस तरह, आपको मुफ्त में ताना-बाना पसंद आता है।

class MyDict(dict):
    def __init__(self, custom_attribute):
        self.bar = custom_attribute

mydict = MyDict('Some name')
mydict['a'] = 1
mydict['b'] = 2

print mydict.bar
for k, v in mydict.items():
    print k, '=>', v

आउटपुट:

Some name
a => 1
b => 2

2

उदाहरण के लिए, इनहर्ट से तानाशाही को संशोधित करें iter, उदाहरण के लिए, 2जब लूप के लिए कुंजी छोड़ें

# method 1
class Dict(dict):
    def __iter__(self):
        keys = self.keys()
        for i in keys:
            if i == 2:
                continue
            yield i

# method 2
class Dict(dict):
    def __iter__(self):
        for i in super(Dict, self).__iter__():
            if i == 2:
                continue
            yield i
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.