„ Програмиране с Python“, ФМИ
Стефан Кънев & Николай Бачийски
04.04.2007г.
Декоратори
- Припомнете си как правихме статични методи:
class Mityo:
# не зависи от инстанцията -- винаги пасуват
def work(): pass
work = staticmethod(work)- Горното се нарича „декориране“ на метода
work
- Подходът за смилане на метод или функция за да бъдат по-различни или по-полезни е често срещан в Python
Синтаксис
def add(a, b):
return a+b
add = accepts2ints(add)
Поради честотата на използване и поради неудобността на горния синтаксис, в Python си има отделен синтаксис за декорирането.
@accepts2ints
def add(a, b):
return a+b
В този случай, accepts2ints
трябва да бъде функция, която приема функция и връща нейната нова версия.
Пример за прост декоратор
def notifyme(f):
def logged(*args, **kwargs):
print f.__name__, 'was called with', args, 'and', kwargs
return f(*args, **kwargs)
return logged
@notifyme
def square(x): return x*x
res = square(25)
square was called.
Няколко декоратора на една функция
class Mityo:
@staticmethod
@notifyme
def work(): pass
Mityo.work()
work was called with () and {}
Горният метод е еквивалентен на:
def work(): pass
work = notifyme(work)
work = classmethod(work)
Или:
work = classmethod(notifyme(work))
Параметри на декораторите
Ако се върнем към примера с accepts2int
, сигурно ще ни се прииска да си напишем малко по-общ декоратор, на когото да казваме типовете, които искаме функцията ни искаме да приема а не да пишем нов декоратор за всяка комбинация, която ни хрумне.
@accepts(int, int)
def add(a, b): return a+b
Това се превежда до следното:
add = accepts(int, int)(add)
Другояче казано accepts
трябва да бъде функция, която приема като аргументи типовете и връща друга фунцкия. Тя пък трябва да приема вече фунцкията, която ще декорираме и да върне декорираната.
Код срещу думи
def accepts(*types):
def accepter(f):
def decorated(*args):
for (i, (arg, t)) in enumerate(zip(args, types)):
if not isinstance(arg, t):
raise TypeError("Argument #%d of '%s' should have been of type %s" % (i, f.__name__, t.__name__))
#TODO: more complex checks: tuple of a type, list of type
return f(*args)
return decorated
return accepter
Още код
instances = {}
def singleton(cls):
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
@singleton
class MyClass:
Вградени декоратори
staticmethod
— прави един метод статиченclassmethod
— прави класов метод (като първи аргумент приема класа)
Работа с файлове
src = raw_input()
target = raw_input()
try:
sourceFile = open(src, 'r')
buffer = []
try:
buffer = sourceFile.readlines()
finally:
sourceFile.close()
targetFile = open(target, 'w')
try:
for line in reversed(buffer):
targetFile.write(line)
finally:
targetFile.close()
except IOError:
print "Tough luck, junior"
With
- Твърдението
with
позволява да преизползвате сходнаtry/catch
логика (и не само) - Синтаксис:
with [израз]:
[блок]
или
with [израз] as [име]:
[блок]
Втори опит
src = raw_input()
target = raw_input()
buffer = []
try:
with open(src) as sourceFile:
buffer = sourceFile.readlines()
with open(target) as targetFile:
for line in reversed(buffer):
targetFile.write(line)
except IOError:
print "Much better, now, ain't it?"
Как работи with?
- Изразът който получава се изпълнява и се взема върнатия обект. Той се нарича context manager
- Методът
__enter__()
на content manager-а се изпълнява. Получената от него стойност се записва в името след as. - Изпълнява се блокът подаден на
with
- Ако се получи изключение в блока, се извиква метода
__exit__(type, value, traceback)
на context manager-а. - Ако не се получи изключение, пак се вика
__exit__(None, None, None)
.
Как се ползва with?
Понеже with
е нова ключова дума, въведена в Python 2.5, се налага да добавите малко код към вашето приложение за да я ползвате:
from __future__ import with_statement
Ако не сте импортирали with
и го ползвате като име, Python ще даде грешка. В Python 2.6 with
ще го има по подразбиране и няма да се налага да го импортвате. Това е стандартния подход за приемане на нови feature-и в езика.
Малък пример
from __future__ import with_statement
class Manager:
def __enter__(self):
print "I've been entered!"
return "ticket"
def __exit__(self, type, value, traceback):
print "I've been exited!"
with Manager() as something:
print "Am I inside?"
print something
I've been entered!
Am I inside?
ticket
I've been exited!
contextlib
Модула contextlib предлага следните неща:
- closing
- nested
- contextmanager
closing
contextlib.closing
е нещо подобно на...
class closing(object):
def __init__(self, thing): self.thing = thing
def __enter__(self): return thing
def __exit__(self, type, value, traceback): self.thing.close()
...и ви позволява да пишете така...
from __future__ import with_statement
from contextlib import closing
import codecs
with closing(urllib.urlopen('http://www.python.org')) as page:
for line in page:
print line
nested
Долния код...
from contextlib import nested
with nested(A, B, C) as (X, Y, Z):
do_something()
...е еквивалентен на...
with A as X:
with B as Y:
with C as Z:
do_something()
contextmanager
contextmanager
е декоратор, който превръща генератор функция в context manager:
from __future__ import with_statement
from contextlib import contextmanager
@contextmanager
def entering(whom):
print "I've been entered by %s" % whom
yield "ticket"
print "I've been exited!"
with entering("someone") as something:
print "Am I inside?"
print something
I've been entered by someone
Am I inside?
ticket
I've been exited!
Няма коментари:
Публикуване на коментар