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

новогодняя звезда к Новому году

Новогодняя звезда

Предлагаем еще новые новогодние идеи. В уроке показана красивая новогодняя звезда как вариант для украшения жилища.

новогодняя звезда

новогодняя звезда

Читать

как просто сделать новогоднюю елку своими руками

Как сделать новогоднюю елку своими руками

Вот отличный МК для того, чтобы узнать, как просто сделать новогоднюю елку своими руками быстро и просто. Рассмотрим три разных способа.

сделать новогоднюю елку своими руками

сделать новогоднюю елку своими руками

Читать

новогодний шар со снегом

Новогодний шар со снегом

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

новогодний шар со снегом

новогодний шар со снегом

Читать

Как это сделано?

Отсутствие перегрузки функций — это то что мне всегда не нравилось в python. Не то что бы без них невозможно было жить, да и виртуальные методы типа __len__ сглаживают проблему, но все-таки. И пусть есть PEAK.rules, но его синтаксис всегда раздражал. Ну вот как можно без боли смотреть на это:

Hightlited/Raw

from peak.rules import abstract, when

@abstract()
def pprint(ob):
"""A pretty-printing generic function"""

@when(pprint, (list,))
def pprint_list(ob):
print "pretty-printing a list"

@when(pprint, (int,))
def pprint_int(ob):
print "pretty-printing an integer"

#......

from peak.rules import abstract, when

@abstract()
def pprint(ob):
"""A pretty-printing generic function"""

@when(pprint, (list,))
def pprint_list(ob):
print "pretty-printing a list"

@when(pprint, (int,))
def pprint_int(ob):
print "pretty-printing an integer"

#......

Во-первых опять нужно придумывать для каждого типа свои имена функций, во-вторых не по-питоновски много лишних нажатий клавиш, даже в С++ это — лишнее: @when(pprint, ( :).

Но как-то ничего принципиально лучше придумать не удавалось. В python 3+ можно будет в конце концов сделать отличную перегрузку методов через метаклассы, но до его массового использования в продакшене пока далековато. И вот недавно, при написании статьи про метаклассы в python 3 и находясь под влияние пересмотра одного видео с последнего pycon-videos, пришла в голову мысль которая оказалась рабочей ( впрочем я бы три раза подумал перед тем как положить такой код в файл, который будет использовать кто-то другой).

Ну собственно угадайте как работает написанное ниже (какая магия зашита в method_overloader.overloadable):

Hightlited/Raw

from method_overloader import overloadable

@overloadable()
class A(object):
def overloaded_func(self, x):
"int"
return "Integer func called {0}".format(x)

def overloaded_func(self, x):
"str"
return "String func called {0!r}".format(x)

def overloaded_func(self, x):
"float"
return "Float func called {0!r}".format(x)

def overloaded_func(self, x):
"list"
return "List func called {0!r}".format(x)

t = A()

print "t.overloaded_func(1) =", t.overloaded_func(1)
print "t.overloaded_func('asas') =", t.overloaded_func("asas")
print "t.overloaded_func(1.1) =", t.overloaded_func(1.1)
print "t.overloaded_func([1, 2, 3]]) =", t.overloaded_func([1, 2, 3])

from method_overloader import overloadable

@overloadable()
class A(object):
def overloaded_func(self, x):
"int"
return "Integer func called {0}".format(x)

def overloaded_func(self, x):
"str"
return "String func called {0!r}".format(x)

def overloaded_func(self, x):
"float"
return "Float func called {0!r}".format(x)

def overloaded_func(self, x):
"list"
return "List func called {0!r}".format(x)

t = A()

print "t.overloaded_func(1) =", t.overloaded_func(1)
print "t.overloaded_func('asas') =", t.overloaded_func("asas")
print "t.overloaded_func(1.1) =", t.overloaded_func(1.1)
print "t.overloaded_func([1, 2, 3]]) =", t.overloaded_func([1, 2, 3])

Запускаем —

    .........$ python tracer.py 
t.overloaded_func(1) = Integer func called 1
t.overloaded_func('asas') = String func called 'asas'
t.overloaded_func(1.1) = Float func called 1.1
t.overloaded_func([1, 2, 3]]) = List func called [1, 2, 3]

