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)
на CM
with 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()