Skip to content

Files and Data Persistance

Working with files and directories

Opening files

Membuka file didalam python cukup mudah, kita hanya perlu menggunakan built-in function yaitu fungsi open()

Code

fh = open('important.txt',mode='rt')

for a in fh.readlines():
    print (a.strip())

fh.close()
Output
Dari Abu Hurairah radhiallahu anhu, Rasulullah Shallallahu Alaihi Wassalam bersabda
... #(1)!
  1. Isi dari file dihapus agar tidak memnuhi catatan

Kode diatas, kita memanggil fungsi open dan memberikan nama file dan kita ingin membaca file tersebut dengan mode menggunakan nilai read text rt.

Setalah file telah dibuka, kita mendapatkan objek file tersebut, yang kita taruh di names fh yang mana kita dapat memanfaatkan konten dari file tersebut. Selanjutnya kita menggunakan fungsi readlines() (menghasilkan list) yang dapat kita manfaatkan untuk meng-iterate hingga semua baris pada file tersebut habis dan kita menampilkan nilai dari setiap baris tersebut. Kita juga menggunakan fungsi strip() pada setiap baris untuk menghilangkan extra space pada konten file tersebut termasuk karakter pemisah baris pada setiap baris file. Karena dengan perintah print diatas sudah menambah baris baru.

Info

Alternatif tidak jika tidak ingin men-sanitasi isi dari file namu tetap ingin menampilkan kondisi tanpa baris tambahan baru dari perintah print adalah menggunakan argumen end dengan isi non-space pada perintah tersebut print, print (a,end='').

Closing file sangatlah penting, karena kita tidak ingin berisiko gagal dalam menghapus resource. Karena jika terjadi, kita bisa menghadapi masalah seperti memory leaks. Maka dari itu, kita harus mempersiapkan kode untuk menghindari masalh tersebut dengan membungkus open file menggunakan try/finally block. Dengan demikian, apapun yang terjadi pada kode dengan adanya error kita memastika file tersebut ditutup.

Code

fh = open('important.txt',mode='rt')

try:
    for a in fh.readlines():
        print (a.strip())
except:
    fh.close()

Kode diatas menghasilkan nilai keluaran yang sama namun dengan cara yang lebih aman. Sekarang lihatlah kode dibawah ini

Code

fh = open('important.txt') # Tidak mengguakan parameter mode

try:
    for a in fh: # Tidak menggunakan readlines()
        print (a.strip())
except:
    fh.close()

Pada kode diatas kita tidak menggunakan parameter mode dengan nilai rt, karena parameter terebut sudah memiliki nilai default rt, dengan demikian kita tidak perlu lagi menspesifikasikannya lagi jika hanya ingin read a text. Selanjutnya kita juga menghapus fungsi readlines(), inilah kemudahan yang ditawarkan python dalam menulis kode, yaitu dengan menawarkan shorthands agar kode jauh lebih pendek dan ringkas.

Using a context manager

Sebelumnya kita telah membaca catatan tentang context maanger, dimana fungsi open juga memliki context manager. Dengan demikian kita dapat meenggunakan #pthon with statement untuk membaca file.

Code

with open('important.txt',mode='rt') as fh:
for a in fh.readlines():
    print(a.strip())

Memanfaatkan context manager membuat kita tidak khawatir untuk meng-close file karena sudah secara otomatis ditanganin oleh context manager ketika kode keluar dari block with statement.

Read and writing a file

Dibawah ini adalah cara kita menulis kedalam sebuah file

Code

from time import sleep
with open('write_file.txt', mode='w') as wf:
    sleep(5)
    print ("bismillah", file=wf)

Untuk menulis kedalams sebauh file, kita menggunakan nilai w (write) pada parameter mode. Dan kita menggunakan fungsi print namun dengan menggati parameter file dengan objek dari file yang telah kita buka, kode diatas menggunakan objek wf.

Kode diatas akan membuat sebuah file baru dengan nama write)file.txt dan isi text bismillah jika file tersebut tidak ada dan akan di truncate jika file tersebut tidak ada. Truncate disini, python tidak menghapus file, namun mengkosongkan file tersebut lalu mengisi dengan nilai pada kode.

Code

from time import sleep
# write_file.txt sudah ada sebelumnya
with open('write_file.txt', mode='w') as wf:
    sleep(5) # buka wilfe write_file.txt sebelum 5 detik berlalu
    print ("bismillah", file=wf)

Cobalah buka file write_file.txt sebelum 5 detik, maka anda akan melihat file tersebut kosong.

Info

Di python, standard input, output atau error streams direpresentasi dengan sys.stdin, sys.stdout dan sys.stderr. Jika input atau output tidak diarahkan kearah lain, maka sys.stdin akan membaca nilai dari keyboard dan sys.stdout dan sys.stderr akan menampilkan nilai pada layar. pada contoh diatas kita mengarahkan output ke dalam objek file.

