company_banner

Подборка @pythonetc, март 2019


    Это десятая подборка советов про 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:



    Обратите внимание, что метод не получает искомую строку в качестве аргумента.
    Mail.ru Group
    1143,00
    Строим Интернет
    Поделиться публикацией

    Похожие публикации

    Комментарии 11

      +2
      Вообще, список элементов разных типов скорей говорит о плохой архитектуре программы
        +1
        Звучит логично, питон позволяет пихать в список что угодно, но это не значит, что стоит так делать.
          0
          Ну в общем случае да, но в данном None могут олицетворять, скажем, «пустые» элементы, своеобразные пропуски в данных или что-то в этом роде.
          Мне, кстати, кажется, что вот такая вот запись куда читабельнее:
          sorted(data, key=lambda x: x and x['a'] or float('inf'))

          Чем авторская:
          sorted(data, key=lambda x: float('inf') if x is None else x['a'])

          Хотя кто-то и поспорил бы.
            0
            Я бы завернул такое в класс и определил там метод is_empty()
              0
              Если это не какие-нибудь промежуточные значения, которые появились только что и будут преобразованы или добавлены куда-то. Делать для них класс, специальный метод… это овер-инженеринг. С таким подходом нам нужно, к пирмеру, везде в коде создать отдельные типы для представления масс, размеров, объёмов, напряжений, зарядов… Да, это целесообразно внекоторых случаях, но н полезность от такого усложнения должна превысить разумный порог.
                +1
                Если список будет в пару миллионов элементов то на этом производительность вашей программы и закончится. Создание экземпляра класса крайне ресурсоемкая операция как по RAM так и по CPU
                  0
                  Если задача решается в одну строчку плодить классы это глупость на мой взгляд.
              0

              0_0 валидно только в виде скрипта. REPL питона будет ругаться SyntaxError: invalid syntax

                +1
                Это было добавлено в 3.6

                www.python.org/dev/peps/pep-0515
                  0

                  не будет:


                  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
                  >>>
                    0
                    Значит видимо у MacOS какие-то особенные REPL ибо и python2 и python3 ругнулись.

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                Самое читаемое