Einenlum.

This Week I Learned: 2022W30

Tue Aug 02 2022

I’m in holidays so I allow myself to skip a week of learning (sorry week 29!) ^^.

Python - Bytecode - Threads

There was a subtle change in how Python 3.10 produces bytecode. This can lead to normal behaviors when you expect a strange one (yes, this sentence is fucked up). I was trying to reproduce a common Python problem with threads and race conditions and I couldn’t.

Before 3.10, this code was leading to an unexpected result, different from 1000000:

import threading


a = 0
threads = []


def x():
    global a
    for i in range(1_000_000):
        a += 1


for _ in range(10):
    thread = threading.Thread(target=x)
    threads.append(thread)
    thread.start()


for thread in threads:
    thread.join()


print(a) # The number was off here

Starting from 3.10, it does print 1000000.

Replacing a += 1 with a += int(1) brings back the unexpected behavior.

Python - async

The async/await syntax is independent from the asyncio module. Actually you can use different event loop than asyncio like uvloop (which seems faster). (Thanks to my friend Olivier for the explanation!)

SQLite

SQLite keeps a journal to keep the integrity of the database which makes it much slower. You can tweak this behavior if you think it’s not worth it (like on your local machine for development). You can learn more about it on Olivier’s blog.

Databases

EdgeDB is a database working with PostgreSQL under the hood. It allows you to work with objects instead of tables directly in the database. You can then use EdgeQL (instead of SQL) to query it. It also handles migrations and pretends to overcome the use of ORMs. Honestly, it sounds promising. I’m just a bit skeptical about the tooling and clients so far. The Python client looks quite rudimentary (no official client for PHP yet) and I wonder you map the database objects to your models and how you easily update an existing one. Anyway, I’ll keep an eye on this. (Thanks again to Olivier for this discovery!)

Python - Range

Weirdly, the Python range function only accepts integers.

range(0, 10) # Ok
# TypeError: 'float' object cannot be interpreted as an integer
range(0.0, 10.0)

On the other hand, you can omit the first argument of range. In this case it starts at 0.

list(range(0, 10))
# is equivalent to
list(range(10))

Good to know: the limit of the Python range is exclusive. (range(0, 10) goes to 9) where as the PHP one is inclusive (range(0, 10); goes to 10).

PHP - Range

The range function not only accepts integers but also floats and strings.

/*
[
    "a",
    "b",
    ...
    "y",
    "z",
   ]
*/
range('a', 'z');

/*
[
    "A",
    "B",
    ...
    "Y",
    "Z",
]
*/
range('A', 'Z');

This can be quite useful. On the other hand, range('a', 'Z') doesn’t work as expected.

/*
[
    "a",
    "`",
    "_",
    "^",
    "]",
    "\",
    "[",
    "Z",
] ?
*/
range('a', 'Z');

To get the full a-Z range, use array_merge (and not + since + is not case sensitive).

/*
[
    "a",
    "b",
    ...
    "y",
    "z",
   ]
*/
range('a', 'z') + range('A', 'Z'); // The first array is not overriden

/*
[
    "a",
    "b",
    ...
    "Y",
    "Z",
   ]
*/
array_merge(range('a', 'z'), range('A', 'Z'));

This can be quite useful. But still less elegant than the Python counterparts.

PHP:

// "ab...yz"
implode('', range('a', 'z'));

// "ab...YZ"
implode('', range('A', 'Z'));

// "ab...YZ"
implode('', array_merge(range('a', 'z'), range('A', 'Z')));

Python:

import string

string.ascii_lowercase # 'ab...yz'

string.ascii_uppercase # 'AB...YZ'

string.ascii_letters # 'ab...YZ'

If you want to generate a range of floats, use the third argument to determine the step.

/*
[
    0.0,
    0.1,
    ...
    1.9,
    2.0,
   ]
*/
range(0.0, 2.0, 0.1);

PHP - Range - Iterators

There is no native range iterator in PHP (as the native Python range function in 3.x and the xrange function in 2.x). Seems like you have to build your own.

Weird that it’s still not in the standard lib.

function xrange($start, $end, $step = 1) {
    for ($i = $start; $i <= $end; $i += $step) {
        yield $i;
    }
}

foreach (xrange(0, 1000000) as $x) {
}

PHP - Python - Iterators

In PHP and Python you can use the yield from expression if your iterator wants to send the values of another iterator directly.

PHP

function xrange($start, $end, $step = 1) {
    for ($i = $start; $i <= $end; $i += $step) {
        yield $i;
    }
}

function my_iterator() {
    /*
        This is equivalent to
        foreach (xrange(0, 10) as $x) {
            yield $x;
        }
    */
    yield from xrange(0, 10);
}

/* [0, 1, ... 9, 10] */
iterator_to_array(my_iterator());

Python

def my_iterator():
    # Equivalent to
    # for x in xrange(0, 10):
    #     yield x
    yield from range(0, 10)

# [0, 1, ... 8, 9]
list(my_iterator())