Архив автора: admin

О сборке мусора, деструкторах и разных питонах

В этом посте я писал почему работа с файлами и другими объектами, требующими гарантированного закрытия должна должна производиться через with. Однако кроме минуса в виде добавления в код лишнего уровеня вложенности with еще и решает только часть проблемы — если код обработки файла не локален (нужно возвращать дескриптор в вызывающий код или хранить неопределенное время) with не может помочь. И собственно никто вообще не может помочь — суровая реальность состоит в том, что python не гарантирует вызов деструктора объекта. Т.е. если вы работаете на CPython, и не создаете циклических ссылок, то за крайне редкими исключениями деструктор будет вызываться вовремя. Но если вы используете ironpython/jython/pypy то ситуация становится совсем печальна.

Для тех кто, вдруг, не в курсе — немного про С++, как пример удобной для программиста реализации деструкторов. С++ гарантирует вызов деструктора у объекта по его уничтожению чтобы не произошло (за исключением полного краха программы/пропадания питания/конца света/etc). Уничтожение наступает либо по выходу из блока в котором определена переменная, либо по удалению объекта с помощью оператора delete при выделении объекта в куче.

    // C++
{
// open file
std::fstream fs(fname, std::ios_base::out);
process_file(fs);
} // file is closed before we get beyong this line, no matter what happened

Гарантированный вызов деструктора — великое программистское благо, позволяющее не заботится о освобождении некоторых ресурсов, не загромождать код всякими with/using/try-finally & Co, и даже для объектов в куче есть всякие unique_ptr. Но все это хорошо работает только в том случае, если объект имел некую локальную область жизни(впрочем unique_ptr/shared_ptr могут и больше). Если же объект выделяется из кучи в одном месте, а освобождается в другом то можно забыть сделать ему delete — получаем утечку памяти. Не смотря на множество способов значительно сократить вероятность такого исхода (например арены) полностью исключить его нельзя.

В качестве решения этой проблемы в современных языках используются сборщики мусора. Периодически они проходятся по памяти, и тем или иным способом удаляют неиспользуемые объекты. Все бы хорошо, но у сборщиков мусора возникают небольшие или большие проблемы с вызовами деструкторов у объектов. Во-первых все сборщики мусора бессильны перед циклическими ссылками. Если объект a ссылается на b, а b ссылается на a, то не понятно в каком порядке вызывать деструкторы. Сначала у a или сначала у b. Если сначала у a, то при вызове деструктора b его ссылка на a будет не валидна и наоборот. Отсутствие информации о смысле взаимосвязей между объектами не дает сборщику мусора вызвать деструкторы в корректном порядке. Копирующие сборщики мусора пошли еще дальше. Они вообще ничего не вызывают, оставляя программиста один на один с этой проблемой и гордо подписываясь «современный сборщик мусора».

Проблема, очевидно, состоит в том что оперативная память это не единственный ресурс требующий освобождения. Еще, как минимум, есть объекты OS, мютексы, транзакции БД, и много много другого. Часть из таких объектов будет через время прикрыта другим кодом (например транзакции БД — но в любом случае они будут создавать лишнюю нагрузку на базу все это время), но объекты OS останутся с процессом до его смерти. А ведь бОльшая часть таких объектов имею локальную область видимости и при гарантированном вызове деструктор был бы идеальным местом для их освобождения. Таким образом переходя от С++ к языкам со сборкой мусора но с недетерменированным вызовом деструкторов мы выигрываем в коде освобождения памяти, но проигрываем на других объектах. Теперь вместо delete «где-то там» вы должны написать dispose/using/with/try-finally прямо тут на каждый объект. Впрочем если файл, например, является не локальным, то и это не поможет. Для примера можно открыть Экслера и глянуть как в яве правильно работать с файлами. Страница ужасного кода ради одного маленького файлика. Не уверен, что такая сборка мусора того стоила.