Selain menggunakan fungsi print dengan mengisi file objek pada argumen file, kita juga dapat memanfaatkan fungsi write pada file objek.

Code

with open('write_file.txt') as wf:
    lines = [a for a in wf]

with open('write_file(2).txt', mode='w') as wf:
    wf.write(''.join(lines))

Read and writing in binary mode

Contoh kode sebleumnya kita memberikan nilai t pada parameter mode. Artinya bahwa, konteks pada file kita anggap sebagai sebuah text.

Jika kita ingin menulis bytes pada sebuah file, kita dapat open file menggunakan binary mode. Cara ini wajib dilakukan ketika kita menghadapi sebuah file yang tidak hanya mengandung text, seperti images, audio atau video, dan secara umum semua tipe format lain. Untuk menangani file dalam binary mide, kita dapat mensepsifikasikan nilai b saat membuat file pada parameter mode.

Code

with open('example.png', mode='rb') as wf:
    for a in wf:
        print(a)
Output
b'\x89PNG\r\n'
b'\x1a\n'
b'\x00\x00\x00\rIHDR\x00\x00\x03)
...
with open('file_binnary.bin', mode='wb') as wf:
    wf.write(b'bismillah in binary')

Protecting against overwriting an existing file

Sebagaimana yang telah kit alihat, python menawarkan kemampuan untuk menulis file menggunakan flag w. File tersebut dibuka dan di truncate kontennya. Artinya, file tersebut akan ditiban dengan file kosong sehingga koten sebelumnya akan hilang. Jika kita tidak ingin itu terjadi, kita dapat menggunakan flag x, denga demikian python hanya akan writing kepada file yang belum ada eksistensinya.

Code

# Percobaan 1
with open ('adakah.txt', mode='xt') as wf:
    wf.write('bismillah')

# Percobaan 2
with open ('adakah.txt', mode='xt') as wf: # will raise an error
    wf.write('alhamdulillah')
Output
---------------------------------------------------------------------------
FileExistsError                           Traceback (most recent call last)
Input In [85], in <cell line: 6>()
    3     wf.write('bismillah')
    5 # Percobaan 2
----> 6 with open ('adakah.txt', mode='xt') as wf:
    7     wf.write('alhamdulillah')

FileExistsError: [Errno 17] File exists: 'adakah.txt'

Checking file and directory existance

Jika anda ingin memastika sebuah file atau direktori ada atau tidak anda bisa menggunakan modul pathlib.

Code

from pathlib import Path
path = Path('adakah.txt')

print(path.is_file())
absolute_path = path.parent.absolute()
print(absolute_path)
print(absolute_path.is_dir())
Output
True
/home/maruffarras/Documents/Notes/kertas-pena
True

Ada cara lain untuk melakukan operasi diatas, yaitu menggunakan os.path module dari pustaka bawaan. os.path berkerja menggunakan strings, namun pathlib menawarkan class-class yang merepresentasikan paths pada filesystem untuk operating system yang berbeda. Maka dari itu sangat disarankan penggunaan pathlib sebisa mungkin.

Manipulating files and directories

Sekarang lihat contoh dibawah ini, kita memanipulasi operasi disk, yang mana kita menggunakan module shutil.

Code

import shutil
from pathlib import Path

path_lib = Path('ops_example')

# Memabersihkan folder dan file pada direktori "ops_example"
if path_lib.is_dir() and path_lib.exists():
    shutil.rmtree(path_lib) # Menghapus semua folder secara recursive hingga akar

path_lib.mkdir() # Buat folder root

path_A = path_lib / 'Home' / 'A'
path_B = path_lib / 'Home' / 'B'
path_C = path_lib / 'Home' / 'C'

# Membuat folder path_A,B,C
path_A.mkdir(parents=True) # parameter parents digukana untuk membuat folder "Home"
path_B.mkdir() # Tidak perlu memnggunakan parameter parents lagi karena filder "Home" sudah dibat
path_C.mkdir()

# Membuat file pada folder "ops_example/Home/A"
for file_name in ('file_a.txt','file_b.txt','file_c.txt'):
    with(open(path_A / file_name, mode='w') as wf):
        wf.write(f"This content is from file {file_name}")

# Before move
!tree ops_example

# Memindahkan folder secara recursive, seperti command mv in unix        
shutil.move(path_A, path_C)

# # Renaming file "file_c.txt" pada path_B
file_c_in_path_C = path_C /'A'/'file_c.txt'
# file_c_in_path_C.rename(file_c_in_path_C.parent / 'file_c_renamed.txt')

# After move
!tree ops_example
Output
ops_example
└── Home
    ├── A
       ├── file_a.txt
       ├── file_b.txt
       └── file_c.txt
    ├── B
    └── C

