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))
И все пак…
def fib(x): if x in [0, 1]: return 1 return fib(x - 1) + fib(x - 2) fib = memoize(fib)
…е грозно. А и има шанс да не видите декоратора, понеже е отдолу.
@memoized def fib(n): if x in [0, 1]: return 1 return fib(x - 1) + fib(x - 2)
Декоратор, който приема параметри.
@memoize('/tmp/fibs') def fib(n): ...
е равно на
def fib(n): ... fib = memoize('/tmp/fibs')(fib)
Да не се бърка с fib = memoize('/tmp/fibs', fib)
Трябва да направим функция 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 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 = staticmethod(work)
или:
work = staticmethod(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 към атрибут само за четене със същото имеАтрибутите на един обект трябва да бъдат достъпвани през хомогенна нотация, която не издава дали те се изчисляват или са записани.
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)
на CMwith open('/etc/passwd') as source_file: buffer = source_file.readlines() print('Done!')
е същото като
source_file = open('/etc/passwd').__enter__() try: buffer = source_file.readlines() source_file.__exit__(None, None, None) except Exception: source_file.__exit__(*sys.exc_info()) print('Done!')
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)
I've been entered!
Am I inside?
42
I've been exited!
Вграденият модул 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()