f от вида:
f(функция) -> функцияРезултата е нова функция, разширяваща функционалността на аргумента си.
def memoize(func):
memory = {}
def memoized(*args):
if args in memory: return memory[args]
result = func(*args)
memory[args] = result
return result
return memoized
def fib(x):
if x in [0, 1]: return 1
return fib(x - 1) + fib(x - 2)
fib = memoize(fib)
print fib(33)
Декоратор, който:
Имате два варианта да го направите
stubbornOpen = withRetries(3, open)
stubbornOpen = withRerties(3)(open)
Ще предпочетем втория вариант
Трябва да направим функция withRetries(number), която да връща декоратор. Тя изглежда така:
def withRetries(number):
def decorator(func):
"""Тяло на декоратора, виждащо number, тук""" # TODO
return decorator
def withRetries(number):
def decorator(func):
def retrying(*args, **kwargs):
retriesLeft = number
while retriesLeft:
try: return func(*args, **kwargs)
except: retriesLeft -= 1
return func(*args, **kwargs)
return retrying
return decorator
Вградените функции staticmethod и classmethod също са декоратори.
class Person(object):
_people = []
def __init__(self, name):
self.name = name
Person._people.append(self)
def nameRegister():
return [_.name for _ in Person._people]
nameRegister = staticmethod(nameRegister)
И все пак…
def larodi(number):
return doStuff(number)
larodi = decorator(larodi)
…е грозно. А и има шанс да не видите декоратора, понеже е отдолу.
@memoized
def fib(n):
# ...
return result
class Person(object):
# ...
@staticmethod
def nameRegister():
return [_.name for _ in Person._people]
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 with (25,) and {}.
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))
Първо се извикват най-вътрешните декоратори.
@accepts(int, int) def add(a, b): return a+b
add = accepts(int, int)(add)
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
duck typing е много важна част от философията на Python. @accepts е забавен пример и дори има някои употреби, но избягвайте да го ползвате масово. В повечето случаи губите, а не печелите.
classmethod — прави метода класов (приема клас, а не обект като първи аргумент)staticmethod — прави метода статиченproperty
class Person(object):
def __init__(self, first, last):
self.first, self.last = first, last
def name(self, value=None):
if value == None:
return '%s %s' % (self.first, self.last)
else:
self.first, self.last = value.split(None, 1)
pijo = Person('Пижо', 'Пендов')
print pijo.first
pijo.last = 'Пендов'
print pijo.last
print pijo.name()
pijo.name('Кънчо Кънчев')
print pijo.last
class Person(object):
def __init__(self, first, last):
self.first, self.last = first, last
def getName(self):
return '%s %s' % (self.first, self.last)
def setName(self):
self.first, self.last = value.split(None, 1)
def __getattr__(self, attr):
if 'name' == attr:
return self.getName()
return object.__getattr__(self, attr)
def __setattr__(self, attr, value):
if 'name' == attr:
self.setName(value)
else:
object.__setattr__(self, attr, value)
class Person(object):
def __init__(self, first, last):
self.first, self.last = first, last
def getName(self):
"""Full name"""
return '%s %s' % (self.first, self.last)
def setName(self, value):
self.first, self.last = value.split(None, 1)
name = property(getName, setName)
property(fget=None, fset=None, fdel=None, doc=None)fget, ако doc е None
class Parrot(object):
def __init__(self):
self._voltage = 100000
@property
def voltage(self):
"""Get the current voltage."""
return self._voltage
voltage в getter към атрибут само за четене със същото имеАтрибутите на един обект трябва да бъдат достъпвани през хомогенна нотация, която не издава дали те се изчисляват или са записани.
class TypedAttr(object):
def __init__(self, type, initial):
self.initial, self.type = initial, type
self.values = {}
def __get__(self, instance, owner):
print self.values
return self.values.get(id(instance), self.initial)
def __set__(self, instance, value):
self.values[id(instance)] = self.type(value)
def __delete__(self, instance):
del self.values[id(instance)]
class C(object):
n = TypedAttr(int, 0)
s = TypedAttr(str, 'baba')
c = C()
c.n = 11
print c.n
c.n = 'baba'
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"
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 израз [as име]:
блок
__enter__() на CM и резултатът се записва в името след as__exit__(type, value, traceback) на CM__exit__(None, None, None) на CMwith се повявява за първи път в Python 2.5from __future__ import with_statement
from __future__ import with_statement
class Manager:
def __enter__(self):
print "I've been entered!"
return 42
def __exit__(self, type, value, traceback):
print "I've been exited!"
with Manager() as something:
print "Am I inside?"
print something
Вграденият модул contextlib ни предлага три много полезни Context Manager-а:
closingnestedcontextmanagercontextlib.closing вика метода close на обекта, с който работим, след изпълнение на блока:
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
Този код:
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 е декоратор, който превръща генератор функция в 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
Pearl Jam FTW!!!