Decorator

Pada artikel ini, kita akan mempelajari tentang decorator, cara membuat decorator, dan kapan kita sebaiknya menggunakan decorator.

 

Tabel Konten


Pengertian Decorator

Python memiliki sebuah fitur menarik bernama decorator yang berfungsi menambah fungsionalitas pada kode program.

Hal ini disebut juga metaprogramming karena ada bagian dari program yang mencoba untuk memodifikasi bagian lainnya pada saat eksekusi.


Syarat Sebelum Mempelajari Decorator

Sebelum dapat memahami tentang decorator, kita harus terlebih dahulu memahami dasar – dasar Python.

Kita harus paham terlebih dahulu bahwa semua yang ada di Python adalah objek (termasuk kelas). Nama pengenal, seperti variabel yang kita deklarasikan merujuk kepada objek tersebut. Begitu juga dengan fungsi. Fungsi adalah termasuk objek juga. Satu objek bisa memiliki banyak pengenal (identifier) yang merujuk kepadanya.

Perhatikan contoh berikut

  def first(msg): print(msg) first("Hello") second = first second("Hello")

Pada saat kode di atas dijalankan, kedua fungsi first dan second menampilkan output yang sama. Di sini, variabel first dan second merujuk pada objek fungsi yang sama.

Sekarang mari kita tinjau hal yang lain. Sebuah fungsi bisa dijadikan sebagai argumen dari fungsi yang lain.

Bila Anda sudah pernah menggunakan fungsi seperti map, filter, dan reduce di Python, maka Anda sudah tahu tentang hal ini.

Fungsi yang menjadikan fungsi lain sebagai argumen disebut juga fungsi dengan orde yang lebih tinggi. Berikut adalah contohnya:

def inc(x): return x + 1 def dec(x): return x - 1 def operate(func, x): result = func(x) return result

Kita bisa memanggil fungsi tersebut seperti berikut:

>>> operate(inc, 3)
4
>>> operate(dec, 3)
2

Lebih lanjut lagi, sebuah fungsi bisa mengembalikan fungsi lain.

def is_called(): def is_returned(): print("Hello") return is_returned new = is_called() #Outputs "Hello" new()

Pada contoh tersebut, is_returned() adalah fungsi bersarang yang didefinisikan dan dikembalikan tiap kali fungsi is_called() dipanggil.

Selain memahami tentang hal di atas, kita juga sudah harus paham tentang python closure


Kembali ke Decorator

Sebuah decorator mengambil fungsi sebagai argumennya, menambahkan beberapa hal, dan kemudian mengembalikannya.

def make_pretty(func): def inner(): print("I got decorated") func() return inner def ordinary(): print("I am ordinary")

Bila kode di atas kita jalankan pada mode interaktif, maka hasilnya adalah seperti berikut:

>>> ordinary()
I am ordinary

>>> # Mari kita buat decorator dari fungsi ordinary
>>> pretty = make_pretty(ordinary)
>>> pretty()
I got decorated
I am ordinary

Pada contoh di atas, make_pretty() adalah sebuah decorator.

Pada baris perintah pretty = make_pretty(ordinary), fungsi ordinary didekorasi dan fungsi kembaliannya diberi nama pretty.

Kita bisa lihat bahwa fungsi decorator menambahkan beberapa fungsionalitas ke fungsi asli. Hal ini mirip dengan pengemasan kado. Decorator bertindak sebagai bungkusnya. Objek yang didekorasi (isi kado) tidak berubah. Akan tetapi, ketika dibungkus, akan terlihat lebih bagus (karena didekorasi).

Umumnya, kita mendekorasi fungsi dan menyimpannya ke variabel seperti berikut:

ordinary = make_pretty(ordinary)

Python memiliki sintaks yang lebih sederhana untuk penulisan fungsi di atas yaitu dengan menggunakan tanda  @ diikuti oleh nama fungsi decorator. Kita menempatkannya di atas fungsi yang akan didekorasi. Maka contoh berikut ini:

@make_pretty
def ordinary():
    print("I am ordinary")

adalah sama dengan yang berikut:

def ordinary():
    print("I am ordinary")
ordinary = make_pretty(ordinary)

Cara yang menggunakan tanda @ di atas adalah sintaks yang lebih elegan untuk mengimplementasikan decorator.


Dekorasi Fungsi Dengan Parameter

Decorator di atas sangat sederhana dan hanya berlaku untuk fungsi yang tidak memiliki parameter. Bagaimana jika fungsi yang akan didekorasi memiliki argumen seperti berikut?

def divide(a, b):
    return a/b

Fungsi tersebut memiliki 2 parameter, a dan b. Bila kita melewatkan nilai 0 ke b, maka akan terjadi error ZeroDivisionError.

>>> divide(2, 5)
0.4
>>> divide(2, 0)
Traceback (most recent call last):
...
ZeroDivisionError: division by zero

Sekarang kita akan membuat decorator untuk mengecek penyebab error ini.

  def smart_divide(func): def inner(a,b): print("Saya akan membagi",a,"dan",b) if b == 0: print("Whoops! tidak bisa membagi dengan 0") return return func(a,b) return inner @smart_divide def divide(a,b): return a/b  

Kode yang menggunakan decorator ini akan mengembalikan None jika terjadi error.

>>> divide(2, 5)
Saya akan membagi 2 dan 5
0.4

>>> divide(2, 0)
Saya akan membagi 2 dan 0
Whoops! tidak bisa membagi dengan 0

Dengan cara tersebut kita bisa mendekorasi fungsi yang memiliki parameter.

Bila kita perhatikan dengan baik, kita akan melihat kalau semua parameter dari fungsi yang di dalam decorator akan menjadi parameter dari fungsi yang didekorasi. Dengan itu, kita bisa membuat decorator yang lebih umum yang dapat bekerja dengan berapapun jumlah parameternya.

Di Python, hal ini dapat dilakukan dengan sintaks function(*args, **kwargs) di mana args adalah tuple dari argumen – argumen berdasarkan posisi, dan kwargs adalah dictionary dari argumen – argumen ber-keyword.

Berikut ini adalah contoh fungsi inner() yang memiliki sembarang parameter didekorasi oleh fungsi works_for_all().

def works_for_all(func):
    def inner(*args, **kwargs):
        print("Saya bisa mendekorasi fungsi apa saja")
        return func(*args, **kwargs)
    return inner()

Menggabungkan Decorator di Python

Beberapa decorator bisa dihubungkan atau digabungkan. Sebuah fungsi bisa didekorasi beberapa kali dengan satu atau beberapa fungsi decorator. Kita meletakkan decorator di atas fungsi yang akan didekorasi. Untuk jelasnya perhatikan contoh berikut:

  def star(func): def inner(*args, **kwargs): print("*" * 30) func(*args, **kwargs) print("*" * 30) return inner def percent(func): def inner(*args, **kwargs): print("%" * 30) func(*args, **kwargs) print("%" * 30) return inner @star @percent def printer(msg): print(msg) printer("Hello")

Outputnya akan tampak seperti berikut:

******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************

 

Kode decorator di atas yaitu:

@star
@percent
def printer(msg):
    print(msg)

adalah sama dengan:

def printer(msg):
    print(msg)
printer = star(percent(printer))

 

Urutan dari decorator perlu diperhatikan. Bila kita mengubah urutannya menjadi:

@percent
@star
def printer(msg):
    print(msg)

maka hasilnya akan menjadi:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************
Hello
******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 

Python Closure

Pada artikel ini, kita akan mempelajari tentang closure (pembungkus), cara mendefinisikan closure, dan alasan mengapa closure digunakan.

Tabel Konten


Variabel Non-lokal Dalam Fungsi Bersarang

Sebelum membahas tentang closure, pertama kali kita harus memahami tentang fungsi bersarang dan variabel non-lokal.