Все это на обычном python без подмены механизма импорта, без ковыряния в ast и т.п. Ответы можно на koder.mail@gmail.com.

P.S. Если что — в python2.X метаклассе невозможно узнать что происходило в теле класса, можно только узнать что вышло в итоге, т.е.:

Hightlited/Raw

class M(object):
s = 1
s = 2

class M(object):
s = 1
s = 2

в метакласс класса прийдет в качестве словаря класса {s : 2} и узнать что еще было s = 1 в метаклассе нельзя.

Ссылки:
          pypi.python.org/pypi/PEAK-Rules
          blip.tv/pycon-us-videos-2009-2010-2011
          docs.python.org/library/ast.html

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

Автор: konstantin danilov

Интерфейсы в python или "Предъявите документы!"

При написании не тривиальных приложений возникает вопрос: над какими библиотеками делать еще один абстрактный слой, а над какими — нет? Какие абстракции делать?

Стоит ли делать прослойку над, например, SQLAlchemy? Это же и так прослойка над SQL и DBAPI. Имеет ли смысл делать уровни абстракций над такими достаточно хорошими и отточенными в смысле интерфейсов библиотеками? Читать

Оператор with

Теория

Оператор with появился в python 2.5, но, не смотря на это, используется до сих пор недостаточно широко. Являясь упрощенной версией анонимных блоков кода with позволяет:

  • исполнить код до начала блока
  • исполнить код по выходу из блока, независимо от того это выход по исключению с помощью return или другим способом
  • обработать исключение, возникшее в блоке.

Синтаксически with выглядит следующим образом:

Hightlited/Raw

with operation:
code

with operation:
code

operation может быть объектом, выражением или конструкцией вида expression as var. Как и много других конструкций он является синтаксическим сахаром для более громоздкого выражения:

Hightlited/Raw

with operation as var:
code

with operation as var:
code

=>

Hightlited/Raw

_obj = operation

# вход в блок
var = _obj.__enter__()

try:
code
except Exception as exc:
# если произошло исключение - передаем его управляющему объекту
if not _obj.__exit__(*sys.exception_info()):
# если он вернул False(None) возбуждаем его
raise
# если True - подавляем исключение
else:
# если не было исключения - передаем None * 3
_obj.__exit__(None, None, None)

_obj = operation

# вход в блок
var = _obj.__enter__()

try:
code
except Exception as exc:
# если произошло исключение - передаем его управляющему объекту
if not _obj.__exit__(*sys.exception_info()):
# если он вернул False(None) возбуждаем его
raise
# если True - подавляем исключение
else:
# если не было исключения - передаем None * 3
_obj.__exit__(None, None, None)

Более подробно с with можно ознакомиться в соответствующем PEP-343. with управляется объектом, называемым менеджером контекста (МК) — _obj в примере выше. Есть два основных способа написания МК — класс с методами __enter__ и __exit__ и генератор:

Hightlited/Raw

import os
from contextlib import contextmanager

# Это только пример.
# Использование такого кода для генерации временных файлов
# небезопасно. Используйте функции 'os.tmpfile'.

class TempoFileCreator(object):
def __init__(self):
self.fname = None
self.fd = None< br />
def __inter__(self):
# вызывается по входу в блок
self.fname = os.tmpnam()
self.fd = open(self.fname, "w+")
return self.fname, self.fd

def __exit__(self, exc_type, exc_val, traceback):
# вызывается по выходу из блока
# если в блоке выброшено исключение, то
# его тип, значение и трейс будут переданы в параметрах

self.fd.close()
os.unlink(self.fname)
self.fd = None
self.fname = None

# здесь написано return None => исключение не будет подавляться

@contextmanager
def tempo_file():
# полностью равноценно классу TempoFileCreator
fname = os.tmpnam()
fd = open(fname, "w+")
try:
yield fname, fd
#сейчас исполняется блок
finally:
# это наш __exit__
fd.close()
os.unlink(fd)

