Python and JavaScript, side by side

This Jupyter Notebook is intended for a live presentation to Pythonistas - links and contact on hozak.info .

Intro

Working in the functional programming style feels very similar in both Python and JavaScript. Differences are mostly in the syntax and in the problems being solved (think Selenium). In contrast, Java, C++, TypeScript, or Python full of Abstract Base Classes have a different mindset, despite syntax similarities with JS.

Following is a selection of topics that I found useful, interesting, or new. It's not an introduction to either language nor a complete guide of anything.

Installation

Installation of command line interface interpreters is similar:

  • official download from python.org vs nodejs.org
  • conda install python=3.8 vs conda install nodejs=14
  • brew install python@3.8 vs brew install node@14
  • pyenv install 3.8 vs nvm install 14
  • or 2 in 1 docker pull nikolaik/python-nodejs 😀

In the browser

Support of online tools like repl.it, cloud environments like AWS lambdas and many other environments or containers is comparable.

JavaScript is also available natively in browsers, mostly consistent between browsers that are released these days. No such luck if you have to support older browsers (think Python 2 in corporate environments). In the browser, JS code has access to a lot of WebAPIs, e.g. document.getElementById(), which are NOT part of the language itself. Due to the fact that new browsers need to support old web pages written in old JS, the language is full of historical accidents that cannot be removed from the spec, but no one is forcing developers to use the deprecated features in new projects (think COBOL in corporate environments).

JS console is easily available in desktop browsers' Dev Tools, usually F12 or a right mouse click > Inspect Element > Console tab.

Finding answers to most questions

  1. google (or duckduckgo) python {topic} vs js {topic}
  2. most reliable results from python.org vs mozilla.org ("MDN")
  3. stackoverflow.com
  4. ask someone

Hello, world!

In [1]:
%%python
from sys import stderr

print('Hello, world!')
print('Hello, stderr!', file=stderr)
Hello, world!
Hello, stderr!
In [2]:
%%script node


console.log('Hello, world!')
console.error('Hello, stderr!')
Hello, world!
Hello, stderr!

Numbers

In [3]:
%%python
log = print

log(1 + 2 * 3)
log(1 + int('2'))
log(0.1 + 0.2)

import math
log(math.pi)
log(1 + 2j)
7
3
0.30000000000000004
3.141592653589793
(1+2j)
In [4]:
%%script node
log = console.log

log(1 + 2 * 3)
log(1 + Number('2'))
log(0.1 + 0.2)


log(Math.PI)
// no complex numbers without library, e.g. mathjs.org
7
3
0.30000000000000004
3.141592653589793

Strings

In [5]:
%%python
log = print

log('a' + 'b' 'c')
log('abc'.upper())
log(len('abc'))
log('a' * 3)

log('abcd'[0])
log('abcd'[-2])

log('efgh'[1:3])
log('ijkl'[:-1])
log('mnop'[1:])
log('qrst'[-3:][:2])
abc
ABC
3
aaa
a
c
fg
ijk
nop
rs
In [6]:
%%script node
log = console.log

log('a' + 'b' + 'c')
log('abc'.toUpperCase())
log('abc'.length)
log('a'.repeat(3))

log('abc'[0])
log('abcd'['abcd'.length - 2])

log('efgh'.slice(1, 3))
log('ijkl'.slice(0, -1))
log('mnop'.slice(1))
log('qrst'.substr(-3, 2))
abc
ABC
3
aaa
a
c
fg
ijk
nop
rs
In [7]:
%%python
log = print

a = 1
log(f'value was {a + 1}')
log(f'{a=}')
value was 2
a=1
In [8]:
%%script node
log = console.log

let a = 1
log(`value was ${a + 1}`)
log({a})
value was 2
{ a: 1 }
In [9]:
%%python
log = print
import re

log(re.match(r'b', 'abcb'))
log(re.search(r'b', 'abcb'))
log(re.findall(r'b', 'abcb'))
None
<re.Match object; span=(1, 2), match='b'>
['b', 'b']
In [10]:
%%script node
log = console.log


log('abcb'.match(/^b/))
log('abcb'.match(/b/))
log('abcb'.match(/b/g))
null
[ 'b', index: 1, input: 'abcb', groups: undefined ]
[ 'b', 'b' ]

Lists / Arrays

In [11]:
%%python
log = print

a, b = [1, 2], [3, 4]

log(a + b + [5])
log(str(a) + str(b) + str(5))

