# Bab 7. OBJECT ORIENTED PROGRAMMING**
**OBJEKTIF:**
1. Mahasiswa Mampu Memahami Prosedural dan *Object Oriented Programming*.
2. Mahasiswa Mampu Memahami *Class* pada Python.
3. Mahasiswa Mampu Memahami *Inheritance* pada Pyhton.
4. Mahasiswa Mampu Memahami *Polymorphism* pada Python
---
**7.1 PROSEDURAL DAN OBJECT ORIENTED PROGRAMMING**
Terdapat dua Metode pemrogramana yang digunakan saat ini yaitu:
1. Prosedural: Memecah program menjadi prosedur-prosedur (fungsi-fungsi)
2. *Object oriented*: Memecah program menjadi *object*-*object*
Pada prosedural *programming* data dan fungsi terpisah. Sedangkan pada *object oriented programming* data dan fungsi adalah satu kesatuan.
Ilustrasi pengolahan data pada prosedural *programming* dan OOP:
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210728152522047.png" alt="image-20210728152522047" style="zoom:47%;" />
Pada prosedural *programming* kita membuat program dengan pendekatan *top down* yaitu kita merancang terlebih dahulu logika utama (fungsi main) dari program lalu memecah program menjadi fungsi-fungsi. Sedangkan pada OOP, kita membuat program dengan pendekatan *bottom up* yaitu kita merancang program dengan membuat *object*-*object* dasar terlebih dahulu dan mengembangkan *object*-*object* tersebut lalu mengintegrasikan *object*-*object* dalam fungsi main. Memecah program menjadi fungsi-fungsi yang kita lakukan pada topik-topik sebelumnya adalah prosedural *programming*. Python selain mendukung prosedural *programming* juga mendukung *object oriented programming*. Pada bagian ini kita akan membahas *object oriented programming*
**7.2 CLASS**
*Object* adalah entitas program yang terdiri dari data dan fungsi. Data yang berada di dalam *object* disebut sebagai *attribute*. Fungsi yang dilakukan oleh *object* disebut sebagai *method*. Sebelum sebuah *object* dapat dibuat, kita harus mendesain *object* tersebut dengan mendefinisikan *class*. Kita dapat membayangkan *class* sebagai “cetakan” yang digunakan untuk mencetak *object*-*object*.
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210728153528610.png" alt="image-20210728153528610" style="zoom:67%;" />
*Syntax* umum penulisan definisi *class*:
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210729165619403.png" alt="image-20210729165619403" style="zoom:63%;" />
Dalam *body class* kita menuliskan:
1. *method* `__ init__` (dua *underscore* sebelum dan setelah kata `init`) untuk mendefinisikan *attribute*-*attribute* apa saja yang terdapat dalam *class* dan untuk menginisialisasi nilai-nilai *attribute* saat instansiasi
2. *method*-*method* lainnya
Contoh definisi *class*:
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210729171421271.png" alt="image-20210729171421271" style="zoom:63%;" />
**7.2.1 METHOD __ init__**
*Method* `__init__` adalah *method* khusus yang digunakan untuk menginisialisasi (memberikan nilai awal) ke *attribute*-*attribute* dari *object*. *Method* `__init__` dipanggil otomatis ketika kita membuat *object* (menginstansiasi) dari *class*. Untuk membuat *object* (menginstansiasi) dari *class* kita memanggil nama *class* dan memberikan nilai argumen ke parameter-parameter dari *method* `__init__`
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210729172201240.png" alt="image-20210729172201240" style="zoom:64%;" />
**7.2.2 MENGINSTANSIASI CLASS**
Kita dapat menginstansiasi lebih dari satu *object* dari suatu *class*.
```python
heli = Dog('terrier', 'coklat')
bleki = Dog('bulldog', 'hitam')
snowy = Dog('terrier', 'putih')
```
Setiap *object* akan mempunyai nilai-nilai *attribute* masing-masing.
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210729172520383.png" alt="image-20210729172520383" style="zoom:64%;" />
Misalkan, kita membuat sebuah program yang mensimulasikan pelemparan koin. Dalam program ini, kita mensimulasikan pelemparan koin berulang kali dan setiap lemparan program menentukan sisi koin (“*Heads*” atau “*Tails*”) yang menghadap atas ketika jatuh. Untuk merepresentasikan koin, kita membuat sebuah *class* dengan nama *Coin*.
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210729172733771.png" alt="image-20210729172733771" style="zoom:50%;" />
contoh program:
```python
import random
# class Coin mensimulasikan sebuah coin
# yang dapat dilempar
class Coin:
# Method __init__ menginisialisasi
# attribute data sisiatas dengan 'Heads'.
def __init__(self):
self.sisiatas = 'Heads'
# Method lempar menggenerasi sebuah angka random
# di antara 0 s.d 1. Jika angka = 0, maka sisiatas
# ditetapkan sebagai 'Heads'
# Jika = 1, maka sisiatas ditetapkan sebagai 'Tails'
def lempar(self):
if random.randint(0, 1) == 0:
self.sisiatas = 'Heads'
else:
self.sisiatas = 'Tails'
# Method get_sideup mengembalikan nilai
# yang direferensikan oleh sisiatas
def get_sisiatas(self):
return self.sisiatas
# Fungsi main
def main():
# Buat object Coin
my_coin = Coin()
# Tampilkan sisi coin yang menghadap atas
print('Sisi koin yang menghadap atas:', my_coin.get_sisiatas())
# Lempar koin
print('Saya melempar koin...')
my_coin.lempar()
# Tampilkan sisi dari koin yang menghadap atas
print('Sisi koin yang menghadap atas:', my_coin.get_sisiatas())
# Panggil fungsi main
main()
```
*Output*:
Sisi koin yang menghadap atas: Heads
Saya melempar koin...
Sisi koin yang menghadap atas: Tails
```python
import random
# class Coin mensimulasikan sebuah coin
# yang dapat dilempar
class Coin:
# Method __init__ menginisialisasi
# attribute data sisiatas dengan 'Heads'.
def __init__(self):
self.sisiatas = 'Heads'
# Method lempar menggenerasi sebuah angka random
# di antara 0 s.d 1. Jika angka = 0, maka sisiatas
# ditetapkan sebagai 'Heads'
# Jika = 1, maka sisiatas ditetapkan sebagai 'Tails'
def lempar(self):
if random.randint(0, 1) == 0:
self.sisiatas = 'Heads'
else:
self.sisiatas = 'Tails'
# Method get_sideup mengembalikan nilai
# yang direferensikan oleh sisiatas
def get_sisiatas(self):
return self.sisiatas
```
Catatan:
*Class Coin* mempunyai:
1. Satu *Attribute*: sisiatas
2. Tiga *Method*: `__ init__` , lempar , get_sisiatas