import os
from contextlib import contextmanager

# Это только пример.
# Использование такого кода для генерации временных файлов
# небезопасно. Используйте функции 'os.tmpfile'.

class TempoFileCreator(object):
def __init__(self):
self.fname = None
self.fd = None

def __inter__(self):
# вызывается по входу в блок
self.fname = os.tmpnam()
self.fd = open(self.fname, "w+")
return self.fname, self.fd

def __exit__(self, exc_type, exc_val, traceback):
# вызывается по выходу из блока
# если в блоке выброшено исключение, то
# его тип, значение и трейс будут переданы в параметрах

self.fd.close()
os.unlink(self.fname)
self.fd = None
self.fname = None

# здесь написано return None => исключение не будет подавляться

@contextmanager
def tempo_file():
# полностью равноценно классу TempoFileCreator
fname = os.tmpnam()
fd = open(fname, "w+")
try:
yield fname, fd
#сейчас исполняется блок
finally:
# это наш __exit__
fd.close()
os.unlink(fd)

Использование:

Hightlited/Raw

with tempo_file() as (fname, fd):
# читаем-пишем в файл
# по выходу из блока он будет удален
pass

with tempo_file() as (fname, fd):
# читаем-пишем в файл
# по выходу из блока он будет удален
pass

Ядро python реализует только первый вариант для контекст менеджера, второй реализуется в contextlib.contextmanager.

В том случае если во внутреннем блоке кода есть оператор yield, т.е. мы работаем в генераторе, __exit__ будет вызван по выходу из генератора или по его удалению. Таким образом если ссылку на генератор сохранить, то __exit__ не будет вызван до тех пор, пока ссылка будет существовать:

Hightlited/Raw

@contextmanager
def cmanager():
yield
print "Exit"

def some_func():
with cmanager():
yield 1

it = some_func()
for val in it:
pass
# Exit напечатается здесь

it = some_func()

del it # или по выходу из текущего блока
# Exit напечатается здесь

@contextmanager
def cmanager():
yield
print "Exit"

def some_func():
with cmanager():
yield 1

it = some_func()
for val in it:
pass
# Exit напечатается здесь

it = some_func()

del it # или по выходу из текущего блока
# Exit напечатается здесь

Подводя итоги — with позволяет сэкономить 2-4 строки кода на каждое использование и повышает читаемость программы, меньше отвлекая нас от логики деталями реализации.

Практика

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

  • Открытие/создание объекта по входу в блок — закрытие/удаление по выходу:

Hightlited/Raw

with open('/tmp/tt.txt') as fd:
pass
# здесь файл закрывается
# переменная fd доступна, но файл уже закрыт
#

with open('/tmp/tt.txt') as fd:
pass
# здесь файл закрывается
# переменная fd доступна, но файл уже закрыт
#

Чаще всего в python программах не закрывают файл вручную, обоснованно полагаясь на подсчет ссылок. Блоки with кроме явного указания области, где файл открыт имеют еще одно небольшое преимущество, связанное с особенностями обработки исключений:

Hightlited/Raw

def i_am_not_always_close_files(fname):
fd = open(fname)

i_am_not_always_close_files("/tmp/x.txt")
# в этой точке файл уже закрыт

def i_am_not_always_close_files(fname):
fd = open(fname)

i_am_not_always_close_files("/tmp/x.txt")
# в этой точке файл уже закрыт

Если внутри фцнкции i_am_not_always_close_files будет возбуждено исключение, то файл не закроется до того момента, пока оно не будет обработано:

Hightlited/Raw

import sys

def i_am_not_always_close_files(fname):
fd = open(fname)
raise RuntimeError('')

try:
i_am_not_always_close_files("/tmp/x.txt")
except RuntimeError:
#тут файл еще открыт
traceback = sys.exc_info()[2]

# спуск на один кадр стека глубже
# 'fd' в его локальных переменных
print traceback.tb_next.tb_frame.f_locals['fd']