Fungsi bersarang adalah fungsi yang didefinisikan di dalam fungsi lain. Fungsi bersarang ini dapat mengakses variabel dari scope (lingkup) fungsi pembungkusnya.

Di Python, variabel non-lokal defaultnya adalah bersifat read only. Untuk dapat memodifikasinya, kita harus mendeklarasikannya sebagai variabel non-lokal (menggunakan keyword nonlocal).

Berikut ini adalah contoh fungsi bersarang dan variabel non-lokal.

def print_msg(msg): # Ini adalah fungsi pembungkus luar def printer(): # Ini adalah fungsi bersarang print(msg) printer() # Eksekusi fungsi # Output: Hello print_msg("Hello")

Bisa kita lihat pada contoh di atas, bahwa fungsi printer() dapat mengakses variabel non-lokal msg dari fungsi pembungkusnya.


Mendefinisikan Fungsi Closure

Pada contoh di atas, apa yang terjadi bila baris terakhir fungsi print_msg() mengembalikan fungsi printer() dan bukan memanggilnya? Perhatikan contoh berikut.

  def print_msg(msg): # Ini adalah fungsi pembungkus def printer(): # Ini adalah fungsi bersarang print(msg) return printer # baris ini diubah dari yang sebelumnya # Mari kita panggil fungsinya # Output: Hello another = print_msg("Hello") another()

Hasilnya tampak tidak biasa. Fungsi print_msg() dipanggil dengan argumen "Hello" dan fungsi kembaliannya dirujuk ke variabel another sehingga another menjadi fungsi. Pada saat pemanggilan another(), pesan yang ada masih tetap eksis meskipun kita sudah selesai mengeksekusi fungsi print_msg(). Padahal pada fungsi biasa, seharusnya variabel fungsi akan terhapus begitu eksekusi terhadap fungsi selesai.

Nilai yang ada pada scope fungsi pembungkus masih diingat meskipun variabel sudah di luar scope fungsi, atau bahkan setelah fungsi tersebut dihapus seperti pada contoh berikut.

>>> del print_msg
>>> print_msg("Hello")
Traceback (most recent call last):
...
NameError: name 'print_msg' is not defined

>>> another()
Hello

Kriteria Closure

Seperti terlihat dari contoh di atas, kita memiliki closure di Python pada saat fungsi bersarang (fungsi yang di dalam) merujuk pada variabel yang ada pada fungsi pembungkusnya.

Kriteria yang harus ada untuk membuat closure di Python adalah sebagai berikut:

  • Kita harus memiliki fungsi bersarang (fungsi dalam fungsi)
  • Fungsi yang di dalam harus merujuk ke variabel fungsi pembungkusnya
  • Fungsi pembungkus harus mengembalikan (me-return) fungsi yang di dalamnya.

Kapan Closure Digunakan?

Closure digunakan untuk menghindari penggunaan variabel global dan menyediakan fungsi penyembunyian data (data hiding) sebagai solusi berorientasi objek terhadap permasalahan.

Jika hanya sedikit (umumnya satu buah) metode yang hendak diimplementasikan pada sebuah kelas, kita lebih baik menggunakan closure daripada mendefinisikan kelas. Tapi, ketika banyak atribut dan metode yang akan dibuat, kita lebih baik menggunakan kelas.

Berikut ini adalah contoh di mana penggunaan closure lebih disarankan ketimbang menggunakan kelas untuk menciptakan objek. Tapi semua terserah pilihan Anda.

  def make_multiplier_of(n): def multiplier(x): return x * n return multiplier # Perkalian 3 times3 = make_multiplier_of(3) # Perkalian 5 times5 = make_multiplier_of(5) # Output: 27 print(times3(9)) # Output: 15 print(times5(3)) # Output: 30 print(times5(times3(2)))

Closure ini banyak dipergunakan dalam decorator di Python.

 

 

Generator

