@Pythonetc compilation March 2019

This is the tenth collection of Python tips and programming from my @pythonetc feed.
Previous selections .
0_0
0_0
Is a completely correct expression in Python.Sort list with None
Sorting a list with
None
-values can be a daunting task:In [1]: data = [
...: dict(a=1),
...: None,
...: dict(a=-3),
...: dict(a=2),
...: None,
...: ]
In [2]: sorted(data, key=lambda x: x['a'])
...
TypeError: 'NoneType' object isnot subscriptable
You can try to delete all None and return them back after sorting (to the beginning or to the end of the list, depending on the task):
In [3]: sorted(
...: (d for d in data if d isnotNone),
...: key=lambda x: x['a']
...: ) + [
...: d for d in data if d isNone
...: ]
Out[3]: [{'a': -3}, {'a': 1}, {'a': 2}, None, None]
But it is inconvenient. Better to use a more complex one
key
:In [4]: sorted(data, key=lambda x: float('inf') if x isNoneelse x['a'])
Out[4]: [{'a': -3}, {'a': 1}, {'a': 2}, None, None]
If we are talking about types for which infinity is unacceptable, you can sort the tuples:
In [5]: sorted(data, key=lambda x: (1, None) if x isNoneelse (0, x['a']))
Out[5]: [{'a': -3}, {'a': 1}, {'a': 2}, None, None]
Call random.seed ()
When you fork the process, the random seed you use will be copied to all the resulting processes. As a result, the same “random” result can be generated in them.
To avoid this, you must manually call in each process
random.seed()
. But if you use the module multiprocessing
, it will do it for you. For example:
import multiprocessing
import random
import os
import sys
deftest(a):
print(random.choice(a), end=' ')
a = [1, 2, 3, 4, 5]
for _ in range(5):
test(a)
print()
for _ in range(5):
p = multiprocessing.Process(
target=test, args=(a,)
)
p.start()
p.join()
print()
for _ in range(5):
pid = os.fork()
if pid == 0:
test(a)
sys.exit()
else:
os.wait()
print()
Get something like this:
4 4 4 5 5
1 4 1 3 3
2 2 2 2 2
Moreover, if you use Python 3.7 and higher, then thanks to the new hook
at_fork
you can do the same with os.fork
. The above Python 3.7 code gives this result:
1 2 2 1 5
4 4 4 5 5
2 4 1 3 1
Addition to 0
At first glance it seems that it is
sum([a, b, c])
equivalent a + b + c
, although in fact it will be equivalent 0 + a + b + c
. So this expression cannot work with types that do not support addition to 0
:classMyInt:def__init__(self, value):
self.value = value
def__add__(self, other):return type(self)(self.value + other.value)
def__radd__(self, other):return self + other
def__repr__(self):
class_name = type(self).__name__
returnf'{class_name}({self.value})'
In : sum([MyInt(1), MyInt(2)])
...
AttributeError: 'int' object has no attribute 'value'
To fix this, you can provide a custom initial element that will be used instead
0
:In : sum([MyInt(1), MyInt(2)], MyInt(0))
Out: MyInt(3)
sum
It is intended for addition float
and int
-types, although it can work with any other custom types. However, he refuses to fold bytes
, bytearray
and str
, since this is intended for this join
:In : sum(['a', 'b'], '')
...
TypeError: sum() can't sum strings [use ''.join(seq) instead]
In : ints = [x for x in range(10_000)]
In : my_ints = [Int(x) for x in ints]
In : %timeit sum(ints)
68.3 µs ± 142 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In : %timeit sum(my_ints, Int(0))
5.81 ms ± 20.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Index Completion in Jupyter Notebook
Using the method,
_ipython_key_completions_
you can customize index completion in a Jupyter Notebook. This way you can control what is displayed on the screen if you press Tab after something like d["x
: 
Note that the method does not receive the search string as an argument.