Once in a while I need to prettify a JSON document (read: add indentations and new lines) to make it (more) readable. Usually what I will do is finding an online prettifier and copy-paste the JSON onto there. What I usually don’t realize is that I already have that tool in my toolbox! I figured this out from a post somewhere on the net, sorry I don’t remember exactly where.
If you have Python installed, that means you already have a tool that can make a JSON document pretty. Just use it as the following.
$ python -m json.tool document.json
Or if you are an stdin
fan, it can also read from there.
$ python -m json.tool < document.json
There you have it!
*Sepertinya judul tulisan ini aga2 tidak menjelaskan xD*
Intinya begini. Saya punya sebuah barisan nilai, katakanlah seperti di bawah ini
nilai = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
Nah saya ingin membuat kelompok untuk setiap 2 buah nilai berurutan. Barisan di atas ingin saya ubah menjadi seperti berikut.
pasangan = [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10), (11, 12)]
Bagaimana cara cepatnya?Β Salah satu trik yang saya temukan (di Internet :P) adalah dengan menggunakan fungsi zip dan iter.
Fungsi zip
sudah dikenal dapat dipakai untuk menggabungkan dua atau lebih barisan untuk membuat barisan kelompok dari setiap elemen yang ada (err..?). Contohnya
>>> angka = [1, 2, 3]
>>> tulisan = ["satu", "dua", "tiga"]
>>> zip(angka, tulisan)
[(1, 'satu'), (2, 'dua'), (3, 'tiga')]
Nah yang saya mau adalah saya ingin membuat kelompok dari setiap dua elemen berurutan. Untuk ini, fungsi iter
dapat kita salahgunakan π Mari kembali ke contoh awal.
>>> nilai = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
>>> iter_nilai = iter(nilai)
>>> zip(iter_nilai, iter_nilai)
[(1, 2), (3, 4), (5, 6), (7, 8), (9, 10), (11, 12)]
Hore jadi π
Apa yang terjadi di belakang layar? Setelah baca2 source code fungsi zip
(Python 2.7.5), setiap barisan yang dimasukkan ke dalam fungsi zip
akan dijadikan iterator dan nilainya akan diambil satu persatu sampai salah satu barisan tidak dapat mengeluarkan elemen berikutnya. Andaikan parameter yang dimasukkan sudah berupa iterator, saya asumsikan Python tidak akan membuat iterator baru. Nah berhubung parameter yang dimasukkan berasal dari iterator yang sama, maka nilai akan disebar selang-seling ke kelompok/tuple
yang dihasilkan.
Bagaimana cara membuat kelompok dari setiap 3 bilangan berurutan? Tambahkan lagi saja parameter si zip
!
>>> nilai = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
>>> iter_nilai = iter(nilai)
>>> zip(iter_nilai, iter_nilai, iter_nilai)
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12)]
Sebelum ini, saya sempat menulis tentang cara membuat fungsi pembanding sendiri untuk keperluan pembuatan himpunan unik. Singkatnya, di bahasa Java, Scala, dan mungkin bahasa lain yang jalan di atas JVM, kita perlu mengimplementasikan ulang method hashCode
dan equals
. Setelah menulis itu, saya sempat berpikir bagaimana cara melakukannya di Python?
Yang ingin saya lakukan adalah jika saya memiliki beberapa objek penampung nilai, saya ingin membuat mereka unik (katakanlah dengan memasukkannya ke dalam set
). Jadi jika ada lebih dari satu objek yang bernilai sama, hanya satu di antara mereka saja yang diambil. Sisanya bisa dibuang karena saya tidak memerlukannya.
Katakanlah saya memiliki kelas seperti berikut.
class M(object):
def __init__(self, posisi, asam_amino):
self.posisi = posisi
self.asam_amino = asam_amino
def __repr__(self):
return "%s%s" % (self.posisi, self.asam_amino)
Lalu saya membuat beberapa objek dari kelas tersebut.
mutasi = [ M(1, "a"), M(2, "b"), M(1, "c"), M(3, "b"), M(1, "a") ]
Mari kita coba panggil fungsi set
>>> print mutasi
[1a, 2b, 1c, 3b, 1a]
>>> print set(mutasi)
set([3b, 1c, 1a, 1a, 2b])
Seperti yang sudah diduga, fungsi set
belum berhasil menghapus nilai ganda.
Setelah ngubek2 sana sini, ternyata di Python kita juga harus mengimplementasikan dua buah method yang sama (versi Python tentunya)! Kedua method tersebut adalah __eq__
dan __hash__
. Kedua method ini dapat diimplementasikan ulang seperti berikut.
def __eq__(self, o):
if not isinstance(o, M):
return False
return self.posisi == o.posisi \
and self.asam_amino == o.asam_amino
def __hash__(self):
return hash((self.posisi, self.asam_amino))
Mari kita coba lagi sekarang.
>>> mutasi = [ M(1, "a"), M(2, "b"), M(1, "c"), M(3, "b"), M(1, "a") ]
>>> print mutasi
[1a, 2b, 1c, 3b, 1a]
>>> print set(mutasi)
set([1c, 1a, 2b, 3b])
Klopt!
Kode lengkap dapat dilihat di https://gist.github.com/fajran/5744836
When I merged all of my scattered blogs few days ago, I tried to import my blog posts from Blogger to this WordPress installation. My first shot was to use WordPress’ own Blogger Importer but apparently the result was not so good (I think). Some texts wereΒ garbled and gone. For example, the iframe
Β tag from SoundCloud in my post aboutΒ OS X’ Text-To-Speech “Improvisation” was eaten by either the converter script or WordPress. Either way, it didn’t work flawlessly and I had to find out another way to export the posts.
Then I realized that Blogger provides a tool to download all of our blog posts, including comments (and I assume the pages as well, but I didn’t test it since I don’t have pages). I tried this out and got a machine readable XML file. Wohoo!
WordPress has it’s own way to do data migration between WordPress blogs. The good thing is it’s also an XML file! If somehow I can convert the XML file from Blogger to the one that WordPress uses, everything should be set π
A couple hours later, I got this very simple converter script. It may not be efficient and it also has several hardcoded values (check the categories part) but it suits what I wanted π
https://gist.github.com/fajran/5659455
Have fun!
Long time ago, I made a chart that shows Ubuntu Repositories’ size over several releases. At that time, there were only 6 releases and 1 release that was still being worked on. Fast forward almost 6 years later, now we have 18 Ubuntu releases, yay! How does that chart look like now?
Well, here we go! I just made some new charts!

