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))
Декоратор, който:
Имате два варианта да го направите
stubborn_open = with_retries(3, open)
stubborn_open = with_rerties(3)(open)
Ще предпочетем втория вариант
Трябва да направим функция with_retries(number)
, която да връща декоратор. Тя изглежда така:
def with_retries(number): def decorator(func): """Тяло на декоратора, виждащо number, тук""" # TODO return decorator
def with_retries(number): def decorator(func): def retrying(*args, **kwargs): retries_left = number while retries_left: try: return func(*args, **kwargs) except: retries_left -= 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 name_register(): return [_.name for _ in Person._people] name_register = staticmethod(name_register)
И все пак…
def larodi(number): return do_stuff(number) larodi = decorator(larodi)
…е грозно. А и има шанс да не видите декоратора, понеже е отдолу.
@memoized def fib(n): # ... return result class Person(object): # ... @staticmethod def name_register(): 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 #{0} of '{1}' should have been " "of type {2}".format(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 '{0} {1}'.format(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 get_name(self): return '{0} {1}'.format(self.first, self.last) def set_name(self): self.first, self.last = value.split(None, 1) def __getattr__(self, attr): if 'name' == attr: return self.get_name() return object.__getattr__(self, attr) def __setattr__(self, attr, value): if 'name' == attr: self.set_name(value) else: object.__setattr__(self, attr, value)
class Person(object): def __init__(self, first, last): self.first, self.last = first, last def get_name(self): """Full name""" return '{0} {1}'.format(self.first, self.last) def set_name(self, value): self.first, self.last = value.split(None, 1) name = property(get_name, set_name)
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: source_file = open(src, 'r') buffer = [] try: buffer = source_file.readlines() finally: source_file.close() target_file = open(target, 'w') try: for line in reversed(buffer): target_file.write(line) finally: target_file.close() except IOError: print("Tough luck, junior")
buffer = [] try: with open(src) as source_file: buffer = source_file.readlines() with open(target) as target_file: for line in reversed(buffer): target_file.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)
на CMclass 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 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 contextlib import contextmanager @contextmanager def entering(whom): print("I've been entered by {0}".format(whom)) yield "ticket" print("I've been exited!") with entering("someone") as something: print("Am I inside?") print(something)
Pearl Jam FTW!!!