Итак посмотрим как себя ведут с деструкторами разные варианты питона. В качестве примера возьмем вот такую программу:

Без подсветки синтаксиса

import gc
import sys

class DestTest(object):
def __init__(self, val):
self.val = val

def __del__(self):
sys.stdout.write(str(self) + '.__del__()n')

def __str__(self):
return "DestTest({})".format(self.val)

def __repr__(self):
return "DestTest({})".format(self.val)

def simple_var():
d = DestTest("simple var")

def mdeleted_var():
d = DestTest("manyally deleted var")
del d

def simple_list():
a = [DestTest("simple list")]

def internal_exc():
try:
d = DestTest("exception_func")
raise IndexError()
except:
pass

def exception_func():
d = DestTest("exception_func")
raise IndexError()

def self_ref_list():
a = [DestTest("self-ref list")]
a.append(a)

def cycle_refs():
d1 = DestTest("cycle_ref_obj1")
d2 = DestTest("cycle_ref_obj2")
d3 = DestTest("free_obj")

d1.ref = d2
d2.ref = d1
d2.ref2 = d3

simple_var()
sys.stdout.write("after simple varn")
sys.stdout.write("n")

simple_list()
sys.stdout.write("after simple listn")
sys.stdout.write("n")

mdeleted_var()
sys.stdout.write("after manually deleted varn")
sys.stdout.write("n")

self_ref_list()
sys.stdout.write("after self ref listn")
gc.collect()
sys.stdout.write("after gc.collect()n")
sys.stdout.write("n")

internal_exc()
sys.stdout.write("after internal excn")

try:
exception_func()
except Exception as x:
pass
sys.stdout.write("after exception funcn")
gc.collect()
sys.stdout.write("after gc.collect()n")
try:
sys.exc_clear()
except AttributeError:
pass
else:
sys.stdout.write("after sys.exc_clear()n")
sys.stdout.write("n")

cycle_refs()
sys.stdout.write("after cycle refsn")
gc.collect()
sys.stdout.write("after gc.collect()n")
sys.stdout.write("n")

d = DestTest("module var")

import gc
import sys

class DestTest(object):
def __init__(self, val):
self.val = val

def __del__(self):
sys.stdout.write(str(self) + '.__del__()n')

def __str__(self):
return "DestTest({})".format(self.val)

def __repr__(self):
return "DestTest({})".format(self.val)

def simple_var():
d = DestTest("simple var")

def mdeleted_var():
d = DestTest("manyally deleted var")
del d

def simple_list():
a = [DestTest("simple list")]

def internal_exc():
try:
d = DestTest("exception_func")
raise IndexError()
except:
pass

def exception_func():
d = DestTest("exception_func")
raise IndexError()

def self_ref_list():
a = [DestTest("self-ref list")]
a.append(a)

def cycle_refs():
d1 = DestTest("cycle_ref_obj1")
d2 = DestTest("cycle_ref_obj2")
d3 = DestTest("free_obj")

d1.ref = d2
d2.ref = d1
d2.ref2 = d3

simple_var()
sys.stdout.write("after simple varn")
sys.stdout.write("n")

simple_list()
sys.stdout.write("after simple listn")
sys.stdout.write("n")

mdeleted_var()
sys.stdout.write("after manually deleted varn")
sys.stdout.write("n")

self_ref_list()
sys.stdout.write("after self ref listn")
gc.collect()
sys.stdout.write("after gc.collect()n")
sys.stdout.write("n")

internal_exc()
sys.stdout.write("after internal excn")

try:
exception_func()
except Exception as x:
pass
sys.stdout.write("after exception funcn")
gc.collect()
sys.stdout.write("after gc.collect()n")
try:
sys.exc_clear()
except AttributeError:
pass
else:
sys.stdout.write("after sys.exc_clear()n")
sys.stdout.write("n")

cycle_refs()
sys.stdout.write("after cycle refsn")
gc.collect()
sys.stdout.write("after gc.collect()n")
sys.stdout.write("n")

