ವಿಷಯಕ್ಕೆ ತೆರಳಿ

ಪೈಥಾನ್ ಜನರೇಟರ್ಸ್ ಮತ್ತು ಇಟರೇಟರ್ಸ್: ಸಂದರ್ಶನ ಪ್ರಶ್ನೆಗಳು

1. ಇಟರೇಟರ್ (Iterator) ಮತ್ತು ಇಟರೇಬಲ್ (Iterable) ನಡುವಿನ ವ್ಯತ್ಯಾಸವೇನು?

ಉತ್ತರ:

  • ಇಟರೇಬಲ್ (Iterable):

    • ಇದು for ಲೂಪ್‌ನಲ್ಲಿ ಬಳಸಬಹುದಾದ ಯಾವುದೇ ಆಬ್ಜೆಕ್ಟ್.
    • ಇದರ ಮೇಲೆ iter() ಫಂಕ್ಷನ್ ಅನ್ನು ಅನ್ವಯಿಸಿ ಇಟರೇಟರ್ ಅನ್ನು ಪಡೆಯಬಹುದು.
    • ಉದಾಹರಣೆ: ಲಿಸ್ಟ್ (List), ಟಪಲ್ (Tuple), ಸ್ಟ್ರಿಂಗ್ (String), ಡಿಕ್ಷನರಿ (Dictionary).
  • ಇಟರೇಟರ್ (Iterator):

    • ಇದು __next__() ಮೆಥಡ್ ಅನ್ನು ಹೊಂದಿರುವ ಆಬ್ಜೆಕ್ಟ್.
    • ಪ್ರತಿ ಬಾರಿ next() ಫಂಕ್ಷನ್ ಅನ್ನು ಕಾಲ್ ಮಾಡಿದಾಗ, ಇದು ಸೀಕ್ವೆನ್ಸ್‌ನ ಮುಂದಿನ ಐಟಂ ಅನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತದೆ.
    • ಎಲ್ಲಾ ಐಟಂಗಳು ಮುಗಿದಾಗ, StopIteration ಎಕ್ಸೆಪ್ಶನ್ ಅನ್ನು ರೈಸ್ ಮಾಡುತ್ತದೆ.
    • ಪ್ರಮುಖ: ಪ್ರತಿಯೊಂದು ಇಟರೇಟರ್ ಕೂಡ ಒಂದು ಇಟರೇಬಲ್, ಆದರೆ ಪ್ರತಿಯೊಂದು ಇಟರೇಬಲ್ ಇಟರೇಟರ್ ಅಲ್ಲ.

ಉದಾಹರಣೆ:

my_list = [1, 2, 3]  # ಇದು ಇಟರೇಬಲ್
my_iterator = iter(my_list)  # ಇದು ಇಟರೇಟರ್

print(next(my_iterator))  # 1
print(next(my_iterator))  # 2
print(next(my_iterator))  # 3
# print(next(my_iterator)) # StopIteration

2. ಜನರೇಟರ್ (Generator) ಎಂದರೇನು?

ಉತ್ತರ: ಜನರೇಟರ್ ಎನ್ನುವುದು ಇಟರೇಟರ್‌ಗಳನ್ನು ರಚಿಸಲು ಒಂದು ಸರಳ ಮತ್ತು ಮೆಮೊರಿ-ಸಮರ್ಥ ವಿಧಾನ. ಇದು ಒಂದು ವಿಶೇಷ ರೀತಿಯ ಫಂಕ್ಷನ್ ಆಗಿದ್ದು, return ಬದಲಿಗೆ yield ಕೀವರ್ಡ್ ಅನ್ನು ಬಳಸುತ್ತದೆ.

  • yield ಕೀವರ್ಡ್ ಅನ್ನು ತಲುಪಿದಾಗ, ಫಂಕ್ಷನ್ ತನ್ನ ಸ್ಟೇಟ್ ಅನ್ನು ಉಳಿಸಿಕೊಂಡು, ಮೌಲ್ಯವನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತದೆ ಮತ್ತು ಪಾಸ್ ಆಗುತ್ತದೆ.
  • ಮುಂದಿನ ಬಾರಿ next() ಅನ್ನು ಕಾಲ್ ಮಾಡಿದಾಗ, ಅದು ನಿಲ್ಲಿಸಿದ ಸ್ಥಳದಿಂದಲೇ ಮುಂದುವರಿಯುತ್ತದೆ.
  • ಇದು ಒಂದೇ ಬಾರಿಗೆ ಎಲ್ಲಾ ಮೌಲ್ಯಗಳನ್ನು ಮೆಮೊರಿಯಲ್ಲಿ ಸಂಗ್ರಹಿಸುವುದಿಲ್ಲ, ಬದಲಿಗೆ ಅಗತ್ಯವಿದ್ದಾಗ ಒಂದೊಂದಾಗಿ ಉತ್ಪಾದಿಸುತ್ತದೆ.

3. yield ಮತ್ತು return ನಡುವಿನ ವ್ಯತ್ಯಾಸವೇನು?

ಉತ್ತರ:

