Writing an Idiomatic Python Code

a.k.a. Pythonic

prepared and presented by Boedy for PyConID 2019
[email protected]

What is Idiomatic Python?

The Zen of Python

In [1]:
import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

[Conventional] Swapping Values Between Variables

In [2]:
var_a = 10
var_b = 20
print(f'var_a={var_a}, var_b={var_b}')

var_tmp = var_a
var_a = var_b
var_b = var_tmp
print(f'var_a={var_a}, var_b={var_b}')
var_a=10, var_b=20
var_a=20, var_b=10

[Pythonic] Swapping Values Between Variables (1/2)

In [3]:
var_a = 10
var_b = 20
print(f'var_a={var_a}, var_b={var_b}')

var_a, var_b = var_b, var_a
print(f'var_a={var_a}, var_b={var_b}')
var_a=10, var_b=20
var_a=20, var_b=10

[Pythonic] Swapping Values Among Variables (2/2)

In [4]:
var_a = 10
var_b = 20
var_c = 30
var_d = 40
print(f'var_a={var_a}, var_b={var_b}, var_c={var_c}, var_d={var_d}')

var_a, var_b, var_c, var_d = var_c, var_b, var_d, var_a
print(f'var_a={var_a}, var_b={var_b}, var_c={var_c}, var_d={var_d}')
var_a=10, var_b=20, var_c=30, var_d=40
var_a=30, var_b=20, var_c=40, var_d=10

[Conventional] Chained Comparison

In [5]:
LOWER = 20
UPPER = 30
a = 25
print(a >= LOWER and a <= UPPER)
True

[Pythonic] Chained Comparison

In [5]:
LOWER = 10
UPPER = 30
a = 25
print(LOWER <= a <= UPPER)
True

[Conventional] Values Packing

In [7]:
a = 10
b = 20
c = 30
print(f'a={a}, b={b}, c={c}')

p = [None, None, None]
p[0] = a
p[1] = b
p[2] = c
print(f'p={p}')
a=10, b=20, c=30
p=[10, 20, 30]

[Pythonic] Values Packing

In [8]:
x, y, z = 10, 20, 30
print(f'x={x}, y={y}, z={z}')

p = x, y, z
print(f'p={p}')
x=10, y=20, z=30
p=(10, 20, 30)

[Conventional] Values Unpacking

In [9]:
p = [10, 20, 30]
print(f'p={p}')

a = p[0]
b = p[1]
c = p[2]
print(f'a={a}, b={b}, c={c}')
p=[10, 20, 30]
a=10, b=20, c=30

[Pythonic] Values Unpacking

In [10]:
p = [10, 20, 30]
print(f'p={p}')

x, y, z = p
print(f'x={x}, y={y}, z={z}')
p=[10, 20, 30]
x=10, y=20, z=30

[Conventional] For Loop

In [11]:
list_ku = [10, 20, 30]

for i in range(len(list_ku)):
    print(list_ku[i])
10
20
30

[Pythonic] For Loop

In [12]:
list_ku = [10, 20, 30]

for item in list_ku:
    print(item)
10
20
30

[Conventional] For Loop with Index

In [13]:
nims  = ['001', '002', '003']
namas = ['bejo', 'karti', 'tejo']

for i in range(len(nims)):
    print(f'nim:{nims[i]}\t nama:{namas[i]}')
nim:001	 nama:bejo
nim:002	 nama:karti
nim:003	 nama:tejo

[Pythonic] For Loop with Index (enumerate)

In [14]:
nims  = ['001', '002', '003']
namas = ['bejo', 'karti', 'tejo']

for i, nim in enumerate(nims):
    print(f'nim:{nim}\t nama:{namas[i]}')
nim:001	 nama:bejo
nim:002	 nama:karti
nim:003	 nama:tejo

[Conventional] For Loop over Multiple Sequence Data Types

In [15]:
nims  = ['001', '002', '003']
namas = ['bejo', 'ani', 'tejo']

for i in range(len(nims)):
    print(f'nim:{nims[i]}\t nama:{namas[i]}')
nim:001	 nama:bejo
nim:002	 nama:ani
nim:003	 nama:tejo