second_a = a
second_a.append('mutable')

copied_a = a.copy()
copied_a.append('different')

log(a)
[1, 2, 3, 4, 5]
[1, 2][3, 4]5
[1, 2, 'mutable']
In [12]:
%%script node
log = console.log

let [a, b] = [[1, 2], [3, 4]]

log([...a, ...b, 5])
log(a + b + 5)

let secondA = a
secondA.push('mutable')

let copiedA = [...a]
copiedA.push('different')

log(a)
[ 1, 2, 3, 4, 5 ]
1,23,45
[ 1, 2, 'mutable' ]
In [13]:
%%python
log = print

log( list('abc') )

log( [c.upper() for c in 'abc'] )
log( [i + 1 for i in [1,2,3]] )
log( [i for i in [1,2,3] if i % 2] )

log( ';'.join(str(i) for i in [1,2,3]))
['a', 'b', 'c']
['A', 'B', 'C']
[2, 3, 4]
[1, 3]
1;2;3
In [14]:
%%script node
log = console.log

log( Array.from('abc') )

log( Array.from('abc').map(c => c.toUpperCase()) )
log( [1,2,3].map(i => i + 1) )
log( [1,2,3].filter(i => i % 2) )

log( [1,2,3].join(';') )
[ 'a', 'b', 'c' ]
[ 'A', 'B', 'C' ]
[ 2, 3, 4 ]
[ 1, 3 ]
1;2;3

Dictionaries / Objects

In [15]:
%%python
log = print

a = {'b': 2, 'c': 'b'}

log(a)
log(a['b'])
log(a[a['c']])
log(a.get('f', None))
log(a.get('f', {}).get('g', 'default'))
{'b': 2, 'c': 'b'}
2
2
None
default
In [16]:
%%script node
log = console.log

let a = {b: 2, c: 'b'}

log(a)
log(a.b)
log(a[a.c])
log(a.f)
log(a.f?.g ?? 'default')
{ b: 2, c: 'b' }
2
2
undefined
default
In [17]:
%%python
log = print

a = {'inner': 1}
b = {'a': a}
log(b)

b['a'][2] = 3
log(a)
{'a': {'inner': 1}}
{'inner': 1, 2: 3}
In [18]:
%%script node
log = console.log

let a = {'inner': 1}
let b = {a}
log(b)

b.a[2] = 3
log(a)
{ a: { inner: 1 } }
{ '2': 3, inner: 1 }

If conditions

In [19]:
%%python
log = print

a, b, c, d = 1, 2, 2, False

if a == b and a == c or not d:
    log('yes')
else:
    log('no')

log('yes' if a == b else 'no')
yes
no
In [20]:
%%script node
log = console.log

let a = 1, b = 2, c = 2, d = false

if (a === b && a === c || !d) {
    log('yes')
} else {
    log('no')
}
log(a == b ? 'yes' : 'no')
yes
no

For loops

In [21]:
%%python
log = print

for i in range(2, 6, 2):
    log(i)

out = ''
for item in ['a', 1, 'b']:
    if type(item) == str: out += item
    out += '.'

log(out)
2
4
a..b.
In [22]:
%%script node
log = console.log

for (let i = 2; i < 6; i += 2) {
    log(i)
}
let out = ''
for (let item of ['a', 1, 'b']) {
    if (typeof item === 'string') out += item
    out += '.'
}
log(out)
2
4
a..b.

Functions, variables and scope

In [23]:
%%python
log = print

def first(a, b="b", c=None, *args):
    second(a)

def second(b):
    log(b)

first(1)
1
In [24]:
%%script node
log = console.log

function first(a, b="b", c, ...rest) {
    second(a)
}
const second = function second(b) {
    log(b)
}
first(1)
1
In [25]:
%%python
log = print

def f(a, **kwargs):
    log(kwargs)

c = {'d': 3}

f(a=1, b=2, **c)
{'b': 2, 'd': 3}
In [26]:
%%script node
log = console.log

function f({a, ...spread}) {
    log(spread)
}
c = {d: 3}

f({a: 1, b: 2, ...c})
{ b: 2, d: 3 }
In [27]:
%%python
log = print

points = [(1, 2), (2, 1)]

points.sort(key=lambda point: point[1])
log(points)
[(2, 1), (1, 2)]
In [28]:
%%script node
log = console.log

const points = [{x: 1, y: 2}, {x: 2, y: 1}]

