Onde um nome vive
O escopo de um nome é a parte do programa onde ele pode ser usado. Um nome criado dentro de uma função é local: existe só enquanto a função executa e é invisível fora dela. Um nome criado no nível superior do arquivo é global: as funções podem lê-lo. Quando o Python encontra um nome, procura primeiro na função, depois em qualquer função envolvente, depois no arquivo e por fim nos nomes embutidos.
rate = 0.001 # global: defined outside any function
def fee(notional):
minimum = 1.0 # local: exists only during the call
return max(minimum, notional * rate)
print(fee(36850.0)) # 36.85
# print(minimum) # would raise NameError: minimum is not definedAtribuir cria um nome local
Ler um global de dentro de uma função não tem problema. Mas atribuir a um nome dentro de uma função cria um novo nome local, mesmo que exista um global com o mesmo nome: o global fica intacto. Isso protege seus dados, e é também por isso que mudar um estado global a partir de uma função exige palavras extras (global) de que você raramente deve precisar. Passe os valores como argumentos e devolva os resultados com return.
# Assigning inside a function makes a new local name
balance = 1000
def deposit(amount):
balance = amount # a different, local balance
return balance
print(deposit(50)) # 50
print(balance) # 1000 (the global one is unchanged)Funções que criam funções
Uma função pode definir outra dentro de si e devolvê-la com return. A função interna mantém acesso às variáveis da externa, mesmo depois que a externa termina. Esse pacote de uma função e os valores que ela lembra é uma closure. É um jeito elegante de montar uma ferramenta pronta a partir de configurações: uma calculadora para ações e outra para ETFs.
# A closure remembers the variables of the function that made it
def make_fee_calculator(rate, minimum):
def fee(notional):
return max(minimum, notional * rate)
return fee
stock_fee = make_fee_calculator(0.001, 1.0)
etf_fee = make_fee_calculator(0.0005, 0.5)
print(stock_fee(36850.0)) # 36.85
print(etf_fee(36850.0)) # 18.425Estado que vive entre chamadas: nonlocal
Uma closure também pode alterar o que lembra. Como atribuir criaria um novo nome local, a função interna declara antes nonlocal count: «este nome pertence à função envolvente». Cada chamada da função externa cria sua própria memória, então dois contadores nunca interferem. É assim que um gerador de identificadores de ordens ou uma média móvel guarda seu estado sem nenhuma variável global.
# nonlocal lets the inner function rebind the outer variable
def make_counter():
count = 0
def next_id():
nonlocal count
count += 1
return count
return next_id
order_ids = make_counter()
print(order_ids(), order_ids(), order_ids()) # 1 2 3
other = make_counter()
print(other()) # 1 (its own count)# State between calls: an exponential moving average
def make_ema(alpha):
value = None
def update(price):
nonlocal value
value = price if value is None else alpha * price + (1 - alpha) * value
return value
return update
ema = make_ema(0.5)
print(ema(100.0), ema(110.0), ema(110.0)) # 100.0 105.0 107.5- Dentro de uma função, os nomes que você atribui são locais; passe dados para dentro e devolva com
return. - Uma função que constrói e devolve uma função transforma configurações em ferramenta.
- Use
nonlocalpara o estado que deve sobreviver entre chamadas, uma memória por closure.
A seguir: list comprehensions, para construir uma nova lista a partir de outra em uma única linha legível.
Lab: o que é avaliado
- Construir um contador que lembra sua contagem com
make_counter25 pts - Empacotar configurações de taxa em uma função com
make_fee_calculator25 pts - Manter uma média móvel entre chamadas com
make_ema25 pts - Criar uma verificação de preço a partir de um limite com
make_limit_checker25 pts