Pada artikel ini, kita akan belajar cara menciptakan iterasi menggunakan generator, apa perbedaan generator dengan iterator dan fungsi, dan mengapa kita harus menggunakan generator.

 

Tabel Konten


Pengertian Generator

Ada banyak hal yang berlebihan (overhead) di dalam membuat iterator di Python. Pertama, kita harus membuat kelas yang menggunakan metode __iter__() dan __next__() . Kemudian kita juga harus menjaga state dari item, dan harus membangkitkan StopIteration pada saat tidak ada lagi item yang tersedia.

Hal ini agak panjang dan rumit. Dalam hal ini, untuk menghindari hal tersebut, kita bisa menggunakan generator.

Generator di Python adalah cara sederhana membuat iterator. Semua overhead yang kita sebutkan di atas, akan ditangani secara otomatis oleh generator.

Singkatnya, generator adalah fungsi yang mengembalikan sebuah objek iterator yang padanya bisa kita lakukan iterasi (satu nilai per satu waktu).


Cara Membuat Generator di Python

Cukup mudah untuk membuat generator di Python. Mirip dengan membuat fungsi biasa. Hanya saja, kita menggantikan pernyataan return dengan yield.

Fungsi yang memiliki minimal satu yield (fungsi bisa berisi lebih dari satu yield atau return), akan menjadi fungsi generator. yield maupun return sama – sama berfungsi mengembalikan suatu nilai dari sebuah fungsi.

Perbedaan return dan yield adalah, return akan menghentikan (terminasi) fungsi secara keseluruhan, sementara yield hanya akan menghentikan sementara (pause) fungsi dan menyimpan semua state variabel yang di dalamnya untuk nantinya bisa dilanjutkan kembali dari state tersebut.


Perbedaan Fungsi Generator dan Fungsi Biasa

Berikut adalah perbedaan fungsi generator dengan fungsi biasa:

  • Fungsi generator berisi satu atau lebih pernyataan yield
  • Pada saat dipanggil, fungsi generator akan mengembalikan objek iterator, tapi tidak langsung dieksekusi.
  • Metode __iter__() dan __next__() sudah diimplementasikan secara otomatis. Jadi kita bisa langsung melakukan iterasi dengan fungsi next().
  • Sekali fungsi menemui yield, fungsi akan pause dan kendali ditransfer kembali ke pemanggil.
  • Variabel lokal dan state-nya diingat untuk pemanggilan selanjutnya.
  • Terakhir, pada saat fungsi diterminasi (dihentikan total), StopIteration dipanggil secara otomatis.

Berikut adalah contoh generator dengan beberapa pernyataan yield

# Fungsi generator sederhana def my_gen(): n = 1 print('This is printed first') # Fungsi generator berisi pernyataan yield yield n n += 1 print('This is printed second') yield n n += 1 print('This is printed at last') yield n

Selanjutnya, hasilnya bisa dijalankan pada mode interaktif seperti berikut:

>>> # my_gen() mengembalikan objek iterator, tapi tidak langsung mengeksekusinya
>>> a = my_gen()

>>> # Kita bisa melakukan iterasi pertama menggunakan next()
>>> next(a)
This is printed first
1
>>> # Sekali fungsi menemukan yield, fungsi akan dipause
>>> # dan kendali dikembalikan ke pemanggil
>>> # Variabel lokal dan state-nya akan diingat untuk pemanggilan selanjutnya
>>> next(a)
This is printed second
2

>>> next(a)
This is printed at last
3

>>> Terakhir, fungsi diterminasi, StopIterasi otomatis dibangkitkan
>>> next(a)
Traceback (most recent call last):
...
StopIteration

Salah satu hal yang perlu diperhatikan untuk contoh di atas adalah, nilai dari variabel n tetap diingat pada tiap pemanggilan fungsi.

Berbeda dengan fungsi normal, variabel lokal tidak akan dihancurkan pada saat pernyataan yield. Selain itu, objek generator hanya bisa diiterasi untuk sekali saja. Untuk mengulang kembali proses, kita harus membuat generator lain menggunakan pernyataan seperti misalnya a = my_gen().

