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