d = DestTest("module var")

В идеальном мире вызов деструктора у объектов класса DestTest во всех этих функциях должен происходить до выхода из соответствующей функции. Итак что получается:

python2.7

    DestTest(simple var).__del__()
after simple var

DestTest(simple list).__del__()
after simple list

DestTest(manyally deleted var).__del__()
after manually deleted var

after self ref list
DestTest(self-ref list).__del__()
after gc.collect()

after exception func
after gc.collect()
DestTest(exception_func).__del__()
after sys.exc_clear()

after cycle refs
after gc.collect()

Exception AttributeError: "'NoneType' object has no attribute 'stdout'" in
ignored

Более-менее. Деструктор у циклических ссылок не был вызван, как и ожидалось. Для уничтожения объектов, связанных с исключением, дошедшем до уровня модуля приходится вызывать sys.clear_exc(), в остальных случаях с исключениями все ок. Интересно, что питон освободил sys.stdout раньше, чем переменную d, в итоге чего ее деструктор отработал не корректно (впрочем это поведение не всегда повторяется).

python3.3

    DestTest(simple var).__del__()
after simple var

DestTest(simple list).__del__()
after simple list

DestTest(manyally deleted var).__del__()
after manually deleted var

after self ref list
DestTest(self-ref list).__del__()
after gc.collect()

DestTest(exception_func).__del__()
after exception func
after gc.collect()

after cycle refs
after gc.collect()

Exception AttributeError: "'NoneType' object has no attribute 'stdout'" in
ignore

Почти то-же самое, что и 2.7, только sys.exc_clear() больше не нужно.

ironpython2.7

    after simple var

after simple list

after manually deleted var

after self ref list
DestTest(self-ref list).__del__()
DestTest(manyally deleted var).__del__()
DestTest(simple list).__del__()
DestTest(simple var).__del__()
after gc.collect()

after exception func
DestTest(exception_func).__del__()
after gc.collect()
after sys.exc_clear()

after cycle refs
DestTest(free_obj).__del__()
DestTest(cycle_ref_obj2).__del__()
DestTest(cycle_ref_obj1).__del__()
after gc.collect()

DestTest(module var).__del__()

Удаляет все, но только при сборке мусора, до те
х пор все объекты живут где-то в памяти.

И — встречаем победителя. Сама лаконичность или мир всем вашим деструкторам от нашей Java и копирующего сборщика мусора: jython2.7a2 — Java HotSpot(TM) Client VM (Oracle Corporation) on java1.7.0_07

    after simple var

after simple list

after manually deleted var

after self ref list
after gc.collect()

after exception func
after gc.collect()
after sys.exc_clear()

after cycle refs
after gc.collect()

Правда в ява есть и другие варианты сборщика мусора, может там чуть по-лучше.

А вывод все тот-же. По возможности — используйте with. По невозможности — попробуйте поправить код, так что-бы можно было использовать with. Иначе — аккуратно вызывайте деструкторы руками

P.S. Как я люблю, когда мне предлагают делать какую-то рутинную работу в коде «аккуратно».

Ссылки:
          koder-ua.blogspot.com/2012/10/python-with.html
          en.wikipedia.org/wiki/Region-based_memory_management

Исходники этого и других постов со скриптами лежат тут — github.com/koder-ua. При использовании их, пожалуйста, ссылайтесь на koder-ua.blogspot.com.

Автор: konstantin danilov

Декорирование брюк принтом

В данном сезоне очень модный укороченный фасон брюк. И вы, наверняка, уже себе прикупили пару-две, а может быть и более таких же. То же самое можно сказать и о вышивке, и другого  рода декорировании – такая отделка нынче как нельзя актуальна. Предлагаем превратить обычные штанишки из магазина в эксклюзивную вещь – украсить брюки принтом.

Украшение брюк

Читать

Качаем музыку из контатка