Kita juga bisa menggunakan generator di dalam loop for secara langsung. Hal ini karena loop for memakai iterator dan melakukan iterasi padanya menggunakan fungsi next(). StopIteration otomatis dilakukan pada akhir iterasi.

# Fungsi generator sederhana def my_gen(): n = 1 print('This is printed first') # Fungsi generator berisi pernyataan yield yield n n += 1 print('This is printed second') yield n n += 1 print('This is printed at last') yield n # Menggunakan loop for for item in my_gen(): print(item)

Pada saat contoh di atas dijalankan, hasilnya akan tampak seperti berikut:

This is printed first
1
This is printed second
2
This is printed at last
3

Generator Menggunakan Loop

Contoh-contoh di atas jarang dipakai. Kita mempelajarinya hanya untuk tahu teknisnya apa yang sebenarnya terjadi pada generator.

Umumnya, fungsi generator diterapkan pada loop yang memiliki kondisi terminasi (penghentian) yang sesuai.

Mari kita buat contoh generator yang berfungsi membalikkan sebuah string.

  def rev_str(my_str): length = len(my_str) for i in range(length - 1,-1,-1): yield my_str[i] # Loop for untuk membalikkan string # Output: # o # l # l # e # h for char in rev_str("hello"): print(char)

Pada contoh di atas, kita menggunakan fungsi range() untuk mendapatkan indeks secara terbalik yang digunakan dalam loop for.

Fungsi generator seperti itu tidak hanya berlaku untuk string, tapi juga untuk iterable lainnya seperti list, tuple, dan lain – lain.


Generator Expression

Generator sederhana bisa dibuat dengan menggunakan generator expression. Dengan generator expression ini, pembuatan generator menjadi mudah.

Mirip dengan fungsi lambda yang membuat fungsi anonim, generator expression juga membuat sebuah fungsi generator anonim.

Sintaks dari generator expression mirip dengan list comprehension. Hanya saja tanda kurung [ ] digantikan dengan tanda kurung ( ).

Perbedaan utama antara list comprehension dengan generator expression adalah, list comprehension langsung menghasilkan keseluruhan anggota list, sedangkan generator expression menghasilkannya satu item per satu waktu.

Tipe seperti generator expression ini sering disebut tipe lazy (malas), karena hanya memproduksi item pada saat diminta. Hal inilah yang menyebabkan generator expression lebih hemat memori ketimbang list comprehension.

# Membuat list my_list = [1, 3, 6, 10] # Tanda [ ] untuk list comprehension # Output: [1, 9, 36, 100] [x**2 for x in my_list] # Hal yang sama bisa menggunakan generator expression # Output: generator object genexpr at 0x0000000002EBDAF8 (x**2 for x in my_list)

Kita bisa lihat di atas, bahwa generator expression tidak langsung memproduksi hasil secara keseluruhan. Generator expression ini hanya mengembalikan item bila diminta.

# Membuat list my_list = [1, 3, 6, 10] a = (x**2 for x in my_list) # Output: 1 print(next(a)) # Output: 9 print(next(a)) # Output: 36 print(next(a)) # Output: 100 print(next(a)) # Output: StopIteration next(a)

Generator expression juga bisa digunakan sebagai argumen fungsi. Bila demikian, tanda kurungnya bisa dihilangkan. Misalnya pada fungsi sum() dan max() seperti berikut:

>>> my_list = [1, 3, 6, 10]
>>> sum(x**2 for x in my_list)
146
>>> max(x**2 for x in my_list)
100

Alasan Penggunaan Generator

Ada beberapa hal yang menjadi alasan penggunaan generator, yaitu sebagai berikut:

1. Mudah diterapkan

