Проблема памяти может возникнуть, когда в процессе выполнения программы нужно иметь большое количество объектов, особенно если есть ограничения на общий размер доступной оперативной памяти.
Ниже приводится обзор некоторых методов уменьшения размера объектов, которые позволяют существенно сократить объем оперативной памяти, необходимой для программ на чистом Python.
Для простоты будем рассматривать структуры в Python для представления точки с координатами x , y , z с доступом к значениям координат по имени.
Dict
В небольших программах, особенно в скриптах, довольно просто и удобно использовать встроенный dict для представления структурной информации:
>>> ob = >>> x = ob[‘x’] >>> ob[‘y’] = y
С появлением более «компактной» реализации в Python 3.6 с упорядоченным набором ключей dict стал еще более привлекательным. Однако, посмотрим на размер его следа в оперативной памяти:
>>> print(sys.getsizeof(ob)) 240
Он занимает много памяти, особенно, если вдруг понадобится создать большое число экземпляров:
Вся суть программирования на Python
1 000 000 | 240 Мб |
10 000 000 | 2.40 Гб |
100 000 000 | 24 Гб |
Class instance
Для любителей все облекать в классы более предпочтительным является определение в виде класса с доступом по имени атрибута:
class Point: # def __init__(self, x, y, z): self.x = x self.y = y self.z = z >>> ob = Point(1,2,3) >>> x = ob.x >>> ob.y = y
Интересна структура экземпляра класса:
PyGC_Head | 24 |
PyObject_HEAD | 16 |
__weakref__ | 8 |
__dict__ | 8 |
ВСЕГО: | 56 |
Здесь __weakref__ это ссылка на список, так называемых, слабых ссылок (weak reference) на данный объект, поле __dict__ это ссылка на словарь экземпляра класса, в котором содержатся значения атрибутов экземпляра (заметим, что ссылки на 64-битной платформе занимают 8 байт). Начиная с Python 3.3, используется общее пространство для хранения ключей в словаре для всех экземпляров класса. Это сокращает размер следа экземпляра в памяти:
>>> print(sys.getsizeof(ob), sys.getsizeof(ob.__dict__)) 56 112
Как результате большое количество экземпляров класса оставляют меньший след в памяти, чем обычный словарь ( dict ):
1 000 000 | 168 Мб |
10 000 000 | 1.68 Гб |
100 000 000 | 16.8 Гб |
Нетрудно заметить, что след экземпляра в памяти все еще велик из-за размера словаря экземпляра.
Instance of class with __slots__
Существенное уменьшение следа экземпляра в памяти достигается путем исключения __dict__ и __weakref__ . Это возможно при помощи «трюка» со __slots__ :
Учил python 30 дней. Полное видео на канале
class Point: __slots__ = ‘x’, ‘y’, ‘z’ def __init__(self, x, y, z): self.x = x self.y = y self.z = z >>> ob = Point(1,2,3) >>> print(sys.getsizeof(ob)) 64
След в памяти стал существенно компактнее:
PyGC_Head | 24 |
PyObject_HEAD | 16 |
x | 8 |
y | 8 |
z | 8 |
ВСЕГО: | 64 |
Использование __slots__ в определении класса приводит к тому, что след большого числа экземпляров в памяти существенно уменьшается:
1 000 000 | 64 Мб |
10 000 000 | 640 Мб |
100 000 000 | 6.4 Гб |
В настоящее время это основной метод существенного сокращения следа памяти экземпляра класса в памяти программы.
Достигается такое сокращение тем, что в памяти после заголовка объекта хранятся ссылки на объекты, а доступ к ним осуществляется при помощи специальных дескрипторов (descriptor), которые находятся в словаре класса:
>>> pprint(Point.__dict__) mappingproxy( . ‘x’: , ‘y’: , ‘z’: >)
Для автоматизации процесса создания класса со __slots__ существует библиотека namedlist. Функция namedlist.namedlist создает класс по структуре идентичный классу со __slots__ :
>>> Point = namedlist(‘Point’, (‘x’, ‘y’, ‘z’))
Другой пакет attrs позволяет автоматизировать процесс создания классов как со __slots__ , так и без него.
Tuple
Для представления наборов данных в Python также есть встроенный тип tuple . Tuple это фиксированная структура или запись, но без имен полей. Для доступа к полю используется индекс поля. Поля tuple раз и навсегда связываются с объектами-значениями в момент создания экземпляра tuple:
>>> ob = (1,2,3) >>> x = ob[0] >>> ob[1] = y # НЕВОЗМОЖНО
Экземпляры tuple вполне компактны:
>>> print(sys.getsizeof(ob)) 72
Они занимают в памяти на 8 байт больше, чем экземпляры классов со __slots__ , так как след tuple в памяти также содержит количеством полей:
PyGC_Head | 24 |
PyObject_HEAD | 16 |
ob_size | 8 |
[0] | 8 |
[1] | 8 |
[2] | 8 |
ВСЕГО: | 72 |
Namedtuple
Так как tuple используется очень широко, то однажды возник запрос на то, чтобы можно было все-таки иметь доступ к полям и по имени тоже. Ответом на этот запрос стал модуль collections.namedtuple .
Функция namedtuple создана, чтобы автоматизировать процесс порождения таких классов:
>>> Point = namedtuple(‘Point’, (‘x’, ‘y’, ‘z’))
Она создает подкласс tuple, в котором определены дескрипторы для доступа в полям по имени. Для нашего примера это будет выглядеть примерно так:
Все экземпляры таких классов имеет след в памяти, идентичный с tuple. Большое число экземпляров оставляют чуть больший след памяти:
1 000 000 | 72 Мб |
10 000 000 | 720 Мб |
Recordclass: мутируемый namedtuple без GC
Так как tuple и, соответственно, namedtuple -классы порождают немутируемые объекты в том смысле, что объект значение ob.x уже нельзя связать с другим объектом-значением, то возник запрос на мутируемый вариант namedtuple. Так как в Python нет встроенного типа, идентичного tuple, поддерживающего присваивания, то было создано множество вариантов. Мы остановимся на recordclass, получившем оценку на stackoverflow. Кроме того, с его помощью можно уменьшить размер следа объекта в памяти по сравнению с размером следа объектов типа tuple .
В пакете recordclass вводится в обиход тип recordclass.mutabletuple , который практически во всем идентичен tuple, но также поддерживает присваивания. На его основе создаются подклассы, которые практически во всем идентичны namedtuples, но также поддерживают присваивание новых значений полям (не создавая новых экземпляров). Функция recordclass подобно функции namedtuple позволяет автоматизировать создание таких классов:
>>> Point = recordclass(‘Point’, (‘x’, ‘y’, ‘z’)) >>> ob = Point(1, 2, 3)
Экземпляры класса имеют аналогичную стуктуру, что и tuple , но только без PyGC_Head :
PyObject_HEAD | 16 |
ob_size | 8 |
x | 8 |
y | 8 |
y | 8 |
ВСЕГО: | 48 |
По умолчанию функция recordclass порождает класс, который не участвует в механизме циклической сборки мусора. Обычно namedtuple и recordclass используют для порождения классов, представляющих записи или простые (нерекурсивные) структуры данных. Корректное их использование в Python не порождает циклических ссылок. По этой причине в следе экземпляров классов, порожденных recordclass по умолчанию, исключен фрагмент PyGC_Head , который необходим для классов, поддерживающих механизм циклической сборки мусора (более точно: в структуре PyTypeObject , соответствующей создаваемому классу в поле flags по умолчанию не установлен флаг Py_TPFLAGS_HAVE_GC ).
Размер следа большого количества экземпляров оказывается меньше, чем у экземпляров класса со __slots__ :
1 000 000 | 48 Мб |
10 000 000 | 480 Мб |
100 000 000 | 4.8 Гб |
Dataobject
Другое решение, предложенное в библиотеке recordclass основано на идее: использовать структуру хранения в памяти, как у экземпляров классов со __slots__ , но не участвовать при этом в механизме циклической сборки мусора. Класс порождается при помощи функции recordclass.make_dataclass :
>>> Point = make_dataclass(‘Point’, (‘x’, ‘y’, ‘z’))
Созданный таким образом класс по умолчанию, создает мутируемые экземпляры.
Другой способ – использовать объявление класса путем наследования от recordclass.dataobject :
class Point(dataobject): x:int y:int z:int
Создаваемые таким образом классы будут порождать экземпляры, которые не участвуют в механизме циклической сборки мусора. Структура экземпляра в памяти такая же, как в случае со __slots__ , но без заголовка PyGC_Head :
PyObject_HEAD | 16 |
x | 8 |
y | 8 |
y | 8 |
ВСЕГО: | 40 |
>>> ob = Point(1,2,3) >>> print(sys.getsizeof(ob)) 40
Для доступа к полям также используются специальные дескрипторы для доступа к полю по его смещению относительно начала объекта, которые размещены в словаре класса:
mappingproxy(, . ‘x’: , ‘y’: , ‘z’: >)
Размер следа большого количества экземпляров оказывается минимально возможным для CPython :
1 000 000 | 40 Мб |
10 000 000 | 400 Мб |
100 000 000 | 4.0 Гб |
Cython
Есть один подход, основанный на использовании Cython. Его достоинство состоит в том, что поля могут принимать значения типов языка C. Дескрипторы для доступа к полям из чистого Python создаются автоматически. Например:
cdef class Python: cdef public int x, y, z def __init__(self, x, y, z): self.x = x self.y = y self.z = z
В этом случае экземпляры имеют еще меньший размер памяти:
>>> ob = Point(1,2,3) >>> print(sys.getsizeof(ob)) 32
След экземпляра в памяти имеет следующую структуру:
PyObject_HEAD | 16 |
x | 4 |
y | 4 |
y | 4 |
пусто | 4 |
ВСЕГО: | 32 |
Размер следа большого количества экземпляров получается меньше:
1 000 000 | 32 Мб |
10 000 000 | 320 Мб |
100 000 000 | 3.2 Гб |
Однако следует помнить, что при доступе из кода на Python всякий раз будет осуществляться преобразование из int в объект Python и наоборот.
Numpy
Использование многомерных массивов или массивов записей для большого количества данных дает выигрыш в памяти. Однако для эффективной обработки на чистом Python следует использовать методы обработки, ориентированные на применение функций из пакета numpy .
>>> Point = numpy.dtype((‘x’, numpy.int32), (‘y’, numpy.int32), (‘z’, numpy.int32)])
Массив и N элементов, инициализированный нулями создается при помощи функции:
>>> points = numpy.zeros(N, dtype=Point)
Размер массива минимально возможный:
1 000 000 | 12 Мб |
10 000 000 | 120 Мб |
100 000 000 | 1.20 Гб |
Обычный доступ к элементам массива и строкам потребует преобразования объекта Python
в значение C int и наоборот. Извлечение одной строки приводит к созданию массива, содержащего единственный элемент. След его уже не будет столь компактен:
>>> sys.getsizeof(points[0]) 68
Поэтому, как было отмечено выше, в коде на Python необходимо осуществлять обработку массивов, используя функции из пакета numpy .
На наглядном и простом примере можно было убедиться, что сообщество разработчиков и пользователей языка программирования Python (CPython) располагает реальными возможностями для существенного сокращения объема памяти, используемой объектами.
Источник: habr.com
Python-сообщество
- Начало
- » Python для новичков
- » Помогите срочно. 1
#1 Янв. 3, 2019 15:40:23
Помогите срочно. 1
Напишите программу, которая уменьшает размер программ, написанных на Питоне (без использования некоторых возможностей языка).
Программа считывает количество строк, затем — сами эти строки по очереди. Каждая строка выводится, но в изменённом виде:
если следуют несколько пробелов подряд не в начале строки, то нужно оставить только один пробел;
если встретился символ комментария #, то его и всё, что за ним, выводить не нужно;
если лишние пробелы или символ комментария встретились внутри (одинарных) кавычек, то ничего убирать или менять в них не надо;
если после кавычки встретилась ещё одна кавычка, то она закрывает первую лишь в том случае, если перед ней не стоит обратный слэш, не экранированный другим обратным слэшем (гарантируется, что за пределами кавычек и комментариев обратный слэш не встречается).
Если ввод и вывод программы осуществляется через одну и ту же консоль, ввод и вывод будет перемешан; это не помешает разделению ввода и вывода при автоматической проверке, поэтому беспокоиться об этом не нужно.
Формат ввода
На первой строке вводится количество строк в программе.
Далее — сама программа.
Формат вывода
Программа, сокращённая по описанным в условии правилам.
Пример 1
Ввод
1
print (‘Привет’)#поздороваемся
Вывод
print (‘Привет’)
Пример 2
Ввод
6
easy = 2 + 2
if easy == 4:# А вдруг нет?
print(‘Квадрат с обрезанными углами:’)
print(‘/-\’)
print(‘|#|’)
print(‘\_/’)
Вывод
easy = 2 + 2
if easy == 4:
print(‘Квадрат с обрезанными углами:’)
print(‘/-\’)
print(‘|#|’)
print(‘\_/’)
#2 Янв. 3, 2019 21:16:13
Помогите срочно. 1
ArtemDav гдето я такое уже видел, но там топикстартер вообще нес какуюто ахинею про быков и коров.
http://python.su/forum/topic/36288/?page=1#post-197244
это что задание на какихто курсах?
давайте начнем с простого: “Программа считывает количество строк, затем — сами эти строки по очереди. Каждая строка выводится”. Вы можете это реализовать?
и что значит “(без использования некоторых возможностей языка)” какие конкретно возможности языка нельзя использовать?
[code python][/code]
Бериегите свое и чужое время.
Отредактировано PEHDOM (Янв. 3, 2019 21:17:29)
#3 Янв. 4, 2019 12:28:47
Помогите срочно. 1
PEHDOM
ArtemDav гдето я такое уже видел, но там топикстартер вообще нес какуюто ахинею про быков и коров.http://python.su/forum/topic/36288/?page=1#post-197244это что задание на какихто курсах?давайте начнем с простого: “Программа считывает количество строк, затем — сами эти строки по очереди. Каждая строка выводится”. Вы можете это реализовать? и что значит “(без использования некоторых возможностей языка)” какие конкретно возможности языка нельзя использовать?
Если я создал тему с этой задачей, наверное я не могу это реализовать….Нельзя использовать библиотеки и методы (def)
#4 Янв. 4, 2019 12:55:49
Помогите срочно. 1
ArtemDav, экий Вы, батенька, лентяй. Даже вот это не хотите сделать:
PEHDOM
давайте начнем с простого: “Программа считывает количество строк, затем — сами эти строки по очереди. Каждая строка выводится”
Неужели так трудно посмотреть в учебнике как открывается файл и как читаются строки из файла, как выводятся в файл строки? Неужели так сложно самому попробовать это набросать в виде скриптика? Обработка строк типа обрезки по позиции символа # или убрать лишние пробелы из строки — неужели это Вам не по силам? Другие условия сокращения тоже не такие уж сложные, как кажутся на первый взгляд.
#5 Янв. 4, 2019 13:14:25
Помогите срочно. 1
ArtemDav
Если я создал тему с этой задачей, наверное я не могу это реализовать
Если ктото напишет задачу за вас, то вы так и не сможете это реализовать, вы можете определиться что вам по силам, а что вы не знаете как делать? Я вам предлагаю начать с простого:
“Программа считывает количество строк, затем — сами эти строки по очереди. Каждая строка выводится”
вы можете это реализовать? Там простейший же инпут в цикле и в том же цикле принт. если вы не можете даже это реализовать, может вам пока рано решать такие задачи, может стоит вернуться и подучить матчасть?.
[code python][/code]
Бериегите свое и чужое время.
Отредактировано PEHDOM (Янв. 4, 2019 13:18:09)
#6 Янв. 4, 2019 17:30:27
Помогите срочно. 1
я смог сделать обрезку до #, и убрать все пробелы, но мне надо оставлять 1, не знаю как это сделать, с остальным проблем нет
#7 Янв. 4, 2019 22:24:47
Помогите срочно. 1
ArtemDav
я смог сделать обрезку до #, и убрать все пробелы, но мне надо оставлять 1, не знаю как это сделать, с остальным проблем нет
отлично, если вкратце то вам нужно посимвольно перебирать и смотреть на предыдущий символ, если это начало строки или если это в кавычках то оставляем как есть, а если предыдущий пробле то обрезаем его.
Както так:
# st = » easy = ‘Этот текст должен остаться ‘ + ‘неизменным !! ! . ‘» # какаято строка которую нужно обработать prev_sym = None # предыдущий символ, если его нет то это начало строки quotes_open = False # флаг открытия кавычки res = [] # результирующий список for char in st: # перебираем посимвольно строку if char == ‘ ‘: # если встретили пробел if prev_sym == None: # если предыдущего символа не было то это начало строки, пробелы не сокращаем res.append(char) elif prev_sym == ‘ ‘: # если предыдущий символ пробел if quotes_open: # и если открыты кавычки, пробелы не сокращаем res.append(char) else: # это не начало строки и не строковая паеременная, нам не нужен еще один пробел continue # в принципе continue не обязателен, можно вообще убрать весь блок else: но тогда не будет явным всесь алгоритм. else: # если предыдущий символ не пробел, и это не начало строки, то нужно этот пробле вписать prev_sym = char res.append(char) elif char == «‘»: # если нам попалась кавычка res.append(char) # мы пока не учитываем кавычку внутри кавычки экранированую обратным слешем, это уже следующий этап prev_sym = char quotes_open = not quotes_open # устанавливаем флаг открытия кавычки в True/False else: # если это не пробел и не кавычки то пихаем как есть res.append(char) prev_sym = char print(‘Исходная строка :’, st, sep = ») print(‘Минифицированя строка:’, ».join(res), sep = ») >>> Исходная строка : easy = ‘Этот текст должен остаться ‘ + ‘неизменным !! ! . ‘ Минифицированя строка: easy = ‘Этот текст должен остаться ‘ + ‘неизменным !! ! . ‘ >>>
[code python][/code]
Источник: python.su
Что нового в Python? Руководство по Python 3 и выше
Изучение
На чтение 11 мин Просмотров 1.5к. Опубликовано 11.06.2021
Обновление с одной версии языка программирования Python до другой может быть настоящей проблемой. Это еще сложнее, если вы пропустили несколько промежуточных версий. Какие самые важные различия? Как они соотносятся с версией, с которой вы знакомы?
Сегодня мы ответим на все эти вопросы от Python 3.0 до Python 3.8. Мы проведем вас через все самые большие изменения, их сравнение с предыдущими версиями Python, и покажем вам примеры кода, которые помогут вам реализовать свой собственный современный код Python. В конце разделов вы получите загружаемый чит-лист, который вы сможете взять с собой, продолжая свой путь к программному обеспечению Python.
Python 2.x против Python 3.0
Python 3 внес много изменений, чтобы упростить синтаксис Python и добавить новые функции. Каждое из этих изменений может показаться незначительным, но вместе они могут серьезно повлиять на работу вашей программы.
В целом Python 3.x имеет:
- Более чистый синтаксис
- Более надежные математические и логические операторы
- Также более универсальное поведение String
- Более строгие правила для типов данных
- Более современная поддержка
Далее мы рассмотрим важные синтаксические изменения, которые помогут вам адаптироваться к Python 3.
Print()
В Python 3.0 поведение печати изменилось с оператора на функцию Python. Теперь поле печати должно быть заключено в круглые скобки как параметр для правильной работы.
print «hello, world» # Python 2 print(«hello, world») # Python 3
Переход от printоператора к printфункции также повлиял на некоторые общие способы печати, такие как разрывы строк и печать в файл.
Во всех версиях Python каждый вызов печати заканчивается разрывом строки. Однако иногда вам нужно печатать в той же строке.
В Python 2 вы могли печатать в той же строке с запятой в конце:
В Python 3 вместо этого вы используете endаргумент ключевого слова после первой строки.
print(5, end =» «)
Наконец, Python 3 сделал синтаксис для печати в файл более читабельным.
print >>open(‘test.txt’,’w’), «Printing to file» # Python 2 print(«Printing to file», file = open(‘test.txt’,’w’)) # Python 3
Версия Python 3 избавляется от символов `>>` в пользу более интуитивно понятного `file =`. Он также изменяет порядок аргументов в соответствии с бизнес-логикой операции, как мы бы сказали «печать ___ в файл ___» поверх «на файл ___, печать ____».
Строки в Unicode и ASCII
В Python 2 все строки по умолчанию сохраняются как ASCII. Они могут быть сохранены как Unicode, используя uперед строкой.
#Python 2 default = «This string is ASCII» unicode = u»This string is Unicode»
В Python 3 все строки по умолчанию хранятся в Юникоде. Unicode предпочтительнее ASCII во всем мире, потому что Unicode поддерживает переменную битовую кодировку и другие наборы символов.
# Python 3 unicode = «This string is Unicode by default»
Изменение Python 3 для поддержки Unicode по умолчанию гарантирует, что вы всегда будете использовать лучший протокол кодирования, если вы специально не откажетесь.
Целочисленное деление
Деление между двумя целыми числами в Python 2 по умолчанию округляется до ближайшего целого числа. Разработчики Python должны были постоянно помнить об этом, чтобы предотвратить странное поведение в своих программах.
Теперь в Python 3 операция динамически переводит типы данных в число с плавающей запятой, если результатом является десятичное значение. Каждое частное теперь будет отражать истинное математическое значение без округления. Вам больше не нужно помнить тонкости работы, и вместо этого вы можете сосредоточиться на своем проекте.
Python2
print 7 / 5
print 7 / -5
Python3
print (7 / 5)
print (7 / -5)
Range против xrange
range()Функция Python 3 заменяет xrange()функцию Python2. Новая, более чистая функция дает тот же результат.
В Python 2 range()возвращал список, а xrange()возвращал объект-итератор xrange. В большинстве случаев они могут использоваться как взаимозаменяемые. Теперь range()поведение Python 2 было удалено.
Python2
for x in xrange(1, 5):
Python3
for x in range(1, 5):
Обработка ошибок
Python 3 теперь требует asключевое слово при реализации обработки ошибок. Это изменение отделяет синтаксис обработки ошибок от синтаксиса параметров функции и упрощает чтение кода обработки ошибок с первого взгляда. Многие разработчики, переходящие с Python 2 на Python 3.0, упускают из виду это небольшое, но важное изменение.
# Python 2 try: trying_to_check_error except NameError, err: print err, ‘Error Caused’
# Python 3 try: trying_to_check_error except NameError as err: # new `as` keyword print (err, ‘Error Caused’)
Исходный код и совместимость библиотек
Код Python 2.x может быть совместим с Python 3, но часто приводит к неожиданному поведению. Однако код Python 3 нельзя запускать с Python 2.
Хотя код Python 2.x можно портировать, библиотеки часто не могут. Перенос библиотек Python 2 на Python 3 может быть трудным и ненадежным. Поскольку все больше пользователей продолжают внедрять новые функции Python 3, большинство разработчиков больше не создают или обновляют библиотеки из Python 2.
В конечном итоге вам придется перейти на Python 3, если вы хотите использовать новейшие инструменты и методы из современных библиотек, пакетов и модулей, особенно тех, которые используются для машинного обучения.
Например, Django для работы требуются современные библиотеки. В результате Django больше не поддерживает версии Python 2.x с обновлениями. Django 1.11 был последней версией, поддерживающей Python 2.7. Теперь Django рекомендует вам загрузить новейшую версию языка программирования Python, чтобы получить лучшие возможности и возможности веб-разработки.
Шпаргалка по Python 3
Python 3.7 против 3.8
Самая последняя версия Python — это версия 3.8. Независимо от того, используете ли вы Python 2 или более раннюю версию Python 3, важно понимать новейшие функции, доступные в этом мощном обновлении.
Ниже мы рассмотрим самые большие изменения с Python 3.7 до 3.8 с примерами кода и пояснениями, которые помогут вам использовать самую современную версию Python, независимо от того, с какой предыдущей версией вы работали.
Выражения присваивания
Выражения присваивания — самая большая функция, добавленная в Python 3.8. С помощью этих выражений вы можете назначать и возвращать значение переменной всего в одной строке. Назначение выражение записывается с новым оператором моржа, (:=).
Python3.x
Источник: bestprogrammer.ru