4 directories, 3 files
ops_example
└── Home
    ├── B
    └── C
        └── A
            ├── file_a.txt
            ├── file_b.txt
            └── file_c.txt

4 directories, 3 files

Pada kode diatas untuk memanipulias file dan direktori kita menggunakan sebuah fungsi bawaan shutil. Fungsi tersebut digunakan untuk menyalin, meng-archive file-file dan direktori-direktori secara recursive (tree).

Kita menggunakan operator slash ++/++ untuk meng-concatenate direktori; pathlib yang menangani seperator yang tepat untuk kita pada belakang layar (seperator direktori berbeda-beda pada operating system).

  • shutil.rmtree ➡ Recursively delete a directory tree.
  • shutil.mv ➡ Recursively move a file or directory to another location. This is similar to the Unix "mv" command. Return the file or directory's destination.

Manipulating pathnames

Untuk mengatahui fungsi-fungsi yang ada didalam pathlib dapat dengan cara melihat niali kembalian dari method yang dipanggil.

Code

from pathlib import Path
file = Path('important.txt')

print(file.absolute())
print(file.name)
print(file.parent.absolute())
print(file.suffix)

print(file.parts)
print(file.absolute().parts)

read_path = Path('ops_example/Home/C/A')
important_path = Path(read_path.absolute() / '..' / '..'/ '..' / '..' / 'important.txt' )

print(important_path)
print(important_path.resolve())


with open(important_path) as pwf:
    for a in pwf:
            print(a)
Output
/home/maruffarras/Documents/Notes/kertas-pena/important.txt
important.txt
/home/maruffarras/Documents/Notes/kertas-pena
.txt
('important.txt',)
('/', 'home', 'maruffarras', 'Documents', 'Notes', 'kertas-pena', 'important.txt')
/home/maruffarras/Documents/Notes/kertas-pena/ops_example/Home/C/A/../../../../important.txt
/home/maruffarras/Documents/Notes/kertas-pena/important.txt
Dari Abu Hurairah radhiallahu anhu, Rasulullah Shallallahu Alaihi Wassalam bersabda
...

Pada baris 15 dan 16 kita menampilkan direktori dari names important_path dimana .. artinya adalah mundur ke direktori sebelumnya. Dengan menggunakan fungsi resolve paython langsung menggantikan kealamat yang lebih mudah dibaca.

Temporary files and directory

Terkadanga sangat beruguna jika kita dapat membuat sebuah file atau direktori sementara ketika menjalankan sebuah kode. Misalkan ketika kita membuat sebua test code yang mempengaruhi disk, ruang peyimpanan, kita dapat menggunakan temporary file dan diretories untuk menjalankan logika yang kita buat. Dan pastinya file dan direktori tersebut akan secara otomatis terhapus setelah menjalankan kode tersebut.

Code

from tempfile import NamedTemporaryFile, TemporaryDirectory
from time import sleep

with TemporaryDirectory(dir='.') as td:
    print('This is temporary value', td)

    with NamedTemporaryFile(mode='w', dir=td) as tf:
        name = tf.name
        print(name)
        sleep(5) # Add sleep to see temporary file and temporary directory
Output
This is temporary value ./tmpml1adclu
/home/maruffarras/Documents/Notes/kertas-pena/tmpml1adclu/tmpw_k8rl2c

Diatas kita membuat sebuah direktori sementara pada direktori dimana python code dijalankan karena kita memberikan nilai parameter dir dengan nilai dot .

Directory content

Dengan python kita juga dapat meng-inspeksi isi dari sebuah folder. Ada dua cara, pertama kita dapat menggunakan fungsi glob dari module Path, kedua menggunakan os.walk.

Code

path = Path('.')

for entry in path.glob('*'):
    print('file : ' if entry.is_file() else 'folder : ',entry)
Output
folder :  .github
file :  Untitled.ipynb
file :  mkdocs.yml
folder :  ops_example
folder :  docs
folder :  .git
file :  adakah.txt
file :  important.txt
folder :  .ipynb_checkpoints
import os

for root, _dir, file in os.walk('./ops_example'):
    root_path = os.path.abspath(root)
    # print(root_path)

    if _dir:
        for a in _dir:
            print('Directories : ',a)

    if file:
        for a in file:
            print('File : ',a)
Output
Directories :  Home
Directories :  B
Directories :  C
Directories :  A
File :  file_a.txt
File :  file_b.txt
File :  file_c_renamed.txt

File and directory compression

Pada catatan kali ini kita akan membahas bagaimana mengompres file. Kita dapat mengkompresnya menjadi zip atau tar.gz. Python menyediakan berbagai cara dan format yang berbeda dalam mengkompres file dan direktori, namun pada catatan kali ini hanya menggunakan cara yang paling umum, ZIP.

Berikut adalah struktur folder beserta file pada direktori tersebut yang akan kita kompress.

