Это десятая подборка советов про Python и программирование из моего авторского канала @pythonetc.
Предыдущие подборки.
0_0
0_0
— полностью корректное выражение на Python.Сортировка списка с None
Сортировка списка с
None
-значениями может быть весьма непростой задачей: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 is not subscriptable
Можно попробовать удалить все None и вернуть их обратно после сортировки (в начало или в конец списка, в зависимости от задачи):
In [3]: sorted(
...: (d for d in data if d is not None),
...: key=lambda x: x['a']
...: ) + [
...: d for d in data if d is None
...: ]
Out[3]: [{'a': -3}, {'a': 1}, {'a': 2}, None, None]
Но это неудобно. Лучше использовать более сложный
key
:In [4]: sorted(data, key=lambda x: float('inf') if x is None else x['a'])
Out[4]: [{'a': -3}, {'a': 1}, {'a': 2}, None, None]
Если же речь идёт о типах, для которых бесконечность недопустима, можно сортировать кортежи:
In [5]: sorted(data, key=lambda x: (1, None) if x is None else (0, x['a']))
Out[5]: [{'a': -3}, {'a': 1}, {'a': 2}, None, None]
Вызов random.seed()
Когда вы форкаете процесс, то используемый вами random seed будет копироваться во все получившиеся процессы. В результате в них может генерироваться одинаковый «случайный» результат.
Чтобы этого избежать, нужно в каждом процессе вручную вызывать
random.seed()
. Но если воспользуетесь модулем multiprocessing
, то он будет делать это за вас.Например:
import multiprocessing
import random
import os
import sys
def test(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()
Получите нечто подобное:
4 4 4 5 5
1 4 1 3 3
2 2 2 2 2
Более того, если вы используете Python 3.7 и выше, то благодаря новому хуку
at_fork
можете делать то же самое с помощью os.fork
.Вышеприведённый код на Python 3.7 даёт такой результат:
1 2 2 1 5
4 4 4 5 5
2 4 1 3 1
Сложение с 0
На первый взгляд кажется, что
sum([a, b, c])
эквивалентно a + b + c
, хотя на самом деле эквивалентом будет 0 + a + b + c
. Значит это выражение не может работать с типами, которые не поддерживают сложение с 0
:class MyInt:
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__
return f'{class_name}({self.value})'
In : sum([MyInt(1), MyInt(2)])
...
AttributeError: 'int' object has no attribute 'value'
Чтобы это исправить, можете предоставлять кастомный начальный элемент, который будет использоваться вместо
0
:In : sum([MyInt(1), MyInt(2)], MyInt(0))
Out: MyInt(3)
sum
предназначена для сложения float
и int
-типов, хотя может работать и с любыми другими кастомными типами. Однако он отказывается складывать bytes
, bytearray
и str
, поскольку для этого предназначена 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)
Завершение индексов в Jupyter Notebook
С помощью метода
_ipython_key_completions_
можно кастомизировать завершения индексов в Jupyter Notebook. Таким образом вы сможете контролировать, что отобразится на экране, если нажать Tab после чего-нибудь вроде d["x
:Обратите внимание, что метод не получает искомую строку в качестве аргумента.
Комментарии (11)
domix32
10.04.2019 11:530_0
валидно только в виде скрипта. REPL питона будет ругатьсяSyntaxError: invalid syntax
Vanadium
11.04.2019 18:16не будет:
Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 05:52:31) [GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> 0_0 0 >>>
domix32
11.04.2019 22:07Значит видимо у MacOS какие-то особенные REPL ибо и python2 и python3 ругнулись.
BubaVV
Вообще, список элементов разных типов скорей говорит о плохой архитектуре программы
worldmind
Звучит логично, питон позволяет пихать в список что угодно, но это не значит, что стоит так делать.
trapwalker
Ну в общем случае да, но в данном None могут олицетворять, скажем, «пустые» элементы, своеобразные пропуски в данных или что-то в этом роде.
Мне, кстати, кажется, что вот такая вот запись куда читабельнее:
Чем авторская:
Хотя кто-то и поспорил бы.
BubaVV
Я бы завернул такое в класс и определил там метод is_empty()
trapwalker
Если это не какие-нибудь промежуточные значения, которые появились только что и будут преобразованы или добавлены куда-то. Делать для них класс, специальный метод… это овер-инженеринг. С таким подходом нам нужно, к пирмеру, везде в коде создать отдельные типы для представления масс, размеров, объёмов, напряжений, зарядов… Да, это целесообразно внекоторых случаях, но н полезность от такого усложнения должна превысить разумный порог.
menstenebris
Если список будет в пару миллионов элементов то на этом производительность вашей программы и закончится. Создание экземпляра класса крайне ресурсоемкая операция как по RAM так и по CPU
Murtagy
Если задача решается в одну строчку плодить классы это глупость на мой взгляд.