points.sort((a, b) => a.y  - b.y)
log(points)
[ { x: 2, y: 1 }, { x: 1, y: 2 } ]
In [31]:
%%python
log = print

a = 1
b = 2



def nasty():
    a = 5    # different 'a'
    global b
    b = 6

nasty()
log(a, b)
1 6
In [32]:
%%script node
log = console.log

var a = 1   // function scope
b = 2       // global scope
let c = 3   // block scope reassignable
const d = 4 // block non-reassignable (may be mutable)

function nasty() {
    var a = 5
    b = 6
    c = 7
}
nasty()
log(a, b, c, d)
1 6 7 4

Closure

In [33]:
%%python
log = print

def outer(a):
    return lambda b: log(a, b)

inner = outer(1)
outer(2)(3)
inner(4)
2 3
1 4
In [34]:
%%script node
log = console.log

function outer(a) {
    return (b) => log(a, b)
}
const inner = outer(1)
outer(2)(3)
inner(4)
2 3
1 4

Generators

In [35]:
%%python
log = print

def my_generator():
    yield 'header'
    yield from [1, 2, 3]

my_iterator = my_generator()

log(next(my_iterator))
for record in my_iterator:
    log(record)
header
1
2
3
In [36]:
%%script node
log = console.log

function* myGenerator() {
    yield 'header'
    yield* [1, 2, 3]
}
let myIterator = myGenerator()

log(myIterator.next().value)
for (const record of myIterator) {
    log(record)
}
header
1
2
3

Async, await

In [37]:
%%python
log = print
from asyncio import sleep, gather, run
from random import random

async def race_condition(n):
    await sleep(random())
    log(n)

async def main():
    await gather(race_condition(1),
                 race_condition(2),
                 race_condition(3))
run(main())
3
1
2
In [50]:
%%script node
log = console.log
const sleep = (t) => new Promise(resolve =>
    setTimeout(resolve, t * 1000))

async function raceCondition(n) {
    await sleep(Math.random())
    log(n)
}

raceCondition(1)
raceCondition(2)
raceCondition(3)
2
3
1

Exceptions

In [39]:
%%python
log = print
a = {"b": 1}

try:
    a["c"]
    raise NotImplementedError('TODO later')
except KeyError as err:
    
    log(err)
finally:
    log('be happy')
'c'
be happy
In [40]:
%%script node
log = console.log
const a = {b: 1}

try {
    a.c.d
    throw new Error('TODO later')
} catch (err) {
    if (!err instanceof TypeError) throw err
    log(err.message)
} finally {
    log('be happy')
}
Cannot read property 'd' of undefined
be happy

Linters

  • pep8 vs eslint

Dependency management

External

In [54]:
%%sh
exit 0

virtualenv venv
source ./venv/bin/activate

touch requirements.txt
pip install -r requirements.txt
pip freeze > requirements-lock.txt
In [55]:
%%sh
exit 0




npm init     # or `touch package.json`
npm install  # or `yarn`

Local

In [41]:
%%writefile utils.py
log = print

def hello(): log('hello')
Overwriting utils.py
In [42]:
%%writefile utils.mjs
const log = console.log

export const hello = () => log('hello')
Overwriting utils.mjs
In [43]:
%%writefile app.py


from utils import hello
hello()
Overwriting app.py
In [44]:
%%writefile app.mjs


import {hello} from './utils.mjs'
hello()
Overwriting app.mjs
In [45]:
%%sh
python app.py
hello
In [46]:
%%sh
node app.mjs
hello

CSS formatting of this Notebook

In [47]:
%%html
<style>
@media (min-width: 1000px) {
  #notebook-container {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }
}

#notebook-container {
    width: 100%;
    max-width: 1200px;
    padding-left: 0;
}

div.text_cell {
    grid-column: 1 / span 2;
    margin-bottom: 1em;
}

.prompt {
    min-width: 60px;
    font-size: 10px;
}

div.input_area {
    background:
     linear-gradient(to bottom, #eef 3em, #f7f7f7 3em);
}
</style>
In [48]:
%%js

require(['notebook/js/codecell'], (codecell) => {
  codecell.CodeCell.options_default.highlight_modes
    .magic_javascript = 
      {reg: [/^%%(?:js|javascript|script node)/]}
  Jupyter.notebook.events.one('kernel_ready.Kernel', () => {
    Jupyter.notebook.get_cells().map((cell) => {
      if (cell.cell_type == 'code'){
          cell.auto_highlight()
      }})
    })
})