Topic 14+: Advanced Python Topics CSGE601020 - Dasar-Dasar Pemrograman 1 Ichlasul Affan, S.Kom., M.Kom. Acknowledgement Some of the design assets used in these slides were provided by ManyPixels under an nonexclusive, worldwide copyright license to download, copy, modify, distribute, perform, and use the assets provided from ManyPixels for free, including for commercial purposes, without permission from or attributing the creator or ManyPixels. Copyright 2020 MANYPIXELS PTE LTD Some additional contents, illustrations and visual design elements are provided by Lintang Matahari Hasani, M.Kom. In this session, you will learn ... Functions: *args, **kwargs, Higher Order Function Bring additional logics using Decorators Classes: Static Method and Class Method Make one-line functions using Lambda Behind the Scene: Generators and Iterators Apa itu Decorator? Decorator = bertugas mendekorasi/mempercantik sesuatu Di dunia nyata, kita menambahkan atau membungkus sesuatu ke sebuah barang/ruang. Tujuannya bisa untuk mempercantik, atau menambah rasa tertentu. Di dunia pemrograman, kita menambahkan logika atau fungsionalitas tambahan terhadap suatu fungsi atau method yang sudah ada. 4 Higher Order Function ➔ ➔ Fungsi atau method itu adalah sebuah objek di Python. Layaknya semua objek yang ada di Python, fungsi atau method bisa di-pass sebagai argument suatu fungsi. class MyClass: def print_hehe(self): print("hehe, saya sebuah method.") def print_meong(): print("meong, saya sebuah fungsi.") an_object = MyClass() print(an_object.print_hehe) print(print_meong) <bound method MyClass.print_hehe of <__main__.MyClass object at 0x000001564DA2CCA0>> <function print_meong at 0x000001564DA03AC0> 5 Pass by Object Reference Ketika fungsi menerima sebuah parameter, dan argumen yang diberikan adalah sebuah variabel, variabel yang menyimpan parameter tersebut pada fungsi akan mengarah ke alamat yang sama dengan variabel yang menerima argumen def process(left_side, right_side): print(left_side, right_side) an_object = MyClass() process(an_object, 25) 6 *args dan **kwargs ➔ ➔ ➔ *args menyimpan argumen yang tidak termasuk ke dalam positional argument yang tertulis di definisi fungsi, dalam bentuk list. **kwargs menyimpan argumen yang tidak termasuk ke dalam keyword argument yang tertulis di definisi fungsi, dalam bentuk dict. Perbedaan antara keduanya terletak di jumlah tanda bintang (*), namanya tidak harus args atau kwargs. def cek_argumen(nama, *args, **kwargs): print(nama) print(args) print(kwargs) cek_argumen("Bambang", 30, 178, lokasi="Depok", instansi="UI") Bambang [30, 178] {"lokasi": "Depok", "instansi": "UI"} 7 *args dan **kwargs ➔ ➔ Kita bisa pass setiap isi dari suatu list ke dalam fungsi dengan menggunakan sintaks * yang sama. Begitupun dengan semua key-value pair pada dict yang bisa diassign sebagai argumen menggunakan sintaks **. def cek_argumen(nama, usia, tinggi, lokasi=None, instansi=None): print(nama) print(usia) print(tinggi) print(lokasi) print(instansi) lst = ["Bambang", 30, 178] dct = {"lokasi": "Depok", "instansi": "UI"} cek_argumen(*lst, **dct) Bambang 30 178 Depok UI 8 Decorator Function ➔ ➔ ➔ Fungsi decorator nantinya akan mengembalikan (return) sebuah fungsi internal. Fungsi internal tersebut akan dijalankan terlebih dahulu, dengan menerima argumen yang sama dengan fungsi yang dibungkus (dari *args dan **kwargs). Fungsi decorator bisa di-apply ke fungsi/method lain dengan menggunakan @nama_fungsi_decorator di atas definisi fungsi tersebut. Contoh: buat decorator yang menambahkan hasil dari fungsi yang didekorasi dengan 5. def tambah_lima(func): def fungsi_internal(*args, **kwargs): return 5 + func(*args, **kwargs) return fungsi_internal @tambah_lima def perkalian(kiri, kanan): return kiri * kanan print(perkalian(5, 7)) 40 9 Decorator Function ➔ ➔ ➔ Fungsi decorator nantinya akan mengembalikan (return) sebuah fungsi internal. Fungsi internal tersebut akan dijalankan terlebih dahulu, dengan menerima argumen yang sama dengan fungsi yang dibungkus (dari *args dan **kwargs). Fungsi decorator bisa di-apply ke fungsi/method lain dengan menggunakan @nama_fungsi_decorator di atas definisi fungsi tersebut. Kode di slide sebelumnya, sama saja seperti: def tambah_lima(kiri, kanan): return 5 + perkalian(kiri, kanan) def perkalian(kiri, kanan): return kiri * kanan print(tambah_lima(perkalian(5, 7))) 40 10 Class Method Decorator ➔ ➔ ➔ Dengan menggunakan decorator @classmethod, method tersebut akan menerima class (bukan objek) sebagai argumen pertamanya. Artinya, kalau ada pemanggilan variable, yang diambil adalah class variable (variable yang disimpan di namespace class, bukan object) Begitupun dengan method yang bisa dipanggil dalam sebuah class method hanya static method atau class method. Bagi yang perlu tahu apa itu class variable, silakan kunjungi kembali materi Intro to Object-Oriented Programming (OOP) [Week 10] class MyClass: angka = 5 def __init__(self): self.angka = 7 @classmethod def get_angka(cls): return cls.angka an_object = MyClass() print(an_object.get_angka()) print(MyClass.get_angka()) 5 5 11 Static Method Decorator ➔ ➔ Dengan menggunakan decorator @staticmethod, method tersebut tidak akan menerima objek atau class di argumen pertamanya. Method yang bisa dipanggil dalam static method harus juga berupa static method, karena tidak ada objek atau class yang di-pass ke argumen. class MyClass: @staticmethod def get_string(): return "hehe" an_object = MyClass() print(an_object.get_string()) print(MyClass.get_string()) hehe hehe 12 Lambda ➔ ➔ ➔ Kita bisa membuat sebuah fungsi “one-liner” dengan menggunakan lambda. Sintaks: lambda arg1, arg2, ...: return_value Fungsi lambda akan menerima beberapa argumen dan langsung akan mengembalikan hasil dari pemrosesan tersebut (tanpa sintaks “return”). penjumlahan_lambda = lambda x, y: x + y def penjumlahan(x, y): return x + y print(penjumlahan_lambda(5, 3)) print(penjumlahan(5, 3)) 8 8 13 Lambda: Kriteria Sorting untuk sorted() ➔ ➔ Salah satu kegunaan Lambda function adalah untuk kriteria pengurutan ketika menggunakan sorted(). Contohnya di sini, kita akan mengurutkan dictionary berdasarkan valuenya dari kecil ke besar. Dengan menggunakan method items(), kita bisa mendapatkan list of “tuple view”, dengan sisi kiri sebagai key, dan sisi kanan sebagai value. Di sini kita ingin mengurutkan value, jadi key kita isi dengan lambda yang akan mengambil index ke-1 dari setiap tuple. dct = {"Bambang": 5, "Usep": 7, "Huki": 1} new_dct = dict(sorted(dct.items(), key=lambda item: item[1])) print(new_dct) {'Huki': 1, 'Bambang': 5, 'Usep': 7} 14 Lambda: Fungsi Berparameter untuk Callback GUI Dengan menggunakan Lambda function, kita bisa menjalankan fungsi dengan parameter yang kita persiapkan sendiri. Contoh: Implementasi button yang akan memasukkan angka berbeda ke dalam instance variable calc_string. from tkinter import * class MyGUI(Tk): def __init__(self): super().__init__() self.calc_string = "" for digit in range(1, 10): button = Button(self, text=str(digit), command=lambda angka=digit: self.insert_angka(angka)) button.grid(column=(digit-1)%3, row=4-((digit-1)//3)) def insert_angka(self, angka): self.calc_string += str(angka) print(self.calc_string) 15 Lambda: Fungsi Berparameter untuk Event Binding GUI Dengan menggunakan Lambda function, kita bisa menjalankan fungsi dengan parameter yang kita persiapkan sendiri. Contoh: Implementasi left-click event untuk membuat persegi panjang dan right-click untuk persegi di sebuah Canvas. from tkinter import * class MyGUI(Tk): def __init__(self): super().__init__() self.canvas = Canvas(self, width=500, height=500) self.canvas.pack() self.canvas.bind("<Button-1>", lambda event: self.draw(event, "persegi_panjang")) self.canvas.bind("<Button-3>", lambda event: self.draw(event, "persegi")) def draw(self, event, mode): width = 50 if mode == "persegi_panjang": width = width * 2 self.canvas.create_rectangle(event.x, event.y, event.x + width, event.y + 50) 16 Generator Function Generator di Python merupakan fungsi yang mengembalikan setiap item pada sequence, satu per satu. Salah satu contoh dari generator adalah ekspresi yang kita gunakan di list/set/tuple/dict comprehension. Akan tetapi, sebenarnya kita bisa membuat generator function sendiri, mirip dengan cara kita membuat fungsi biasa. >>> [x * 3 for x in range(7) if x % 2 == 0] [0, 6, 12, 18] >>> (x * 3 for x in range(7) if x % 2 == 0) <generator object <genexpr> at 0x000001F9925649E0> >>> gen = (x * 3 for x in range(7) if x % 2 == 0) >>> for val in gen: ... print(val) 0 6 12 18 17 Generator Function Keyword yield akan mengembalikan sebuah “generator object” yang akan menjalankan fungsi tersebut hingga bertemu dengan yield berikutnya. >>> (x * 3 for x in range(7) if x % 2 == 0) <generator object <genexpr> at 0x000001F9925649E0> Untuk mendapatkan isi dari yield tersebut, bisa menggunakan fungsi bawaan next() def fungsi_generator(): for x in range(7): if x % 2 == 0: yield x * 3 Ketika panggilan next() berikutnya sudah tidak ada pertemuan dengan yield lagi dan eksekusi fungsi selesai, panggilan next() tersebut akan mengembalikan None. Kita bisa gunakan generator function ini untuk looping expression pada for loop. sama saja dengan >>> gen_obj = fungsi_generator() >>> next(gen_obj) 0 >>> next(gen_obj) 6 >>> next(gen_obj) 12 >>> next(gen_obj) 18 >>> next(gen_obj) >>> next(gen_obj) 18 Iterator Function Iterator function adalah fungsi generator untuk melakukan iterasi satu per satu terhadap isi dari suatu struktur data (list, set, tuple, dictionary, string, dst.) Dalam Python, kita bisa mendefinisikan “iterator method” untuk suatu kelas dengan mengimplementasikan method __iter__. ● ● ● Untuk mendapatkan objek generatornya, gunakan fungsi bawaan iter(). Selanjutnya, gunakan fungsi next() untuk mendapatkan hasil setiap iterasinya. Jika iterasi sudah habis, akan muncul error StopIteration yang perlu dihandle. class FriendList: def __init__(self): self.friends = [] def add(self, name): self.friends.append(name) def __iter__(self): sorted_friends = sorted(self.friends) for friend in sorted_friends: yield friend >>> friendlist = FriendList() >>> friendlist.add("Huki") >>> friendlist.add("Areng") >>> friendlist.add("Cimung") >>> iter_obj = iter(friendlist) >>> next(iter_obj) "Areng" >>> next(iter_obj) "Cimung" >>> next(iter_obj) "Huki" 19 Q&A Session