#

# в этой точке файл уже закрыт

import sys

def i_am_not_always_close_files(fname):
fd = open(fname)
raise RuntimeError('')

try:
i_am_not_always_close_files("/tmp/x.txt")
except RuntimeError:
#тут файл еще открыт
traceback = sys.exc_info()[2]

# спуск на один кадр стека глубже
# 'fd' в его локальных переменных
print traceback.tb_next.tb_frame.f_locals['fd']

#

# в этой точке файл уже закрыт

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

Еще пример:

Hightlited/Raw

# создадим виртуальную машину
with create_virtual_machine(root_passwd) as vm_ip:
# выполним на ней тестирования скрипта автоматической установки
test_auto_deploy_script(vm_ip, root_passwd)
# по выходу уничтожим vm_ip

# создадим виртуальную машину
with create_virtual_machine(root_passwd) as vm_ip:
# выполним на ней тестирования скрипта автоматической установки
test_auto_deploy_script(vm_ip, root_passwd)
# по выходу уничтожим vm_ip

  • Захват/освобождение объекта Эту семантику поддерживают все стандартные объекты синхронизации

Hightlited/Raw

import threading
lock = Threading.Lock()

with lock:
# блокровка захваченна
pass
# блокировка отпущенна


import threading
lock = Threading.Lock()

with lock:
# блокровка захваченна
pass
# блокировка отпущенна

  • Временное изменение настроек (примеры из документации python)

Hightlited/Raw

import warnings
from decimal import localcontext

with warnings.catch_warnings():
warnings.simplefilter("ignore")
# в этом участке кода все предепреждения игнорируются

with localcontext() as ctx:
ctx.prec = 42 # расчеты с типом Decimal выполняются с
# заоблачной точностью
s = calculate_something()

import warnings
from decimal import localcontext

with warnings.catch_warnings():
warnings.simplefilter("ignore")
# в этом участке кода все предепреждения игнорируются

with localcontext() as ctx:
ctx.prec = 42 # расчеты с типом Decimal выполняются с
# заоблачной точностью
s = calculate_something()

  • Смена текущей директории (пример использования библиотеки fabric)

Hightlited/Raw

from fabric.context_managers import lcd

os.chdir('/opt')
print os.getcwd() # => /opt

with lcd('/tmp'):
print os.getcwd() # => /tmp

print os.getcwd() # => /opt

from fabric.context_managers import lcd

os.chdir('/opt')
print os.getcwd() # => /opt

with lcd('/tmp'):
print os.getcwd() # => /tmp

print os.getcwd() # => /opt

Нужно помнить, что изменение таким образом глобальных настроек в многопоточной программе может доставить много веселых минут при отладке.

  • Подмена/восстановление объекта (временный monkey patching, пример использования библиотеки mock)

Hightlited/Raw

import mock

my_mock = mock.MagicMock()
with mock.patch('__builtin__.open', my_mock):
# open подменена на mock.MagicMock
with open('foo') as h:
pass

import mock

my_mock = mock.MagicMock()
with mock.patch('__builtin__.open', my_mock):
# open подменена на mock.MagicMock
with open('foo') as h:
pass

  • Транзакции баз данных….

Менеджер транзакций для sqlalchemy

Hightlited/Raw

from config import DB_URI
from db_session import get_session

class DBWrapper(object):

def __init__(self):
self.session = None

def __enter__(self):
self.session = get_session(DB_URI)

def __exit__(self, exc, *args):
# при выходе из 'with':
if exc is None:
# если все прошло успешно коммитим
# транзакцию и закрываем курсор
self.session.commit()

# если было исключение - откатываем
self.session.close()

# тут методы, скрывающие работу с базой

with DBWrapper() as dbw: # открываем транзакцию
dbw.get_some_data()
dbw.update_some_data("...")

from config import DB_URI
from db_session import get_session

class DBWrapper(object):

def __init__(self):
self.session = None

def __enter__(self):
self.session = get_session(DB_URI)