[Pythonic] For Loop over Multiple Sequence Data Types (1/2)

In [16]:
nims  = ['001', '002', '003']
namas = ['bejo', 'ani', 'tejo']

for nim, nama in zip(nims, namas):
    print(f'nim:{nim}\t nama:{nama}')
nim:001	 nama:bejo
nim:002	 nama:ani
nim:003	 nama:tejo

[Pythonic] For Loop over Multiple Sequence Data Types (2/2)

In [17]:
nims    = ['001', '002', '003']
namas   = ['bejo', 'ani', 'tejo']
hobbies = ['soccer', 'swimming', 'hiking']

for nim, nama, hobby in zip(nims, namas, hobbies):
    print(f'nim:{nim}\t nama:{nama}\t hobby:{hobby}')
nim:001	 nama:bejo	 hobby:soccer
nim:002	 nama:ani	 hobby:swimming
nim:003	 nama:tejo	 hobby:hiking

[Conventional] Mapping

In [18]:
def lipat_ganda(x):
    return x*2

listku = [10, 20, 30]
listmu = []

for item in listku:
    listmu.append(lipat_ganda(item))

print(f'listku={listku}')
print(f'listmu={listmu}')
listku=[10, 20, 30]
listmu=[20, 40, 60]

[Functional] Mapping

In [19]:
listku = [10, 20, 30]
listmu = list(map(lipat_ganda, listku))

print(f'listku={listku}')
print(f'listmu={listmu}')
listku=[10, 20, 30]
listmu=[20, 40, 60]

[Functional] Mapping + Lambda Expression

In [20]:
listku = [10, 20, 30]
listmu = list(map(lambda x: x*2, listku))

print(f'listku={listku}')
print(f'listmu={listmu}')
listku=[10, 20, 30]
listmu=[20, 40, 60]

[Pythonic] Comprehension

In [21]:
listku = [10, 20, 30]
listmu = [x*2 for x in listku]

print(f'listku={listku}')
print(f'listmu={listmu}')
listku=[10, 20, 30]
listmu=[20, 40, 60]

[Conventional] Filtering

In [22]:
listmu = [30, 60, 10, 55]
listku = []

for nilai in listmu:
    if nilai > 50:
        listku.append(nilai)
        
print(f'listku={listku}')
listku=[60, 55]

[Functional] Filtering

In [23]:
def saring(x):
    if x > 50:
        return True
    return False

listmu = [30, 60, 10, 55]
listku = []

listku = list(filter(saring, listmu))

print(f'listku={listku}')
listku=[60, 55]

[Functional] Filtering + Lambda Expression

In [24]:
listmu = [30, 60, 10, 55]
listku = []

listku = list(filter(lambda x: True if x>50 else False, listmu))

print(f'listku={listku}')
listku=[60, 55]

[Pythonic] Comprehension

In [25]:
listmu = [30, 60, 10, 55]
listku = [nilai for nilai in listmu if nilai > 50]

print(f'listku={listku}')
listku=[60, 55]

[Conventional] Merging Multiple Sequence Data Type

In [26]:
nama = ['bejo', 'karti', 'tejo']
usia = [23, 31, 26]
listku = []

for i in range(len(nama)):
    listku.append([nama[i], usia[i]])

print(f'listku={listku}')
listku=[['bejo', 23], ['karti', 31], ['tejo', 26]]

[Pythonic] Merging Multiple Sequence Data Type

In [27]:
listku = [[d_nama, d_usia] for d_nama, d_usia in zip(nama, usia)]

print(f'listku={listku}')
listku=[['bejo', 23], ['karti', 31], ['tejo', 26]]

[Conventional] Slicing a Sequence Data Type

In [28]:
listmu = [10, 20, 30, 40, 50, 60]
listku = []

for i in range(2, 5):
    listku.append(listmu[i])

print(f'listmu={listmu}')
print(f'listku={listku}')
listmu=[10, 20, 30, 40, 50, 60]
listku=[30, 40, 50]

[Pythonic] Slicing a Sequence Data Type

In [29]:
listmu = [10, 20, 30, 40, 50, 60]
print(f'listmu={listmu}')