Generator bisa diterapkan dengan lebih mudah dan ringkas dibandingkan dengan kelas iterator. Berikut ini contoh penggunaan kelas iterator untuk menghasilkan deret bilangan pangkat 2 dari 0 sampai yang bilangan ditentukan.

class PowTwo:
    def __init__(self, max=0):
        self.max = max

    def __iter__(self):
        self.n = 0
        return self

    def __next__(self):
        if self.n > self.max:
            raise StopIteration

        result = 2 ** self.n
        self.n += 1
        return result

Dan berikut hasilnya sama dengan menggunakan fungsi generator.

def PowTwoGen(max = 0):
    n = 0
    while n < max:
        yield 2 ** n
        n += 1

Dari contoh di atas, terlihat bahwa penggunaan generator lebih mudah dan ringkas dibandingkan iterator.

2. Lebih Hemat Memori

Fungsi biasa yang dibuat untuk menghasilkan sequence, akan menyimpan keseluruhan sequence sebelum mengembalikan hasilnya. Hal ini akan menghabiskan memori bila jumlah itemnya sangat besar.

Sementara itu, generator jauh lebih hemat memori karena hanya memproduksi item satu persatu yaitu hanya saat dipanggil.

3. Merepresentasikan Stream Tak Berhingga (Infinite Stream)

Generator adalah cara yang paling bagus untuk mewakili aliran data yang tak berhingga. Stream (aliran) data yang tak berhingga, tidak bisa disimpan dalam memori, sehingga cocok menggunakan generator.

Berikut adalah contoh untuk membangkitkan semua bilangan genap yang ada (setidaknya secara teoritis saja)

def all_even():
    n = 0
    while True:
        yield n
        n += 2

4. Pipelining Generator

Pipeline adalah menjadikan input dari sebuah proses menjadi proses dari operasi yang lain. Generator bisa digunakan untuk pipeline dari serangkaian operasi. Hal ini akan lebih jelas bila diilustrasikan dengan contoh.

Anggaplah kita mempunyai sebuah file log dari rantai makanan cepat saji terkenal. File log ini memiliki sebuah kolom (kolom ke-4) yang mencatat jumlah pizza yang terjual setiap jam dan kita ingin menjumlahkannya untuk mendapatkan jumlah pizza yang terjual dalam 5 tahun.

Kita asumsikan bahwa semua yang kosong di kolom ke 4 sebagai 'N/A'. Maka, generator yang bisa diterapkan untuk hal ini adalah seperti berikut.

with open('penjualan.log') as file:
    pizza_col = (line[3] for line in file)
    per_jam = (int(x) for x in pizza_col if x != 'N/A')
    print("Total pizza terjual = ", sum(per_jam))

Pipelining menjadikan kode menjadi lebih efisien dan mudah dibaca.

 

Iterator

Iterator adalah objek yang padanya bisa dilakukan iterasi. Di  tutorial ini, kita akan mempelajari bagaimana cara kerja iterator dan cara membuat iterator kita sendiri menggunakan metode __iter__ dan __next__.