1)Открываем страницу аудиозаписей и пролистываем до самого конца

2)Если наш браузер Chrome то жмем F12 и нажимаем на 3ю строку в появившемся окошке, тегсворачивается, мы жмем по нему ПКМ и выбираем Copy as HTML (я думаю не составит труда получить код страницы после отработавших js в других браузерах)

3)Открываем наш любимый текстовый редактор(gedit например) и вставляем в него текст (тут молимся чтобы он переварил эпичную строку в пару миллионов символов. офисные редакторы в данном случае наверное не применимы), затем сохраняем все это дело в файл (audio.vk.txt в данном случае)

4)Открываем терминал  и выполняем:

grep -o ‘http://cs.*.mp3’ audio2.vk.txt | grep -o ‘http://cs.*.mp3’ > mp3links.txt && wget -i mp3links.txt

 

автор идеи:Savalik

за что ему огромное спасибо!

Автор: r1za
Дата публикации: 2012-10-29T18:45:00.001-07:00

Создаём QR коды на Python (Перевод)

Как-то раз я задумался: было бы забавно создать программу при помощи wxPython, которая генерирует QR коды и затем отображает их на экране. Поскольку, как понятно, я хотел написать её на Python, то после недолгих поисков я обнаружил трёх кандидатов на это дело:
Я воспользовался python-qrcode и pyqrnative, так как они прекрасно работают как на Windows, так и на Mac и Linux. Кроме того, они не требуют ничего больше, только Python Imaging Library. Проект pyqrcode требует дополнительных модулей и не работает на Windows, так что я даже не стал с ним заморачиваться. В итоге я взял старый код моего  Photo Viewer и немного его переиначил, чтобы получить генератор QR кодов. Если интересно — давайте поподробнее!

Приступаем

Как я уже упомянул в начале, Вам понадобится Python Imaging Library. Мы будем использовать wxPython для создания GUI, так что без него тоже не обойтись. И кроме того Вам понадобится загрузить python-qrcode и pyqrnative. Основное отличие, которое я обнаружил между ними — python-qrcode гораздо быстрее создаёт изображение и изображение именно привычного нам вида. По каким-то причинам, pyqrnative требует больше времени и создаёт более плотно выглядящие QR коды. Возможно, у каждого из проектов есть опции, меняющие вид кодов, но у обоих проектов отвратительная документация. В итоге пришлось копаться в исходниках больше, чем когда-либо.

Создаём QR коды

Итак, как только мы установили всё, что нам нужно, мы можем запустить следующий код и посмотреть, на что же способен Python:
import os
import wx
 
try:
import qrcode
except ImportError:
qrcode = None
 
try:
import PyQRNative
except ImportError:
PyQRNative = None
 
########################################################################
class QRPanel(wx.Panel):
""""""
 
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent=parent)
self.photo_max_size = 240
sp = wx.StandardPaths.Get()
self.defaultLocation = sp.GetDocumentsDir()
 
img = wx.EmptyImage(240,240)
self.imageCtrl = wx.StaticBitmap(self, wx.ID_ANY,
wx.BitmapFromImage(img))
 
qrDataLbl = wx.StaticText(self, label="Текст для перевода в QR код:")
self.qrDataTxt = wx.TextCtrl(self, value="http://www.mousevspython.com", size=(200,-1))
instructions = "Имя файла с QR кодом"
instructLbl = wx.StaticText(self, label=instructions)
self.qrPhotoTxt = wx.TextCtrl(self, size=(200,-1))
browseBtn = wx.Button(self, label='Изменить место для сохранения')
browseBtn.Bind(wx.EVT_BUTTON, self.onBrowse)
defLbl = "Сохраняется по умолчанию в: " + self.defaultLocation
self.defaultLocationLbl = wx.StaticText(self, label=defLbl)
 
