global_one = 1
def foo():
local_one = 2
print(locals())
print(globals()) # {..., 'global_one': 1}
foo() # {'local_one': 2}
global_one = 1
def foo():
print(global_one)
foo()
А какво ще изведе следният код?
global_one = 1
def foo():
global_one = 2
print(global_one)
print(locals())
foo()
class Foo:
global_one = 2
print(global_one)
print(locals())
print(globals())
Една променлива "умира" заедно със своя скоуп
def outer(x):
print(x)
def inner():
x = 0
print(x)
inner()
print(x)
Имаме closure, когато вложена функция достъпва променлива, дефинирана в обграждаща функция
def start(x):
def increment(y):
return x + y
return increment
first_inc = start(0)
second_inc = start(8)
first_inc(3)
second_inc(3)
first_inc(1)
second_inc(2)
def spam(n):
spams = ("spam", ) * (n - 1)
return "I would like {} and spam".format(", ".join(spams))
def eggs(n):
return "I would like {} eggs".format(n)
def spam(n, server):
spams = ("spam", ) * (n - 1)
return "I would like {} and spam, dear {}".format(", ".join(spams), server)
def eggs(n, server):
return "I would like {} eggs, dear {}".format(n, server)
spam(3, "sir")
def spam(n):
spams = ("spam", ) * (n - 1)
return "I would like {} and spam".format(", ".join(spams))
def eggs(n):
return "I would like {} eggs".format(n)
def served_by(func, server):
def cached_server(n):
return "{}, dear {}".format(func(n), server)
return cached_server
eggs = served_by(eggs, "sir")
spam = served_by(spam, "sir")
def thank_you(func):
def with_thanks(n):
return "{}. Thank you very much!".format(func(n))
return with_thanks
eggs = thank_you(served_by(eggs, "sir"))
spam = served_by(spam, "sir")
def fibonacci(x):
if x in [0,1]:
return 1
return fibonacci(x-1) + fibonacci(x-2)
Рекурсивната версия на fibonacci, освен че е бавна, е много бавна. особено когато x >= 40.
Проблемът е, че fibonacci се извиква стотици пъти с един и същ аргумент. Можем спокойно да прегенерираме първите стотина резултати в един речник или...
if x not in memory:
memory[x] = fibonacci(x)
print(memory[x])
Разбира се, тази идея може да се използва и на много повече места! Можем да я направим още по-елегантно.
def memorize(func):
memory = {}
def memorized(*args):
if args in memory:
return memory[args]
result = func(*args)
memory[args] = result
return result
return memorized
fibonacci = memorize(fibonacci)
def fibonacci(x):
if x in [0,1]:
return 1
return fibonacci(x-1) + fibonacci(x-2)
fibonacci = memorize(fibonacci)
Декорацията става след дефиницията на функцията.
@memorize
def fibonacci(x):
if x in [0,1]:
return 1
return fibonacci(x-1) + fibonacci(x-2)
@memorize
def fibonacci(x):
if x in [0,1]:
return 1
return fibonacci(x-1) + fibonacci(x-2)
def notifyme(f):
def logged(*args, **kwargs):
print(f.__name__, ' called with', args, 'and', kwargs)
return f(*args, **kwargs)
return logged
@notifyme
def square(x):
return x * x
res = square(25) # 625
#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))
def served_by(server):
def decorator(func):
def cached_server(n):
return "{}, dear {}".format(func(n), server)
return cached_server
return decorator
def thank_you(func):
def with_thanks(n):
return "{}. Thank you very much!".format(func(n))
return with_thanks
@served_by("sir")
def spam(n):
spams = ("spam", ) * (n - 1)
return "I would like {} and spam".format(", ".join(spams))
@thank_you
@served_by("sir")
def eggs(n):
return "I would like {} eggs".format(n)
Всъщност, защо да не си направим следния декоратор:
@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}' is not {2}""".format(
i,
f.__name__,
t.__name__))
#TODO: more complex checks
return f(*args)
return decorated
return accepter
duck typing е много важна част от философията на Python. @accepts е забавен пример и дори има някои употреби, но избягвайте да го ползвате масово. В повечето случаи губите, а не печелите.
class Battery(object):
def __init__(self):
self._voltage = 100000
@property
def voltage(self):
"""Get the current voltage."""
return self._voltage
Това превръща voltage в getter към атрибут само за четене със същото име
b = Battery()
print(b.voltage) # 100000
@voltage.setter
def voltage(self, value):
self._voltage = value
@staticmethod
def register(name):
Person.people.append(name)
print(len(Person.people), "people are registered now")
[...]
@classmethod
def greet(cls, someone):
print(someone, "was greeted from", cls)