ধারক বস্তুর (পাইথন) জন্য কীভাবে __iter __ (স্ব) বাস্তবায়ন করবেন


115

আমি একটি কাস্টম ধারক বস্তু লিখেছি।

এই পৃষ্ঠা অনুসারে , আমার এই বিষয়টিকে আমার অবজেক্টে প্রয়োগ করা দরকার:

__iter__(self)

তবে পাইথন রেফারেন্স ম্যানুয়ালটিতে আইট্রেটার প্রকারের লিঙ্কটি অনুসরণ করার পরে , কীভাবে আপনার নিজের প্রয়োগ করতে হবে তার কোনও উদাহরণ দেওয়া হয়নি।

কেউ কি একটি স্নিপেট (বা কোনও সংস্থার লিঙ্ক) পোস্ট করতে পারে, এটি কীভাবে এটি করতে পারে তা দেখায়?

আমি যে ধারকটি লিখছি তা হ'ল একটি মানচিত্র (অর্থাত্ অনন্য কী দ্বারা মানগুলি সংরক্ষণ করে)। ডিক্টগুলি এভাবে পুনরাবৃত্তি করা যায়:

for k, v in mydict.items()

এই ক্ষেত্রে আমার পুনরাবৃত্তিতে দুটি উপাদান (একটি টিপল?) ফেরত দিতে সক্ষম হতে হবে? কীভাবে এই জাতীয় পুনরাবৃত্তি কার্যকর করা যায় তা এখনও পরিষ্কার নয় (বেশ কয়েকটি উত্তর যে দয়া করে সরবরাহ করা হয়েছে)। কেউ দয়া করে কীভাবে মানচিত্রের মতো ধারক বস্তুর জন্য একটি ইেটরেটর কার্যকর করতে পারে তার উপর আরও কিছু আলোকপাত করতে পারেন? (অর্থাত্ একটি কাস্টম শ্রেণি যা ডিকের মতো কাজ করে)?

উত্তর:


120

আমি সাধারণত একটি জেনারেটর ফাংশন ব্যবহার করব। প্রতিবার আপনি যখন একটি উত্পাদনের বিবৃতি ব্যবহার করবেন, এটি ক্রমে একটি আইটেম যুক্ত করবে।

নিম্নলিখিতটি একটি পুনরুক্তি তৈরি করবে যা পাঁচটি ফলন দেয় এবং তারপরে কিছু_ তালিকাতে প্রতিটি আইটেম।

def __iter__(self):
   yield 5
   yield from some_list

প্রাক-3.3, yield fromবিদ্যমান ছিল না, তাই আপনাকে এটি করতে হবে:

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

কেউ কি স্টপআইটারেশন বাড়াতে হবে? এই থামার জন্য কিভাবে পার্থক্য আছে?
জোনাথন

1
@ জোনাথনলিডার্স যখন সমস্ত উপাদান পাওয়া যায় some_list
লাইক

প্রকৃতপক্ষে - এই ব্যবহারের ক্ষেত্রে - আপনার কেবলমাত্র স্টিপট্রেটিশন বাড়াতে হবে যদি আপনি অবসানের আগে মানগুলি বন্ধ করতে চান some_list
টিম পিপলস

21
StopIterationজেনারেটর ফাংশন ফিরে আসার পরে পাইথন দ্বারা স্বয়ংক্রিয়ভাবে উত্থাপিত হয়, হয় আপনি স্পষ্টভাবে কল করে returnবা ফাংশনটির শেষে পৌঁছে (যা সমস্ত ফাংশনের মতো return Noneশেষের দিকে অন্তর্ভুক্ত থাকে)। স্পষ্টভাবে উত্থাপন StopIterationপ্রয়োজন নেই এবং পাইথন 3.5 হিসাবে আসলে কাজ না (দেখতে হবে PEP 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

এনবি : একদিকে পুনরাবৃত্তকারী এবং অন্যদিকে পুনরাবৃত্তির পরিবর্তে পুনরাবৃত্তকারী পাত্রে যে আয়তন রয়েছে তার মধ্যে পার্থক্য স্পষ্ট করার প্রয়োজনীয়তার প্রতি আমার দৃষ্টি আকর্ষণ করার জন্য গ্লেন মেনার্ডকে ধন্যবাদ জানাই।


13
পুনরাবৃত্তিযোগ্য বস্তু এবং পুনরাবৃত্তকারীগুলি মিশ্রন করবেন না - আপনি কোনও পুনরাবৃত্তিযোগ্য অবজেক্টের জন্য ইটেটর থেকে উত্তরাধিকারী হতে চান না যা কোনও পুনরুক্তিকারী নয় (যেমন একটি ধারক)।
গ্লেন মেইনার্ড

@ গ্লেন: আপনি সঠিক যে একটি সাধারণ ধারক পুনরাবৃত্তিকারী নয়। আমি কেবল এই প্রশ্নটি অনুসরণ করেছি, যা পুনরাবৃত্তকারী প্রকারের উল্লেখ করে। আমি উত্তরটির শেষে বলেছি যে আরও উপযুক্ত বিকল্প থেকে উত্তরাধিকারী হওয়া আরও উপযুক্ত I আমি উত্তরে এই বিষয়টি পরিষ্কার করব।
মুহাম্মদ আলকারৌরি

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- জানুয়ারি / 044455.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
আইসআর্ডার

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__ দিয়ে পুনরাবৃত্তি করা উচিত । নিম্নলিখিতটি একটি সাধারণ উদাহরণ যা ম্যাপিং তৈরি করে এবং পাইথন 3 এ বি সি ম্যাপিং প্রসারিত করে কাজ করে।x -> x * x

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। এটি একটি যৌক্তিক পছন্দ মতো মনে হয় যদি এটি ডিকের মতো কাজ করে; হয়তো এটা করা উচিত হবে একটি অভি। এইভাবে, আপনি বিনামূল্যে ডিকের মতো পুনরাবৃত্তি পান।

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
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.