Написать пост

7 фишек Python для лучшего обращения с памятью

Аватарка пользователя Елена Капаца

Подобрали фишек Python, чтобы использовать язык по полной и разумнее обращаться с CPU.

Обложка поста 7 фишек Python для лучшего обращения с памятью

В Python никогда не бывает избытка полезных приемов. Чем больше вы таких изучите, тем выше вероятность, что сможете быстро справиться с любыми трудностями на практике. В этой статье я поделюсь хитростями, которые могут пригодиться как новичкам, так и опытным разработчикам.

ВРЕЗКА. Мы собрали уже много полезных приемов (часть 1, часть 2, часть 3), по-прежнему открываем для себя новые, и с удовольствием делимся с вами еще одной подборкой на тему эффективного расхода памяти.

Режим разработчика

В нем интерпретатор выдаст больше полезных предупреждений:

  • незакрытые файлы;
  • неожиданные корутины (асинхронные функции);
  • неизвестная кодировка для str.encode (по умолчанию для пустых строк флажок не установлен);
  • проблемы с распределением памяти.

Настройку можно активировать с помощью аргумента -X. Запускаем скрипт с помощью командной строки:

			python3 -X main.py
		

Python откроется в командной строке в интерактивном режиме:

			Python 3.7.9 (v3.7.9:13c94747c7, Aug 15 2020, 01:31:08) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits"or"license"for more information.* Текст уведомления *
>>>
		

Больше про devmode в документации.

Измененная копия

В Python 3.13 добавлена функция copy.replace(), которая позволяет создавать модифицированную копию неизменяемого объекта вроде кортежей, экземпляров datetime, и прочего (документация):

			import copy

my_tuple = (1, 2, 3)
tuple_cp = copy.replace(my_tuple, my_tuple[1] = 4) # Обновим значение второго элемента
		

Группы исключений с except*

PEP 654 добавляет ExceptionGroupи новый синтаксис для обращения с ней — except*:

			try:
  raise ExceptionGroup('', [
    ValueError(),
    KeyError('привет),
    OSError(),
  ])
except* KeyError as e:
  print('catch1:', repr(e))
except* ValueError as e:
  print('catch2:', repr(e))
except* KeyError as e:
  1/0
		

Программа ловит все исключения и отображаетт их в таком каскадном трейсбеке:

			catch1: ExceptionGroup('', [KeyError('привет')])
catch2: ExceptionGroup('', [ValueError()])
  + Exception Group Traceback (most recent call last):
  |   File "<stdin>", line 2, in <module>
  | ExceptionGroup:  (1 sub-exception)
  +-+---------------- 1 ----------------
    | OSError
    +------------------------------------
		

setdefault() для заполнения словаря

Случается, что нам нужно установить дефолтное значения для различных ключей в словаре. Например, это пригодится при отслеживании числа слов в абзаце. Базовое решение выглядит так:

			>>> text = "Сегодня первое апреля. У нас до сих пор холодно.">>>>>> counts = {}
>>> for word in text.split():
>>>    if word in counts: # Проверим, существует ли ключ в словаре;>>>        counts[word] += 1 # Если слово есть, увеличим значение на 1
>>>    else:
>>>      counts[word] = 1 # Если слова нет, добавим его и установим счетчик, равный 1… {'Сегодня': 1, 'первое': 1, 'апреля.': 1, 'У': 1, 'нас': 1, 'до': 1, 'сих': 1, 'пор': 1, 'холодно.': 1}
		

Лаконичный и более эффективный с точки зрения памяти способ — setdefault():

			counts = {}
for word in text.split():
    counts.setdefault(word, 0)
    counts[word] += 1
		

Несколько паттернов для regex

Вы наверняка знакомы со спецсимволом ‘pipe’ — |, который позволяет сопоставлять объекты. К примеру, в словах «Супермэн» и «суперсила» есть общая приставка «супер». Значит, эту часть можно вынести за скобки, а внутри указать оставшиеся — «мэн», «сила»:

			import re

heros = re.compile(r"упер(сила|мэн)") # букву «с» опустим из-за регистра

h1 = heros.search("У тебя есть суперсила.")
h2 =  heros.search("Это летит Супермэн.")

print(h1.group())
print(h2.group())
		

Модуль re найдет запрошенное два раза:

			уперсила
упермэн
		

isalnum() для проверки пароля на качество

Если необходимо проверить креды (чтобы состояли из букв и цифр), можно использовать встроенную функцию isalnum():

			password = "ABCabc123"
print(password.isalnum()) # Проверим пароль на соответствие правилам
		

Генераторы для экономии памяти

Отрабатывают эффективнее вместо самописных итераторов и вызываются с помощью специального синтаксиса — круглых скобок и конструкции i for i in range(10000):

			import sys
my_list = [li for i in range(10000)] # Классический способ
print(sum(my_list))print(sys-getsizeof(my_list), "bytes")

my_gen = (i for i in range(10000)) # Быстрый способ
print(sum(my_gen))
print{sys-getsizeof(my_gen), "bytes")
		

В первом случае список весит 87 тыс. байт, во втором — всего 128 байт:

			49995000
87632 bytes
49995000
128 bytes
		

Заключение

Некоторые приемы в документации языка программирования могут быть не очень понятными до тех пор, пока вы не попробуете их запустить. Забывать код нормально, достаточно просто добавить статью в закладки, чтобы вернуться к описанным трюкам в будущем.

Следите за новыми постами
Следите за новыми постами по любимым темам
1К открытий2К показов