def __exit__(self, exc, *args):
# при выходе из 'with':
if exc is None:
# если все прошло успешно коммитим
# транзакцию и закрываем курсор
self.session.commit()

# если было исключение - откатываем
self.session.close()

# тут методы, скрывающие работу с базой

with DBWrapper() as dbw: # открываем транзакцию
dbw.get_some_data()
dbw.update_some_data("...")

  • ….и не только баз данных

Hightlited/Raw

from threading import local
import subprocess

# обобщенная транзакция - выполняет набор обратных действий
# при возникновении в блоке 'with' не обработанного исключения

class Transaction(object):
def __init__(self, parent):
self.rollback_cmds = []
self.set_parent(parent)

def set_parent(self, parent):
# родительская транзакция
# если откатывается родительская транзакция, то она автоматом
# откатывает и дочерние, даже если они было уже успешно закрыты
# если откатывается дочерняя, то родительская может продолжить
# исполнение, если код выше по стеку обработает исключение

if parent is not None:
self.parent_add = parent.add
else:
self.parent_add = lambda *cmd : None

def __enter__(self):
return self

def __exit__(self, exc, *dt):
if exc is None:
self.commit()
else:
self.rollback()

def add(self, cmd):
self.parent_add(cmd)
self.transaction.append(cmd)

def commit(self):
self.transaction = []

def rollback(self):
for cmd in reversed(self.transaction):
if isinstance(cmd, basestring):
subprocess.check_call(cmd, shell=True)
else:
cmd[0](*cmd[1:])


class AutoInheritedTransaction(object):
# словарь, id потока => [список вложенных транзакций]
# позволяет автоматически находить родительскую транзакцию
# в том случае, если для каждого потока может быть не более
# одной цепи вложенных транзакций

transactions = local()

def __init__(self):
super(AutoInheritedTransaction, self).__init__(self.current())
self.register()

def register(self):
self.transaction.list = getattr(self.transaction, 'list') + [self]

@classmethod
def current(cls):
return getattr(self.transaction, 'list', [None])[-1]

used_loop_devs = []

with AutoInheritedTransaction() as tr:
# создаем loop устройство
loop_name = subprocess.check_output("losetup -f --show /tmp/fs_image")
# вызов для его удаления
tr.add("losetup -d " + loop_name)

# записываем новое устройство в массив
used_loop_devs.append(loop_name)
tr.add(lambda : used_loop_devs.remove(
used_loop_devs.index(
loop_name)))

# монтируем его
subprocess.check_output("mount {0} /mnt/some_dir")
tr.add("umount /mnt/some_dir")

some_code

from threading import local
import subprocess

# обобщенная транзакция - выполняет набор обратных действий
# при возникновении в блоке 'with' не обработанного исключения

class Transaction(object):
def __init__(self, parent):
self.rollback_cmds = []
self.set_parent(parent)

def set_parent(self, parent):
# родительская транзакция
# если откатывается родительская транзакция, то она автоматом
# откатывает и дочерние, даже если они было уже успешно закрыты
# если откатывается дочерняя, то родительская может продолжить
# исполнение, если код выше по стеку обработает исключение

if parent is not None:
self.parent_add = parent.add
else:
self.parent_add = lambda *cmd : None

def __enter__(self):
return self

def __exit__(self, exc, *dt):
if exc is None:
self.commit()
else:
self.rollback()

def add(self, cmd):
self.parent_add(cmd)
self.transaction.append(cmd)

def commit(self):
self.transaction = []

def rollback(self):
for cmd in reversed(self.transaction):
if isinstance(cmd, basestring):
subprocess.check_call(cmd, shell=True)
else:
cmd[0](*cmd[1:])


class AutoInheritedTransaction(object):
# словарь, id потока => [список вложенных транзакций]
# позволяет автоматически находить родительскую транзакцию
# в том случае, если для каждого потока может быть не более
# одной цепи вложенных транзакций

transactions = local()

def __init__(self):
super(AutoInheritedTransaction, self).__init__(self.current())
self.register()