Tabel Konten:

  • Pengertian Iterator
  • Iterasi Menggunakan Iterator di Python
  • Bagaimana Loop Bekerja?
  • Membangun Iterator Sendiri
  • Iterator Tak Berhingga (Infinite Iterator)
  •  


    Pengertian Iterator

    Iterator di Python ada di mana – mana. Iterator ini  sebenarnya sudah diterapkan di dalam looping for, list comprehension, generator, dan lain – lain, hanya saja tidak tampak secara langsung.

    Iterator di Python sederhananya hanyalah suatu objek yang padanya dapat dilakukan iterasi atau looping. Objek akan mengembalikan data, satu data per satu waktu.

    Secara teknis, Objek iterator di Python harus menerapkan dua metode, yaitu __iter__() dan __next__(), yang disebut dengan protokol iterator.

    Suatu objek dikatakan iterable jika dari objek tersebut bisa kita buat iterator. Sebagian besar objek di Python seperti list, tuple, string, dan lain-lain adalah iterable.

    Fungsi iter() (fungsi yang memanggil metode __iter__()) akan mengembalikan iterator dari iterable yang menjadi argumennya.


    Iterasi Menggunakan Iterator di Python

    Kita menggunakan fungsi next() untuk melakukan iterasi pada semua item di iterator. Pada saat iterasi mencapai item terakhir dan tidak ada lagi data untuk dikembalikan, maka fungsi ini akan memunculkan StopIteration. Berikut adalah contohnya.

      # mendefinisikan list my_list = [4, 7, 0, 3] # membuat iterator dengan iter() my_iter = iter(my_list) # iterasi pada my_iter menggunakan next() # print 4 print(next(my_iter)) # print 7 print(next(my_iter)) # next(obj) sama dengan obj.__next__() # print 0 print(my_iter.__next__()) # print 3 print(my_iter.__next__()) # Berikut ini akan memunculkan error karena item sudah habis next(my_iter)

    Cara yang lebih bagus untuk melakukan iterasi pada iterator adalah dengan menggunakan loop for. Misalnya adalah seperti berikut:

    >>> for item in my_list
           print(item)
    
    4
    7
    0
    3

    Bagaimana Loop Bekerja?

    Seperti kita lihat pada contoh diatas, loop for bisa melakukan iterasi otomatis pada list.

    Seperti kita ketahui, loop for bisa melakukan iterasi pada iterable. Mari kita lihat lebih dekat bagaimana loop for diimplementasikan pada Python.

    for item in iterable:
        # proses item

    Di belakang layar, implementasi dari for di atas adalah sebagai berikut:

    # membuat iterator dari iterable
    iter_obj = iter(iterable)
    
    # loop infinite
    while True:
        try:
            # memanggil item selanjutnya
            item = next(iter_obj)
            # lakukan sesuatu pada item
        except StopIteration:
            # jika muncul StopIteration, keluar dari loop
            break

    Jadi, secara internal, loop for menciptakan objek iterator, iter_obj dengan memanggil fungsi iter().

    Loop for ini sebenarnya adalah sebuah loop while infinite (tak berhingga).

    Di dalam loop, fungsi next() akan dipanggil untuk mendapatkan item berikutnya dan body dari loop for akan dieksekusi menggunakan nilai item bersangkutan. Setelah semua item habis, StopIteration akan dimunculkan dan ini menandai akhir dari loop.


    Membangun Iterator Sendiri

    Membangun iterator dari dasar adalah hal yang mudah. Kita hanya perlu menerapkan metode __iter__() dan __next__().

    Metode __iter__() akan mengembalikan objek iterator. Bila diperlukan, beberapa pengaturan awal bisa dilakukan.

    Metode __next__() akan mengembalikan item selanjutnya yang ada di dalam sequence. Saat sudah mencapai item terakhir, maka pemanggilan berikutnya akan menghasilkan StopIteration.

    Berikut contoh program yang menampilkan pangkat 2 dari bilangan setiap kali iterasi. Eksponennya mulai dari nol sampai bilangan yang diset oleh user.

    class PowTwo: """Kelas yang mengimplementasikan iterator pangkat dua""" def __init__(self, max = 0): self.max = max def __iter__(self): self.n = 0 return self def __next__(self): if self.n <= self.max: result = 2 ** self.n self.n += 1 return result else: raise StopIteration

    Selanjutnya kita dapat membuat iterator dan melakukan iterasi seperti berikut

    >>> a = PowTwo(4)
    >>> i = iter(a)
    >>> next(i)
    1
    >>> next(i)
    2
    >>> next(i)
    4
    >>> next(i)
    8
    >>> next(i)
    16
    >>> next(i)
    Traceback (most recent call last):
    ...
    StopIteration

    Kita juga bisa melakukan iterasi menggunakan loop for pada iterator yang sudah kita buat.

    >>> for i in PowTwo(5):
            print(i)
    
    1
    2
    4
    8
    16
    32

    Iterator Tak Berhingga (Infinite Iterator)

    Kadangkala kita memerlukan iterator yang itemnya tidak akan habis. Tapi kita harus hati – hati untuk menangani iterator seperti ini.

    Berikut ini akan ditunjukkan contoh dari iterator tak berhingga.

    Fungsi built-in iter() dapat dipanggil menggunakan dua argumen, di mana argumen pertama adalah objek callable (sebuah fungsi) dan yang kedua adalah sentinel/penentu. Iterator memanggil fungsi ini sampai nilai yang dikembalikan sama dengan nilai sentinel.

    >>> int()
    0
    >>> inf = iter(int, 1)
    >>> next(inf)
    0
    >>> next(inf)
    0

    Kita bisa lihat bahwa fungsi int() tanpa argumen akan selalu mengembalikan nilai 0. Oleh sebab itu, saat fungsi int() dilewatkan ke iter(int, 1), akan dikembalikan iterator yang memanggil fungsi int() terus menerus sampai nilainya sama dengan sentinel 1. Dan hal ini,nilai 1 tidak akan pernah terjadi, sehingga yang didapat adalah iterator tak berhingga.

    Kita juga bisa membuat iterator tak berhingga kita sendiri. Secara teori, iterator berikut ini akan mengembalikan semua bilangan ganjil.

    class InfIter: """Infinite iterator yang mengembalikan semua bilangan ganjil""" def __iter__(self): self.num = 1 return self def __next__(self): num = self.num self.num += 2 return num

    Contoh penggunaannya adalah seperti berikut:

    >>> a = iter(InfIter())
    >>> next(a)
    1
    >>> next(a)
    3
    >>> next(a)
    5
    >>> next(a)
    7

    dan begitu selanjutnya…

    Kita perlu hati – hati untuk memasukkan kondisi terminasi untuk menghentikan iterasi pada iterator tak berhingga ini. Hal ini mirip dengan loop menggunakan while.

    Keuntungan dari penggunaan iterator adalah bisa menghemat penggunaan memori. Seperti pada contoh di atas, kita bisa mendapatkan semua bilangan ganjil tanpa harus menyimpan semua bilangan di dalam memori. Secara teori, kita bisa menyimpan item tak berhingga pada memori yang terbatas.

    Selain itu, iterator membuat kode terlihat lebih rapi dan bagus.

    Ada cara lain yang lebih mudah untuk membuat iterator di Python, yaitu dengan generator menggunakan yield.

    Pemformatan (Formatting) di Python

    Python memiliki metode pemformatan (formatting) yang powerful. Ada dua cara atau style formatting di Python, yaitu dengan style lama dan dengan style baru. Style lama menggunakan tanda persen %, sedangkan style baru menggunakan metode format(). Di artikel ini, Anda akan menjumpai formatting string dan bilangan lengkap yang mencakup penggunaan format cara lama dan baru disertai dengan contoh – contohnya.

    Continue reading “Pemformatan (Formatting) di Python”

    Pemrograman Berorientasi Objek di Python

    Pengertian Pemrograman Berorientasi Objek

    Pemrograman berorientasi objek atau dalam bahasa inggris disebut Object Oriented Programming (OOP) adalah paradigma atau teknik pemrograman di mana semua hal dalam program dimodelkan seperti objek dalam dunia nyata. Objek di dunia nyata memiliki ciri atau attribut dan juga aksi atau kelakuan (behaviour).

    Continue reading “Pemrograman Berorientasi Objek di Python”

    List Comprehension

    List comprehension adalah cara mudah untuk mendefinisikan dan membuat list di Python.

    List comprehension terdiri dari sebuah ekspresi diikuti oleh pernyataan for yang diletakkan di dalam tanda kurung [ ].Dengan menggunakan list comprehension kita bisa membuat list secara otomatis dalam satu baris perintah saja. Ini sangat berguna jika anggota list yang hendak kita buat cukup banyak.

    Continue reading “List Comprehension”