ops_example
└── Home
    ├── B
    └── C
        └── A
            ├── file_a.txt
            ├── file_b.txt
            └── file_c_renamed.txt

Code

with ZipFile ('zipped.zip', mode='w') as zp:
for root,_dir,files in os.walk('ops_example'):
    zp.write(root)
    print(root)
    for f in files:
        file = f'{root}/{f}'
        print(file)
        zp.write(file)
Output
ops_example
ops_example/Home
ops_example/Home/B
ops_example/Home/C
ops_example/Home/C/A
ops_example/Home/C/A/file_a.txt
ops_example/Home/C/A/file_b.txt
ops_example/Home/C/A/file_c_renamed.txt

Info

Jika anda tertarik untuk belajar lebih lanjut tentang data compression, anda dapat membaca di Data Compression and Archiving section pada standard library python [https://docs.python.org/3.9/library/

archiving.html](https://docs.python.org/3.9/library/ archiving.html)

Data interchange formats

Pada saat ini, arsitektur perangkat lunak sedang tenar dengan memecah aplikasi kedalam beberapa komponen. Apakah itu service-oriented architecture paradigm atau bahkan kedalam microservices realm, komponen tersebut akan saling bertukar data. Namun, walaupun kita membuat codebase tunggal untuks satu buah aplikasi yang ada dalam satu buah project, pasti tetap kita membutuhkan perturakan data menggunakan APIs, program lain atau bahkan hanya sekedar pertukaran data antara frontend dan backend pada sebuah website yang mana bisa jadi frontend dan backend tidak menggunakan bahasa pemprogramman yang sama.

Memilih format yang digunakan untuk pertukaran informasi sangatlah penting. Memilih format langauge-agnostic adalah pilihan yang paling tepat, dimana format tersebut dapat berkomunikasi dengan semua (hampir semua) bahasa pemprogramman.

Pada dunia pengembangan aplikasi, beberapa format yang populer menjadi standar mutlak (de facto) untuk pertukaran data. Yang paling tenar adalah XML, YAML, dan JSON. Pada lingkungan python, JSON adalah yang paling sering digunakan. Menurut pendapat saya, karena JSON mudah untuk dibaca dan dilakukan operasi pada data tersebut, berbeda dengan XML.

Working with JSON

JSON adalah akronim dari JavaScript Object Notation dan bagian dari bahasa pemprogramman JavaScript.

JSON terdiri dari dua buah struktur: sebuah collection dari nama dan nilai yang berpasangan, dan list of values yang berurutan. JSON sangat mirip dengan dict dan list pada python. Berbicara tentang tipe data, JSON dapat menampung strings, numbers, dan nilai yang terdiri dari true atau false serta null.

Code

import sys
import json

data = {
'big_number' : 2**3141,
'max_float' : sys.float_info.max,
'_list' : [1,2,3,4,5]
}

json_data = json.dumps(data) # Dict to JSON (STRING)
back_data = json.loads(json_data) # Json(String) to Dict


print(type(json_data))
print(type(back_data))
Output
<class 'str'>
<class 'dict'>

Kode diatas kita mulai dengan meng- import module sys dan json. Selanjutnya kita membuat sebuah dictionary sederhana dengan beberap nilai didalaman nya sebuah list. Kita ingin mencoba untuk me- serialize dan deserialize angka yang besar dari kedua tipe data int dan float, maka dari itu kita memberikan nilai 23141 dan nilai float terbesar yang sistem dapat tampung.

Kita me-serialize menggunakan json.dumps() yang mana membutuhkan parameter data dan menkonversinya kedalam bentu JSON (String). Data tersebut lalu kita masukan lagi kedalam fungsi json.loads(), yang mana melakukan operasi kebalikannya, yaitu merubah JSON(String) kedalam dictionary python lagi.

Sekarang mari kita lihat hasil serialize berupa JSON(String)

Code

addres_info = {
    'street': '221B Baker St',
    'zip': 'NW1 6XE',
    'city': 'London',
    'country': 'UK'
}

personal_info = {
'name':'farras',
'birth_date': {
    'date' : 27,
    'month' : 12,
    'year' : 1995
},
'address' : addres_info
}

json_data = json.dumps(personal_info, indent=2)
print(json_data)
Output
{
    "name": "farras",
    "birth_date": {
        "date": 27,
        "month": 12,
        "year": 1995
    },
    "address": {
        "street": "221B Baker St",
        "zip": "NW1 6XE",
        "city": "London",
        "country": "UK"
    }
}

Python menawarkan shortcut yang sangat membantu kita termasuk saat kita berurusan dengan data collection. Namun kesalahan kecil yang kita tulis pada kode kita dapat membuat bugs yang terkirakan. Misalkan contoh dibawah ini.

Code

dict_1 = {'name':'faris','place':'bogor','married':False}, #(1)!

json_dict_1 = json.dumps(dict_1, indent=2)
print(json_dict_1)
  1. Penulisan , koma yang tidak sengaja mengubah tipe data dict_1 menjadi tuple, sehingga dumps() akan mengkonversinya menjadi JSON list.
Output
[
    {
        "name": "faris",
        "place": "bogor",
        "married": false
    }
]
dict_1 = {'name':'faris','place':'bogor','married':False}

json_dict_1 = json.dumps(dict_1, indent=2)
print(json_dict_1)
Output
{
    "name": "faris",
    "place": "bogor",
    "married": false
}

Lost information due to excahnge data

Mari sekarang kita lihat kode menarik dibawah ini.

Code

numbers_tuple = tuple(a for a in range (0,5))
numbers_list = [a for a in range(0,5)]

print(f'numbers_tuple : {type(numbers_tuple)}')
print(f'number_list : {type(numbers_list)}')

back_numbers_tuple = json.loads(json.dumps(numbers_tuple))
back_numbers_list = json.loads(json.dumps(numbers_list))

print(f'back_numbers_tuple : {type(back_numbers_tuple)}')
print(f'back_number_list : {type(back_numbers_list)}')
Output
numbers_tuple : <class 'tuple'>
number_list : <class 'list'>
back_numbers_tuple : <class 'list'>
back_number_list : <class 'list'>

Pada kode diatas kita membuat tuple dan list yang ditampung dengan names numbers_tuple dannumber_list, hasilnya dapat dilihat dengan cara memanggil built-in function type untuk menge-check tipe data kedua variable tersebut. Pada baris selanjutnya kita juga menggunakan fungsi type pada variabel yang kita serialize ke JSON dan deserialize kembali ke python objek. Ternyata untuk variable dengan tipe data tuple setelah kembali dirubah dari JSON ke python objek kehilangan tipe data aslinya.

Ini adala masalah yang umu terjadi. Misalkan, kita tidak selalu dapat me-serialize semua python objek kedalam python karena tidak terlau jelas bagi python mengetahui JSON tersebut dikembalikan seperti apa kedalam python. Katakan, datetime, sebagai contoh. Instance dari class tersebut adalah sebuah objek pada python yang JSON tidak dapat serialize. Jika kita mengtransformasi kedalam sebuah string seperti 2018-03-04T12:00:30Z (ISO 8601) yang merepresentasikan sebuah tanggal dengan waktu dan timzone, maka, apa yang harus JSON lakukan untuk me-deserialize ? haruskah mengembalikannya kembali ke datetime object ?

Jawabannya adalah, jika kita berusan dengan data yang berubah, kita harus selalu mengubahnya objek kita kedalam format yang lebih simple sebelum me-serialize kedalam JSON. Semakin kita mampu memanage data kedalam bentuk yang lebih simple, semakin mudah bagi kita untuk merepresentasikan data kedalam sebuah format seperti JSON yang mana memliki batas.

Pada kasus tertentu, mungkin untuk penggunan internal, sangat berguna jika kita dapat men-serialize constum object. insyaAllah kita akan membahas pada catatan ini, yaitu membuat Costume Encoder / Decoder.

Custom encoding/decoding with JSON

Python mendukung perubahan tipe data dari data python ke JSON sebagai berikut;

Python Json
dict object
tuple array
list array
str string
int number
float number
True true
False false
None null

Lalu bagaimana jita kita men-serialize objek pada python ? akan muncul TypeError

Code

from datetime import date
_date = date(2023,1,31)
data = {'_id_product':'HP 14-s','series':'241hdfhh2h7aui7','price':15760000,'date_price':_date}

try:
    respone_json = json.dumps(_date, indent=2)
except TypeError as te:
    print(te)
Output
Object of type date is not JSON serializable

Berdasarkan dokumentasi, dikatakan jika kita ingin men-serialize objek lain maka,

To extend this to recognize other objects, subclass and implement a.default() method with another method that returns a serializable object for o if possible, otherwise it should call the superclass implementation (to raise TypeError).

Code

from datetime import date

class CostumeJSON(json.JSONEncoder):
    def default(self,obj):
        print(f'Masuk kedalam Consume Enconder {obj=}',end='\r\n'*2)
        if isinstance(obj,date):
            return {'-meta':'_date','tanggal':obj.day,'bulan':obj.month,'tahun':obj.year}
        return self.default(obj)

_date = date(2023,1,31)
data = {'_id_product':'HP 14-s','series':'241hdfhh2h7aui7','price':15760000,'date_price':_date}

try:
    respone_json = json.dumps(data, cls=CostumeJSON, indent=2)
    print(respone_json)
except TypeError as te:
    print(te)
Output
Masuk kedalam Consume Enconder obj=datetime.date(2023, 1, 31)

{
"_id_product": "HP 14-s",
"series": "241hdfhh2h7aui7",
"price": 15760000,
"date_price": {
    "-meta": "_date",
    "tanggal": 31,
    "bulan": 1,
    "tahun": 2023
    }
}

Lalu bagaimana mengembalikan JSON(String) kembali kedalam data di python ? kita dapat menggunakan function costume yang diberikan pada parameter object_hook pada fungsi json.loads().

Dibawah ini adalah penjelasan official dari python tentang object_hook.

object_hook is an optional function that will be called with the result of any object literal decode (a dict). The return value of object_hook will be used instead of the dict. This feature can be used to implement custom decoders (e.g. JSON-RPC class hinting).

Code

class CostumeJSON(json.JSONEncoder):
    def default(self,obj):
        # Sama dengan method pada class CostumeJSON diatas
        pass

    @staticmethod
    def costume_decoder(obj):
        try:
            if obj['_meta'] == '_date':
                return date(obj['tahun'],obj['bulan'],obj['tanggal'])
        except KeyError: #(1)! return obj when KeyError is raised is a must
            return obj

# respone_json adalah JSON hasil serialize pada contoh sebelumnya        
data_out = json.loads(respone_json, object_hook=CostumeJSON.costume_decoder)
print(data_out)
Output
{'_id_product': 'HP 14-s', 'series': '241hdfhh2h7aui7', 'price': 15760000, 'date_price': datetime.date(2023, 1, 31)}

I/O, streams, and request

I/O artinya adala Input/Output, I/O digunakans ecara luas untuk komunikasi antara komputer dan dunia luar. Ada beberapa tipe dari I/O, namun insyaAllah kita hanya membahas beberapa jenis I/O saja, tidak semuanya.

Yang pertama adana io.StringIO class, adalah semua _in-memory stream` untuk text I/O. Kedua yang ada dicatatan ini adalah HTTP request.

Using an in-memory stream

In-memory objects can be useful in a multitude of situations. Memory is much faster than a disk, it's always available, and for small amounts of data can be the perfect choice.

Code

import io

stream = io.StringIO()

# Menggunakan method write
stream.write("Bismillah.\n")

# Menggunakan parameter file dengan nilai stream pada function print
print('Dengan menyebut nama Allah', file=stream)

print(stream.getvalue())
stream.close()
Output
Bismillah.
Dengan menyebut nama Allah

Pada kode diatas, kita import module io dari standard library. Yang sangat menarik adalah, modul tersebut memiliki banyak tools yang berhubungan dengan stream dan Input/Output. Salah satunya adalah StringOP adalah sebuah in-memory buffer yang akan kita gunakan untuk membuat dua kalimat menggunakan dua metode yang berbeda, sebagaimana yang kita menulis pada sebuah file. Samah halnya, kita dapat menggunakan method StringIO.write() atau kita dapat menggunakan print dan mengarahkan outputnya langsung ke datastream.

Dengan memanggil fungsi getvalue() kita dapat mengambil konten dari stream. Selanjutnya kita print hasilnya, dan diakhir baris kita menutup stream menggunakan close() yang menyebabkan text buffre langsung dibuang.

Ingat, adacara lain yang lebih elgan mengingat kita mungkin lupa menutup nya menggunakan fungsi close(). Yup, menggunakan context manager.

Code

import io

def read_from_io():
    with io.StringIO() as st:
        # Menggunakan method write
        st.write("Bismillah.\n")

        # Menggunakan parameter file dengan nilai stream pada function print
        print('Dengan menyebut nama Allah', file=st)

        return st.getvalue()

print(read_from_io())

Making HTTP request

Pada bagian catatan ini, insyaAllah kita akan mengekspolirasi dua contoh pada HTTP request. Kita akan menggunakan request library untuk contoh berikut, yang mana anda dapat menginstallnya menggunakan perintah pip install requests. Atau jika anda menggunakan anaconda anda dapat menggunakan conda install requests.

Kita akan melakukan HTTP reuqest ke httpbn.org yang mana dikembangkan oleh Kenneth Reitz, pembuat dari pustaka request juga. 👍👍👍

Code

from requests import request

urls = {
    "get": "https://httpbin.org/get?t=learn+python+programming",
    "headers": "https://httpbin.org/headers",
    "ip": "https://httpbin.org/ip",
    "user-agent": "https://httpbin.org/user-agent",
    "UUID": "https://httpbin.org/uuid",
    "JSON": "https://httpbin.org/json",
}


def get_content(title,url):
    print(f'Request for {title}')    
    rsp = request('GET',url).json()
    print(rsp)
    print(f'{type(rsp)=}') # check type
    print('-'*30)

for title, url in urls.items():
    get_content(title,url)
Output
Request for get
{'args': {'t': 'learn python programming'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.27.1', 'X-Amzn-Trace-Id': 'Root=1-6498e2e6-0a209958522fd24b68f6dc8d'}, 'origin': '140.213.134.96', 'url': 'https://httpbin.org/get?t=learn+python+programming'}
type(rsp)=<class 'dict'>
------------------------------
Request for headers
{'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.27.1', 'X-Amzn-Trace-Id': 'Root=1-6498e2e7-320394a203f804d672c40689'}}
type(rsp)=<class 'dict'>
------------------------------
Request for ip
{'origin': '140.213.136.102'}
type(rsp)=<class 'dict'>
------------------------------
Request for user-agent
{'user-agent': 'python-requests/2.27.1'}
type(rsp)=<class 'dict'>
------------------------------
Request for UUID
{'uuid': 'a89a1d04-5d0d-45fd-b2fc-74949ba7f75a'}
type(rsp)=<class 'dict'>
------------------------------
Request for JSON
{'slideshow': {'author': 'Yours Truly', 'date': 'date of publication', 'slides': [{'title': 'Wake up to WonderWidgets!', 'type': 'all'}, {'items': ['Why <em>WonderWidgets</em> are great', 'Who <em>buys</em> WonderWidgets'], 'title': 'Overview', 'type': 'all'}], 'title': 'Sample Slide Show'}}
type(rsp)=<class 'dict'>
------------------------------

Ketika kita melakukan request ke sebuah website atau API, kita mendapatkan objek respone. Contoh diatas kita langsung menggunakan method request.json() dibandingkan menggunakan request.text lalu menggunakan json.loads untuk mengubahnya ke dict pada python. Dengan method request.json() kita mengambil jalan pintas langsung merubahnya menjadi ditct.

Info

ketika kita melakukan operasi request pada aplikasi kita, kita akan sangat membutuhkan pendekatan yang lebih bagus dalam menangani error dan semisalnya, insyaAllah kita akan membahas pada sub catatan Introduction to API development

GET adalah salah satu dari metode dari HTTP dan yang paling sering digunakan. sekarang mari kita gunakan metode lainnya yaitu POST. Ini dalah tipe request yang digunakan ketika kita ingin mengirim sebuah data ke server. Biasanya, setiap kita men-submit form pada sebuah web, kita membuat sebuah POST request.

Code

url = 'http://httpbin.org/post'
data = {'title':'Mulia dengan manhaj salaf'}

rsp = request('POST',url, data=data)

print(rsp.json())
Output
{'args': {}, 'data': '', 'files': {}, 'form': {'title': 'Mulia dengan manhaj salaf'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br', 'Content-Length': '31', 'Content-Type': 'application/x-www-form-urlencoded', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.27.1', 'X-Amzn-Trace-Id': 'Root=1-6498e50e-4579f68554ba761a646f5d26'}, 'json': None, 'origin': '140.213.136.102', 'url': 'http://httpbin.org/post'}

Kode diatasn hanya berbeda sedikit dengan tipe GET. Pada POST ini kita mengganti parameter pertama dengan post dan kita juga mengirim data dalam betuk dict kedalam parameter data.

Persisting data on disk

Persist data adalah, data yang ditulis pada penyimpanan permanen (tidak mudah hilang) seperti hard drive, contohnya, dan data tersebut tidak akan terhapus setelah peroses penulisan tersebut berakhir.

Serializing data using py pickle

Modul pickle dari pustaka bawaan python, menawarkan tools yang berguna untuk mengkonversi Python objects kedalam byte streams dan sebaliknya. Modul ini, di sisilain, tidak bisa dibaca oleh orang, menterjemah ke bytes, dan spesifik ke python jika dibandingkan dengan JSON yang dapat dibaca manusia, terjemah ke string dan universal specific. Untungmya (pickle), python interospection capabilities mendukung banya tipe data, tidak seperti dengan JSON hanya mendungkung beberapa data, dan jika tidak maka kita harus membuat costume class turunan dari JSONEncoder. Baca lebih lanjut tentang JSON disini

Selain yang telah dipaparkan pada catatan diatas tentang perbeedaan antara picke dan json, ada satu hal juga yang tidak kalah penting, yaitu fokus kemanan yang harus dikhawatirkan jika kita mempertimbangkan untuk menggunakan pickle. Unpickling data yang mencurigakan dan sangat berbahana, makadari itu, jika kita menetapkan penggunakan pickle pada aplikasi kita, maka kita harus ekstra hati2 dalam unpickling data dari luar.

Warning

Please, mohon gunakan cryptographic signatur untuk memastika data yang telah di pickle tidak dirusak (berubah tanpa kengininan yang sendiri dan baik). InsyaAllah kita akan ada catatan tentang Cryptography dan Tokens.

Code

from dataclasses import dataclass
import pickle

@dataclass
class Person:
    first_name:str
    last_name:str
    _id:int

    def greets(self):
        print(f'Assalamualiakum, my name is {self.first_name} {self.last_name} and my id number is {self._id}')

people = [
    Person('Muhamad','Farras',27),
    Person('Tania','Dwi',11),
    Person('Muhamad','Faris',5),
    Person('Nu\'man','Noah',13)
]

# Writting to binnary
with open('my_fam.pickle', mode='wb') as stream:
    pickle.dump(people, stream)

# Load the data
with open('my_fam.pickle', mode='rb') as stream:
    loaded_data = pickle.load(stream)

for a in loaded_data:
    a.greets()
Output
Assalamualiakum, my name is Muhamad Farras and my id number is 27
Assalamualiakum, my name is Tania Dwi and my id number is 11
Assalamualiakum, my name is Muhamad Faris and my id number is 5
Assalamualiakum, my name is Nu'man Noah and my id number is 13
Output
.
├── adakah.txt
├── docs
├── important.txt
├── mkdocs.yml
├── my_fam.pickle #(1)! 
└── Untitled.ipynb
  1. my_fam.pickle persist in hardware

Pada kode diatas saya membuat sebuah class menggunakan dataclass decorator. yang menampung 3 property dan membuat sebuah function untuk menge-print sebuah salam berdasarkan data dari property tersebut. Selanjutnya saya membuat sebuah list yang menampung objek inisialisasi dari class #!Person. Selanjutnya kita menulis python objek kesebuah file menggunakan pickle.dump(objek, stream), disini tidak perlu melakukan perulangan jika objek yang ingin dibentuk filenya berada didalam sebauh list. Samah halnya saat load data, menggunakan fungsi pickle.load(stream) tidak perlu membuat for loop untuk mengambil sebua python objek pada file tersebut.

Dalam aplikasi sehari-hari, pickle digunakan ketika kita ingin mempertahankan pyhon data object yang tidak digunakan oleh aplikasi lain. (khusus untuk aplikasi tersebut) . Dalam praktiknya, kita sangat jarang menggunakan libary ini

Saving data with shelve

shelf mempertahankan dictionary-like object. Bagusnya adalah, nilai-nilai yang kita simpan didalam shelf dapat dalam bentuk objek yang dapat kita pickle, sehingga tidak terbatas terhadapt tipe data sebagaimana kita menggunakan database.

Code

import shelve

class Student:
    def __init__(self, id, name):
        self.id = id
        self.name = name

    def information(self):
        print(f'Id\'s {self.name} is {self.id}')


with shelve.open('my_data.shelve') as db:
    db['frs'] = Student(27,'Farras')
    db['tna'] = Student(11,'Tania')
    db['list'] = [1,2,3,4,5]
    db['delete'] = 'We have to delete this one latter'

    del db['delete']
    list_1 = db['list']
    list_1.extend((5,6,7,8,9))
    db['list'] = list_1
    print(db['list'])
    print(list(db.keys()))
Output
[1, 2, 3, 4, 5, 5, 6, 7, 8, 9]
['frs', 'tna', 'list']

Perhatikan baris yang di hight line, yaitu bagaiman kita meng-extract list dari shelf, memodifikasinya dan menyimpannya lagi. Jika kita langsung mengubah nilai list tanpa harus meng-extract dan memberikan nilai yang baru maka kita harus memberikan nilai True pada pameter writeback.

Code

import shelve

class Student:
    def __init__(self, id, name):
        self.id = id
        self.name = name

    def information(self):
        print(f'Id\'s {self.name} is {self.id}')


with shelve.open('my_data.shelve', writeback=True) as db:
    db['frs'] = Student(27,'Farras')
    db['tna'] = Student(11,'Tania')
    db['list'] = [1,2,3,4,5]
    db['delete'] = 'We have to delete this one latter'

    del db['delete']
    db['list'].extend((5,6,7,8,9)) # work
    print(db['list'])
    print(list(db.keys()))
Output
[1, 2, 3, 4, 5, 5, 6, 7, 8, 9]
['frs', 'tna', 'list']
import shelve

class Student:
    def __init__(self, id, name):
        self.id = id
        self.name = name

    def information(self):
        print(f'Id\'s {self.name} is {self.id}')


with shelve.open('my_data.shelve') as db:
    db['frs'] = Student(27,'Farras')
    db['tna'] = Student(11,'Tania')
    db['list'] = [1,2,3,4,5]
    db['delete'] = 'We have to delete this one latter'

    del db['delete']
    db['list'].extend((5,6,7,8,9)) # Doesny work
    print(db['list'])
    print(list(db.keys()))
Output
[1, 2, 3, 4, 5]
['frs', 'tna', 'list']

Alasan mengapa fitur ini tidak aktif bawaanya karena, fitur ini membutuhkan konsumsi memori yang banyak dan memperlambat penutupan context shelf.


sample

Code


Output

Code


Output


Output

Code



Output


Output