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) на CM
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-а:
closingnestedcontextmanagercontextlib.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!!!