O fato de duas perguntas terem a mesma resposta não significa que sejam duplicatas. Trivial responder a si mesmo, mas não é trivial ver o raciocínio, ou se há uma maneira de contornar.
mehmet
Respostas:
111
Por que não experimentá-lo?
>>> defsome_func():... return2... >>> a = 2>>> if (a = some_func()):
File "<stdin>", line 1if (a = some_func()):
^
SyntaxError: invalid syntax
>>>
isso é intencionalmente proibido, pois Guido, ditador benevolente do python, os considera desnecessários e mais confusos do que úteis. É a mesma razão pela qual não há operadores pós-incremento ou pré-incremento (++).
Matt Boehm,
4
ele permitiu a adição de atribuição aumentada no 2.0 porque 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 . :-)
wescpy
esperança, isso será possível em breve. Python só evolui lentamente
Nik O'Lai
5
"porque não experimentar" - porque quem sabe qual pode ser a sintaxe? Talvez o OP tenha tentado isso e não funcionou, mas isso não significa que a sintaxe não seja diferente ou que não haja uma maneira de fazer isso que não fosse intencional
Levi H
59
ATUALIZAÇÃO - a resposta original está perto do final
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.
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.
tz é 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 s
Melhorado:
s = _format_time(self._hour, self._minute,
self._second, self._microsecond,
timespec)
if tz := self._tzstr():
s += tz
return s
sysconfig.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:
whileTrue:
line = fp.readline()
ifnot line:
break
m = define_rx.match(line)
if m:
n, v = m.group(1, 2)
try:
v = int(v)
except ValueError:
passvars[n] = v
else:
m = undef_rx.match(line)
if m:
vars[m.group(1)] = 0
Melhorado:
while line := fp.readline():
if m := define_rx.match(line):
n, v = m.group(1, 2)
try:
v = int(v)
except ValueError:
passvars[n] = v
elif m := undef_rx.match(line):
vars[m.group(1)] = 0
Simplificando 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 inrange(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-halfwhile (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 effectif 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 returnedwhile 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 codeelse:
# Child code
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.
Gosto dessa resposta porque, na verdade, ela aponta por que tal "recurso" pode ter sido deliberadamente deixado de fora do Python. Ao ensinar programação para iniciantes, tenho visto muitos cometerem esse erro if (foo = 'bar')enquanto pretendem testar o valor de foo.
Jonathan Cross
2
@JonathanCross, Este "recurso" será adicionado na versão 3.8. É improvável que seja usado com a moderação que deveria - mas pelo menos não é uma planície=
John La Rooy
@JohnLaRooy: Olhando os exemplos, acho que "improvável que seja usado com a moderação que deveria" foi certeiro; Dos cerca de 10 exemplos, descobri que apenas dois realmente melhoram o código. (Ou seja, como a única expressão em uma condição while, para evitar duplicar a linha ou ter a condição de loop no corpo, ou em uma cadeia elif para evitar aninhamento)
Aleksi Torhamo
Não funciona com o ternário 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) .
Hi-Angel
39
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.
Simples? Esse recurso pode simplificar bastante o meu código, porque poderia tê-lo tornado mais compacto e, portanto, mais legível. Agora preciso de duas linhas, onde antes precisava. Nunca entendi por que Python rejeitou recursos de outras linguagens de programação por muitos anos (e muitas vezes por um bom motivo). Especialmente esse recurso de que estamos falando aqui é muito, muito útil.
Regis maio
6
Menos código nem sempre é mais simples ou mais legível. Pegue uma função recursiva, por exemplo. Seu equivalente em loop é geralmente mais legível.
FMF de
1
Eu não gosto da versão C dela, mas eu realmente sinto falta de ter algo como o da ferrugem if letquando tenho uma cadeia if elif, mas preciso armazenar e usar o valor da condição em cada caso.
Thayne
1
Devo dizer que o código que estou escrevendo agora (o motivo pelo qual pesquisei esse problema) é MUITO mais feio sem esse recurso. Em vez de usar if seguido por muitos else ifs, preciso continuar recuando o próximo if sob o último else.
MikeKulls
17
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:
classDataHolder(object):def__init__(self, value=None): self.value = value
defset(self, value): self.value = value; return value
defget(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 initer(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:
defgetwhile(func, *a, **k):whileTrue:
x = func(*a, **k)
ifnot x: breakyield x
for a in getwhile(somefunc):
# use a
Eu votaria nisso duas vezes, se pudesse. Esta é uma ótima solução para aqueles momentos em que algo assim é realmente necessário. Eu adaptei sua solução para uma classe Matcher regex, que é instanciada uma vez e então .check () é usado na instrução if e .result () é usado dentro de seu corpo para recuperar a correspondência, se houver. Obrigado! :)
Teekin
16
Sim, mas apenas a partir do Python 3.8.
O PEP 572 propõe Expressões de Atribuição e já foi aceito.
# Handle a matched regexif (match := pattern.search(data)) isnotNone:
# 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)) isnotNone]
No seu caso específico, você será capaz de escrever
@javadba o cara acertou muito mais vezes do que errou. Eu entendo que ter uma única pessoa responsável pela visão resulta em uma estratégia muito mais coerente do que o projeto por comitê; Posso comparar e contrastar com C ++, que é meu principal pão com manteiga.
Mark Ransom
Eu sinto que ruby e scala (v linguagens diferentes) acertam significativamente mais do que python: mas em qualquer caso, aqui não é o lugar ...
StephenBoesch
4
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 regexif (match := pattern.search(data)) isnotNone:
# Do something with match
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.xTrue = some_variable
printTrue# returns 5
Em Python 3, True e False são palavras-chave, então não há mais risco.
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
fonte
x = x + 1
requer tempo de pesquisa adicional enquantox += 1
era um pouco mais rápido, mas tenho certeza de que ele nem gostava de fazer isso . :-)ATUALIZAÇÃO - a resposta original está perto do final
Python 3.8 trará PEP572
https://lwn.net/Articles/757713/
https://www.python.org/dev/peps/pep-0572/#examples-from-the-python-standard-library
Resposta original
http://docs.python.org/tutorial/datastructures.html
Veja também:
http://effbot.org/pyfaq/why-can-ti-use-an-assignment-in-an-expression.htm
fonte
if (foo = 'bar')
enquanto pretendem testar o valor defoo
.=
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 parafoo()
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.
fonte
if let
quando 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
somefunc
pode retornar quando retornar um valor falso (por exemplo0
), é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
fonte
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
fonte
Não. A atribuição em Python é uma declaração, não uma expressão.
fonte
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
fonte
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)
fonte
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.
fonte
== True
do 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)*
fonte