listku = listmu[2:5]
print(f'listku={listku}')

listku = listmu[4:]
print(f'listku={listku}')

listku = listmu[:3]
print(f'listku={listku}')

kata = 'PEMBELAJARAN'
print(kata[3:10])
listmu=[10, 20, 30, 40, 50, 60]
listku=[30, 40, 50]
listku=[50, 60]
listku=[10, 20, 30]
BELAJAR

[Conventional] Reversing a Sequence Data Type

In [30]:
listmu = [10, 20, 30, 40, 50, 60]
listku = []

for i in range(len(listmu)):
    reverse_i = len(listmu) - 1 - i
    listku.append(listmu[reverse_i])
    
print(f'listmu={listmu}')
print(f'listku={listku}')
listmu=[10, 20, 30, 40, 50, 60]
listku=[60, 50, 40, 30, 20, 10]

[Pythonic] Reversing a Sequence Data Type

In [31]:
listmu = [10, 20, 30, 40, 50, 60]
print(f'listmu={listmu}')

listku = listmu[::-1]
print(f'listku={listku}')

kata = 'BELAJAR'
print(f'kata={kata}')
print(f'kata={kata[::-1]}')
listmu=[10, 20, 30, 40, 50, 60]
listku=[60, 50, 40, 30, 20, 10]
kata=BELAJAR
kata=RAJALEB

[Conventional] Splitting a String

In [32]:
teks   = 'belajar python dasar'
listku = []
kata   = ''

for huruf in teks:
    if huruf != ' ':
        kata += huruf
    else:
        listku.append(kata)
        kata = ''

listku.append(kata)    

print(f'teks  : {teks}')
print(f'listku: {listku}')
teks  : belajar python dasar
listku: ['belajar', 'python', 'dasar']

[Pythonic] Splitting a String (1/2)

In [33]:
teks = 'belajar python dasar'
listku = teks.split()

print(f'teks  : {teks}')
print(f'listku: {listku}')
teks  : belajar python dasar
listku: ['belajar', 'python', 'dasar']

[Pythonic] Splitting a String (2/2)

In [34]:
teks = 'belajar:python:dasar'
listku = teks.split(':')

print(f'teks  : {teks}')
print(f'listku: {listku}')
teks  : belajar:python:dasar
listku: ['belajar', 'python', 'dasar']

[Conventional] String Concatenation

In [35]:
listku = ['belajar', 'python', 'dasar']
kalimat = ''

for kata in listku:
    kalimat += kata + ' '

print(f'listku : {listku}')
print(f'kalimat: {kalimat}')
listku : ['belajar', 'python', 'dasar']
kalimat: belajar python dasar 

[Pythonic] String Concatenation

In [36]:
listku = ['belajar', 'python', 'dasar']
kalimat = ' '.join(listku)

print(f'listku : {listku}')
print(f'kalimat: {kalimat}')
listku : ['belajar', 'python', 'dasar']
kalimat: belajar python dasar

[Conventional] Switch Case

In [37]:
def one():
    print('satu-one')

def two():
    print('dua-two')

def three():
    print('tiga-three')

case = 'dua'

if case == 'satu':
    one()
elif case == 'dua':
    two()
elif case == 'tiga':
    three()
dua-two

[Pythonic] Switch Case

In [38]:
def one():
    print('satu-one')

def two():
    print('dua-two')

def three():
    print('tiga-three')

case = 'dua'

switch = {
    'satu': one,
    'dua': two,
    'tiga': three
}

switch[case]()
dua-two

[Conventional] List Concatenation

In [39]:
a = [10, 20, 30]
b = [40, 50, 60]

print(f'a={a}')
print(f'b={b}')

c = [None] * (len(a) + len(b))

for i in range(len(a)):
    c[i] = a[i]
    
for i in range(len(b)):
    c[i+len(a)] = b[i]
    
print(f'c={c}')
a=[10, 20, 30]
b=[40, 50, 60]
c=[10, 20, 30, 40, 50, 60]

[Pythonic] List Concatenation

In [40]:
a = [10, 20, 30]
b = [40, 50, 60]

print(f'a={a}')
print(f'b={b}')