The full charts can be seen atΒ http://fajran.github.io/vis-ubuntu-pkg/
As you can see, the repositories are constantly growing. Ubuntu starts with about 8 GB of packages and now it has more than 50 GB. The number of packages is also growing from just over 12.5 thousands and now it almost three times larger.
How did I count the numbers? I took the package index files (a.k.a. Packages
file) and counted the number of packages inside. I also summed the size of each packages to get the size of the repository. The files are from the at-release repository which means I excluded the -updates
and -security
repositories. The at-release repository is presumably frozen and therefore the size won’t change anymore, where the latest two repositories are for the updates, which by definition are always updated. Well.. except when the release is not supported anymore.
I put all of the codes I wrote and use in my GitHub repository https://github.com/fajran/vis-ubuntu-pkg. Feel free to check, modify, and update it. I release the whole thing as public domain!
Daripada blog ini kembali kosong, mari kita bahas Python Trivia babak pertama dan babak kedua yang saya keluarkan lebih dari satu tahun lalu! Mari kita mulai dari babak pertama.
Berikut ini cuplikan kode yang saya tulis:
class Kantong(object):
def __init__(self, data=[]):
self.data = data
def add_data(self, angka):
self.data.append(angka)
def cetak(self):
print 'Daftar angka:', self.data
Lalu mari kita pakai kelas di atas seperti berikut.
satu = Kantong()
satu.add_data(1)
satu.add_data(2)
satu.cetak()
Apa keluarannya?
Daftar angka: [1, 2]
Data yang kita miliki adalah [1, 2]
yang berasal dari 2 pemanggilan add_data()
dengan parameter 1
dan 2
. Gak ada yg protes kan? π
Setelah itu, kita buah sebuah objek lain dari kelas yg sama. Lalu 3 buah data baru dimasukkan dan lalu dicetak.
dua = Kantong()
dua.add_data(3)
dua.add_data(4)
dua.add_data(5)
dua.cetak()
Hasilnya adalah.. jreng jreng jreng..
Daftar angka: [1, 2, 3, 4, 5]
Lho lho lho.. kok ada [1, 2]
dan bukan cuma [3, 4, 5]
padahal kita cuma memasukkan 3 angka di objek baru ini?
Mengapa begini mengapa begitu?
Kalau kita baca kelas Kandong
sekali lagi, fungsi add_data
akan memasukkan angka ke variabel self.data
. Variabel ini diinisialisasi pada bagian constructor __init__()
dan nilai awal yg dimasukkan adalah nilai yang ada dalam variabel data
dari parameter si constructor.
Jika parameter pada constructor ini tidak diisi, maka secara default nilai data
adalah sebuah list kosong []
...
def __init__(self, data=[]):
...
Objek satu
dan dua
yang dibuat dari kelas Kantong
ini tidak menyertakan parameter saat objek dibuat. Alhasil, nilai []
tadi akan dijadikan nilai awal untuk self.data
.
Pertanyaannya adalah kapan nilai []
dibuat? Ternyata nilai ini dibuat hanya satu kali pada saat kelas didefinisikan! Artinya, jika nilai tadi tidak diganti, semua objek yang berasal dari kelas Kantong
akan memiliki variabel self.data
yang sama! Walau tersebar di banyak objek, tapi variabel tersebut menunjuk ke sebuah list yang sama sehingga jika ada penambahan/pengurangan isi list tersebut, semua objek yang memegang list tersebut akan dapat melihatnya.
Lalu bagaimana?
Jika yang kita maksud dengan tidak memberikan parameter adalah sebuah list baru (dan kosong) akan dibuat, maka kelas di atas bisa diubah menjadi berikut.
class Kantong(object):
def __init__(self, data=None):
if data is None:
self.data = []
else:
self.data = data
...
Dengan demikian, jika parameter tidak dimasukkan (yang artinya data
akan bernilai None
), variabel self.data
akan diisi dengan sebuah list baru yang berbeda dengan list dari objek lain dari kelas yang sama.
Kesimpulannya, pada cara pertama, nilai self.data
secara default akan diisi sebuah list yang dibuat pada saat kelas didefinisikan. Pemanggilan constructor (juga termasuk fungsi) akan menggunakan list yang sama dan efeknya adalah adanya penggunaan data bersama. Sedangkan pada contoh kelas ke dua, nilai self.data
dibuat pada saat constructor/fungsi dijalankan sehingga pada setiap eksekusi, list baru dan berbeda akan dibuat dan efeknya tidak akan ada penggunaan data bersama.
Bagaimana dengan trivia babak kedua?
Kasus sama juga terjadi di sini.
a = [1] * 5
Kode di atas dapat dibaca seperti berikut. Buat sebuah list baru yang isinya adalah isi list sekarang yang diulang sebanyak 5 kali. Nilai dari a
adalah seperti berikut.
[1, 1, 1, 1, 1]
Jika kita ubah salah satu elemennya..
a[3] = 200
Maka nilai si a
akan menjadi..
[1, 1, 1, 200, 1]
Masih oke kan? π Apa yang terjadi jika kita menggunakan dict
dan bukan angka 1
?
b = [{}] * 5
Nilai si b
tentunya akan menjadi seperti berikut.
[{}, {}, {}, {}, {}]
Bagimana jika salah dict
diubah isinya?
b[0]['a'] = 100
Baris kode di atas artinya ambil elemen nomor 0 dari b
(yaitu sebuah dict
), lalu kita set kunci 'a'
supaya bernilai 100
. Bagaimana jika kita cetak nilai si b
?
[{'a': 100}, {'a': 100}, {'a': 100}, {'a': 100}, {'a': 100}]
Sesuatu yang (mungkin) tidak diharapkan terjadi lagi π Lagi-lagi hal ini terjadi karena sebenarnya 5 buah dict
yang ada di dalam b
sebenarnya adalah dict
yang sama namun dimasukkan sebanyak 5 kali di dalam list b
.
Mudah2an jelas π
Daripada blog ini kembali kosong, mari kita bahas Python Trivia babak pertama dan babak kedua yang saya keluarkan lebih dari satu tahun lalu! Mari kita mulai dari babak pertama.
Berikut ini cuplikan kode yang saya tulis:
class Kantong(object):
def __init__(self, data=[]):
self.data = data
def add_data(self, angka):
self.data.append(angka)
def cetak(self):
print 'Daftar angka:', self.data
Lalu mari kita pakai kelas di atas seperti berikut.
satu = Kantong()
satu.add_data(1)
satu.add_data(2)
satu.cetak()
Apa keluarannya?
Daftar angka: [1, 2]
Data yang kita miliki adalah [1, 2]
yang berasal dari 2 pemanggilan add_data()
dengan parameter 1
dan 2
. Gak ada yg protes kan? π
Setelah itu, kita buah sebuah objek lain dari kelas yg sama. Lalu 3 buah data baru dimasukkan dan lalu dicetak.
dua = Kantong()
dua.add_data(3)
dua.add_data(4)
dua.add_data(5)
dua.cetak()
Hasilnya adalah.. jreng jreng jreng..
Daftar angka: [1, 2, 3, 4, 5]
Lho lho lho.. kok ada [1, 2]
dan bukan cuma [3, 4, 5]
padahal kita cuma memasukkan 3 angka di objek baru ini?
Mengapa begini mengapa begitu?
Kalau kita baca kelas Kandong
sekali lagi, fungsi add_data
akan memasukkan angka ke variabel self.data
. Variabel ini diinisialisasi pada bagian constructor __init__()
dan nilai awal yg dimasukkan adalah nilai yang ada dalam variabel data
dari parameter si constructor.
Jika parameter pada constructor ini tidak diisi, maka secara default nilai data
adalah sebuah list kosong []
...
def __init__(self, data=[]):
...
Objek satu
dan dua
yang dibuat dari kelas Kantong
ini tidak menyertakan parameter saat objek dibuat. Alhasil, nilai []
tadi akan dijadikan nilai awal untuk self.data
.
Pertanyaannya adalah kapan nilai []
dibuat? Ternyata nilai ini dibuat hanya satu kali pada saat kelas didefinisikan! Artinya, jika nilai tadi tidak diganti, semua objek yang berasal dari kelas Kantong
akan memiliki variabel self.data
yang sama! Walau tersebar di banyak objek, tapi variabel tersebut menunjuk ke sebuah list yang sama sehingga jika ada penambahan/pengurangan isi list tersebut, semua objek yang memegang list tersebut akan dapat melihatnya.
Lalu bagaimana?
Jika yang kita maksud dengan tidak memberikan parameter adalah sebuah list baru (dan kosong) akan dibuat, maka kelas di atas bisa diubah menjadi berikut.
class Kantong(object):
def __init__(self, data=None):
if data is None:
self.data = []
else:
self.data = data
...
Dengan demikian, jika parameter tidak dimasukkan (yang artinya data
akan bernilai None
), variabel self.data
akan diisi dengan sebuah list baru yang berbeda dengan list dari objek lain dari kelas yang sama.
Kesimpulannya, pada cara pertama, nilai self.data
secara default akan diisi sebuah list yang dibuat pada saat kelas didefinisikan. Pemanggilan constructor (juga termasuk fungsi) akan menggunakan list yang sama dan efeknya adalah adanya penggunaan data bersama. Sedangkan pada contoh kelas ke dua, nilai self.data
dibuat pada saat constructor/fungsi dijalankan sehingga pada setiap eksekusi, list baru dan berbeda akan dibuat dan efeknya tidak akan ada penggunaan data bersama.
Bagaimana dengan trivia babak kedua?
Kasus sama juga terjadi di sini.
a = [1] * 5
Kode di atas dapat dibaca seperti berikut. Buat sebuah list baru yang isinya adalah isi list sekarang yang diulang sebanyak 5 kali. Nilai dari a
adalah seperti berikut.
[1, 1, 1, 1, 1]
Jika kita ubah salah satu elemennya..
a[3] = 200
Maka nilai si a
akan menjadi..
[1, 1, 1, 200, 1]
Masih oke kan? π Apa yang terjadi jika kita menggunakan dict
dan bukan angka 1
?
b = [{}] * 5
Nilai si b
tentunya akan menjadi seperti berikut.
[{}, {}, {}, {}, {}]
Bagimana jika salah dict
diubah isinya?
b[0]['a'] = 100
Baris kode di atas artinya ambil elemen nomor 0 dari b
(yaitu sebuah dict
), lalu kita set kunci 'a'
supaya bernilai 100
. Bagaimana jika kita cetak nilai si b
?
[{'a': 100}, {'a': 100}, {'a': 100}, {'a': 100}, {'a': 100}]
Sesuatu yang (mungkin) tidak diharapkan terjadi lagi π Lagi-lagi hal ini terjadi karena sebenarnya 5 buah dict
yang ada di dalam b
sebenarnya adalah dict
yang sama namun dimasukkan sebanyak 5 kali di dalam list b
.
Mudah2an jelas π
Saat ini saya sedang membutuhkan akses ke sebuah pustaka yang hanya tersedia dalam platform Microsoft .NET. Berhubung apa yang saya buat semuanya tertulis dalam bahasa Python, tepatnya CPython, saya perlu mencari cara bagaimana agar saya bisa mengakses pustaka dalam .NET tersebut dari Python. Setelah ngubek sana sini, akhirnya ketemu juga (salah satu) caranya, yaitu melalui Component Object Model alias COM. Singkatnya, COM ini memungkinkan berbagai aplikasi dengan berbagai bahasa untuk berkomunikasi.
Membuat komponen COM dengan C#
Dalam kasus yang saya hadapi, saya perlu mengakses pustaka .NET dari Python. Oleh karena itu saya membutuhkan sebuah komponen COM yang tertulis dalam, misalnya, C# yang dapat mengakses pustaka tersebut. Komponen dalam C# ini lalu saya atur agar bisa diakses melalui COM sehingga pada akhirnya aplikasi Python saya bisa mengakesenya.
Kali ini saya akan mencontohkan cara membuat sebuah kelas dalam C# yang dapat diakses melalui COM. Kelas ini adalah sebuah kelas kalkulator yang berisi fungsi penjumlahan angka. Contoh ini saya adaptasi dari beberapa referensi bagus yang ada di [1,2,3].
Buat sebuah proyek C# baru bernama Matematika
dan kemudian buat sebuah sebuah kelas dengan isi seperti berikut.
namespace Matematika {
public interface IKalkulator {
int tambah(int a, int b);
}
public class Kalkulator : IKalkulator {
public int tambah(int a, int b) {
return a + b;
}
}
}
Interface adalah bagian penting dalam pembuatan komponen COM karena dalam COM pengaksesan komponen akan mengacu kepada interface yang dibuat.
Untuk keperluan pendaftaran komponen, kita memerlukan sebuah kunci. Buka Visual Studio Command Prompt, pindah ke direktori proyek (direktori dimana kode sumber berada, bukan direktori Solution-nya visual studio) dan jalankan perintah berikut.
sn.exe -k Matematika.snk
Setelah itu, buka berkas AssemblyInfo.cs
(ada di bawah Solution -> Matematika -> Properties) dan tambahkan 3 baris berikut.
[assembly: ComVisible(true)]
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("Matematika.snk")]
Lakukan kompilasi proyek untuk membuat berkas pustaka Matematika.dll
. Berkas pustaka ini akan berada di bawah direktori binRelease
yang ada di bawah direktori proyek.
Agar pustaka atau komponen COM ini dapat diakses dari aplikasi lain, kita harus mendaftarkan pustaka ini terlebih dahulu. Untuk ini, kita perlu menggunakan akses sebagai Administrator. Buka Visual Studio Command Prompt lagi namun kali ini sebagai Administrator (Klik kanan di shortcut dan pilih Run as Administrator). Masuk ke direktori tempat pustaka Matematika.dll
tadi berada (direktori binRelease
di bawah direktori kerja) lalu jalankan perintah pendaftaran berikut.
regasm.exe Matematika.dll /tlb:Matematika.tlb /codebase
Setelah perintah di atas dijalankan, Anda akan mendapatkan pesan kalau pustaka sudah didaftarkan dan juga sebuah berkas baru bernama Matematika.tlb
yang berisi deskripsi mengenai seluruh interface dan struktur data yang ada.
Sebelum lanjut ke cara mengakses komponen tadi, buka lagi berkas AssemblyInfo.cs
dan dapatkan tanda pengenal aplikasi yang barusan dibuat. Tanda pengenal ini adalah sebuah GUID yang didefinisikan seperti berikut.
[assembly: Guid("9808bb58-40fe-4373-acc3-f577b55a2a99")]
Deretan angka dan huruf di atas, yaitu 9808bb58-40fe-4373-acc3-f577b55a2a9
, akan menjadi (salah satu) tanda pengenal yang dibutuhkan saat mengakses komponen COM tadi.
Mengakses COM dari Python
Python tidak menyediakan dukungan terhadap COM secara langsung sehingga kita perlu memasang modul tambahan yang dapat membantu kita untuk mengakses COM. Saya menemukan ada dua modul yang bisa digunakan, yaitu PyWin32 dan comtypes. Dalam petualangan beberapa hari terakhir ini, saya belum berhasil mengakses COM yang saya buat di .NET dengan bantuan PyWin32 namun comtypes dapat melakukannya dengan cukup mudah. Oleh karena itu, saya akan mencontohkan penggunaan comtypes untuk mengakses komponen COM yang tadi dibuat. Berhubung comtypes itu bukan bawaan asli dari Python, unduh dan pasang dulu modul ini dari http://pypi.python.org/pypi/comtypes.
Kalau sudah, buat sebuah berkas matematika.py
yang berisi kode berikut.
import comtypes
import comtypes.client as cc
guid = comtypes.GUID("{9808bb58-40fe-4373-acc3-f577b55a2a99}")
cc.GetModule((guid, 1, 0))
from comtypes.gen import Matematika
obj = cc.CreateObject("Matematika.Kalkulator", None, None,
Matematika.IKalkulator)
a = 10
b = 200
hasil = obj.tambah(a, b)
print a, '+', b, '=', hasil
Setelah itu, jalankan dengan python.exe
C:tmp>c:Python26python.exe matematika.py
# Generating comtypes.gen._9808BB58_40FE_4373_ACC3_F577B55A2A99_0_1_0
# Generating comtypes.gen._00020430_0000_0000_C000_000000000046_0_2_0
# Generating comtypes.gen.stdole
# Generating comtypes.gen._BED7F4EA_1A96_11D2_8F08_00A0C9A6186D_0_2_0
# Generating comtypes.gen.mscorlib
# Generating comtypes.gen.Matematika
10 + 200 = 210
Dalam eksekusinya, comtypes akan membuat beberapa modul tambahan (yang berfungsi kode penghubung dengan komponen COM) secara otomatis seperti yang diperlihatkan dalam hasil di atas. Modul dan pesan ini hanya akan dibuat satu kali sehingga pada eksekusi selanjutnya pesan di atas tidak akan tampil lagi.
Jika ada perubahan dalam kode C# yang kita buat, selama kita tidak mengubah interface, kita cukup melakukan kompilasi ulang dan pustaka baru akan langsung dipakai. Jika interface berubah, pustaka perlu didaftarkan ulang (dan berkas .tlb
-nya juga perlu dibuat ulang) agar perubahan dapat diketahui. Berdasarkan berkas .tlb
yang berubah, comtypes akan melakukan pembuatan ulang modul penghubung secara otomatis juga.
Referensi
- http://msdn.microsoft.com/en-us/library/aa645738(v=vs.71).aspx
- http://support.microsoft.com/kb/828736
- http://cppkid.wordpress.com/2009/01/02/how-to-call-a-managed-dll-from-unmanaged-code/
Saat ini saya sedang membutuhkan akses ke sebuah pustaka yang hanya tersedia dalam platform Microsoft .NET. Berhubung apa yang saya buat semuanya tertulis dalam bahasa Python, tepatnya CPython, saya perlu mencari cara bagaimana agar saya bisa mengakses pustaka dalam .NET tersebut dari Python. Setelah ngubek sana sini, akhirnya ketemu juga (salah satu) caranya, yaitu melalui Component Object Model alias COM. Singkatnya, COM ini memungkinkan berbagai aplikasi dengan berbagai bahasa untuk berkomunikasi.
Membuat komponen COM dengan C#
Dalam kasus yang saya hadapi, saya perlu mengakses pustaka .NET dari Python. Oleh karena itu saya membutuhkan sebuah komponen COM yang tertulis dalam, misalnya, C# yang dapat mengakses pustaka tersebut. Komponen dalam C# ini lalu saya atur agar bisa diakses melalui COM sehingga pada akhirnya aplikasi Python saya bisa mengakesenya.
Kali ini saya akan mencontohkan cara membuat sebuah kelas dalam C# yang dapat diakses melalui COM. Kelas ini adalah sebuah kelas kalkulator yang berisi fungsi penjumlahan angka. Contoh ini saya adaptasi dari beberapa referensi bagus yang ada di [1,2,3].
Buat sebuah proyek C# baru bernama Matematika
dan kemudian buat sebuah sebuah kelas dengan isi seperti berikut.
namespace Matematika {
public interface IKalkulator {
int tambah(int a, int b);
}
public class Kalkulator : IKalkulator {
public int tambah(int a, int b) {
return a + b;
}
}
}
Interface adalah bagian penting dalam pembuatan komponen COM karena dalam COM pengaksesan komponen akan mengacu kepada interface yang dibuat.
Untuk keperluan pendaftaran komponen, kita memerlukan sebuah kunci. Buka Visual Studio Command Prompt, pindah ke direktori proyek (direktori dimana kode sumber berada, bukan direktori Solution-nya visual studio) dan jalankan perintah berikut.
sn.exe -k Matematika.snk
Setelah itu, buka berkas AssemblyInfo.cs
(ada di bawah Solution -> Matematika -> Properties) dan tambahkan 3 baris berikut.
[assembly: ComVisible(true)]
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("Matematika.snk")]
Lakukan kompilasi proyek untuk membuat berkas pustaka Matematika.dll
. Berkas pustaka ini akan berada di bawah direktori binRelease
yang ada di bawah direktori proyek.
Agar pustaka atau komponen COM ini dapat diakses dari aplikasi lain, kita harus mendaftarkan pustaka ini terlebih dahulu. Untuk ini, kita perlu menggunakan akses sebagai Administrator. Buka Visual Studio Command Prompt lagi namun kali ini sebagai Administrator (Klik kanan di shortcut dan pilih Run as Administrator). Masuk ke direktori tempat pustaka Matematika.dll
tadi berada (direktori binRelease
di bawah direktori kerja) lalu jalankan perintah pendaftaran berikut.
regasm.exe Matematika.dll /tlb:Matematika.tlb /codebase
Setelah perintah di atas dijalankan, Anda akan mendapatkan pesan kalau pustaka sudah didaftarkan dan juga sebuah berkas baru bernama Matematika.tlb
yang berisi deskripsi mengenai seluruh interface dan struktur data yang ada.
Sebelum lanjut ke cara mengakses komponen tadi, buka lagi berkas AssemblyInfo.cs
dan dapatkan tanda pengenal aplikasi yang barusan dibuat. Tanda pengenal ini adalah sebuah GUID yang didefinisikan seperti berikut.
[assembly: Guid("9808bb58-40fe-4373-acc3-f577b55a2a99")]
Deretan angka dan huruf di atas, yaitu 9808bb58-40fe-4373-acc3-f577b55a2a9
, akan menjadi (salah satu) tanda pengenal yang dibutuhkan saat mengakses komponen COM tadi.
Mengakses COM dari Python
Python tidak menyediakan dukungan terhadap COM secara langsung sehingga kita perlu memasang modul tambahan yang dapat membantu kita untuk mengakses COM. Saya menemukan ada dua modul yang bisa digunakan, yaitu PyWin32 dan comtypes. Dalam petualangan beberapa hari terakhir ini, saya belum berhasil mengakses COM yang saya buat di .NET dengan bantuan PyWin32 namun comtypes dapat melakukannya dengan cukup mudah. Oleh karena itu, saya akan mencontohkan penggunaan comtypes untuk mengakses komponen COM yang tadi dibuat. Berhubung comtypes itu bukan bawaan asli dari Python, unduh dan pasang dulu modul ini dari http://pypi.python.org/pypi/comtypes.
Kalau sudah, buat sebuah berkas matematika.py
yang berisi kode berikut.
import comtypes
import comtypes.client as cc
guid = comtypes.GUID("{9808bb58-40fe-4373-acc3-f577b55a2a99}")
cc.GetModule((guid, 1, 0))
from comtypes.gen import Matematika
obj = cc.CreateObject("Matematika.Kalkulator", None, None,
Matematika.IKalkulator)
a = 10
b = 200
hasil = obj.tambah(a, b)
print a, '+', b, '=', hasil
Setelah itu, jalankan dengan python.exe
C:tmp>c:Python26python.exe matematika.py
# Generating comtypes.gen._9808BB58_40FE_4373_ACC3_F577B55A2A99_0_1_0
# Generating comtypes.gen._00020430_0000_0000_C000_000000000046_0_2_0
# Generating comtypes.gen.stdole
# Generating comtypes.gen._BED7F4EA_1A96_11D2_8F08_00A0C9A6186D_0_2_0
# Generating comtypes.gen.mscorlib
# Generating comtypes.gen.Matematika
10 + 200 = 210
Dalam eksekusinya, comtypes akan membuat beberapa modul tambahan (yang berfungsi kode penghubung dengan komponen COM) secara otomatis seperti yang diperlihatkan dalam hasil di atas. Modul dan pesan ini hanya akan dibuat satu kali sehingga pada eksekusi selanjutnya pesan di atas tidak akan tampil lagi.
Jika ada perubahan dalam kode C# yang kita buat, selama kita tidak mengubah interface, kita cukup melakukan kompilasi ulang dan pustaka baru akan langsung dipakai. Jika interface berubah, pustaka perlu didaftarkan ulang (dan berkas .tlb
-nya juga perlu dibuat ulang) agar perubahan dapat diketahui. Berdasarkan berkas .tlb
yang berubah, comtypes akan melakukan pembuatan ulang modul penghubung secara otomatis juga.
Referensi
- http://msdn.microsoft.com/en-us/library/aa645738(v=vs.71).aspx
- http://support.microsoft.com/kb/828736
- http://cppkid.wordpress.com/2009/01/02/how-to-call-a-managed-dll-from-unmanaged-code/
Ternyata metaclass di Python itu sangat menarik karena kita bisa melakukan ilmu hitam π Salah satu ilmu hitam yang sedang/akan saya pakai adalah membuat deklarasi kelas yang baru saja dibuat menjadi lenyap secara tiba2 dan alih-alih kita mendapatkan sebuah objek/instance dari kelas tersebut.
Mengapa saya memerlukan sesuatu yang “aneh” seperti ini? Singkatnya saya sedang membuat semacam domain specific language yang berbasiskan Python. Dengan menggunakan trik sulap di atas, saya bisa membuat bahasa yang saya buat tadi menjadi lebih mudah dipakai. Maaf kalo saya blom bisa menjelaskan dg lebih detail.
Di Python, metaclass bisa digambarkan sebagai sebuah kode yang dijalankan saat sebuah kelas dideklarasikan. Dengan demikian kita bisa mengutak atik wujud sebuah kelas sebelum kelas teresebut siap dipakai. Contoh penggunaan dasarnya adalah sebagai berikut.
class MetaProvinsi(type):
def __new__(cls, name, bases, attrs):
return type.__new__(cls, name, bases, attrs)
class Provinsi(object):
__metaclass__ = MetaProvinsi
Pada contoh di atas, ada sebuah kelas bernama Provinsi
yang memiliki metaclass MetaProvinsi
. Kode pada fungsi __new__
di atas adalah bagian kode yang dapat diisi untuk mengubah wujud kelas Provinsi
. Fungsi ini menerima 4 buah parameter yaitu:
cls
yang menunjuk ke diri sendiri (yaitu MetaProvinsi
),
name
yang berisi nama kelas yang dibuat (dalam contoh di atas, name
berisi Provinsi),
bases
yang berisi daftar kelas yang diturunkan oleh si Provinsi
(yaitu object
), dan
attrs
yang berupa sebuah dictionary berisi daftar atribut (instance variables dan/atau methods) dari Provinsi
.
Metaclass pada contoh di atas masih tidak melakukan hal apa-apa sehingga kelas Provinsi
tsb sebenarnya tidak mengalami perubahan apa-apa. Sekarang mari kita ubah sedikit contoh di atas.
class MetaKabupaten(type):
def __new__(cls, name, bases, attrs):
attrs['nama_kelas'] = name
return type.__new__(cls, name, bases, attrs)
class Kabupaten(object):
__metaclass__ = MetaKabupaten
Kali ini kita mengubah dict attrs
dengan menambahkan sebuah isian dengan kunci nama_kelas
yang berisi nilai dari variabel name
, yaitu nama kelas yang baru saja dibuat yaitu Kabupaten
.
Apa bedanya contoh pertama dengan kedua? mari kita buat objek dari kelas Provinsi
dan Kabupaten
yang sudah dideklarasikan di atas.
p = Provinsi()
print p.nama_kelas
Andai kode di atas dijalankan, maka Python akan mengatakan bahwa atribut nama_kelas
tidak ditemukan pada objek dari kelas Provinsi
AttributeError: 'Provinsi' object has no attribute 'nama_kelas'
Alasannya sudah jelas karena pada saat mendeklarasikan kelas Provinsi
(maupun saat menginisialisasi kelas tersebut), kita tidak pernah membuat sebuah instance variable bernama nama_kelas
. Sekarang kita coba dengan kelas Kabupaten
k = Kabupaten()
print k.nama_kelas
Bukan sulap bukan sihir, Python dapat dijalankan sampai selesai dan kita mendapat sebuah keluaran di layar seperti berikut.
Kabupaten
Keluaran tersebut dihasilkan dari k.nama_kelas
yaitu nilai sebuah instance variable bernama nama_kelas
dari objek k
yang dibangun dari kelas Kabupaten
. Kapan instance variable nama_kelas
tersebut hadir? Jawabannya adalah pada saat kelas Kabupaten
tersebut dibuat, lebih tepatnya dilakukan oleh metaclass MetaKabupaten
.
Sekian dulu pengantar penggunaan Metaclass dari saya. Lain kali saya contohkan ilmu hitam yg sedang saya pakai maupun contoh pe(nyalah)gunaan lainnya. Contoh kode bisa dilihat di https://gist.github.com/814525
Ternyata metaclass di Python itu sangat menarik karena kita bisa melakukan ilmu hitam π Salah satu ilmu hitam yang sedang/akan saya pakai adalah membuat deklarasi kelas yang baru saja dibuat menjadi lenyap secara tiba2 dan alih-alih kita mendapatkan sebuah objek/instance dari kelas tersebut.
Mengapa saya memerlukan sesuatu yang “aneh” seperti ini? Singkatnya saya sedang membuat semacam domain specific language yang berbasiskan Python. Dengan menggunakan trik sulap di atas, saya bisa membuat bahasa yang saya buat tadi menjadi lebih mudah dipakai. Maaf kalo saya blom bisa menjelaskan dg lebih detail.
Di Python, metaclass bisa digambarkan sebagai sebuah kode yang dijalankan saat sebuah kelas dideklarasikan. Dengan demikian kita bisa mengutak atik wujud sebuah kelas sebelum kelas teresebut siap dipakai. Contoh penggunaan dasarnya adalah sebagai berikut.
class MetaProvinsi(type):
def __new__(cls, name, bases, attrs):
return type.__new__(cls, name, bases, attrs)
class Provinsi(object):
__metaclass__ = MetaProvinsi
Pada contoh di atas, ada sebuah kelas bernama Provinsi
yang memiliki metaclass MetaProvinsi
. Kode pada fungsi __new__
di atas adalah bagian kode yang dapat diisi untuk mengubah wujud kelas Provinsi
. Fungsi ini menerima 4 buah parameter yaitu:
cls
yang menunjuk ke diri sendiri (yaitu MetaProvinsi
),
name
yang berisi nama kelas yang dibuat (dalam contoh di atas, name
berisi Provinsi),
bases
yang berisi daftar kelas yang diturunkan oleh si Provinsi
(yaitu object
), dan
attrs
yang berupa sebuah dictionary berisi daftar atribut (instance variables dan/atau methods) dari Provinsi
.
Metaclass pada contoh di atas masih tidak melakukan hal apa-apa sehingga kelas Provinsi
tsb sebenarnya tidak mengalami perubahan apa-apa. Sekarang mari kita ubah sedikit contoh di atas.
class MetaKabupaten(type):
def __new__(cls, name, bases, attrs):
attrs['nama_kelas'] = name
return type.__new__(cls, name, bases, attrs)
class Kabupaten(object):
__metaclass__ = MetaKabupaten
Kali ini kita mengubah dict attrs
dengan menambahkan sebuah isian dengan kunci nama_kelas
yang berisi nilai dari variabel name
, yaitu nama kelas yang baru saja dibuat yaitu Kabupaten
.
Apa bedanya contoh pertama dengan kedua? mari kita buat objek dari kelas Provinsi
dan Kabupaten
yang sudah dideklarasikan di atas.
p = Provinsi()
print p.nama_kelas
Andai kode di atas dijalankan, maka Python akan mengatakan bahwa atribut nama_kelas
tidak ditemukan pada objek dari kelas Provinsi
AttributeError: 'Provinsi' object has no attribute 'nama_kelas'
Alasannya sudah jelas karena pada saat mendeklarasikan kelas Provinsi
(maupun saat menginisialisasi kelas tersebut), kita tidak pernah membuat sebuah instance variable bernama nama_kelas
. Sekarang kita coba dengan kelas Kabupaten
k = Kabupaten()
print k.nama_kelas
Bukan sulap bukan sihir, Python dapat dijalankan sampai selesai dan kita mendapat sebuah keluaran di layar seperti berikut.
Kabupaten
Keluaran tersebut dihasilkan dari k.nama_kelas
yaitu nilai sebuah instance variable bernama nama_kelas
dari objek k
yang dibangun dari kelas Kabupaten
. Kapan instance variable nama_kelas
tersebut hadir? Jawabannya adalah pada saat kelas Kabupaten
tersebut dibuat, lebih tepatnya dilakukan oleh metaclass MetaKabupaten
.
Sekian dulu pengantar penggunaan Metaclass dari saya. Lain kali saya contohkan ilmu hitam yg sedang saya pakai maupun contoh pe(nyalah)gunaan lainnya. Contoh kode bisa dilihat di https://gist.github.com/814525
Teka teki Python babak 2, masih ada hubungannya dg babak pertama.
Salah satu cara cepat untuk membuat list dengan n
elemen adalah dg menggunakan operator kali seperti contoh di bawah
>>> [1] * 5
[1, 1, 1, 1, 1]
>>> [None] * 5
[None, None, None, None, None]
Pada list yang dihasilkan, tentu saja kita bisa ngubah nilai elemennya
>>> a = [1] * 5
>>> a
[1, 1, 1, 1, 1]
>>> a[3] = 200
>>> a
[1, 1, 1, 200, 1]
Nah.. coba perhatikan kode berikut dan tebak apa nilai akhir dari variabel b
>>> b = [{}] * 5
>>> b
[{}, {}, {}, {}, {}]
>>> b[0]['a'] = 100
Teka teki Python babak 2, masih ada hubungannya dg babak pertama.
Salah satu cara cepat untuk membuat list dengan n
elemen adalah dg menggunakan operator kali seperti contoh di bawah
>>> [1] * 5
[1, 1, 1, 1, 1]
>>> [None] * 5
[None, None, None, None, None]
Pada list yang dihasilkan, tentu saja kita bisa ngubah nilai elemennya
>>> a = [1] * 5
>>> a
[1, 1, 1, 1, 1]
>>> a[3] = 200
>>> a
[1, 1, 1, 200, 1]
Nah.. coba perhatikan kode berikut dan tebak apa nilai akhir dari variabel b
>>> b = [{}] * 5
>>> b
[{}, {}, {}, {}, {}]
>>> b[0]['a'] = 100
Coba compile dan jalanin kode berikut di otak. Jangan nyontek!
class Kantong(object):
def __init__(self, data=[]):
self.data = data
def add_data(self, angka):
self.data.append(angka)
def cetak(self):
print 'Daftar angka:', self.data
satu = Kantong()
satu.add_data(1)
satu.add_data(2)
satu.cetak()
dua = Kantong()
dua.add_data(3)
dua.add_data(4)
dua.add_data(5)
dua.cetak()
Apa keluarannya?
Sekarang coba pake interpreter python
yang Anda punya. Jalankan dan lihat apa keluarannya.
Sama kah dengan yang Anda pikirkan sebelumnya? Kalau sama, selamat!. Kalau beda, coba cari tau apa masalahnya π
Coba compile dan jalanin kode berikut di otak. Jangan nyontek!
class Kantong(object):
def __init__(self, data=[]):
self.data = data
def add_data(self, angka):
self.data.append(angka)
def cetak(self):
print 'Daftar angka:', self.data
satu = Kantong()
satu.add_data(1)
satu.add_data(2)
satu.cetak()
dua = Kantong()
dua.add_data(3)
dua.add_data(4)
dua.add_data(5)
dua.cetak()
Apa keluarannya?
Sekarang coba pake interpreter python
yang Anda punya. Jalankan dan lihat apa keluarannya.
Sama kah dengan yang Anda pikirkan sebelumnya? Kalau sama, selamat!. Kalau beda, coba cari tau apa masalahnya π