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 към атрибут само за четене със същото имеАтрибутите на един обект трябва да бъдат достъпвани през хомогенна нотация, която не издава дали те се изчисляват или са записани.