c = a + b
print(f'c={c}')
a=[10, 20, 30]
b=[40, 50, 60]
c=[10, 20, 30, 40, 50, 60]

[Conventional] Ternary Expression

In [41]:
a = 10
if a < 0:
    b = 'negatif'
else:
    b = 'positif'
    
print(f'{a} bernilai {b}')
10 bernilai positif

[Pythonic] Ternary Expression

In [42]:
a = 10
b = 'negatif' if a < 0 else 'positif'

print(f'{a} bernilai {b}')
10 bernilai positif

[Conventional] Unused Variables

In [43]:
for i in range(3):
    print('hello world')
hello world
hello world
hello world

[Pythonic] Don't Care Character (1/3)

In [44]:
for _ in range(3):
    print('hello world')
hello world
hello world
hello world

[Pythonic] Don't Care Character (2/3)

In [45]:
x = [10, 20, 30, 40]
a, b, *_ = x

print(f'a:{a}, b:{b}')
a:10, b:20

[Pythonic] Don't Care Character for DIgital Placeholder (3/3)

In [46]:
gaji = 5_000_000

print(f'gaji: {gaji}')
gaji: 5000000

[Conventional] Searching in a Sequence Data Type

In [1]:
listku = [10, 20, 30, 40, 50]
dicari = 30
ketemu = False

for x in listku:
    if x == dicari:
        ketemu = True
        break
        
if ketemu:
    print('ditemukan')
else:
    print('tidak ditemukan')
ditemukan

[Pythonic] in Operator

In [48]:
listku = [10, 20, 30, 40, 50]
dicari = 30

if dicari in listku:
    print('ditemukan')
else:
    print('tidak ditemukan')
ditemukan

[Conventional] Function with Multiple Return Values

In [49]:
def fungsiku():
    nim = '007'
    nama = 'asep'
    ipk = 1.25
    siswa = [nim, nama, ipk]
    return siswa

hasil = fungsiku()

print(f'hasil1: {hasil[0]}')
print(f'hasil2: {hasil[1]}')
print(f'hasil2: {hasil[2]}')
hasil1: 007
hasil2: asep
hasil2: 1.25

[Pythonic] Function with Multiple Return Values

In [50]:
def fungsimu():
    nim = '007'
    nama = 'asep'
    ipk = 1.25
    return nim, nama, ipk

a, b, c = fungsimu()

print(f'hasil1: {a}')
print(f'hasil2: {b}')
print(f'hasil2: {c}')
hasil1: 007
hasil2: asep
hasil2: 1.25

[Conventional] Aggregating Values from Multiple Sequence Data Types

In [51]:
nim = ['001', '002', '003']
nama = ['bejo', 'karti', 'tejo']
ipk = [3.25, 2.33, 1.88]
siswa = []

for i in range(len(nim)):
    siswa.append((nim[i], nama[i], ipk[i]))

print(f'siswa: {siswa}')
siswa: [('001', 'bejo', 3.25), ('002', 'karti', 2.33), ('003', 'tejo', 1.88)]

[Pythonic] zip Function

In [52]:
nim = ['001', '002', '003']
nama = ['bejo', 'karti', 'tejo']
ipk = [3.25, 2.33, 1.88]

siswa = list(zip(nim, nama, ipk))

print(f'siswa: {siswa}')
siswa: [('001', 'bejo', 3.25), ('002', 'karti', 2.33), ('003', 'tejo', 1.88)]

[Conventional] Extracting Values from Aggregated Sequence Data Types

In [53]:
siswa = [('001', 'bejo', 3.25),
         ('002', 'karti', 2.33),
         ('003', 'tejo', 1.88)]
nim, nama, ipk = [], [], []

for data in siswa:
    nim.append(data[0])
    nama.append(data[1])
    ipk.append(data[2])

print(f'nim : {nim}')
print(f'nama: {nama}')
print(f'ipk : {ipk}')
nim : ['001', '002', '003']
nama: ['bejo', 'karti', 'tejo']
ipk : [3.25, 2.33, 1.88]

[Pythonic] "unzip" Function

In [54]:
siswa = [('001', 'bejo', 3.25),
         ('002', 'karti', 2.33),
         ('003', 'tejo', 1.88)]

