പൈത്തൺ ഇറ്ററേഷന്റെ ശക്തി പ്രയോജനപ്പെടുത്തുക. __iter__, __next__ മെത്തേഡുകൾ ഉപയോഗിച്ച് കസ്റ്റം ഇറ്ററേറ്ററുകൾ നിർമ്മിക്കുന്നതിനെക്കുറിച്ചുള്ള സമഗ്രമായ ഗൈഡ്.
പൈത്തണിന്റെ ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ ലളിതമായി: __iter__, __next__ എന്നിവയുടെ ആഴത്തിലുള്ള വിശകലനം
പ്രോഗ്രാമിംഗിലെ ഏറ്റവും അടിസ്ഥാനപരമായ ആശയങ്ങളിലൊന്നാണ് ഇറ്ററേഷൻ. പൈത്തണിൽ, ലളിതമായ for ലൂപ്പുകൾ മുതൽ സങ്കീർണ്ണമായ ഡാറ്റാ പ്രോസസ്സിംഗ് പൈപ്പ് ലൈനുകൾ വരെ എല്ലാത്തിനും കരുത്ത് പകരുന്ന മനോഹരവും കാര്യക്ഷമവുമായ ഒരു സംവിധാനമാണിത്. നിങ്ങൾ ഒരു ലിസ്റ്റിലൂടെ ലൂപ്പ് ചെയ്യുമ്പോഴും, ഒരു ഫയലിൽ നിന്ന് വരികൾ വായിക്കുമ്പോഴും, ഡാറ്റാബേസ് ഫലങ്ങളുമായി പ്രവർത്തിക്കുമ്പോഴും എല്ലാ ദിവസവും നിങ്ങളിത് ഉപയോഗിക്കുന്നുണ്ട്. എന്നാൽ ഇതിന്റെ പിന്നിൽ എന്താണ് സംഭവിക്കുന്നതെന്ന് എപ്പോഴെങ്കിലും ചിന്തിച്ചിട്ടുണ്ടോ? ഇത്രയധികം വ്യത്യസ്ത തരം ഒബ്ജക്റ്റുകളിൽ നിന്ന് 'അടുത്ത' ഐറ്റം എങ്ങനെ ലഭിക്കുമെന്ന് പൈത്തണിന് എങ്ങനെ അറിയാം?
ഉത്തരം ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ എന്നറിയപ്പെടുന്ന ശക്തവും ലളിതവുമായ ഒരു ഡിസൈൻ പാറ്റേണിലാണ് സ്ഥിതി ചെയ്യുന്നത്. പൈത്തണിലെ എല്ലാ സീക്വൻസ് പോലുള്ള ഒബ്ജക്റ്റുകളും സംസാരിക്കുന്ന പൊതുവായ ഭാഷയാണ് ഈ പ്രോട്ടോക്കോൾ. ഈ പ്രോട്ടോക്കോൾ മനസിലാക്കുകയും നടപ്പിലാക്കുകയും ചെയ്യുന്നതിലൂടെ, പൈത്തണിന്റെ ഇറ്ററേഷൻ ടൂളുകളുമായി പൂർണ്ണമായും പൊരുത്തപ്പെടുന്ന നിങ്ങളുടെ സ്വന്തം കസ്റ്റം ഒബ്ജക്റ്റുകൾ സൃഷ്ടിക്കാൻ കഴിയും, ഇത് നിങ്ങളുടെ കോഡ് കൂടുതൽ പ്രകടിപ്പിക്കാനും മെമ്മറി-കാര്യക്ഷമമാക്കാനും തികച്ചും 'പൈത്തോണിക്' ആക്കാനും സഹായിക്കുന്നു.
ഈ സമഗ്രമായ ഗൈഡ് നിങ്ങളെ ഇറ്ററേറ്റർ പ്രോട്ടോക്കോളിലേക്ക് ആഴത്തിൽ കൊണ്ടുപോകും. `__iter__`, `__next__` മെത്തേഡുകൾക്ക് പിന്നിലെ മാന്ത്രികത ഞങ്ങൾ അനാവരണം ചെയ്യും, ഇറ്ററബിളും ഇറ്ററേറ്ററും തമ്മിലുള്ള നിർണായക വ്യത്യാസം വ്യക്തമാക്കും, കൂടാതെ തുടക്കം മുതൽ നിങ്ങളുടെ സ്വന്തം കസ്റ്റം ഇറ്ററേറ്ററുകൾ നിർമ്മിക്കുന്നതിലൂടെ നിങ്ങളെ നയിക്കും. നിങ്ങൾ പൈത്തണിന്റെ ആന്തരികതകളെക്കുറിച്ച് കൂടുതൽ മനസ്സിലാക്കാൻ ആഗ്രഹിക്കുന്ന ഒരു ഇന്റർമീഡിയറ്റ് ഡെവലപ്പർ ആണെങ്കിലും, അല്ലെങ്കിൽ കൂടുതൽ സങ്കീർണ്ണമായ API-കൾ രൂപകൽപ്പന ചെയ്യാൻ ലക്ഷ്യമിടുന്ന ഒരു വിദഗ്ദ്ധനാണെങ്കിലും, ഇറ്ററേറ്റർ പ്രോട്ടോക്കോളിൽ പ്രാവീണ്യം നേടുന്നത് നിങ്ങളുടെ യാത്രയിലെ ഒരു നിർണ്ണായക ഘട്ടമാണ്.
'എന്തുകൊണ്ട്': ഇറ്ററേഷന്റെ പ്രാധാന്യവും ശക്തിയും
സാങ്കേതികമായ നിർവ്വഹണത്തിലേക്ക് കടക്കുന്നതിന് മുമ്പ്, ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ എന്തുകൊണ്ട് ഇത്ര പ്രധാനമാണെന്ന് മനസ്സിലാക്കേണ്ടത് അത്യാവശ്യമാണ്. ഇതിന്റെ പ്രയോജനങ്ങൾ `for` ലൂപ്പുകൾ പ്രവർത്തനക്ഷമമാക്കുന്നതിലും അപ്പുറമാണ്.
മെമ്മറി കാര്യക്ഷമതയും ലേസി ഇവാലുവേഷനും
നിങ്ങൾക്ക് നിരവധി ജിഗാബൈറ്റ് വലുപ്പമുള്ള ഒരു വലിയ ലോഗ് ഫയൽ പ്രോസസ്സ് ചെയ്യണമെന്ന് സങ്കൽപ്പിക്കുക. നിങ്ങൾ മുഴുവൻ ഫയലും മെമ്മറിയിലെ ഒരു ലിസ്റ്റിലേക്ക് വായിക്കുകയാണെങ്കിൽ, നിങ്ങളുടെ സിസ്റ്റത്തിന്റെ റിസോഴ്സുകൾ തീർന്നുപോകാൻ സാധ്യതയുണ്ട്. ലേസി ഇവാലുവേഷൻ എന്ന ആശയത്തിലൂടെ ഇറ്ററേറ്ററുകൾ ഈ പ്രശ്നം മനോഹരമായി പരിഹരിക്കുന്നു.
ഒരു ഇറ്ററേറ്റർ എല്ലാ ഡാറ്റയും ഒരേസമയം ലോഡ് ചെയ്യുന്നില്ല. പകരം, അത് ആവശ്യപ്പെടുമ്പോൾ മാത്രം, ഒരു സമയം ഒരു ഐറ്റം ജനറേറ്റ് ചെയ്യുകയോ ലഭ്യമാക്കുകയോ ചെയ്യുന്നു. അത് സീക്വൻസിൽ എവിടെയാണെന്ന് ഓർമ്മിക്കാൻ ഒരു ആന്തരിക സ്റ്റേറ്റ് നിലനിർത്തുന്നു. ഇതിനർത്ഥം നിങ്ങൾക്ക് വളരെ കുറഞ്ഞ, സ്ഥിരമായ മെമ്മറി ഉപയോഗിച്ച് അനന്തമായ ഒരു ഡാറ്റാ സ്ട്രീം (സിദ്ധാന്തത്തിൽ) പ്രോസസ്സ് ചെയ്യാൻ കഴിയും. നിങ്ങളുടെ പ്രോഗ്രാം ക്രാഷ് ആകാതെ ഒരു വലിയ ഫയൽ വരി за വരിയായി വായിക്കാൻ നിങ്ങളെ അനുവദിക്കുന്ന അതേ തത്വമാണിത്.
വൃത്തിയുള്ളതും വായിക്കാവുന്നതും സാർവത്രികവുമായ കോഡ്
ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ സീക്വൻഷ്യൽ ആക്സസ്സിനായി ഒരു സാർവത്രിക ഇന്റർഫേസ് നൽകുന്നു. ലിസ്റ്റുകൾ, ട്യൂപ്പിളുകൾ, ഡിക്ഷണറികൾ, സ്ട്രിംഗുകൾ, ഫയൽ ഒബ്ജക്റ്റുകൾ, കൂടാതെ മറ്റ് പല തരങ്ങളും ഈ പ്രോട്ടോക്കോൾ പാലിക്കുന്നതിനാൽ, അവയെല്ലാം ഉപയോഗിക്കുന്നതിന് നിങ്ങൾക്ക് ഒരേ സിന്റാക്സ്—`for` ലൂപ്പ്—ഉപയോഗിക്കാം. ഈ ഏകീകൃതത പൈത്തണിന്റെ വായനാക്ഷമതയുടെ ഒരു മൂലക്കല്ലാണ്.
ഈ കോഡ് പരിഗണിക്കുക:
കോഡ്:
my_list = [1, 2, 3]
for item in my_list:
print(item)
my_string = "abc"
for char in my_string:
print(char)
with open('my_file.txt', 'r') as f:
for line in f:
print(line)
ഇത് പൂർണ്ണസംഖ്യകളുടെ ഒരു ലിസ്റ്റാണോ, അക്ഷരങ്ങളുടെ ഒരു സ്ട്രിംഗാണോ, അതോ ഒരു ഫയലിൽ നിന്നുള്ള വരികളാണോ എന്നതിനെക്കുറിച്ച് `for` ലൂപ്പ് ആശങ്കപ്പെടുന്നില്ല. അത് ഒബ്ജക്റ്റിനോട് അതിന്റെ ഇറ്ററേറ്ററിനായി ചോദിക്കുകയും, തുടർന്ന് ഇറ്ററേറ്ററിനോട് അതിന്റെ അടുത്ത ഐറ്റത്തിനായി ആവർത്തിച്ച് ചോദിക്കുകയും ചെയ്യുന്നു. ഈ അബ്സ്ട്രാക്ഷൻ അവിശ്വസനീയമാംവിധം ശക്തമാണ്.
ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ വിഘടിക്കുന്നു
പ്രോട്ടോക്കോൾ അതിശയകരമാംവിധം ലളിതമാണ്, വെറും രണ്ട് പ്രത്യേക മെത്തേഡുകളാൽ നിർവചിക്കപ്പെട്ടിരിക്കുന്നു, ഇവയെ "ഡണ്ടർ" (ഡബിൾ അണ്ടർസ്കോർ) മെത്തേഡുകൾ എന്ന് വിളിക്കുന്നു:
- `__iter__()`
- `__next__()`
ഇവ പൂർണ്ണമായി മനസ്സിലാക്കാൻ, ബന്ധപ്പെട്ടതും എന്നാൽ വ്യത്യസ്തവുമായ രണ്ട് ആശയങ്ങൾ തമ്മിലുള്ള വ്യത്യാസം നാം ആദ്യം മനസ്സിലാക്കണം: ഒരു ഇറ്ററബിൾ, ഒരു ഇറ്ററേറ്റർ.
ഇറ്ററബിൾ vs. ഇറ്ററേറ്റർ: ഒരു നിർണ്ണായക വ്യത്യാസം
പുതിയ ആളുകൾക്ക് ഇത് പലപ്പോഴും ആശയക്കുഴപ്പമുണ്ടാക്കുന്ന ഒരു കാര്യമാണ്, പക്ഷേ ഈ വ്യത്യാസം നിർണ്ണായകമാണ്.
എന്താണ് ഒരു ഇറ്ററബിൾ?
ഒരു ഇറ്ററബിൾ എന്നത് ലൂപ്പ് ചെയ്യാൻ കഴിയുന്ന ഏതൊരു ഒബ്ജക്റ്റുമാണ്. ഒരു ഇറ്ററേറ്റർ ലഭിക്കുന്നതിന് ബിൽറ്റ്-ഇൻ `iter()` ഫംഗ്ഷനിലേക്ക് കൈമാറാൻ കഴിയുന്ന ഒരു ഒബ്ജക്റ്റാണിത്. സാങ്കേതികമായി, ഒരു ഒബ്ജക്റ്റ് `__iter__` മെത്തേഡ് നടപ്പിലാക്കുന്നുവെങ്കിൽ അത് ഇറ്ററബിൾ ആയി കണക്കാക്കപ്പെടുന്നു. അതിന്റെ `__iter__` മെത്തേഡിന്റെ ഒരേയൊരു ഉദ്ദേശ്യം ഒരു ഇറ്ററേറ്റർ ഒബ്ജക്റ്റ് തിരികെ നൽകുക എന്നതാണ്.
ബിൽറ്റ്-ഇൻ ഇറ്ററബിളുകളുടെ ഉദാഹരണങ്ങൾ ഉൾപ്പെടുന്നു:
- ലിസ്റ്റുകൾ (`[1, 2, 3]`)
- ട്യൂപ്പിളുകൾ (`(1, 2, 3)`)
- സ്ട്രിംഗുകൾ (`"hello"`)
- ഡിക്ഷണറികൾ (`{'a': 1, 'b': 2}` - കീകളിൽ ഇറ്ററേറ്റ് ചെയ്യുന്നു)
- സെറ്റുകൾ (`{1, 2, 3}`)
- ഫയൽ ഒബ്ജക്റ്റുകൾ
നിങ്ങൾക്ക് ഒരു ഇറ്ററബിളിനെ ഒരു കണ്ടെയ്നർ അല്ലെങ്കിൽ ഡാറ്റയുടെ ഉറവിടമായി ചിന്തിക്കാം. ഇനങ്ങൾ എങ്ങനെ സ്വയം നിർമ്മിക്കാമെന്ന് അതിന് അറിയില്ല, പക്ഷേ അത് ചെയ്യാൻ കഴിയുന്ന ഒരു ഒബ്ജക്റ്റ് എങ്ങനെ സൃഷ്ടിക്കാമെന്ന് അതിനറിയാം: ഇറ്ററേറ്റർ.
എന്താണ് ഒരു ഇറ്ററേറ്റർ?
ഒരു ഇറ്ററേറ്റർ ആണ് ഇറ്ററേഷൻ സമയത്ത് മൂല്യങ്ങൾ ഉത്പാദിപ്പിക്കുന്നതിനുള്ള യഥാർത്ഥ ജോലി ചെയ്യുന്ന ഒബ്ജക്റ്റ്. ഇത് ഡാറ്റയുടെ ഒരു സ്ട്രീമിനെ പ്രതിനിധീകരിക്കുന്നു. ഒരു ഇറ്ററേറ്റർ രണ്ട് മെത്തേഡുകൾ നടപ്പിലാക്കണം:
- `__iter__()`: ഈ മെത്തേഡ് ഇറ്ററേറ്റർ ഒബ്ജക്റ്റ് തന്നെ (`self`) തിരികെ നൽകണം. ഇറ്ററേറ്ററുകളെ ഇറ്ററബിളുകൾ പ്രതീക്ഷിക്കുന്നിടത്ത്, ഉദാഹരണത്തിന്, ഒരു `for` ലൂപ്പിൽ ഉപയോഗിക്കാൻ കഴിയുന്നതിനായി ഇത് ആവശ്യമാണ്.
- `__next__()`: ഈ മെത്തേഡ് ഇറ്ററേറ്ററിന്റെ എഞ്ചിൻ ആണ്. ഇത് സീക്വൻസിലെ അടുത്ത ഐറ്റം തിരികെ നൽകുന്നു. തിരികെ നൽകാൻ കൂടുതൽ ഐറ്റങ്ങൾ ഇല്ലാത്തപ്പോൾ, അത് `StopIteration` എക്സെപ്ഷൻ ഉയർത്തണം. ഈ എക്സെപ്ഷൻ ഒരു പിശകല്ല; ഇറ്ററേഷൻ പൂർത്തിയായി എന്ന് ലൂപ്പിംഗ് ഘടനയ്ക്ക് നൽകുന്ന സാധാരണ സിഗ്നലാണിത്.
ഒരു ഇറ്ററേറ്ററിന്റെ പ്രധാന സ്വഭാവസവിശേഷതകൾ ഇവയാണ്:
- അത് സ്റ്റേറ്റ് നിലനിർത്തുന്നു: ഒരു ഇറ്ററേറ്റർ സീക്വൻസിലെ അതിന്റെ നിലവിലെ സ്ഥാനം ഓർമ്മിക്കുന്നു.
- അത് ഒരു സമയം ഒരു മൂല്യം ഉത്പാദിപ്പിക്കുന്നു: `__next__` മെത്തേഡ് വഴി.
- അത് ഉപയോഗിച്ച് തീരുന്നതാണ്: ഒരു ഇറ്ററേറ്റർ പൂർണ്ണമായി ഉപയോഗിച്ചുകഴിഞ്ഞാൽ (അതായത്, അത് `StopIteration` ഉയർത്തിയാൽ), അത് ശൂന്യമാണ്. നിങ്ങൾക്ക് അത് റീസെറ്റ് ചെയ്യാനോ വീണ്ടും ഉപയോഗിക്കാനോ കഴിയില്ല. വീണ്ടും ഇറ്ററേറ്റ് ചെയ്യുന്നതിന്, നിങ്ങൾ യഥാർത്ഥ ഇറ്ററബിളിലേക്ക് തിരികെ പോയി അതിൽ `iter()` വീണ്ടും വിളിച്ച് ഒരു പുതിയ ഇറ്ററേറ്റർ നേടണം.
നമ്മുടെ ആദ്യത്തെ കസ്റ്റം ഇറ്ററേറ്റർ നിർമ്മിക്കാം: ഒരു ഘട്ടം ഘട്ടമായുള്ള ഗൈഡ്
സിദ്ധാന്തം മികച്ചതാണ്, പക്ഷേ പ്രോട്ടോക്കോൾ മനസ്സിലാക്കാനുള്ള ഏറ്റവും നല്ല മാർഗം അത് സ്വയം നിർമ്മിക്കുക എന്നതാണ്. ഒരു നിശ്ചിത പരിധി വരെ ഒരു ആരംഭ സംഖ്യയിൽ നിന്ന് ഇറ്ററേറ്റ് ചെയ്യുന്ന ഒരു കൗണ്ടറായി പ്രവർത്തിക്കുന്ന ഒരു ലളിതമായ ക്ലാസ് നമുക്ക് സൃഷ്ടിക്കാം.
ഉദാഹരണം 1: ഒരു ലളിതമായ കൗണ്ടർ ക്ലാസ്
നമ്മൾ `CountUpTo` എന്ന ഒരു ക്ലാസ് സൃഷ്ടിക്കും. നിങ്ങൾ അതിന്റെ ഒരു ഇൻസ്റ്റൻസ് സൃഷ്ടിക്കുമ്പോൾ, നിങ്ങൾ ഒരു പരമാവധി സംഖ്യ വ്യക്തമാക്കും, നിങ്ങൾ അതിലൂടെ ഇറ്ററേറ്റ് ചെയ്യുമ്പോൾ, അത് 1 മുതൽ ആ പരമാവധി സംഖ്യ വരെ നൽകും.
കോഡ്:
class CountUpTo:
"""ഒരു നിശ്ചിത പരമാവധി സംഖ്യ വരെ 1 മുതൽ എണ്ണുന്ന ഒരു ഇറ്ററേറ്റർ."""
def __init__(self, max_num):
print("CountUpTo ഒബ്ജക്റ്റ് ആരംഭിക്കുന്നു...")
self.max_num = max_num
self.current = 0 # ഇത് സ്റ്റേറ്റ് സൂക്ഷിക്കും
def __iter__(self):
print("__iter__ വിളിച്ചു, self തിരികെ നൽകുന്നു...")
# ഈ ഒബ്ജക്റ്റ് അതിന്റെ സ്വന്തം ഇറ്ററേറ്ററാണ്, അതിനാൽ ഞങ്ങൾ self തിരികെ നൽകുന്നു
return self
def __next__(self):
print("__next__ വിളിച്ചു...")
if self.current < self.max_num:
self.current += 1
return self.current
else:
# ഇതാണ് നിർണ്ണായക ഭാഗം: നമ്മൾ പൂർത്തിയാക്കി എന്ന് സിഗ്നൽ നൽകുന്നു.
print("StopIteration ഉയർത്തുന്നു.")
raise StopIteration
# ഇത് എങ്ങനെ ഉപയോഗിക്കാം
print("കൗണ്ടർ ഒബ്ജക്റ്റ് സൃഷ്ടിക്കുന്നു...")
counter = CountUpTo(3)
print("\nfor ലൂപ്പ് ആരംഭിക്കുന്നു...")
for number in counter:
print(f"For ലൂപ്പിന് ലഭിച്ചത്: {number}")
കോഡ് വിഘടനവും വിശദീകരണവും
`for` ലൂപ്പ് പ്രവർത്തിക്കുമ്പോൾ എന്ത് സംഭവിക്കുന്നു എന്ന് നമുക്ക് വിശകലനം ചെയ്യാം:
- ഇനിഷ്യലൈസേഷൻ: `counter = CountUpTo(3)` നമ്മുടെ ക്ലാസ്സിന്റെ ഒരു ഇൻസ്റ്റൻസ് ഉണ്ടാക്കുന്നു. `__init__` മെത്തേഡ് പ്രവർത്തിക്കുകയും `self.max_num` 3 ആയും `self.current` 0 ആയും സജ്ജമാക്കുന്നു. നമ്മുടെ ഒബ്ജക്റ്റിന്റെ സ്റ്റേറ്റ് ഇപ്പോൾ ആരംഭിച്ചു.
- ലൂപ്പ് ആരംഭിക്കുന്നു: `for number in counter:` എന്ന വരിയിൽ എത്തുമ്പോൾ, പൈത്തൺ ആന്തരികമായി `iter(counter)` വിളിക്കുന്നു.
- `__iter__` വിളിക്കപ്പെടുന്നു: `iter(counter)` എന്ന കോൾ നമ്മുടെ `counter.__iter__()` മെത്തേഡിനെ പ്രവർത്തിപ്പിക്കുന്നു. നമ്മുടെ കോഡിൽ നിന്ന് കാണാനാകുന്നതുപോലെ, ഈ മെത്തേഡ് ഒരു സന്ദേശം പ്രിന്റ് ചെയ്യുകയും `self` തിരികെ നൽകുകയും ചെയ്യുന്നു. ഇത് `for` ലൂപ്പിനോട് പറയുന്നു, "നിങ്ങൾക്ക് `__next__` വിളിക്കേണ്ട ഒബ്ജക്റ്റ് ഞാൻ തന്നെയാണ്!"
- ലൂപ്പ് ആരംഭിക്കുന്നു: ഇപ്പോൾ `for` ലൂപ്പ് തയ്യാറാണ്. ഓരോ ഇറ്ററേഷനിലും, അത് ലഭിച്ച ഇറ്ററേറ്റർ ഒബ്ജക്റ്റിൽ (അതായത് നമ്മുടെ `counter` ഒബ്ജക്റ്റ്) `next()` വിളിക്കും.
- ആദ്യത്തെ `__next__` കോൾ: `counter.__next__()` മെത്തേഡ് വിളിക്കപ്പെടുന്നു. `self.current` 0 ആണ്, ഇത് `self.max_num` (3) നേക്കാൾ കുറവാണ്. കോഡ് `self.current`-നെ 1 ആയി വർദ്ധിപ്പിക്കുകയും അത് തിരികെ നൽകുകയും ചെയ്യുന്നു. `for` ലൂപ്പ് ഈ മൂല്യം `number` വേരിയബിളിലേക്ക് നൽകുന്നു, കൂടാതെ ലൂപ്പ് ബോഡി (`print(...)`) പ്രവർത്തിക്കുന്നു.
- രണ്ടാമത്തെ `__next__` കോൾ: ലൂപ്പ് തുടരുന്നു. `__next__` വീണ്ടും വിളിക്കപ്പെടുന്നു. `self.current` 1 ആണ്. അത് 2 ആയി വർദ്ധിപ്പിക്കുകയും തിരികെ നൽകുകയും ചെയ്യുന്നു.
- മൂന്നാമത്തെ `__next__` കോൾ: `__next__` വീണ്ടും വിളിക്കപ്പെടുന്നു. `self.current` 2 ആണ്. അത് 3 ആയി വർദ്ധിപ്പിക്കുകയും തിരികെ നൽകുകയും ചെയ്യുന്നു.
- അവസാനത്തെ `__next__` കോൾ: `__next__` ഒരിക്കൽ കൂടി വിളിക്കപ്പെടുന്നു. ഇപ്പോൾ, `self.current` 3 ആണ്. `self.current < self.max_num` എന്ന വ്യവസ്ഥ തെറ്റാണ്. `else` ബ്ലോക്ക് പ്രവർത്തിക്കുകയും, `StopIteration` ഉയർത്തുകയും ചെയ്യുന്നു.
- ലൂപ്പ് അവസാനിപ്പിക്കുന്നു: `for` ലൂപ്പ് `StopIteration` എക്സെപ്ഷൻ പിടിക്കാൻ രൂപകൽപ്പന ചെയ്തിട്ടുള്ളതാണ്. അത് ചെയ്യുമ്പോൾ, ഇറ്ററേഷൻ പൂർത്തിയായി എന്ന് മനസ്സിലാക്കുകയും ഭംഗിയായി അവസാനിപ്പിക്കുകയും ചെയ്യുന്നു. പ്രോഗ്രാം ലൂപ്പിന് ശേഷമുള്ള ഏത് കോഡും തുടർന്ന് പ്രവർത്തിപ്പിക്കുന്നു.
ഒരു പ്രധാന കാര്യം ശ്രദ്ധിക്കുക: നിങ്ങൾ അതേ `counter` ഒബ്ജക്റ്റിൽ വീണ്ടും `for` ലൂപ്പ് പ്രവർത്തിപ്പിക്കാൻ ശ്രമിച്ചാൽ, അത് പ്രവർത്തിക്കില്ല. ഇറ്ററേറ്റർ ഉപയോഗിച്ച് തീർന്നു. `self.current` ഇതിനകം 3 ആണ്, അതിനാൽ `__next__`-ലേക്കുള്ള തുടർന്നുള്ള ഏത് കോളും ഉടൻ തന്നെ `StopIteration` ഉയർത്തും. നമ്മുടെ ഒബ്ജക്റ്റ് അതിൻ്റെ സ്വന്തം ഇറ്ററേറ്റർ ആയതിൻ്റെ ഒരു ഫലമാണിത്.
വിപുലമായ ഇറ്ററേറ്റർ ആശയങ്ങളും യഥാർത്ഥ ലോക പ്രയോഗങ്ങളും
ലളിതമായ കൗണ്ടറുകൾ പഠിക്കാൻ നല്ലൊരു മാർഗമാണ്, എന്നാൽ ഇറ്ററേറ്റർ പ്രോട്ടോക്കോളിന്റെ യഥാർത്ഥ ശക്തി കൂടുതൽ സങ്കീർണ്ണവും ഇഷ്ടാനുസൃതവുമായ ഡാറ്റാ ഘടനകളിൽ പ്രയോഗിക്കുമ്പോഴാണ് പ്രകാശിക്കുന്നത്.
ഇറ്ററബിളും ഇറ്ററേറ്ററും സംയോജിപ്പിക്കുന്നതിലെ പ്രശ്നം
നമ്മുടെ `CountUpTo` ഉദാഹരണത്തിൽ, ക്ലാസ് ഇറ്ററബിളും ഇറ്ററേറ്ററും ആയിരുന്നു. ഇത് ലളിതമാണ്, പക്ഷേ ഒരു പ്രധാന പോരായ്മയുണ്ട്: ഫലമായുണ്ടാകുന്ന ഇറ്ററേറ്റർ ഉപയോഗിച്ച് തീരുന്നതാണ്. നിങ്ങൾ അതിലൂടെ ഒരു തവണ ലൂപ്പ് ചെയ്തുകഴിഞ്ഞാൽ, അത് കഴിഞ്ഞു.
കോഡ്:
counter = CountUpTo(2)
print("ആദ്യത്തെ ഇറ്ററേഷൻ:")
for num in counter: print(num) # ശരിയായി പ്രവർത്തിക്കുന്നു
print("\nരണ്ടാമത്തെ ഇറ്ററേഷൻ:")
for num in counter: print(num) # ഒന്നും പ്രിന്റ് ചെയ്യുന്നില്ല!
ഇത് സംഭവിക്കുന്നത് സ്റ്റേറ്റ് (`self.current`) ഒബ്ജക്റ്റിൽ തന്നെ സംഭരിച്ചിരിക്കുന്നതുകൊണ്ടാണ്. ആദ്യത്തെ ലൂപ്പിന് ശേഷം, `self.current` 2 ആണ്, കൂടാതെ `__next__` എന്നതിലേക്കുള്ള കൂടുതൽ കോളുകൾ `StopIteration` ഉയർത്തുകയേയുള്ളൂ. ഈ സ്വഭാവം ഒരു സാധാരണ പൈത്തൺ ലിസ്റ്റിൽ നിന്ന് വ്യത്യസ്തമാണ്, അത് നിങ്ങൾക്ക് ഒന്നിലധികം തവണ ഇറ്ററേറ്റ് ചെയ്യാൻ കഴിയും.
കൂടുതൽ കരുത്തുറ്റ ഒരു പാറ്റേൺ: ഇറ്ററബിളിനെ ഇറ്ററേറ്ററിൽ നിന്ന് വേർതിരിക്കുക
പൈത്തണിന്റെ ബിൽറ്റ്-ഇൻ കളക്ഷനുകൾ പോലെ പുനരുപയോഗിക്കാവുന്ന ഇറ്ററബിളുകൾ സൃഷ്ടിക്കാൻ, രണ്ട് റോളുകളും വേർതിരിക്കുന്നതാണ് ഏറ്റവും നല്ല രീതി. കണ്ടെയ്നർ ഒബ്ജക്റ്റ് ഇറ്ററബിൾ ആയിരിക്കും, അതിന്റെ `__iter__` മെത്തേഡ് വിളിക്കുമ്പോഴെല്ലാം അത് ഒരു പുതിയ, ഫ്രഷ് ഇറ്ററേറ്റർ ഒബ്ജക്റ്റ് സൃഷ്ടിക്കും.
നമ്മുടെ ഉദാഹരണം രണ്ട് ക്ലാസുകളായി പുനർനിർമ്മിക്കാം: `Sentence` (ഇറ്ററബിൾ), `SentenceIterator` (ഇറ്ററേറ്റർ).
കോഡ്:
class SentenceIterator:
"""സ്റ്റേറ്റിനും മൂല്യങ്ങൾ ഉത്പാദിപ്പിക്കുന്നതിനും ഉത്തരവാദിയായ ഇറ്ററേറ്റർ."""
def __init__(self, words):
self.words = words
self.index = 0
def __next__(self):
try:
word = self.words[self.index]
except IndexError:
raise StopIteration()
self.index += 1
return word
def __iter__(self):
# ഒരു ഇറ്ററേറ്റർ ഒരു ഇറ്ററബിളും ആയിരിക്കണം, സ്വയം തിരികെ നൽകുന്നു.
return self
class Sentence:
"""ഇറ്ററബിൾ കണ്ടെയ്നർ ക്ലാസ്."""
def __init__(self, text):
# കണ്ടെയ്നർ ഡാറ്റ സൂക്ഷിക്കുന്നു.
self.words = text.split()
def __iter__(self):
# ഓരോ തവണ __iter__ വിളിക്കുമ്പോഴും, അത് ഒരു പുതിയ ഇറ്ററേറ്റർ ഒബ്ജക്റ്റ് സൃഷ്ടിക്കുന്നു.
return SentenceIterator(self.words)
# ഇത് എങ്ങനെ ഉപയോഗിക്കാം
my_sentence = Sentence('This is a test')
print("ആദ്യത്തെ ഇറ്ററേഷൻ:")
for word in my_sentence:
print(word)
print("\nരണ്ടാമത്തെ ഇറ്ററേഷൻ:")
for word in my_sentence:
print(word)
ഇപ്പോൾ, ഇത് ഒരു ലിസ്റ്റ് പോലെ തന്നെ പ്രവർത്തിക്കുന്നു! ഓരോ തവണ `for` ലൂപ്പ് ആരംഭിക്കുമ്പോഴും, അത് `my_sentence.__iter__()` വിളിക്കുന്നു, ഇത് സ്വന്തം സ്റ്റേറ്റോടുകൂടിയ (`self.index = 0`) ഒരു പുതിയ `SentenceIterator` ഇൻസ്റ്റൻസ് സൃഷ്ടിക്കുന്നു. ഇത് ഒരേ `Sentence` ഒബ്ജക്റ്റിൽ ഒന്നിലധികം, സ്വതന്ത്രമായ ഇറ്ററേഷനുകൾക്ക് അനുവദിക്കുന്നു. ഈ പാറ്റേൺ കൂടുതൽ കരുത്തുറ്റതാണ്, പൈത്തണിന്റെ സ്വന്തം കളക്ഷനുകൾ ഇങ്ങനെയാണ് നടപ്പിലാക്കിയിരിക്കുന്നത്.
ഉദാഹരണം: അനന്തമായ ഇറ്ററേറ്ററുകൾ
ഇറ്ററേറ്ററുകൾ പരിമിതമായിരിക്കണമെന്നില്ല. അവയ്ക്ക് അനന്തമായ ഒരു ഡാറ്റാ ശ്രേണിയെ പ്രതിനിധീകരിക്കാൻ കഴിയും. ഇവിടെയാണ് അവരുടെ മടിയനായ, ഒരു സമയം ഒന്നിനെ മാത്രം നൽകുന്ന സ്വഭാവം ഒരു വലിയ നേട്ടമാകുന്നത്. നമുക്ക് ഫിബൊനാച്ചി സംഖ്യകളുടെ അനന്തമായ ഒരു ശ്രേണിക്കായി ഒരു ഇറ്ററേറ്റർ സൃഷ്ടിക്കാം.
കോഡ്:
class FibonacciIterator:
"""ഫിബൊനാച്ചി സംഖ്യകളുടെ അനന്തമായ ഒരു ശ്രേണി സൃഷ്ടിക്കുന്നു."""
def __init__(self):
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
result = self.a
self.a, self.b = self.b, self.a + self.b
return result
# ഇത് എങ്ങനെ ഉപയോഗിക്കാം - ശ്രദ്ധിക്കുക: ഒരു ബ്രേക്ക് ഇല്ലാതെ അനന്തമായ ലൂപ്പ്!
fib_gen = FibonacciIterator()
for i, num in enumerate(fib_gen):
print(f"Fibonacci({i}): {num}")
if i >= 10: # നമ്മൾ ഒരു നിർത്താനുള്ള വ്യവസ്ഥ നൽകണം
break
ഈ ഇറ്ററേറ്റർ ഒരിക്കലും സ്വയം `StopIteration` ഉയർത്തുകയില്ല. ലൂപ്പ് അവസാനിപ്പിക്കാൻ ഒരു വ്യവസ്ഥ (ഒരു `break` സ്റ്റേറ്റ്മെന്റ് പോലെ) നൽകേണ്ടത് വിളിക്കുന്ന കോഡിന്റെ ഉത്തരവാദിത്തമാണ്. ഡാറ്റാ സ്ട്രീമിംഗ്, ഇവന്റ് ലൂപ്പുകൾ, ന്യൂമെറിക്കൽ സിമുലേഷനുകൾ എന്നിവയിൽ ഈ പാറ്റേൺ സാധാരണമാണ്.
പൈത്തൺ ഇക്കോസിസ്റ്റത്തിലെ ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ
`__iter__`, `__next__` എന്നിവ മനസ്സിലാക്കുന്നത് പൈത്തണിൽ എല്ലായിടത്തും അവയുടെ സ്വാധീനം കാണാൻ നിങ്ങളെ അനുവദിക്കുന്നു. പൈത്തണിന്റെ നിരവധി സവിശേഷതകൾ തടസ്സമില്ലാതെ ഒരുമിച്ച് പ്രവർത്തിക്കാൻ സഹായിക്കുന്ന ഏകീകൃത പ്രോട്ടോക്കോളാണിത്.
`for` ലൂപ്പുകൾ *യഥാർത്ഥത്തിൽ* എങ്ങനെ പ്രവർത്തിക്കുന്നു
നമ്മൾ ഇത് പരോക്ഷമായി ചർച്ചചെയ്തു, പക്ഷേ നമുക്കിത് വ്യക്തമാക്കാം. പൈത്തൺ ഈ വരി കാണുമ്പോൾ:
`for item in my_iterable:`
അത് അണിയറയിൽ താഴെ പറയുന്ന ഘട്ടങ്ങൾ നിർവഹിക്കുന്നു:
- ഒരു ഇറ്ററേറ്റർ ലഭിക്കുന്നതിനായി അത് `iter(my_iterable)` വിളിക്കുന്നു. ഇത്, `my_iterable.__iter__()` വിളിക്കുന്നു. തിരികെ ലഭിക്കുന്ന ഒബ്ജക്റ്റിനെ നമുക്ക് `iterator_obj` എന്ന് വിളിക്കാം.
- അത് ഒരു അനന്തമായ `while True` ലൂപ്പിലേക്ക് പ്രവേശിക്കുന്നു.
- ലൂപ്പിനുള്ളിൽ, അത് `next(iterator_obj)` വിളിക്കുന്നു, ഇത് `iterator_obj.__next__()` വിളിക്കുന്നു.
- `__next__` ഒരു മൂല്യം തിരികെ നൽകുകയാണെങ്കിൽ, അത് `item` വേരിയബിളിലേക്ക് നൽകുകയും `for` ലൂപ്പ് ബ്ലോക്കിനുള്ളിലെ കോഡ് പ്രവർത്തിപ്പിക്കുകയും ചെയ്യുന്നു.
- `__next__` ഒരു `StopIteration` എക്സെപ്ഷൻ ഉയർത്തുകയാണെങ്കിൽ, `for` ലൂപ്പ് ഈ എക്സെപ്ഷൻ പിടിക്കുകയും അതിന്റെ ആന്തരിക `while` ലൂപ്പിൽ നിന്ന് പുറത്തുകടക്കുകയും ചെയ്യുന്നു. ഇറ്ററേഷൻ പൂർത്തിയായി.
കോംപ്രിഹെൻഷനുകളും ജനറേറ്റർ എക്സ്പ്രഷനുകളും
ലിസ്റ്റ്, സെറ്റ്, ഡിക്ഷണറി കോംപ്രിഹെൻഷനുകൾ എല്ലാം ഇറ്ററേറ്റർ പ്രോട്ടോക്കോളിനാൽ പ്രവർത്തിക്കുന്നു. നിങ്ങൾ എഴുതുമ്പോൾ:
`squares = [x * x for x in range(10)]`
പൈത്തൺ ഫലപ്രദമായി `range(10)` ഒബ്ജക്റ്റിൽ ഒരു ഇറ്ററേഷൻ നടത്തുന്നു, ഓരോ മൂല്യവും നേടുന്നു, ലിസ്റ്റ് നിർമ്മിക്കുന്നതിനായി `x * x` എന്ന എക്സ്പ്രഷൻ പ്രവർത്തിപ്പിക്കുന്നു. ലേസി ഇറ്ററേഷന്റെ കൂടുതൽ നേരിട്ടുള്ള ഉപയോഗമായ ജനറേറ്റർ എക്സ്പ്രഷനുകളുടെ കാര്യത്തിലും ഇത് ശരിയാണ്:
`lazy_squares = (x * x for x in range(1000000))`
ഇത് മെമ്മറിയിൽ ഒരു ദശലക്ഷം ഐറ്റങ്ങളുള്ള ഒരു ലിസ്റ്റ് സൃഷ്ടിക്കുന്നില്ല. ഇത് ഒരു ഇറ്ററേറ്റർ (പ്രത്യേകിച്ച്, ഒരു ജനറേറ്റർ ഒബ്ജക്റ്റ്) സൃഷ്ടിക്കുന്നു, അത് നിങ്ങൾ അതിലൂടെ ഇറ്ററേറ്റ് ചെയ്യുമ്പോൾ വർഗ്ഗങ്ങൾ ഓരോന്നായി കണക്കാക്കും.
ജനറേറ്ററുകൾ: ഇറ്ററേറ്ററുകൾ സൃഷ്ടിക്കാനുള്ള ലളിതമായ വഴി
`__iter__`, `__next__` എന്നിവയോടുകൂടിയ ഒരു പൂർണ്ണ ക്ലാസ് സൃഷ്ടിക്കുന്നത് നിങ്ങൾക്ക് പരമാവധി നിയന്ത്രണം നൽകുമെങ്കിലും, ലളിതമായ കേസുകൾക്ക് ഇത് ദൈർഘ്യമേറിയതാകാം. ഇറ്ററേറ്ററുകൾ സൃഷ്ടിക്കുന്നതിന് പൈത്തൺ വളരെ ലളിതമായ ഒരു സിന്റാക്സ് നൽകുന്നു: ജനറേറ്ററുകൾ.
`yield` കീവേഡ് ഉപയോഗിക്കുന്ന ഒരു ഫംഗ്ഷനാണ് ജനറേറ്റർ. നിങ്ങൾ ഒരു ജനറേറ്റർ ഫംഗ്ഷൻ വിളിക്കുമ്പോൾ, അത് കോഡ് പ്രവർത്തിപ്പിക്കുന്നില്ല. പകരം, അത് ഒരു ജനറേറ്റർ ഒബ്ജക്റ്റ് തിരികെ നൽകുന്നു, അത് ഒരു പൂർണ്ണമായ ഇറ്ററേറ്ററാണ്.
നമ്മുടെ `CountUpTo` ഉദാഹരണം ഒരു ജനറേറ്ററായി മാറ്റിയെഴുതാം:
കോഡ്:
def count_up_to_generator(max_num):
"""1 മുതൽ max_num വരെ സംഖ്യകൾ നൽകുന്ന ഒരു ജനറേറ്റർ ഫംഗ്ഷൻ."""
print("ജനറേറ്റർ ആരംഭിച്ചു...")
current = 1
while current <= max_num:
yield current # ഇവിടെ താൽക്കാലികമായി നിർത്തി ഒരു മൂല്യം തിരികെ അയയ്ക്കുന്നു
current += 1
print("ജനറേറ്റർ പൂർത്തിയായി.")
# ഇത് എങ്ങനെ ഉപയോഗിക്കാം
counter_gen = count_up_to_generator(3)
for number in counter_gen:
print(f"For ലൂപ്പിന് ലഭിച്ചത്: {number}")
അത് എത്ര ലളിതമാണെന്ന് നോക്കൂ! `yield` കീവേഡാണ് ഇവിടുത്തെ മാന്ത്രികത. `yield` കാണുമ്പോൾ, ഫംഗ്ഷന്റെ സ്റ്റേറ്റ് മരവിപ്പിക്കുന്നു, മൂല്യം വിളിക്കുന്നയാൾക്ക് അയയ്ക്കുന്നു, ഫംഗ്ഷൻ താൽക്കാലികമായി നിർത്തുന്നു. അടുത്ത തവണ ജനറേറ്റർ ഒബ്ജക്റ്റിൽ `__next__` വിളിക്കുമ്പോൾ, ഫംഗ്ഷൻ നിർത്തിയിടത്ത് നിന്ന് പ്രവർത്തനം പുനരാരംഭിക്കുന്നു, മറ്റൊരു `yield`-ൽ എത്തുന്നതുവരെ അല്ലെങ്കിൽ ഫംഗ്ഷൻ അവസാനിക്കുന്നതുവരെ. ഫംഗ്ഷൻ പൂർത്തിയാകുമ്പോൾ, നിങ്ങൾക്കായി ഒരു `StopIteration` സ്വയമേവ ഉയർത്തപ്പെടുന്നു.
അണിയറയിൽ, പൈത്തൺ `__iter__`, `__next__` മെത്തേഡുകളുള്ള ഒരു ഒബ്ജക്റ്റ് സ്വയമേവ സൃഷ്ടിച്ചിട്ടുണ്ട്. ജനറേറ്ററുകൾ പലപ്പോഴും കൂടുതൽ പ്രായോഗികമായ തിരഞ്ഞെടുപ്പാണെങ്കിലും, ഡീബഗ്ഗിംഗ്, സങ്കീർണ്ണമായ സിസ്റ്റങ്ങൾ രൂപകൽപ്പന ചെയ്യൽ, പൈത്തണിന്റെ പ്രധാന മെക്കാനിക്സ് എങ്ങനെ പ്രവർത്തിക്കുന്നു എന്ന് മനസ്സിലാക്കൽ എന്നിവയ്ക്ക് അടിസ്ഥാനപരമായ പ്രോട്ടോക്കോൾ മനസ്സിലാക്കേണ്ടത് അത്യാവശ്യമാണ്.
മികച്ച രീതികളും സാധാരണ പിഴവുകളും
ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ നടപ്പിലാക്കുമ്പോൾ, സാധാരണ പിശകുകൾ ഒഴിവാക്കാൻ ഈ മാർഗ്ഗനിർദ്ദേശങ്ങൾ മനസ്സിൽ വയ്ക്കുക.
മികച്ച രീതികൾ
- ഇറ്ററബിളും ഇറ്ററേറ്ററും വേർതിരിക്കുക: ഒന്നിലധികം ട്രാവെർസലുകളെ പിന്തുണയ്ക്കേണ്ട ഏതൊരു കണ്ടെയ്നർ ഒബ്ജക്റ്റിനും, എല്ലായ്പ്പോഴും ഇറ്ററേറ്റർ ഒരു പ്രത്യേക ക്ലാസ്സിൽ നടപ്പിലാക്കുക. കണ്ടെയ്നറിന്റെ `__iter__` മെത്തേഡ് ഓരോ തവണയും ഇറ്ററേറ്റർ ക്ലാസ്സിന്റെ ഒരു പുതിയ ഇൻസ്റ്റൻസ് തിരികെ നൽകണം.
- എപ്പോഴും `StopIteration` ഉയർത്തുക: `__next__` മെത്തേഡ് അവസാനം സൂചിപ്പിക്കുന്നതിനായി വിശ്വസനീയമായി `StopIteration` ഉയർത്തണം. ഇത് മറക്കുന്നത് അനന്തമായ ലൂപ്പുകളിലേക്ക് നയിക്കും.
- ഇറ്ററേറ്ററുകൾ ഇറ്ററബിൾ ആയിരിക്കണം: ഒരു ഇറ്ററേറ്ററിന്റെ `__iter__` മെത്തേഡ് എപ്പോഴും `self` തിരികെ നൽകണം. ഇത് ഒരു ഇറ്ററബിൾ പ്രതീക്ഷിക്കുന്ന എവിടെയും ഒരു ഇറ്ററേറ്റർ ഉപയോഗിക്കാൻ അനുവദിക്കുന്നു.
- ലാളിത്യത്തിനായി ജനറേറ്ററുകൾ തിരഞ്ഞെടുക്കുക: നിങ്ങളുടെ ഇറ്ററേറ്റർ ലോജിക് ലളിതവും ഒരൊറ്റ ഫംഗ്ഷനായി പ്രകടിപ്പിക്കാൻ കഴിയുന്നതുമാണെങ്കിൽ, ഒരു ജനറേറ്റർ മിക്കവാറും എല്ലായ്പ്പോഴും വൃത്തിയുള്ളതും കൂടുതൽ വായിക്കാവുന്നതുമാണ്. നിങ്ങൾക്ക് ഇറ്ററേറ്റർ ഒബ്ജക്റ്റുമായി കൂടുതൽ സങ്കീർണ്ണമായ സ്റ്റേറ്റോ മെത്തേഡുകളോ ബന്ധപ്പെടുത്തേണ്ടിവരുമ്പോൾ ഒരു പൂർണ്ണ ഇറ്ററേറ്റർ ക്ലാസ് ഉപയോഗിക്കുക.
സാധാരണ പിഴവുകൾ
- ഉപയോഗിച്ചു തീരുന്ന ഇറ്ററേറ്റർ പ്രശ്നം: ചർച്ച ചെയ്തതുപോലെ, ഒരു ഒബ്ജക്റ്റ് അതിൻ്റെ സ്വന്തം ഇറ്ററേറ്റർ ആയിരിക്കുമ്പോൾ, അത് ഒരു തവണ മാത്രമേ ഉപയോഗിക്കാൻ കഴിയൂ എന്ന് അറിഞ്ഞിരിക്കുക. നിങ്ങൾക്ക് ഒന്നിലധികം തവണ ഇറ്ററേറ്റ് ചെയ്യണമെങ്കിൽ, നിങ്ങൾ ഒന്നുകിൽ ഒരു പുതിയ ഇൻസ്റ്റൻസ് സൃഷ്ടിക്കണം അല്ലെങ്കിൽ വേർതിരിച്ച ഇറ്ററബിൾ/ഇറ്ററേറ്റർ പാറ്റേൺ ഉപയോഗിക്കണം.
- സ്റ്റേറ്റ് മറന്നുപോകുന്നത്: `__next__` മെത്തേഡ് ഇറ്ററേറ്ററിന്റെ ആന്തരിക സ്റ്റേറ്റ് പരിഷ്കരിക്കണം (ഉദാഹരണത്തിന്, ഒരു ഇൻഡെക്സ് വർദ്ധിപ്പിക്കുകയോ ഒരു പോയിന്റർ മുന്നോട്ട് നീക്കുകയോ ചെയ്യുക). സ്റ്റേറ്റ് അപ്ഡേറ്റ് ചെയ്തില്ലെങ്കിൽ, `__next__` ഒരേ മൂല്യം വീണ്ടും വീണ്ടും തിരികെ നൽകും, ഇത് ഒരു അനന്തമായ ലൂപ്പിന് കാരണമായേക്കാം.
- ഇറ്ററേറ്റ് ചെയ്യുമ്പോൾ ഒരു കളക്ഷൻ പരിഷ്കരിക്കുന്നത്: ഒരു കളക്ഷനിൽ ഇറ്ററേറ്റ് ചെയ്യുമ്പോൾ അത് പരിഷ്കരിക്കുന്നത് (ഉദാഹരണത്തിന്, ഒരു ലിസ്റ്റിൽ ഇറ്ററേറ്റ് ചെയ്യുന്ന `for` ലൂപ്പിനുള്ളിൽ നിന്ന് ഐറ്റങ്ങൾ നീക്കം ചെയ്യുന്നത്) പ്രവചനാതീതമായ പെരുമാറ്റത്തിലേക്ക് നയിച്ചേക്കാം, അതായത് ഐറ്റങ്ങൾ ഒഴിവാക്കുകയോ അപ്രതീക്ഷിത പിശകുകൾ ഉയർത്തുകയോ ചെയ്യാം. നിങ്ങൾക്ക് ഒറിജിനൽ പരിഷ്കരിക്കണമെങ്കിൽ കളക്ഷന്റെ ഒരു കോപ്പിയിൽ ഇറ്ററേറ്റ് ചെയ്യുന്നത് സാധാരണയായി സുരക്ഷിതമാണ്.
ഉപസംഹാരം
അതിൻ്റെ ലളിതമായ `__iter__`, `__next__` മെത്തേഡുകളോടുകൂടിയ ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ പൈത്തണിലെ ഇറ്ററേഷന്റെ അടിത്തറയാണ്. ഇത് ഭാഷയുടെ ഡിസൈൻ തത്വശാസ്ത്രത്തിന്റെ ഒരു തെളിവാണ്: ശക്തവും സങ്കീർണ്ണവുമായ സ്വഭാവങ്ങളെ പ്രാപ്തമാക്കുന്ന ലളിതവും സ്ഥിരവുമായ ഇന്റർഫേസുകൾക്ക് മുൻഗണന നൽകുന്നു. സീക്വൻഷ്യൽ ഡാറ്റാ ആക്സസ്സിനായി ഒരു സാർവത്രിക കരാർ നൽകുന്നതിലൂടെ, ഈ പ്രോട്ടോക്കോൾ `for` ലൂപ്പുകൾ, കോംപ്രിഹെൻഷനുകൾ, മറ്റ് എണ്ണമറ്റ ടൂളുകൾ എന്നിവയ്ക്ക് അതിന്റെ ഭാഷ സംസാരിക്കാൻ തിരഞ്ഞെടുക്കുന്ന ഏതൊരു ഒബ്ജക്റ്റുമായും തടസ്സമില്ലാതെ പ്രവർത്തിക്കാൻ അനുവദിക്കുന്നു.
ഈ പ്രോട്ടോക്കോളിൽ പ്രാവീണ്യം നേടുന്നതിലൂടെ, പൈത്തൺ ഇക്കോസിസ്റ്റത്തിലെ ഒന്നാംതരം പൗരന്മാരായ നിങ്ങളുടെ സ്വന്തം സീക്വൻസ് പോലുള്ള ഒബ്ജക്റ്റുകൾ സൃഷ്ടിക്കാനുള്ള കഴിവ് നിങ്ങൾ അൺലോക്ക് ചെയ്തിരിക്കുന്നു. ഡാറ്റ മടിയനായി പ്രോസസ്സ് ചെയ്യുന്നതിലൂടെ കൂടുതൽ മെമ്മറി-കാര്യക്ഷമമായതും, സാധാരണ പൈത്തൺ സിന്റാക്സുമായി വൃത്തിയായി സംയോജിപ്പിക്കുന്നതിലൂടെ കൂടുതൽ അവബോധജന്യമായതും, ആത്യന്തികമായി കൂടുതൽ ശക്തവുമായ ക്ലാസുകൾ നിങ്ങൾക്ക് ഇപ്പോൾ എഴുതാൻ കഴിയും. അടുത്ത തവണ നിങ്ങൾ ഒരു `for` ലൂപ്പ് എഴുതുമ്പോൾ, ഉപരിതലത്തിന് തൊട്ടുതാഴെ നടക്കുന്ന `__iter__`, `__next__` എന്നിവയുടെ മനോഹരമായ നൃത്തത്തെ അഭിനന്ദിക്കാൻ ഒരു നിമിഷം എടുക്കുക.