É possível ter atribuição em uma condição?
Por ex.
if (a=some_func()):
# Use a
Respostas:
Por que não experimentá-lo?
>>> def some_func():
... return 2
...
>>> a = 2
>>> if (a = some_func()):
File "<stdin>", line 1
if (a = some_func()):
^
SyntaxError: invalid syntax
>>>
Então não.
Atualização: isso é possível (com sintaxe diferente) no Python 3.8
x = x + 1requer tempo de pesquisa adicional enquanto x += 1era um pouco mais rápido, mas tenho certeza de que ele nem gostava de fazer isso . :-)
Python 3.8 trará PEP572
Resumo
Esta é uma proposta para criar uma maneira de atribuir a variáveis dentro de uma expressão usando a notação NOME: = expr. Uma nova exceção, TargetScopeError, é adicionada e há uma alteração na ordem de avaliação.
https://lwn.net/Articles/757713/
A "bagunça do PEP 572" foi o tópico de uma sessão do Python Language Summit 2018 liderada pelo benevolente ditador vitalício (BDFL) Guido van Rossum. O PEP 572 busca adicionar expressões de atribuição (ou "atribuições inline") à linguagem, mas tem visto uma discussão prolongada sobre vários tópicos enormes na lista de e-mail python-dev - mesmo depois de várias rodadas de ideias python. Esses tópicos costumavam ser controversos e claramente volumosos a ponto de muitos provavelmente simplesmente os ignorarem. No encontro, Van Rossum deu uma visão geral da proposta de recurso, que ele parece inclinado a aceitar, mas ele também queria discutir como evitar esse tipo de explosão de discussão no futuro.
https://www.python.org/dev/peps/pep-0572/#examples-from-the-python-standard-library
Exemplos da biblioteca padrão Python
site.py env_base é usado apenas nessas linhas, colocando sua atribuição no if move-o como o "cabeçalho" do bloco.
Atual:
env_base = os.environ.get("PYTHONUSERBASE", None) if env_base: return env_baseMelhorado:
if env_base := os.environ.get("PYTHONUSERBASE", None): return env_base _pydecimal.pyEvite se aninhado e remova um nível de indentação.
Atual:
if self._is_special: ans = self._check_nans(context=context) if ans: return ansMelhorado:
if self._is_special and (ans := self._check_nans(context=context)): return ansO código copy.py parece mais regular e evita vários if aninhados. (Veja o Apêndice A para a origem deste exemplo.)
Atual:
reductor = dispatch_table.get(cls) if reductor: rv = reductor(x) else: reductor = getattr(x, "__reduce_ex__", None) if reductor: rv = reductor(4) else: reductor = getattr(x, "__reduce__", None) if reductor: rv = reductor() else: raise Error( "un(deep)copyable object of type %s" % cls)Melhorado:
if reductor := dispatch_table.get(cls): rv = reductor(x) elif reductor := getattr(x, "__reduce_ex__", None): rv = reductor(4) elif reductor := getattr(x, "__reduce__", None): rv = reductor() else: raise Error("un(deep)copyable object of type %s" % cls) datetime.pytz é usado apenas para s + = tz, mover sua atribuição dentro de if ajuda a mostrar seu escopo.
Atual:
s = _format_time(self._hour, self._minute, self._second, self._microsecond, timespec) tz = self._tzstr() if tz: s += tz return sMelhorado:
s = _format_time(self._hour, self._minute, self._second, self._microsecond, timespec) if tz := self._tzstr(): s += tz return ssysconfig.py Chamar fp.readline () na condição while e chamar .match () nas linhas if tornam o código mais compacto, sem
tornando mais difícil de entender.
Atual:
while True: line = fp.readline() if not line: break m = define_rx.match(line) if m: n, v = m.group(1, 2) try: v = int(v) except ValueError: pass vars[n] = v else: m = undef_rx.match(line) if m: vars[m.group(1)] = 0Melhorado:
while line := fp.readline(): if m := define_rx.match(line): n, v = m.group(1, 2) try: v = int(v) except ValueError: pass vars[n] = v elif m := undef_rx.match(line): vars[m.group(1)] = 0Simplificando as compreensões de lista Uma compreensão de lista pode mapear e filtrar com eficiência, capturando a condição:
results = [(x, y, x/y) for x in input_data if (y := f(x)) > 0]Da mesma forma, uma subexpressão pode ser reutilizada dentro da expressão principal, dando a ela um nome no primeiro uso:
stuff = [[y := f(x), x/y] for x in range(5)]Observe que em ambos os casos a variável y é limitada no escopo de conteúdo (ou seja, no mesmo nível dos resultados ou coisas).
Capturando valores de condição As expressões de atribuição podem ser usadas com bons resultados no cabeçalho de uma instrução if ou while:
# Loop-and-a-half while (command := input("> ")) != "quit": print("You entered:", command) # Capturing regular expression match objects # See, for instance, Lib/pydoc.py, which uses a multiline spelling # of this effect if match := re.search(pat, text): print("Found:", match.group(0)) # The same syntax chains nicely into 'elif' statements, unlike the # equivalent using assignment statements. elif match := re.search(otherpat, text): print("Alternate found:", match.group(0)) elif match := re.search(third, text): print("Fallback found:", match.group(0)) # Reading socket data until an empty string is returned while data := sock.recv(8192): print("Received data:", data)Particularmente com o loop while, isso pode eliminar a necessidade de ter um loop infinito, uma atribuição e uma condição. Ele também cria um paralelo suave entre um loop que simplesmente usa uma chamada de função como sua condição e um que usa isso como sua condição, mas também usa o valor real.
Fork Um exemplo do mundo UNIX de baixo nível:
if pid := os.fork(): # Parent code else: # Child code
http://docs.python.org/tutorial/datastructures.html
Observe que em Python, ao contrário de C, a atribuição não pode ocorrer dentro de expressões. Os programadores C podem reclamar sobre isso, mas evita uma classe comum de problemas encontrados em programas C: digitar = em uma expressão quando == era pretendido.
Veja também:
http://effbot.org/pyfaq/why-can-ti-use-an-assignment-in-an-expression.htm
if (foo = 'bar')enquanto pretendem testar o valor de foo.
=
a if a := 1 == 1 else False→ Invalid syntax: c Então, aparentemente, ainda tem que recorrer ao antigo estranho (lambda: (ret := foo(), ret if ret else None))()[-1] (ou seja, como uma forma de evitar a chamada repetida para foo()no ternário) .
Não, o BDFL não gostou desse recurso.
De onde estou, Guido van Rossum, "Benevolent Dictator For Life", lutou muito para manter o Python tão simples quanto possível. Podemos questionar algumas das decisões que ele tomou - eu teria preferido que ele dissesse 'Não Mas o fato de não haver um comitê para projetar Python, mas sim um "conselho consultivo" confiável, baseado em grande parte no mérito, filtrando as sensibilidades de um designer, produziu uma linguagem incrível, IMHO.
if letquando tenho uma cadeia if elif, mas preciso armazenar e usar o valor da condição em cada caso.
Não diretamente, de acordo com esta receita minha - mas como a receita diz, é fácil construir o equivalente semântico, por exemplo, se você precisar transliterar diretamente de um algoritmo de referência codificado em C (antes de refatorar para Python mais idiomático, é claro; -). Ie:
class DataHolder(object):
def __init__(self, value=None): self.value = value
def set(self, value): self.value = value; return value
def get(self): return self.value
data = DataHolder()
while data.set(somefunc()):
a = data.get()
# use a
BTW, uma forma Pythônica muito idiomática para o seu caso específico, se você souber exatamente qual valor falso somefuncpode retornar quando retornar um valor falso (por exemplo 0), é
for a in iter(somefunc, 0):
# use a
então, neste caso específico, a refatoração seria muito fácil ;-).
Se o retorno poderia ser qualquer tipo de valor falsish (0, None, '', ...), uma possibilidade é:
import itertools
for a in itertools.takewhile(lambda x: x, iter(somefunc, object())):
# use a
mas você pode preferir um gerador personalizado simples:
def getwhile(func, *a, **k):
while True:
x = func(*a, **k)
if not x: break
yield x
for a in getwhile(somefunc):
# use a
Sim, mas apenas a partir do Python 3.8.
O PEP 572 propõe Expressões de Atribuição e já foi aceito.
Citando a parte de sintaxe e semântica do PEP:
# Handle a matched regex
if (match := pattern.search(data)) is not None:
# Do something with match
# A loop that can't be trivially rewritten using 2-arg iter()
while chunk := file.read(8192):
process(chunk)
# Reuse a value that's expensive to compute
[y := f(x), y**2, y**3]
# Share a subexpression between a comprehension filter clause and its output
filtered_data = [y for x in data if (y := f(x)) is not None]
No seu caso específico, você será capaz de escrever
if a := some_func():
# Use a
Não. A atribuição em Python é uma declaração, não uma expressão.
Graças ao novo recurso do Python 3.8 , será possível fazer isso a partir desta versão, embora não usando, =mas o operador de atribuição do tipo Ada :=. Exemplo dos documentos:
# Handle a matched regex
if (match := pattern.search(data)) is not None:
# Do something with match
Você pode definir uma função para fazer a atribuição para você:
def assign(name, value):
import inspect
frame = inspect.currentframe()
try:
locals_ = frame.f_back.f_locals
finally:
del frame
locals_[name] = value
return value
if assign('test', 0):
print("first", test)
elif assign('xyz', 123):
print("second", xyz)
Uma das razões pelas quais as atribuições são ilegais em condições é que é mais fácil cometer um erro e atribuir Verdadeiro ou Falso:
some_variable = 5
# This does not work
# if True = some_variable:
# do_something()
# This only works in Python 2.x
True = some_variable
print True # returns 5
Em Python 3, True e False são palavras-chave, então não há mais risco.
== Truedo lado certo de qualquer maneira.
O operador designado - também conhecido informalmente como o operador morsa - foi criado em 28 de fevereiro de 2018 no PEP572 .
Para fins de integridade, postarei as partes relevantes para que você possa comparar as diferenças entre 3.7 e 3.8:
3.7
---
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
test: or_test ['if' or_test 'else' test] | lambdef
test_nocond: or_test | lambdef_nocond
lambdef: 'lambda' [varargslist] ':' test
lambdef_nocond: 'lambda' [varargslist] ':' test_nocond
or_test: and_test ('or' and_test)*
and_test: not_test ('and' not_test)*
not_test: 'not' not_test | comparison
comparison: expr (comp_op expr)*
3.8
---
if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite]
namedexpr_test: test [':=' test] <---- WALRUS OPERATOR!!!
test: or_test ['if' or_test 'else' test] | lambdef
or_test: and_test ('or' and_test)*
and_test: not_test ('and' not_test)*
not_test: 'not' not_test | comparison
comparison: expr (comp_op expr)*