nim, nama, ipk = zip(*siswa)

print(f'nim : {nim}')
print(f'nama: {nama}')
print(f'ipk : {ipk}')
nim : ('001', '002', '003')
nama: ('bejo', 'karti', 'tejo')
ipk : (3.25, 2.33, 1.88)

[Conventional] String Formatting

In [55]:
nama = 'bejo'
usia = 25

print('nama saya ' + nama + ' usia saya ' + str(usia) + ' tahun ')
print('nama saya %s usia saya %d tahun' % (nama, usia))
nama saya bejo usia saya 25 tahun 
nama saya bejo usia saya 25 tahun

[Pythonic] String Formatting (1/3)

In [56]:
nama = 'bejo'
usia = 25

print('nama saya {} usia saya {} tahun'.format(nama, usia))
print('nama saya {1} usia saya {0} tahun'.format(usia, nama))
nama saya bejo usia saya 25 tahun
nama saya bejo usia saya 25 tahun

[Pythonic] Keyword Argument for String Formatting (2/3)

In [57]:
siswa = {
    'nim': '001',
    'nama': 'bejo',
    'usia': 25,
    'ipk': 3.75
}

print('nama:{nama} usia:{usia}'.format(**siswa))
nama:bejo usia:25

[Pythonic] f-string (3/3)

In [58]:
nama = 'bejo'
usia = 25
  
print(f'nama:{nama} usia:{usia}')
nama:bejo usia:25

[Conventional] Merging Dictionaries

In [59]:
data1 = {'nim': '001', 'nama': 'bejo'}
data2 = {'ipk': 3.25, 'semester': 4}
siswa = {}

for kunci in data1:
    siswa[kunci] = data1[kunci]

for kunci in data2:
    siswa[kunci] = data2[kunci]

print(f'data1: {data1}')
print(f'data2: {data2}')
print(f'siswa: {siswa}')
data1: {'nim': '001', 'nama': 'bejo'}
data2: {'ipk': 3.25, 'semester': 4}
siswa: {'nim': '001', 'nama': 'bejo', 'ipk': 3.25, 'semester': 4}

[Pythonic] Merging Dictionaries

In [60]:
data1 = {'nim': '001', 'nama': 'bejo'}
data2 = {'ipk': 3.25, 'semester': 4}
data3 = {'kota': 'Palembang', 'hobi': 'membaca'}

siswa = {**data1, **data2, **data3}

print(f'data1: {data1}')
print(f'data2: {data2}')
print(f'data3: {data3}')
print(f'siswa: {siswa}')
data1: {'nim': '001', 'nama': 'bejo'}
data2: {'ipk': 3.25, 'semester': 4}
data3: {'kota': 'Palembang', 'hobi': 'membaca'}
siswa: {'nim': '001', 'nama': 'bejo', 'ipk': 3.25, 'semester': 4, 'kota': 'Palembang', 'hobi': 'membaca'}

[Conventional] Too Much or Operators

In [61]:
nama = 'bejo'

if nama == 'bejo' or nama == 'tejo' or nama == 'karti':
    print('Siswa teladan')
else:
    print('Siswa reguler')
Siswa teladan

[Pythonic] Replacing or Operators with in Operator

In [62]:
nama = 'bejo'

if nama in ('bejo', 'tejo', 'karti'):
    print('Siswa teladan')
else:
    print('Siswa reguler')
Siswa teladan

[Conventional] String Translation Table

In [63]:
translasi = {
    'a': '4',
    'i': '1',
    'e': '3',
    'o': '0'
}

string_input = 'kota cirebon'
string_output = ''

for x in string_input:
    val = translasi.get(x)
    string_output += val if val else x
    
print(f'hasil konversi: {string_output}')
hasil konversi: k0t4 c1r3b0n

[Pythonic] String Translation Table

In [64]:
sumber = 'aieostb'
tujuan = '4130578'
tabel_translasi = str.maketrans(sumber, tujuan)

string_input = 'bos bajaj'
string_output = string_input.translate(tabel_translasi)

print(f'hasil konversi: {string_output}')
hasil konversi: 805 84j4j