qrcodeBtn = wx.Button(self, label="Создать QR при помощи qrcode")
qrcodeBtn.Bind(wx.EVT_BUTTON, self.onUseQrcode)
pyQRNativeBtn = wx.Button(self, label="Создать QR при помощи PyQRNative")
pyQRNativeBtn.Bind(wx.EVT_BUTTON, self.onUsePyQR)
 
# компоновщик
self.mainSizer = wx.BoxSizer(wx.VERTICAL)
qrDataSizer = wx.BoxSizer(wx.HORIZONTAL)
locationSizer = wx.BoxSizer(wx.HORIZONTAL)
qrBtnSizer = wx.BoxSizer(wx.VERTICAL)
 
qrDataSizer.Add(qrDataLbl, 0, wx.ALL, 5)
qrDataSizer.Add(self.qrDataTxt, 1, wx.ALL|wx.EXPAND, 5)
self.mainSizer.Add(wx.StaticLine(self, wx.ID_ANY),
0, wx.ALL|wx.EXPAND, 5)
self.mainSizer.Add(qrDataSizer, 0, wx.EXPAND)
self.mainSizer.Add(self.imageCtrl, 0, wx.ALL, 5)
locationSizer.Add(instructLbl, 0, wx.ALL, 5)
locationSizer.Add(self.qrPhotoTxt, 0, wx.ALL, 5)
locationSizer.Add(browseBtn, 0, wx.ALL, 5)
self.mainSizer.Add(locationSizer, 0, wx.ALL, 5)
self.mainSizer.Add(self.defaultLocationLbl, 0, wx.ALL, 5)
 
qrBtnSizer.Add(qrcodeBtn, 0, wx.ALL, 5)
qrBtnSizer.Add(pyQRNativeBtn, 0, wx.ALL, 5)
self.mainSizer.Add(qrBtnSizer, 0, wx.ALL|wx.CENTER, 10)
 
self.SetSizer(self.mainSizer)
self.Layout()
 
#----------------------------------------------------------------------
def onBrowse(self, event):
""""""
dlg = wx.DirDialog(self, "Выберите папку:",
style=wx.DD_DEFAULT_STYLE)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
self.defaultLocation = path
self.defaultLocationLbl.SetLabel("Сохраняем в: %s" % path)
dlg.Destroy()
 
#----------------------------------------------------------------------
def onUseQrcode(self, event):
"""

https://github.com/lincolnloop/python-qrcode

"
""
qr = qrcode.QRCode(version=1, box_size=10, border=4)
qr.add_data(self.qrDataTxt.GetValue())
qr.make(fit=True)
x = qr.make_image()
 
qr_file = os.path.join(self.defaultLocation, self.qrPhotoTxt.GetValue() + ".jpg")
img_file = open(qr_file, 'wb')
x.save(img_file, 'JPEG')
img_file.close()
self.showQRCode(qr_file)
 
#----------------------------------------------------------------------
def onUsePyQR(self, event):
"""

http://code.google.com/p/pyqrnative/

"
""
qr = PyQRNative.QRCode(20, PyQRNative.QRErrorCorrectLevel.L)
qr.addData(self.qrDataTxt.GetValue())
qr.make()
im = qr.makeImage()
 
qr_file = os.path.join(self.defaultLocation, self.qrPhotoTxt.GetValue() + ".jpg")
img_file = open(qr_file, 'wb')
im.save(img_file, 'JPEG')
img_file.close()
self.showQRCode(qr_file)
 
#----------------------------------------------------------------------
def showQRCode(self, filepath):
""""""
img = wx.Image(filepath, wx.BITMAP_TYPE_ANY)
# масштабируем изображение
W = img.GetWidth()
H = img.GetHeight()
if W > H:
NewW = self.photo_max_size
NewH = self.photo_max_size * H / W
else:
NewH = self.photo_max_size
NewW = self.photo_max_size * W / H
img = img.Scale(NewW,NewH)
 
