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-а:
closing
nested
contextmanager
contextlib.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!!!