Fungsi `main` yang menggunakan *object Coin*:

Program berikut mendemonstrasikan pembuatan lebih dari satu *object Coin*:
```python
# coin_multiple_instance.py
# Mendemokan tiga object coin
import random
# class Coin mensimulasikan sebuah coin
# yang dapat dilempar
class Coin:
# Method __init__ menginisialisasi
# attribute data sisiatas dengan 'Heads'.
def __init__(self):
self.__sisiatas = 'Heads'
# Method lempar menggenerasi sebuah angka random
# di antara 0 s.d 1. Jika angka = 0, maka sisiatas
# ditetapkan sebagai 'Heads'
# Jika = 1, maka sisiatas ditetapkan sebagai 'Tails'
def lempar(self):
if random.randint(0, 1) == 0:
self.__sisiatas = 'Heads'
else:
self.__sisiatas = 'Tails'
# Method get_sisiatas mengembalikan nilai
# yang direferensikan oleh sisiatas
def get_sisiatas(self):
return self.__sisiatas
# Fungsi main
def main():
# Buat tiga object (instance) dari class Coin
coin1 = Coin() # Instansiasi Coin dan namakan dengan coin1
coin2 = Coin() # Instansiasi Coin dan namakan dengan coin2
coin3 = Coin() # Instansiasi Coin dan namakan dengan coin3
# Tampilkan sisi atas dari setiap koin
print('Saya mempunyai tiga koin dengan sisi menghadap atas:')
print(coin1.get_sisiatas())
print(coin2.get_sisiatas())
print(coin3.get_sisiatas())
print()
# Lempar koin
print('Saya melempar tiga koin...')
print()
coin1.lempar()
coin2.lempar()
coin3.lempar()
# Tampilkan sisi atas dari setiap koin
print('Sekarang sisi-sisi berikut yang menghadap atas:')
print(coin1.get_sisiatas())
print(coin2.get_sisiatas())
print(coin3.get_sisiatas())
print()
# Panggil fungsi main
main()
```
*Output*:
Saya mempunyai tiga koin dengan sisi menghadap atas:
Heads
Heads
Heads
Saya melempar tiga koin...
Sekarang sisi-sisi berikut yang menghadap atas:
Heads
Heads
Tails
**7.2.3 ENCAPSULATION**
Prinsip utama OOP adalah *encapsulation* (pengkapsulan) yang berarti *attribute* dan *method* merupakan satu kesatuan dan *attribute* haruslah *private* . *Attribute* haruslah *private* berarti *attribute*-*attribute* *object* tersembunyi dari kode di luar *object* dan hanya *method* dalam *object* tersebut yang dapat mengakses dan membuat perubahan ke data *attribute*. Dengan menyembunyikan *attribute* dari kode di luar *object*, kita memproteksi *attribute*-*attribute* dari perubahan yang membuat *object* kita tidak konsisten dengan desain *class* kita.
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210729175417627.png" alt="image-20210729175417627" style="zoom:47%;" />
Pada contoh *class Coin* sebelumnya, *attribute* sisiatas belum tersembunyi. Karena belum tersembunyi, kita bisa mengubah *attribute* sisiatas tanpa melalui *method*.
```python
my_coin = Coin()
my_coin.sisiatas = 'Head'
```
Pada contoh di atas kita mengubah sisiatas menjadi nilai '*Head*' dan ini tidak sesuai dengan desain *class* kita, karena kita mendesain *attribute* sisiatas hanya dapat bernilai '*Heads*' atau '*Tails*'.
Untuk membuat *attribute* menjadi *private* kita menambahkan dua karakter *underscore* sebelum nama *attribute*:
```python
self.__sisiatas = 'Heads'
```
Contoh program *class Coin* yang menyembunyikan *attribute*-nya:
```python
import random
# class Coin mensimulasikan sebuah coin yang dapat dilempar
class Coin:
# Method __init__ menginisialisasi attribute data sisiatas dengan 'Heads'.
def __init__(self):
self.__sisiatas = 'Heads'
# Method lempar menggenerasi sebuah angka random
# di antara 0 s.d 1. Jika angka = 0, maka sisiatas ditetapkan sebagai 'Heads'
# Jika = 1, maka sisiatas ditetapkan sebagai 'Tails'
def lempar(self):
if random.randint(0, 1) == 0:
self.__sisiatas = 'Heads'
else:
self.__sisiatas = 'Tails'
# Method get_sisiatas mengembalikan nilai yang direferensikan oleh sisiatas
def get_sisiatas(self):
return self.__sisiatas
```
Kita menyembunyikan attribute dengan menambahkan dua *underscore* sebelum nama *attribute*.
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730075021170.png" alt="image-20210730075021170" style="zoom:60%;" />
Ketika kita mencoba mengakses *attribute* secara langsung atau mencoba mengubah nilainya tanpa melalui *method* maka interpreter akan memberikan *error*
```python
my_coin = Coin()
print(my_coin.__sisiatas)
```
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730075206456.png" alt="image-20210730075206456" />
Pada contoh program yang mensimulasikan lemparan koin, kita menuliskan definisi *class Coin* dan fungsi `main` yang menggunakan *class Coin* dalam satu *file*. Ini tidak masalah jika kita menuliskan program kecil yang hanya menggunakan satu atau dua *class*, namun ketika kita menuliskan program dengan banyak *class* kita perlu mengorganisasi *class*-*class* tersebut. Programmer umumnya mengorganisasi *class* dalam sebuah *module*.
Contoh *class Coin* pada *module*:
File: coin.py
```python
import random
# class Coin mensimulasikan sebuah coin
# yang dapat dilempar
class Coin:
# Method __init__ menginisialisasi
# attribute data sisiatas dengan 'Heads'.
def __init__(self):
self.__sisiatas = 'Heads'
# Method lempar menggenerasi sebuah angka random
# di antara 0 s.d 1. Jika angka = 0, maka sisiatas
# ditetapkan sebagai 'Heads'
# Jika = 1, maka sisiatas ditetapkan sebagai 'Tails'
def lempar(self):
if random.randint(0, 1) == 0:
self.__sisiatas = 'Heads'
else:
self.__sisiatas = 'Tails'
# Method get_sisiatas mengembalikan nilai yang
# direferensikan oleh sisiatas
def get_sisiatas(self):
return self.__sisiatas
```
File: test_coin.py
```python
import coin
# Fungsi main
def main():
# Buat sebuah object dari class Coin
my_coin = coin.Coin()
# Tmapilkan sisi coin yang menghadap atas
print('Sisi koin yang menghadap atas:', my_coin.get_sisiatas())
# Lempar koin
print('Saya melempar koin...')
my_coin.lempar()
# Tampilkan sisi dari koin yang menghadap atas
print('Sisi koin yang menghadap atas: ', my_coin.get_sisiatas())
# Panggil fungsi main
main()
```
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730112334483.png" alt="image-20210730112334483" style="zoom:60%;" />
**7.2.4 UNIFIED MODELING LANGUANGE**
Ketika mendesain *class*, programmer umumnya menggunakan *diagram Unified Modeling Languange* (atau disingkat dengan UML) untuk merepresentasikan desain dari *class*.
*Diagram* UML adalah sebuah kotak yang dibagi menjadi tiga bagian:
1. Bagian atas: nama *class*
2. Bagian tengah: *attribute*-*attribute* dari *class*
3. Bagian bawah: *method*-*method* dari *class*
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730112852973.png" alt="image-20210730112852973" style="zoom:67%;" />
Misalkan kita membuat sebuah *class BankAccount* yang mensimulasikan sebuah rekening tabungan bank:
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730113138869.png" alt="image-20210730113138869" style="zoom:57%;" />
*Class BankAccount* merepresentasikan rekening tabungan bank:
```python
# bankaccount.py
# class BankAccount mensimulasikan sebuah rekening bank
class BankAccount:
# Method __init__ menginisilasiasi attribute __saldo
def __init__(self, saldo):
self.__saldo = saldo
# Method deposit (menabung) melakukan setoran
# sejumlah uang ke rekening sehingga saldo bertambah
def deposit(self, jumlah):
self.__saldo += jumlah
# Method withdraw (menarik) melakukan penarikan
# sejumlah uang dari rekening bank
def withdraw(self, jumlah):
if self.__saldo >= jumlah:
self.__saldo -= jumlah
else:
print('Gagal: Dana tidak mencukupi')
# Method get_saldo mengembalikan saldo
def get_saldo(self):
return self.__saldo
```
Program berikut mendemonstrasikan *class BankAccount*:
```python
# test_bankaccount.py
# Program ini mendemonstrasikan object dari class BankAccount
import bankaccount
def main():
# Buat saldo awal
saldo_awal = float(input('Masukkan saldo awal: '))
# Buat object BankAccount
tabungan = bankaccount.BankAccount(saldo_awal)
# Tabung gaji pengguna
gaji = float(input('Masukkan gaji Anda minggu ini: '))
print('Saya akan depositokan gaji Anda ke rekening Anda.')
tabungan.deposit(gaji)
# Tampilkan saldo
print(f'Saldo Anda adalah Rp.{tabungan.get_saldo():,.2f}’)
# Mengambil sejumlah uang
cash = float(input(
'Berapa banyak uang yang ingin Anda ambil? '))
print('Saya akan ambil uang tersebut dari rekening Anda.')
tabungan.withdraw(cash)
# Tampilkan saldo
print(f'Saldo Anda adalah Rp.{tabungan.get_saldo():,.2f}')
# Panggil fungsi main
main()
```
*Output*:
Masukkan saldo awal: 1000000
Masukkan gaji Anda minggu ini: 250000
Saya akan depositokan gaji Anda ke rekening Anda.
Saldo Anda adalah Rp.1,250,000.00
Berapa banyak uang yang ingin Anda ambil? 100000
Saya akan ambil sebanyak uang tersebut dari rekening Anda.
Saldo Anda adalah Rp.1,150,000.00
**7.2.5 METHOD __ str__ **
Seringkali kita perlu menampilkan sebuah pesan yang mengindikasikan *state* dari *object*. *State* dari *object* adalah nilai-nilai *attribute* dalam satu waktu. Menampilkan *state* adalah hal yang umum dilakukan, sehingga programmer biasanya mempunyai sebuah *method* mengembalikan sebuah `string` yang menampilkan *state* dari *object*. Dalam python, kita memberikan nama *method* yang mengembalikan *state* dari *object* dengan nama spesial: `__str__`
Menambahkan *method* `__ str__`` pada *class BankAccount*:
```python
def __str__(self):
return f'Saldo Anda adalah Rp.{self.__saldo:,.2f}'
```
Kita tidak memanggil langsung *method* `__str__`, *method* ini dipanggil otomatis ketika kita memberikan argument nama *object* ke fungsi `print`:
```python
tabungan = BankAccount(100000)
print(tabungan)
```
Catatan:
Ketika kita memberikan nama *object* sebagai argument ke fungsi `print`, interpreter otomatis memanggil *method* `__str__`.
*Class BankAccount* dengan *method* `__str__`:
```python
# bankaccount2.py
# class BankAccount mensimulasikan sebuah rekening bank
class BankAccount:
# Method __init__ menginisilasiasi attribute __saldo
def __init__(self, saldo):
self.__saldo = saldo
# Method deposit (menabung) melakukan setoran
# sejumlah uang ke rekening sehingga saldo bertambah
def deposit(self, jumlah):
self.__saldo += jumlah
# Method withdraw (menarik) melakukan penarikan
# sejumlah uang dari rekening bank
def withdraw(self, jumlah):
if self.__saldo >= jumlah:
self.__saldo -= jumlah
else:
print('Gagal: Dana tidak mencukupi')
# Method get_saldo mengembalikan saldo
def get_saldo(self):
return self.__saldo
# Method __str__ mengembalikan sebuah string yang menunjukkan state dari object
def __str__(self):
return f'Saldo Anda adalah Rp.{self.__saldo:,.2f}'
```
Program yang mendemonstrasikan *BankAccount* dengan *method* `__str__`:
```python
# test_bankaccount2.py
# Program ini mendemonstrasikan object dari class BankAccount
import bankaccount2
def main():
# Buat saldo awal
saldo_awal = float(input('Masukkan saldo awal: '))
# Buat object BankAccount
tabungan = bankaccount2.BankAccount(saldo_awal)
# Tabung gaji pengguna
gaji = float(input('Masukkan gaji Anda minggu ini: '))
print('Saya akan depositokan gaji Anda ke rekening Anda.')
tabungan.deposit(gaji)
# Tampilkan saldo
print(tabungan)
# Mengambil sejumlah uang
cash = float(input('Berapa banyak uang yang ingin Anda ambil? '))
print('Saya akan ambil uang tersebut dari rekening Anda.')
tabungan.withdraw(cash)
# Tampilkan saldo
print(tabungan)
# Panggil fungsi main
main()
```
*Output*:
Masukkan saldo awal: 1000000
Masukkan gaji Anda minggu ini: 250000
Saya akan depositokan gaji Anda ke rekening Anda.
Saldo Anda adalah Rp.1,250,000.00
Berapa banyak uang yang ingin Anda ambil? 100000
Saya akan ambil sebanyak uang tersebut dari rekening Anda.
Saldo Anda adalah Rp.1,150,000.00
**7.2.6 METHOD ACCESSOR DAN MUTATOR**
*Encapsulation* pada OOP berarti kita harus mendesain *class* kita dengan menyembunyikan *attribute*. Ini juga berarti kita harus menyediakan *method*-*method* yang dapat digunakan untuk mengubah nilai *attribute* dan yang dapat digunakan untuk mengakses nilai *attribute*. *Method*-*method* untuk mengakses nilai *attribute* tanpa mengubah nilainya disebut sebagai *method accessor* dan *method*-*method* untuk mengubah nilai *attribute* disebut sebagai *method mutator*. Pada contoh *class BankAccount* kita mempunyai *method accessor* yang kita gunakan untuk mengakses nilai *attribute* saldo: *method* get_saldo. Namun pada *class BankAccount* kita tidak mempunyai *method mutator* karena kita mendesain *class* kita dengan tidak memperbolehkan *attribute* saldo diubah langsung namun melalui *method*-*method* tidak langsung: *method* deposit dan *method* withdraw.
Misalkan kita menambahkan sebuah *attribute* nomor_rekening pada *class BankAccount* dan kita membolehkan *attribute* ini ditetapkan dan diakses oleh kode di luar *object* melalui sebuah *method mutator* dan sebuah *method accessor*.
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730115926234.png" alt="image-20210730115926234" style="zoom:67%;" />
*Method*-*method accessor* umumnya dinamakan dengan diawali kata *get* yang diikuti dengan nama *attribute*, sehingga *method accessor* seringkali disebut juga sebagai *getters*. Sedangkan *method*-*method* *mutator* umumnya dinamakan dengan diawali kata *set* yang diikuti dengan nama *attribute*, sehingga *method mutator* seringkali disebut juga sebagai *setters*.
*Class BankAccount* dengan *attribute* nomor_rekening dan *method accessor* dan *mutatornya*:
```python
# bankaccount3.py
# class BankAccount mensimulasikan sebuah rekening bank
class BankAccount:
# Method __init__ menginisilasiasi attribute __saldo
def __init__(self, saldo, nomor_rekening):
self.__saldo = saldo
self.__nomor_rekening = nomor_rekening
# Method deposit (menabung) melakukan setoran
# sejumlah uang ke rekening sehingga saldo bertambah
def deposit(self, jumlah):
self.__saldo += jumlah
# Method withdraw (menarik) melakukan penarikan
# sejumlah uang dari rekening bank
def withdraw(self, jumlah):
if self.__saldo >= jumlah:
self.__saldo -= jumlah
else:
print('Gagal: Dana tidak mencukupi')
# Method get_saldo mengembalikan saldo
def get_saldo(self):
return self.__saldo
# Method accessor untuk nomor_rekening
def get_nomor_rekening(self):
return self.__nomor_rekening
# Method mutator untuk nomor_rekening
def set_nomor_rekening(self, nomor_rekening):
self.__nomor_rekening = nomor_rekening
# Method __str__ mengembalikan sebuah string
# yang menunjukkan state dari object
def __str__(self):
return f'Nomor rekening: {self.__nomor_rekening}' + \
f'\nSaldo: Rp.{self.__saldo:,.2f}'
```
Program yang mendemonstrasikan *class BankAccount* dengan *attribute* nomor_rekening beserta *accessor* dan *mutator*nya:
```python
# test_bankaccount3.py
# Program ini mendemonstrasikan object dari class BankAccou
nt
import bankaccount3
def main():
# Buat saldo awal
saldo_awal = float(input('Masukkan saldo awal: '))
# Buat object BankAccount
tabungan = bankaccount3.BankAccount(saldo_awal, '123456789')
# Tabung gaji pengguna
gaji = float(input('Masukkan gaji Anda minggu ini: '))
print('Saya akan depositokan gaji Anda ke rekening Anda.')
tabungan.deposit(gaji)
# Tampilkan state
print(tabungan)
# Mengambil sejumlah uang
cash = float(input('Berapa banyak uang yang ingin Anda ambil? '))
print('Saya akan ambil uang tersebut dari rekening Anda.')
tabungan.withdraw(cash)
# Tampilkan state
print(tabungan)
# Ubah nomor rekening
nomor_rekening = input('Masukkan nomor rekening baru: ')
tabungan.set_nomor_rekening(nomor_rekening)
# Tampilkan nomor rekening
print(f'Nomor rekening baru {tabungan.get_nomor_rekening()}')
# Panggil fungsi main
main()
```
*Output* program test_bankaccount3.py:
Masukkan saldo awal: 1000000
Masukkan gaji Anda minggu ini: 250000
Saya akan depositokan gaji Anda ke rekening Anda.
Nomor rekening: 123456789
Saldo: Rp.1,250,000.00
Berapa banyak uang yang ingin Anda ambil? 100000
Saya akan ambil uang tersebut dari rekening Anda.
Nomor rekening: 123456789
Saldo: Rp.1,150,000.00
Masukkan nomor rekening baru: 234567890
Nomor rekening baru 234567890
**7.2.7 OBJEK SEBAGAI ARGUMEN FUNGSI**
Ketika kita membuat program yang berkerja dengan *object*, seringkali kita perlu membuat sebuah fungsi yang menerima *object* sebagai argument. Sebagai contoh, fungsi berikut menerima *object Coin* sebagai argument:
```python
def status_koin(obj_coin):
print('Sisi koin yang menghadap atas:', obj_coin.get_sisiatas())
```
Kode berikut menunjukkan bagaimana kita membuat *object Coin* lalu memberikannya sebagai argument ke fungsi status_koin:
```python
my_coin = coin.Coin()
status_koin(my_coin)
```
Ketika kita memberikan argument berupa *object* ke suatu fungsi, variabel parameter dari fungsi tersebut akan menerima referensi ke *object* tersebut, sehingga kita dapat memanggil *method*-*method* dari *object* tersebut.
Program yang mendemonstrasikan pemberian *object* ke fungsi:
```python
# Program ini mendemonstrasikan pemberian argumen
# berupa object ke fungsi
import coin
# Fungsi lempar_koin melempar koin
def lempar_koin(obj_coin):
obj_coin.lempar()
# Fungsi main
def main():
# Buat object Coin
my_coin = coin.Coin()
# Ini akan menampilkan 'Heads'
print(my_coin.get_sisiatas())
# Berikan object my_coin ke fungsi lempar_koin
lempar_koin(my_coin)
# Ini akan menampilkan 'Heads' atau 'Tails'
print(my_coin.get_sisiatas())
# Panggil fungsi main
main()
```
*Output*:
Heads
Tails
*Output*:
Heads
Heads
**7.3 INHERITANCE**
Konsep penting OOP setelah *encapsulation* adalah *inheritance* (pewarisan). Konsep *inheritance* didasarkan pada pengamatan objek-objek di dunia nyata. Banyak objek merupakan versi spesialisasi dari object lainnya yang lebih generik. Misalkan, serangga merupakan jenis hewan dengan karakteristik-karakteristik tertentu sedangkan lebah dan belalang adalah versi spesialisasi dari serangga yang mempunyai (mewarisi) karakteristik-karakteristik dari serangga namun masing-masing juga mempunyai karakteristik-karakteristik khusus tertentu.
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730122121353.png" alt="image-20210730122121353" style="zoom:57%;" />
**7.3.1 SUPERCLASS DAN SUBCLASS**
*Class* yang merupakan versi spesialisasi dari suatu *class* disebut dengan *subclass* (atau *class* turunan). Sedangkan *class* yang merupakan versi generik dari suatu *class* disebut dengan *superclass* (atau *class* dasar).
*Syntax* umum penulisan *subclass*:
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730122408778.png" alt="image-20210730122408778" style="zoom:57%;" />
*Subclass* akan mewarisi semua *attribute* dan *method* dari *superclass*. Pada *subclass* juga dapat ditambahkan *attribute*-*attribute* dan *method*-*method* baru yang membuatnya versi spesialisasi dari *superclass*.

<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730144642514.png" alt="image-20210730144642514" style="zoom:65%;" />
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730144751703.png" alt="image-20210730144751703" style="zoom:67%;" />
*Output*:
Nama saya adalah Budi Susilo
Umur saya adalah 17
Saya tingkat 12
**7.3.2 CONTOH PENERAPAN INHERITANCE**
Misalkan kita membuat sebuah program manajemen inventaris untuk suatu diler mobil:
1. Diler mobil menjual tiga jenis mobil: sedan, pickup, SUV
2. Kita mendesain program untuk menyimpan *attribute-attribute* data dari inventaris mobil berikut: merek, model, dan harga
Selain tiga data yang sama untuk ketiga jenis mobil, setiap jenis mobil mempunyai data *attribute* tambahan sebagai berikut:
1. Sedan mempunyai *attribute* tambahan: banyak pintu (2 pintu atau 4 pintu)
2. Pickup mempunyai *attribute* tambahan: tipe penggerak (penggerak dua-roda atau penggerak empat-roda)
3. SUV mempunyai attribute tambahan: kapasitas penumpang (5 penumpang atau 7 penumpang)
Jika kita membuat sebuah *class* untuk masing-masing jenis mobil, program yang akan kita tulis tidaklah efisien karena ketiga jenis mobil mempunyai banyak *attribute-attribute* yang sama.
Daripada membuat tiga *class* berbeda, kita dapat membuat sebuah *class* generik (*superclass*) dengan *attribute-attribute* yang sama dari ketiga jenis mobil dan membuat tiga *class spesialisasi* (*subclass*) dengan *attribute* khusus dari ketiga jenis mobil.
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730145515990.png" alt="image-20210730145515990" style="zoom:67%;" />
*Superclass* Mobil:
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730150128094.png" alt="image-20210730150128094" style="zoom:57%;" />
*Subclass* Sedan:
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730150306735.png" alt="image-20210730150306735" style="zoom:57%;" />
*Subclass* Pickup:
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730150651077.png" alt="image-20210730150651077" style="zoom:57%;" />
*Subclass* SUV:
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730150538775.png" alt="image-20210730150538775" style="zoom:57%;" />
Simpan definisi *superclass* Mobil dan *subclass* Sedan, Pickup, SUV dalam *module* kendaraan lalu uji dengan program berikut:
```python
# Program ini mendemonstrasikan class Sedan, Pickup, dan SUV
import kendaraan
def main():
# Buat object Sedan
sedan = kendaraan.Sedan('BMW', '323', 500000000, 4)
# Buat object Pickup
pickup = kendaraan.Pickup('Toyota', 'Hilux', 350000000, '4WD')
# Buat object SUV
suv = kendaraan.SUV('Hyundai', 'Santa Fe', 550000000, 6)
print('Data Kendaraan')
print('=================')
# Tampilkan data Sedan
print('Merek:', sedan.get_merek())
print('Model:', sedan.get_model())
print('Jumlah Pintu:', sedan.get_pintu())
print()
# Tampilkan data Pickup
print('Merek:', pickup.get_merek())
print('Model:', pickup.get_model())
print(f'Harga: Rp.{pickup.get_harga(): ,.2f}')
print('Tipe Penggerak:', pickup.get_tipe_penggerak())
print()
# Tampilkan data SUV
print('Merek:', suv.get_merek())
print('Model:', suv.get_model())
print(f'Harga: Rp.{suv.get_harga(): ,.2f}')
print('Kapasitas Penumpang:', suv.get_kap_penumpang())
print()
# Panggil fungsi main
main()
```
*Output*:
Data Kendaraan
=================
Merek: BMW
Model: 323
Harga: Rp. 500,000,000.00
Jumlah Pintu: 4
Merek: Toyota
Model: Hilux
Harga: Rp. 350,000,000.00
Tipe Penggerak: 4WD
Merek: Hyundai
Model: Santa Fe
Harga: Rp. 550,000,000.00
Kapasitas Penumpang: 6
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730153015066.png" alt="image-20210730153015066" style="zoom:67%;" />
**7.4 POLYMORPHISM**
Konsep penting OOP setelah *encapsulation* dan *inheritance* adalah *polymorphism*. “*Poly*” berarti banyak dan “*Morphism*” berarti bentuk. *Polymorphism* diartikan sebagai kemampuan objek untuk mempunyai bentuk yang berbeda-beda. *Polymorphism* memungkinkan *method* dalam *subclass* memiliki nama yang sama seperti *method* pada *superclass* namun mempunyai implementasi berbeda. Proses untuk mendefinisikan *method* dengan nama sama namun dengan implementasi berbeda ini disebut dengan *method overriding*.
Contoh Polymorphism:
<img src="D:\ilab wfh\KOMP-00-023\revisi wfh\bab 7\gambar 7\28.jpg" alt="28" style="zoom:57%;" />
Contoh program:
```python
# hewan.py
# Class mamalia merepresentasikan mamalia generik
class Mamalia:
# Method __init__ menerima sebuah argumen untuk
# spesies mamalia
def __init__(self, spesies):
self.__spesies = spesies
# Method tampilkan_spesies menampilkan sebuah pesan
# yang menunjukkan spesies dari mamalia.
def tampilkan_spesies(self):
print('Saya adalah seekor', self.__spesies)
# Method buat_suara menampilkan suara generik dari mamalia
def buat_suara(self):
print('Grrrr')
```
Catatan:
Class Mamalia mempunyai tiga method: `__init__()`, tampilkan_spesies(), buat_suara()
Contoh kode yang membuat *instance* dari *class* Mamalia dan memanggil *method-method* dalam *class* tersebut:
```python
import hewan
mamalia = hewan.Mamalia('Mamalia Generik')
mamalia.tampilkan_spesies()
mamalia.buat_suara()
```
*Output*:
Saya adalah seekor Mamalia Generik Grrrrr
```python
# Class Anjing adalah subclass dari Mamalia
class Anjing(Mamalia):
# Method __init__ memanggil method __init__
# dari superclass dan memberikan argumen spesies 'Anjing'
def __init__(self):
Mamalia.__init__(self, 'Anjing')
# Method buat_suara meng-override method buat_suara
# dari superclass Mamalia
def buat_suara(self):
print('Guk! Guk!')
```
*Output*:
Saya adalah seekor Anjing
Guk! Guk!
Catatan:
1. *Class* Anjing adalah *subclass* dari *class* Mamalia.
2. Method `__init__` yang kita definisikan pada *class* Anjing meng*override* *method* `__init__` yang diwariskan dari *superclass* Mamalia.
3. *Class* Mamalia yang merupakan *superclass* dari *class* Anjing mempunyai *method* buat_suara. Pada *subclass* Anjing kita meng-*override method* buat_suara dengan mendefiniskan *method* dengan nama yang sama namun dengan implementasi berbeda (disini kita menampilkan `string` 'Guk! Guk!' ke layar).
Contoh kode yang membuat *instance* dari *class* Anjing dan memanggil *method*-*method*nya:
```python
import hewan
anjing = hewan.Anjing()
anjing.tampilkan_spesies()
anjing.buat_suara()
```
Catatan:
1. Karena di *subclass* Anjing tidak ada definisi *method* tampilkan_spesies maka *method* tampilkan_spesies dari *superclass* Mamalia yang dipanggil.
2. Pada *subclass* Anjing kita meng-*override* *method* buat_suara() sehingga *method* yang kita definisikan pada *subclass* Anjing yang digunakan ketika kita memanggil *method* buat_suara() pada *object* Anjing.
```python
# Class Kucing adalah subclass dari class Mamalia
class Kucing(Mamalia):
# Method __init__ memanggil method __init__
# dari superclass dan memberikan argumen spesies 'Kucing'
def __init__(self):
Mamalia.__init__(self, 'Kucing')
# Method buat_suara meng-override method buat_suara
# dari superclass Mamalia
def buat_suara(self):
print('Meong!')
```
*Output*:
Saya adalah seekor Kucing
Meong!
Contoh kode yang membuat *instance* dari *class* Kucing dan memanggil *method-methodnya*:
```python
import hewan
kucing = hewan.Kucing()
kucing.tampilkan_spesies()
kucing.buat_suara()
```
Catatan:
1. Karena di *subclass* Kucing tidak ada definisi *method* tampilkan_spesies maka *method* tampilkan_spesies dari *superclass* Mamalia yang dipanggil.
2. Pada *subclass* Kucing kita meng-*override* *method* buat_suara() sehingga *method* yang kita definisikan pada *subclass* Kucing yang digunakan ketika kita memanggil *method* buat_suara() pada *object* Kucing.
**7.4.1 KEUNTUNGAN POLYMORPHISM**
*Polymorphism* memberikan fleksibilitas dalam mendesain program. Sebagai contoh, kita dapat membuat sebuah fungsi seperti berikut:
```python
def tampilkan_info_mamalia(makhluk):
makhluk.tampilkan_spesies()
makhluk.buat_suara()
```
Kita bisa memanggil fungsi info_mamalia dengan argumen *object* apapun selama *object* tersebut memiliki *method* tampilkan_spesies dan buat_suara. Tanpa *polymorphism* kita harus membuat sebuah fungsi untuk setiap jenis *object*.
```python
# demo_polymorphism_fungsi.py
# Program ini mendemonstrasikan fungsi yang menerima object dengan polymorphism
import hewan
# Fungsi ini menerima object sebagai argumen
# dan memanggil method tampilkan_spesies dan buat_suara
def tampilkan_info_mamalia(makhluk):
makhluk.tampilkan_spesies()
makhluk.buat_suara()
# Fungsi main
def main():
# Buat object Mamalia, object Anjing, dan object Kucing
mamalia = hewan.Mamalia('Mamalia Generik')
anjing = hewan.Anjing()
kucing = hewan.Kucing()
# Tampilkan informasi spesies satu per satu
print('Berikut ini beberapa hewan dan')
print('suara yang mereka buat.')
tampilkan_info_mamalia(mamalia)
print()
tampilkan_info_mamalia(anjing)
print()
tampilkan_info_mamalia(kucing)
# Panggil fungsi main
main()
```
*Output*:
Berikut ini beberapa hewan dan
suara yang mereka buat.
Saya adalah seekor Mamalia Generik
Grrrr
Saya adalah seekor Anjing
Guk! Guk!
Saya adalah seekor Kucing
Meong!
**7.4.2 FUNGSI ISINSTANCE**
Ketika kita membuat fungsi yang menerima sebuah *object* sebagai argumen dan memanggil *method-methodnya* kita harus memastikan *object* tersebut memiliki *method-method* yang dipanggil.
Misalkan:
```python
# salah_tipe.py
def tampilkan_info_mamalia(makhluk):
makhluk.tampilkan_spesies()
makhluk.buat_suara()
def main():
# Berikan sebuah string ke tampilkan_info_mamalia
tampilkan_info_mamalia('Saya adalah string')
main()
```
*Output* dari program akan menghasilkan *error*, karena *object* yang diberikan ke fungsi tidak mempunyai *method-method* yang dipanggil dalam fungsi:
<img src="C:\Users\Asus\AppData\Roaming\Typora\typora-user-images\image-20210730160315596.png" alt="image-20210730160315596" style="zoom:57%;" />
Kita bisa menggunakan fungsi `isinstance` untuk menentukan apakah suatu *object* adalah turunan dari *class* tertentu atau *subclass* dari suatu *class*.
*Syntax* pemanggilan fungsi `isinstance`:
```python
isintance(object, namaClass)
```
Jika *object* yang direferensikan dari *object* merupakan turunan dari nama *class* atau *subclass* dari nama *class f*ungsi mengembalikan nilai *True* dan mengembilkan nilai *False* jika bukan. Kita dapat menggunakan fungsi `isinstance` sebagai *exception handler*.
```python
def tampilkan_info_mamalia(makhluk):
if isinstance(makhluk, hewan.Mamalia):
makhluk.tampilkan_spesies()
makhluk.buat_suara()
else:
print('Ini bukan mamalia.')
```
Contoh program dari fungsi `isinstance`
```python
# demo_polymorphism_fungsi2.py
# Program ini mendemonstrasikan fungsi yang menerima object dengan polymorphism
import hewan
# Fungsi ini menerima object sebagai argument dan memanggil method tampilkan_spesies dan buat_suara
def tampilkan_info_mamalia(makhluk):
if isinstance(makhluk, hewan.Mamalia):
makhluk.tampilkan_spesies()
makhluk.buat_suara()
else:
print('Ini bukan mamalia.')
# Fungsi main
def main():
# Buat object Mamalia, object Anjing, dan object Kucing
mamalia = hewan.Mamalia('Mamalia Generik')
anjing = hewan.Anjing()
kucing = hewan.Kucing()
# Tampilkan informasi spesies satu per satu
print('Berikut ini beberapa hewan dan')
print('suara yang mereka buat.')
tampilkan_info_mamalia(mamalia)
print()
tampilkan_info_mamalia(anjing)
print()
tampilkan_info_mamalia(kucing)
print()
tampilkan_info_mamalia('Saya adalah string')
# Panggil fungsi main
main()
```
*Output*:
Berikut ini beberapa hewan dan suara yang mereka buat.
Saya adalah seekor Mamalia Generik
Grrrr
Saya adalah seekor Anjing
Guk! Guk!
Saya adalah seekor Kucing
Meong!
Ini bukan mamalia
**REFERENSI**
1] Gaddis, Tony. 2012. Starting Out With Python Second Edition. United States of America: Addison-Wesley.