self.imageCtrl.SetBitmap(wx.BitmapFromImage(img))
self.Refresh()
 
 
########################################################################
class QRFrame(wx.Frame):
""""""
 
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="QR Code Viewer", size=(550,500))
panel = QRPanel(self)
 
if __name__ == "__main__":
app = wx.App(False)
frame = QRFrame()
frame.Show()
app.MainLoop()
Код для изменения и отображения картинки разъяснён в одной из предыдущих статей (не переведена), так что единственное, что может вызвать у Вас непонимание — два метода создания кодов: onUseQrcode и onUsePyQR. Я всего лишь взял примеры с их сайтов и немного поменял их, чтобы сгенерировать изображение с кодом. Они достаточно самоочевидны, но почти не документированны, так что я не смогу сказать Вам, что же тут происходит. И, хотя оба проекта страдают на момент написания от отсутствия документации, код, тем не менее работает:
Как можно видеть — вполне стандартный код. А этот, созданный PyQRNative, выглядит гораздо более «плотным»:
Оба кода были корректно распознаны моим телефоном на  Android. Так что если Вам надо создать для вашего проекта QR код — я надеюсь, эта статья Вам поможет!

Обновление от 21/05/2012

Один из моих читателей (Mike Farmer) сообщил мне недавно, что в PyQRNative “первый аргумент — размер контейнера, а второй — избыточность / коррекция ошибок”. У меня были такие подозрения по поводу второго аргумента, но я не знаю, что делают эти уровни коррекции ошибок. К счастью, Mr. Farmer объяснил это мне: Если уровень коррекции ошибок низкий, то смазывание кода или его намокание приведёт его в нечитабельное состоянии. Если же Вы будете использовать более высокий уровень коррекции, то за счёт добавления / дублирования данных внутри кода, он сможет пережить даже потоки слёз. Более того, при высоком уровне коррекции ошибок Вы можете даже наложить на код дополнительное изображение, и он  это переживёт. В любом случае, если Вы изменяете первый параметр, Вы можете изменить размер кода. Зачем? Ну, чем больше информации Вы хотите засунуть в код, тем больше он должен быть. Mr. Farmer предложил забавный тестовый код, который позволяет вычислить минимальный возможный для вашей информации размер кода:

import PyQRNative
 
def makeQR(data_string,path,level=1):
quality={1: PyQRNative.QRErrorCorrectLevel.L,
2: PyQRNative.QRErrorCorrectLevel.M,
3: PyQRNative.QRErrorCorrectLevel.Q,
4: PyQRNative.QRErrorCorrectLevel.H}
size=3
while 1:
try:
q = PyQRNative.QRCode(size,quality[level])
q.addData(data_string)
q.make()
im=q.makeImage()
im.save(path,format="png")
break
except TypeError:
size+=1

Исходник

Автор: Ishayahu Lastov

Говядина в томатах с вином

«КАК стать Барменом«

По этому рецепту получается безумно ароматный соус и мягкое мясо. Просто и вкусно!

Ингредиенты:

4 ст.л. несоленого сливочного масла
4 ст.л. оливкового масла
2 мелко порезанные моркови
2 мелко порезанные средние луковицы
500 г говяжья нога (голяшка)
1 зубчик чеснока
350 мл томатного пюре
120 мл белого сухого вина

Приготовление:

1. Разогрейте половину сливочного и оливкового масла в большой сковороде и обжарьте морковь и лук.

2. Сделайте небольшой надрез в мясе и вставьте туда зубчик чеснока.

3. Когда овощи немного поджарятся и приобретут цвет, добавьте голяшку и обжарьте ее, часто переворачивая.

4. Через несколько минут добавьте воды 250-500 мл, по необходимости, томатное пюре и оставшееся масло.

5. Варите на медленном огне до мягкости мяса, около 3 часов.

6. Добавьте вино, когда мясо будет готово.


Автор: Мирослав

Релиз Demakein 0.2: Шалмей (Перевод)

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

Автор: Ishayahu Lastov