def register(self):
self.transaction.list = getattr(self.transaction, 'list') + [self]

@classmethod
def current(cls):
return getattr(self.transaction, 'list', [None])[-1]

used_loop_devs = []

with AutoInheritedTransaction() as tr:
# создаем loop устройство
loop_name = subprocess.check_output("losetup -f --show /tmp/fs_image")
# вызов для его удаления
tr.add("losetup -d " + loop_name)

# записываем новое устройство в массив
used_loop_devs.append(loop_name)
tr.add(lambda : used_loop_devs.remove(
used_loop_devs.index(
loop_name)))

# монтируем его
subprocess.check_output("mount {0} /mnt/some_dir")
tr.add("umount /mnt/some_dir")

some_code

Эта модель программирования позволяет группировать в одной точке код прямой и обратной операции и избавляет от вложенных try/finally. Также with предоставляет естественный интерфейс для STM. cpython-withatomic — один из вариантов STM для руthon с поддержкой with.

  • Подавление исключений

Hightlited/Raw

def supress(*ex_types):
# стоит добавить логирования подавляемого исключения
try:
yield
except Exception as x:
if not isinstance(x, ex_types):
raise

with supress(OSError):
os.unlink("some_file")

def supress(*ex_types):
# стоит добавить логирования подавляемого исключения
try:
yield
except Exception as x:
if not isinstance(x, ex_types):
raise

with supress(OSError):
os.unlink("some_file")

  • Генерация XML/HTML других структурированных языков.

Hightlited/Raw

from xmlbuilder import XMLBuilder

# новый xml документ

x = XMLBuilder('root')
x.some_tag
x.some_tag_with_data('text', a='12')

# вложенные теги
with x.some_tree(a='1'):
with x.data:
x.mmm
x.node(val='11')

print str(x) # <= string object


from xmlbuilder import XMLBuilder

# новый xml документ

x = XMLBuilder('root')
x.some_tag
x.some_tag_with_data('text', a='12')

# вложенные теги
with x.some_tree(a='1'):
with x.data:
x.mmm
x.node(val='11')

print str(x) # <= string object

Получим в итоге:

Hightlited/Raw



/>
a="12">text
a="1">

/>
val="11" />






text






Код библиотеки находится на xmlbuilder.

  • Трассировка блока в логере (установка sys.settrace)

Hightlited/Raw

import sys
import contextlib

def on_event(fr, evt, data):
print fr, evt, data
return on_event

@contextlib.contextmanager
def trace_me():

prev_trace = sys.gettrace()
sys.settrace(on_event)
try:
yield
finally:
sys.settrace(prev_trace)
print "after finally"


with trace_me():
print "in with"
x = 1
y = 2
print "before gettrace"
sys.gettrace()
print "after gettrace"

import sys
import contextlib

def on_event(fr, evt, data):
print fr, evt, data
return on_event

@contextlib.contextmanager
def trace_me():

prev_trace = sys.gettrace()
sys.settrace(on_event)
try:
yield
finally:
sys.settrace(prev_trace)
print "after finally"


with trace_me():
print "in with"
x = 1
y = 2
print "before gettrace"
sys.gettrace()
print "after gettrace"

Этот
код напечатает:

    in with
before gettrace
after gettrace
call None
line None
line None
line None
call None
line None
after finally

Для лучшего понимания трассировки питона — python-aware-python.

Ссылки:
          www.python.org/dev/peps/pep-0343
          docs.python.org/reference/compound_stmts.html#the-with-statement
          github.com/koder-ua/megarepo/tree/master/xmlbuilder/xmlbuilder
          www.voidspace.org.uk/python/mock/compare.html#mocking-a-context-manager
          en.wikipedia.org/wiki/Monkey_patch
          www.sqlalchemy.org
          fabfile.org
          en.wikipedia.org/wiki/Software_Transaction_Memory
          bitbucket.org/arigo/cpython-withatomic
          blip.tv/pycon-us-videos-2009-2010-2011/pycon-2011-python-aware-python-4896752

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

Автор: konstantin danilov