ಲಕ್ಷಣ return yield
ಕಾರ್ಯ ಫಂಕ್ಷನ್ ಅನ್ನು ಕೊನೆಗೊಳಿಸುತ್ತದೆ ಮತ್ತು ಒಂದು ಮೌಲ್ಯವನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತದೆ. ಫಂಕ್ಷನ್ ಅನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ
ನಿಲ್ಲಿಸುತ್ತದೆ, ಮೌಲ್ಯವನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತದೆ, ಮತ್ತು ತನ್ನ ಸ್ಟೇಟ್ ಅನ್ನು ಉಳಿಸಿಕೊಳ್ಳುತ್ತದೆ.
ಬಳಕೆ ಸಾಮಾನ್ಯ ಫಂಕ್ಷನ್‌ಗಳಲ್ಲಿ. ಜನರೇಟರ್ ಫಂಕ್ಷನ್‌ಗಳಲ್ಲಿ.
ಕಾಲ್ ಫಂಕ್ಷನ್ ಪ್ರತಿ ಬಾರಿ ಕಾಲ್ ಆದಾಗ ಮೊದಲಿನಿಂದ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ. ಫಂಕ್ಷನ್ ನಿಲ್ಲಿಸಿದ ಸ್ಥಳದಿಂದಲೇ ಮುಂದುವರಿಯುತ್ತದೆ.
ಮೌಲ್ಯಗಳು ಒಂದೇ ಒಂದು ಮೌಲ್ಯವನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತದೆ. ಮೌಲ್ಯಗಳ ಸರಣಿಯನ್ನು (ಸೀಕ್ವೆನ್ಸ್) ಒಂದೊಂದಾಗಿ ಉತ್ಪಾದಿಸುತ್ತದೆ.

4. ಜನರೇಟರ್ ಎಕ್ಸ್‌ಪ್ರೆಶನ್ (Generator Expression) ಎಂದರೇನು?

ಉತ್ತರ: ಜನರೇಟರ್ ಎಕ್ಸ್‌ಪ್ರೆಶನ್, ಲಿಸ್ಟ್ ಕಾಂಪ್ರಹೆನ್ಷನ್‌ಗೆ ಹೋಲುತ್ತದೆ, ಆದರೆ ಇದು ಜನರೇಟರ್ ಆಬ್ಜೆಕ್ಟ್ ಅನ್ನು ರಚಿಸುತ್ತದೆ. ಇದನ್ನು ಸ್ಕ್ವೇರ್ ಬ್ರಾಕೆಟ್‌ಗಳ ([]) ಬದಲು ಪೇರೆಂಥಿಸಿಸ್ (()) ಬಳಸಿ ಬರೆಯಲಾಗುತ್ತದೆ.

ಇದು ಕೂಡ ಮೆಮೊರಿ-ಸಮರ್ಥವಾಗಿದೆ, ಏಕೆಂದರೆ ಇದು ಎಲ್ಲಾ ಮೌಲ್ಯಗಳನ್ನು ಒಂದೇ ಬಾರಿಗೆ ರಚಿಸುವುದಿಲ್ಲ.

ಉದಾಹರಣೆ:

# ಲಿಸ್ಟ್ ಕಾಂಪ್ರಹೆನ್ಷನ್ (ಮೆಮೊರಿಯಲ್ಲಿ ಪೂರ್ಣ ಲಿಸ್ಟ್ ರಚಿಸುತ್ತದೆ)
my_list = [i * i for i in range(10)]

# ಜನರೇಟರ್ ಎಕ್ಸ್‌ಪ್ರೆಶನ್ (ಜನರೇಟರ್ ಆಬ್ಜೆಕ್ಟ್ ರಚಿಸುತ್ತದೆ)
my_generator = (i * i for i in range(10))

print(my_list)
print(my_generator)  # <generator object <genexpr> at ...>

for num in my_generator:
    print(num, end=" ")

5. ಜನರೇಟರ್‌ಗಳ ಉಪಯೋಗಗಳೇನು?

ಉತ್ತರ:

  1. ಮೆಮೊರಿ ದಕ್ಷತೆ (Memory Efficiency): ದೊಡ್ಡ ಡೇಟಾಸೆಟ್‌ಗಳೊಂದಿಗೆ ಕೆಲಸ ಮಾಡುವಾಗ ಇದು ಅತ್ಯಂತ ಉಪಯುಕ್ತ. ಲಕ್ಷಾಂತರ ಐಟಂಗಳನ್ನು ಹೊಂದಿರುವ ಲಿಸ್ಟ್ ಅನ್ನು ರಚಿಸುವ ಬದಲು, ಜನರೇಟರ್ ಬಳಸಿ ಒಂದೊಂದಾಗಿ ಐಟಂಗಳನ್ನು ಪ್ರೊಸೆಸ್ ಮಾಡಬಹುದು. ಇದು ಮೆಮೊರಿ ಬಳಕೆಯನ್ನು ಗಣನೀಯವಾಗಿ ಕಡಿಮೆ ಮಾಡುತ್ತದೆ.
  2. ಲೇಜಿ ಇವ್ಯಾಲ್ಯುಯೇಶನ್ (Lazy Evaluation): ಮೌಲ್ಯಗಳು ಅಗತ್ಯವಿದ್ದಾಗ ಮಾತ್ರ ಉತ್ಪಾದಿಸಲ್ಪಡುತ್ತವೆ.
  3. ಸರಳ ಕೋಡ್: ಕಸ್ಟಮ್ ಇಟರೇಟರ್ ಕ್ಲಾಸ್‌ಗಳನ್ನು (__iter__ ಮತ್ತು __next__ ಮೆಥಡ್‌ಗಳೊಂದಿಗೆ) ಬರೆಯುವುದಕ್ಕಿಂತ ಜನರೇಟರ್ ಫಂಕ್ಷನ್‌ಗಳನ್ನು ಬರೆಯುವುದು ಸುಲಭ.
  4. ಪೈಪ್‌ಲೈನಿಂಗ್ (Pipelining): ಹಲವಾರು ಜನರೇಟರ್‌ಗಳನ್ನು ಒಟ್ಟಿಗೆ ಜೋಡಿಸಿ ಡೇಟಾ ಪ್ರೊಸೆಸಿಂಗ್ ಪೈಪ್‌ಲೈನ್‌ಗಳನ್ನು ರಚಿಸಬಹುದು.