This blog is a learning diary from satwikkansal/wtfpython:https://github.com/satwikkansal/wtfpython

CPython optimization (called string interning) tries to use existing immutable objects in some cases rather than creating a new object every time. The decision of when to implicitly intern a string is implementation dependent. There are some facts that can be used to guess if a string will be interned or not:

All length 0 and length 1 strings are interned.

Strings are interned at compile time (‘wtf’ will be interned but ”.join([‘w’, ‘t’, ‘f’] will not be interned)

Strings that are not composed of ASCII letters, digits or underscores, are not interned. This explains why ‘wtf!’ was not interned due to !. Cpython implementation of this rule can be referenced back to these block of peephole cpython lines:

/* Peephole optimizations for bytecode compiler. */

#define MAX_INT_SIZE 128 /* bits */

#define MAX_COLLECTION_SIZE 20 /* items */

#define MAX_STR_SIZE 20 /* characters */

#define MAX_TOTAL_ITEMS 1024 /* including nested collections */

static PyObject *

safe_multiply(PyObject *v, PyObject *w)

{

if (PyLong_Check(v) && PyLong_Check(w) && Py_SIZE(v) && Py_SIZE(w)) {

size_t vbits = _PyLong_NumBits(v);

size_t wbits = _PyLong_NumBits(w);

if (vbits == (size_t)-1 || wbits == (size_t)-1) {

return NULL;

}

if (vbits + wbits > MAX_INT_SIZE) {

return NULL;

}

}

else if (PyLong_Check(v) && (PyTuple_Check(w) || PyFrozenSet_Check(w))) {

Py_ssize_t size = PyTuple_Check(w) ? PyTuple_GET_SIZE(w) :

PySet_GET_SIZE(w);

if (size) {

long n = PyLong_AsLong(v);

if (n MAX_COLLECTION_SIZE / size) {

return NULL;

}

if (n && check_complexity(w, MAX_TOTAL_ITEMS / n) < 0) {

return NULL;

}

}

}

else if (PyLong_Check(v) && (PyUnicode_Check(w) || PyBytes_Check(w))) {

Py_ssize_t size = PyUnicode_Check(w) ? PyUnicode_GET_LENGTH(w) :

PyBytes_GET_SIZE(w);

if (size) {

long n = PyLong_AsLong(v);

if (n MAX_STR_SIZE / size) {

return NULL;

}

}

}

else if (PyLong_Check(w) &&

(PyTuple_Check(v) || PyFrozenSet_Check(v) ||

PyUnicode_Check(v) || PyBytes_Check(v)))

{

return safe_multiply(w, v);

}

return PyNumber_Multiply(v, w);

}

Python Generator

Generator functions allow to declare a function that behaves like an iterator.

In a generator expression, the in clause is evaluated at declaration time, but the conditional clause is evaluated at runtime

Now it reverts back to the details of Generator as is outlined in Python.org page:

https://wiki.python.org/moin/Generators

Why Generator? It’s always about efficiency. Generator allows a lazy/on-demand generation of values, therefore, consumes less memory usage. For example, both range and xrange yield a range of numbers, but range returns a list while xrange returns a GENERATOR.

1 #the for loop will generate each i (i.e. 1,2,3,4,5, …), add it to total, and throw it away

2 #before the next i is generated. This is opposed to iterating through range(…), which creates

3 #a potentially massive list and then iterates through it.

4 total = 0

5 for i in irange(1000000):

6 total += i

1 #square is a generator

2 square = (i*i for i in irange(1000000))

3 #add the squares

4 total = 0

5 for i in square:

6 total += i

Toggle line numbers

1 #add squares less than 100

2 square = (i*i for i in count())

3 bounded_squares = takewhile(lambda x : x< 100, square)

4 total = 0

5 for i in bounded_squares:

6 total += i

is operator chcck is both operands are of the same object, while == compares the values of both operands are same. That’s why if you enter [] == [], output is true, but if you wanter [] is [], the output is false, because the two empty lists are located at two difference meory place.