මම කැමතියි තව ටිකක් ආලෝකය විහිදුවන්න iter
, __iter__
සහ __getitem__
තිර පිටුපස සිදුවන්නේ කුමක්ද යන්න. එම දැනුමෙන් සන්නද්ධව ඔබට කළ හැකි හොඳම දේ වන්නේ මන්දැයි ඔබට තේරුම් ගත හැකිය
try:
iter(maybe_iterable)
print('iteration will probably work')
except TypeError:
print('not iterable')
මම පළමුව කරුණු ලැයිස්තුගත කර පසුව ඔබ for
පයිතන් වල ලූපයක් භාවිතා කරන විට සිදුවන්නේ කුමක්ද යන්න පිළිබඳ ඉක්මන් මතක් කිරීමක් කර පසුව කරුණු නිදර්ශනය කිරීම සඳහා සාකච්ඡාවක් පවත්වමි.
කරුණු
ඔබ යම් වස්තුවක් සිට iterator ලබා ගත හැක o
ඇමතීම මගින් iter(o)
පහත කොන්දේසි අවම වශයෙන් එක් සත්ය නම්:
අ) o
සතුව, __iter__
ක iterator වස්තුව නැවත වන ක්රමය. Iterator යනු __iter__
and සහ a __next__
(Python 2 :) ක්රමයක් ඇති ඕනෑම වස්තුවකි next
.
ආ) o
සතුව __getitem__
ක්රමය.
නිදසුනක් සඳහා Iterable
හෝ Sequence
, හෝ ගුණාංගය පරීක්ෂා කිරීම __iter__
ප්රමාණවත් නොවේ.
වස්තුවක් නම් o
උපකරණ පමණක් __getitem__
, නමුත් __iter__
, iter(o)
උත්සාහක දිනුම් සිට භාණ්ඩ ලබා ගැනීමට බව iterator ඉදි කරන o
දර්ශකය 0 ට ඇරඹෙන iterator පූර්ණ සංඛ්යාමය දර්ශකය ඕනෑම අල්ලා ඇත IndexError
(නමුත් වෙනත් කිසිදු දෝෂ) මතු පසුව මතු වන බව StopIteration
ම.
වඩාත් සාමාන්ය අර්ථයෙන් ගත් කල, iter
එය නැවත උත්සාහ කළ තැනැත්තා විසින් නැවත ලබා දුන් අයෙක් පරීක්ෂා කර බැලීමට හැකියාවක් තිබේද යන්න පරීක්ෂා කිරීමට ක්රමයක් නොමැත .
වස්තුවක් o
ක්රියාත්මක කරන්නේ නම් __iter__
, iter
ශ්රිතය මඟින් ආපසු ලබා දුන් වස්තුව __iter__
අනුකාරකයක් බව සහතික කරයි . වස්තුවක් පමණක් ක්රියාත්මක කරන්නේ දැයි සනීපාරක්ෂක පරීක්ෂණයක් නොමැත __getitem__
.
__iter__
ජය. වස්තුවක් නම් o
උපකරණ දෙකම __iter__
හා __getitem__
, iter(o)
කියනු ඇත __iter__
.
ඔබේම වස්තූන් නැවත ක්රියාත්මක කිරීමට අවශ්ය නම්, සෑම විටම __iter__
ක්රමය ක්රියාත්මක කරන්න .
for
ලූප
ඉදිරියට යාම සඳහා, ඔබ for
පයිතන් හි ලූපයක් භාවිතා කරන විට කුමක් සිදුවේද යන්න පිළිබඳ අවබෝධයක් අවශ්ය වේ. ඔබ දැනටමත් දන්නවා නම් ඊළඟ කොටසට දකුණට යාමට නිදහස් වන්න.
ඔබ for item in o
කිසියම් ක්රියාකාරී වස්තුවක් සඳහා භාවිතා කරන විට o
, පයිතන් ඇමතුම් ලබා iter(o)
දෙන අතර ප්රතිලාභ අගය ලෙස iterator වස්තුවක් අපේක්ෂා කරයි. ඉරේටරයක් යනු __next__
(හෝ next
පයිතන් 2) ක්රමයක් සහ __iter__
ක්රමයක් ක්රියාත්මක කරන ඕනෑම වස්තුවකි .
සම්මුතිය අනුව, __iter__
අනුකාරකයේ ක්රමය මඟින් වස්තුව නැවත ලබා දිය යුතුය (එනම් return self
). පයිතන් පසුව ඉස්මතු වන next
තුරු StopIteration
නැවත ක්රියාකරු අමතයි . මේ සියල්ල ව්යංගයෙන් සිදු වන නමුත් පහත දැක්වෙන නිරූපණය එය දෘශ්යමාන කරයි:
import random
class DemoIterable(object):
def __iter__(self):
print('__iter__ called')
return DemoIterator()
class DemoIterator(object):
def __iter__(self):
return self
def __next__(self):
print('__next__ called')
r = random.randint(1, 10)
if r == 5:
print('raising StopIteration')
raise StopIteration
return r
A ට වඩා අනුමාන කිරීම DemoIterable
:
>>> di = DemoIterable()
>>> for x in di:
... print(x)
...
__iter__ called
__next__ called
9
__next__ called
8
__next__ called
10
__next__ called
3
__next__ called
10
__next__ called
raising StopIteration
සාකච්ඡාව සහ නිදර්ශන
1 සහ 2 ලක්ෂ්යයන්හි: අනුකාරකයක් ලබා ගැනීම සහ විශ්වාස කළ නොහැකි චෙක්පත්
පහත පන්තිය සලකා බලන්න:
class BasicIterable(object):
def __getitem__(self, item):
if item == 3:
raise IndexError
return item
iter
නිදසුනක් සමඟ ඇමතීම මඟින් ක්රියාත්මක වන BasicIterable
බැවින් කිසිදු ගැටළුවක් නොමැතිව නැවත ක්රියා කරන්නෙකු ආපසු එනු ඇත .BasicIterable
__getitem__
>>> b = BasicIterable()
>>> iter(b)
<iterator object at 0x7f1ab216e320>
කෙසේ වෙතත්, එය බව සටහන් කිරීම වැදගත් වේ b
ද නැත __iter__
විශේෂණය හා නිදසුනක් ලෙස සැලකේ Iterable
හෝ Sequence
:
>>> from collections import Iterable, Sequence
>>> hasattr(b, '__iter__')
False
>>> isinstance(b, Iterable)
False
>>> isinstance(b, Sequence)
False
ඒ නිසයි හොඳින් Python Luciano Ramalho විසින් ඉල්ලා නිර්දේශ iter
හැකි සහ හැසිරවීමේ TypeError
වස්තුවක් iterable දැයි පරීක්ෂා කිරීමට වඩාත්ම නිවැරදි ක්රමයක් ලෙස. පොතෙන් කෙලින්ම උපුටා දැක්වීම:
පයිතන් 3.4 අනුව, වස්තුවක් නැවත x
ක්රියාත්මක කළ හැකිද යන්න පරීක්ෂා කිරීම සඳහා වඩාත් නිවැරදි ක්රමය නම්, ව්යතිරේකයක් නොමැති නම් එය ඇමතීම iter(x)
සහ හැසිරවීමයි TypeError
. මෙය භාවිතයට වඩා නිරවද්ය ය isinstance(x, abc.Iterable)
, මන්ද යත් iter(x)
, උරුමය __getitem__
ක්රමය ද සලකා බලන අතර Iterable
ඒබීසී එසේ නොකරයි.
කරුණු 3 මත: එකම සපයන වස්තූන් එල්ලාවල මහතා __getitem__
, නමුත්__iter__
BasicIterable
අපේක්ෂිත පරිදි කෘති නිදසුනක් උපුටා දැක්වීම: පයිතන් දර්ශකයක් මඟින් අයිතම ලබා ගැනීමට උත්සාහ කරන අනුකාරකයක් සාදයි, එය බිංදුවෙන් පටන් ගෙන, IndexError
මතු වන තුරු . ආදර්ශන වස්තුවෙහි __getitem__
ක්රමය මඟින් නැවත item
ලබා __getitem__(self, item)
දුන් අනුකාරකය විසින් තර්කය ලෙස සැපයූ දේ ආපසු ලබා දේ iter
.
>>> b = BasicIterable()
>>> it = iter(b)
>>> next(it)
0
>>> next(it)
1
>>> next(it)
2
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
StopIteration
ඊලඟ අයිතමය ආපසු ලබා දිය නොහැකි වූ විට අනුකාරකය මතු වන බවත්, IndexError
ඒ සඳහා මතු කර item == 3
ඇති දේ අභ්යන්තරව හසුරුවන බවත් සලකන්න . මේ නිසා ලූපයක් BasicIterable
සමඟ for
ලූපයක් බලාපොරොත්තු වන පරිදි ක්රියා කරයි:
>>> for x in b:
... print(x)
...
0
1
2
iter
දර්ශකය මඟින් අයිතම වෙත ප්රවේශ වීමට උත්සාහ කරන්නන් විසින් නැවත ලබා දුන් ආකාරය පිළිබඳ සංකල්පය ගෙදර ගෙන යාම සඳහා තවත් උදාහරණයක් මෙන්න . WrappedDict
උරුම නොවේ dict
, එයින් අදහස් වන්නේ නිදසුන් සඳහා __iter__
ක්රමයක් නොමැති බවයි .
class WrappedDict(object): # note: no inheritance from dict!
def __init__(self, dic):
self._dict = dic
def __getitem__(self, item):
try:
return self._dict[item] # delegate to dict.__getitem__
except KeyError:
raise IndexError
ඇමතුම් __getitem__
පවරනු dict.__getitem__
ලබන්නේ වර්ග වරහන් අංකනය හුදෙක් කෙටිමං සඳහා වන බව සලකන්න .
>>> w = WrappedDict({-1: 'not printed',
... 0: 'hi', 1: 'StackOverflow', 2: '!',
... 4: 'not printed',
... 'x': 'not printed'})
>>> for x in w:
... print(x)
...
hi
StackOverflow
!
4 වන සහ 5 වන ස්ථානවල: iter
අනුකාරකයක් ඇමතූ විට එය පරීක්ෂා කරයි__iter__
:
විට iter(o)
වස්තුවක් ඉල්ලා සිටී o
, iter
නැවත අගය බවට වග බලා ගන්න වනු ඇත __iter__
, මෙම ක්රමය දැනට නම්, යම් iterator වේ. මෙයින් අදහස් කරන්නේ ආපසු ලබා දුන් වස්තුව ක්රියාත්මක කළ යුතු බවයි __next__
(හෝ next
පයිතන් 2 හි) සහ __iter__
. iter
සපයන වස්තූන් සඳහා කිසිදු සනීපාරක්ෂක පරීක්ෂණයක් සිදු කළ නොහැක __getitem__
, මන්ද එය පූර්ණ සංඛ්යා දර්ශකයෙන් වස්තුවේ අයිතමවලට ප්රවේශ විය හැකිද යන්න පරීක්ෂා කිරීමට ක්රමයක් නොමැති බැවිනි.
class FailIterIterable(object):
def __iter__(self):
return object() # not an iterator
class FailGetitemIterable(object):
def __getitem__(self, item):
raise Exception
FailIterIterable
උදාහරණ වලින් අනුකාරකයක් තැනීම ක්ෂණිකව අසමත් වන අතර, අනුකාරකයක් තැනීම FailGetItemIterable
සාර්ථක වන නමුත් පළමු ඇමතුමට ව්යතිරේකයක් දමනු ඇත __next__
.
>>> fii = FailIterIterable()
>>> iter(fii)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: iter() returned non-iterator of type 'object'
>>>
>>> fgi = FailGetitemIterable()
>>> it = iter(fgi)
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/path/iterdemo.py", line 42, in __getitem__
raise Exception
Exception
6 වන කරුණ: __iter__
ජය
මෙය සරල ය. වස්තුවක උපකරණ නම් __iter__
හා __getitem__
, iter
කියනු ඇත __iter__
. පහත පන්තිය සලකා බලන්න
class IterWinsDemo(object):
def __iter__(self):
return iter(['__iter__', 'wins'])
def __getitem__(self, item):
return ['__getitem__', 'wins'][item]
සහ නිදසුනක් මත ලූප කිරීමේදී ප්රතිදානය:
>>> iwd = IterWinsDemo()
>>> for x in iwd:
... print(x)
...
__iter__
wins
7 වන කරුණ: ඔබේ නැවත ක්රියාත්මක කළ හැකි පන්ති ක්රියාත්මක කළ යුතුය __iter__
බොහෝ builtin අනුක්රමය කැමති ඇයි ඔබ ඇසිය හැක list
මෙරට ක්රියාත්මක __iter__
වන විට ක්රමය __getitem__
ප්රමාණවත් වනු ඇත.
class WrappedList(object): # note: no inheritance from list!
def __init__(self, lst):
self._list = lst
def __getitem__(self, item):
return self._list[item]
සියල්ලට පසු, (පංති වරහන් අංකනය භාවිතා කරමින්) ඇමතුම් ලබා __getitem__
දෙන ඉහත පන්තියේ අවස්ථා පිළිබඳ නැවත list.__getitem__
කියවීම හොඳින් ක්රියාත්මක වේ:
>>> wl = WrappedList(['A', 'B', 'C'])
>>> for x in wl:
... print(x)
...
A
B
C
ඔබගේ අභිරුචි පුනරාවර්තනයන් ක්රියාත්මක කළ යුතු හේතු __iter__
පහත පරිදි වේ:
- ඔබ ක්රියාත්මක කරන්නේ නම්
__iter__
, නිදසුන් පුනරාවර්තන ලෙස සලකනු isinstance(o, collections.abc.Iterable)
ලබන අතර නැවත පැමිණේ True
.
- ආපසු ලබා දුන් වස්තුව
__iter__
අනුකාරකයක් නොවේ නම්, iter
වහාම අසමත් වී a මතු කරයි TypeError
.
__getitem__
පසුගාමී අනුකූලතා හේතූන් මත විශේෂ හැසිරවීම පවතී. චතුර ලෙස පයිතන් වෙතින් නැවත උපුටා දැක්වීම:
ඕනෑම පයිතන් අනුක්රමයක් නැවත ක්රියාත්මක කළ හැක්කේ එබැවිනි: ඒවා සියල්ලම ක්රියාත්මක __getitem__
වේ. ඇත්ත වශයෙන්ම, සම්මත අනුක්රමයන් ද ක්රියාත්මක වන අතර __iter__
, ඔබත් එසේ කළ යුතුය, මන්දයත් විශේෂ හැසිරවීම __getitem__
පසුගාමී අනුකූලතා හේතූන් මත පවතින අතර අනාගතයේ දී එය නැති වී යා හැකිය (මා මෙය ලියන විට එය ප්රතික්ෂේප නොකළද).
__getitem__
වස්තුවක් නැවත සැකසීමට ද ප්රමාණවත් වේ