[Conventional] Multiple Assignments

In [65]:
a = 10
b = 10
c = 10

print(f'a:{a}, b:{b}, c:{c}')
a:10, b:10, c:10

[Pythonic] Multiple Assignments

In [66]:
a = b = c = 10

print(f'a:{a}, b:{b}, c:{c}')
a:10, b:10, c:10

[Conventional] Functional Calls only Support Positional Argument

In [67]:
def cetak_nama(f_name, l_name):
    print(f'{f_name} {l_name}')

cetak_nama('Karti', 'Susanti')
cetak_nama('Susanti', 'Karti')
Karti Susanti
Susanti Karti

[Pythonic] Functional Calls also Support Keyword Argument

In [68]:
def cetak_nama(f_name, l_name):
    print(f'{f_name} {l_name}')

cetak_nama(f_name='Karti', l_name='Susanti')
cetak_nama(l_name='Susanti', f_name='Karti')
Karti Susanti
Karti Susanti

[Pythonic] Function with Default Argument

In [69]:
def cetak_nama(f_name, l_name='ajah'):
    print(f'{f_name} {l_name}')
    
cetak_nama('Bejo')
cetak_nama('Karti', 'Susanti')
Bejo ajah
Karti Susanti

[Not So Pythonic] Sorting with sort Method

In [70]:
siswa = ['karti susanti', 'tejo purnawan', 'bejo rahmadi']
print(f'siswa  : {siswa}')

siswa.sort()
print(f'terurut: {siswa}')
siswa  : ['karti susanti', 'tejo purnawan', 'bejo rahmadi']
terurut: ['bejo rahmadi', 'karti susanti', 'tejo purnawan']

[Pythonic] Sorting with sorted Function

In [71]:
siswa = ['karti susanti', 'tejo purnawan', 'bejo rahmadi']
print(f'siswa  : {siswa}')

terurut = sorted(siswa)
print(f'terurut: {terurut}')
siswa  : ['karti susanti', 'tejo purnawan', 'bejo rahmadi']
terurut: ['bejo rahmadi', 'karti susanti', 'tejo purnawan']

[Conventional] Custom Sorting

In [72]:
siswa = ['karti susanti', 'tejo purnawan', 'bejo rahmadi']
print(f'siswa: {siswa}')

for i in range(len(siswa)-1):
    for j in range(i+1, len(siswa)):
        if siswa[i].split()[-1] > siswa[j].split()[-1]:
            siswa[i], siswa[j] = siswa[j], siswa[i]

print(f'terurut: {siswa}')
siswa: ['karti susanti', 'tejo purnawan', 'bejo rahmadi']
terurut: ['tejo purnawan', 'bejo rahmadi', 'karti susanti']

[Pythonic] Custom Sorting (1/2)

In [73]:
def custom_key(nama):
    return nama.split()[-1]

siswa = ['karti susanti', 'tejo purnawan', 'bejo rahmadi']
print(f'siswa: {siswa}')

terurut = sorted(siswa, key=custom_key)
print(f'terurut: {terurut}')
siswa: ['karti susanti', 'tejo purnawan', 'bejo rahmadi']
terurut: ['tejo purnawan', 'bejo rahmadi', 'karti susanti']

[Pythonic] Custom Sorting (2/2)

In [74]:
siswa = ['karti susanti', 'tejo purnawan', 'bejo rahmadi']
print(f'siswa: {siswa}')

terurut = sorted(siswa, key=lambda x: x.split()[-1])
print(f'terurut: {terurut}')
siswa: ['karti susanti', 'tejo purnawan', 'bejo rahmadi']
terurut: ['tejo purnawan', 'bejo rahmadi', 'karti susanti']

And there are many other tricks...

to improve our workflow with Python :)

Other Learning Materials

In [75]:
from IPython.display import YouTubeVideo
YouTubeVideo('BHP3J8akr74')
Out[75]:
In [76]:
YouTubeVideo('3Og1xO15HhQ')
Out[76]:
In [77]:
YouTubeVideo('kwlRZUjzsns')
Out[77]:

Q&A

Questions and Comments are Welcome :)

That's all folks

Thank You.... :)

May The Force be with you....