Que dos preguntas tengan la misma respuesta no significa que sean duplicadas. Trivial para responderse a sí mismo, pero no trivial para ver el razonamiento, o si hay una forma de evitarlo.
mehmet
Respuestas:
111
¿Por qué no probarlo?
>>> defsome_func():... return2... >>> a = 2>>> if (a = some_func()):
File "<stdin>", line 1if (a = some_func()):
^
SyntaxError: invalid syntax
>>>
esto está intencionalmente prohibido ya que Guido, el benevolente dictador pitón, los encuentra innecesarios y más confusos que útiles. Es la misma razón por la que no hay operadores de post-incremento o pre-incremento (++).
Matt Boehm
4
Permitió la adición de asignación aumentada en 2.0 porque x = x + 1requiere tiempo de búsqueda adicional mientras que x += 1era algo más rápido, pero estoy seguro de que ni siquiera le gustó mucho hacer eso . :-)
wescpy
Espero que esto sea posible pronto. Python evoluciona lentamente
Nik O'Lai
4
"¿Por qué no probarlo?" Porque ¿quién sabe cuál podría ser la sintaxis? Tal vez OP lo intentó y no funcionó, pero eso no significa que la sintaxis no sea diferente, o que no haya una forma de hacerlo que no sea la intencionada
Levi H
57
ACTUALIZACIÓN: la respuesta original está casi al final
Resumen
Esta es una propuesta para crear una forma de asignar variables dentro de una expresión utilizando la notación NOMBRE: = expr. Se agrega una nueva excepción, TargetScopeError, y hay un cambio en el orden de evaluación.
El "lío de PEP 572" fue el tema de una sesión de Python Language Summit 2018 dirigida por el benevolente dictador de por vida (BDFL) Guido van Rossum. PEP 572 busca agregar expresiones de asignación (o "asignaciones en línea") al lenguaje, pero ha visto una discusión prolongada sobre múltiples hilos enormes en la lista de correo de python-dev, incluso después de múltiples rondas sobre python-ideas. Esos hilos a menudo eran polémicos y claramente voluminosos hasta el punto de que muchos probablemente simplemente los ignoraban. En la cumbre, Van Rossum dio una descripción general de la propuesta de la función, que parece inclinado a aceptar, pero también quería discutir cómo evitar este tipo de explosión de hilos en el futuro.
tz solo se usa para s + = tz, mover su asignación dentro de if ayuda a mostrar su alcance.
Actual:
s = _format_time(self._hour, self._minute,
self._second, self._microsecond,
timespec)
tz = self._tzstr()
if tz:
s += tz
return s
Mejorado:
s = _format_time(self._hour, self._minute,
self._second, self._microsecond,
timespec)
if tz := self._tzstr():
s += tz
return s
sysconfig.py Llamar a fp.readline () en la condición while y llamar a .match () en las líneas if hace que el código sea más compacto sin
haciéndolo más difícil de entender.
Actual:
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:
pass
vars[n] = v
else:
m = undef_rx.match(line)
if m:
vars[m.group(1)] = 0
Mejorado:
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)] = 0
Simplificar las comprensiones de listas Una comprensión de listas puede mapear y filtrar de manera eficiente al capturar la condición:
results = [(x, y, x/y) for x in input_data if (y := f(x)) > 0]
De manera similar, una subexpresión se puede reutilizar dentro de la expresión principal, dándole un nombre en el primer uso:
stuff = [[y := f(x), x/y] for x in range(5)]
Tenga en cuenta que en ambos casos la variable y está vinculada en el ámbito contenedor (es decir, al mismo nivel que los resultados o cosas).
Captura de valores de condición Las expresiones de asignación se pueden utilizar con buenos resultados en el encabezado de una declaración if o 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 con el ciclo while, esto puede eliminar la necesidad de tener un ciclo infinito, una asignación y una condición. También crea un paralelo suave entre un bucle que simplemente usa una llamada de función como su condición, y uno que usa eso como su condición pero también usa el valor real.
Fork Un ejemplo del mundo UNIX de bajo nivel:
if pid := os.fork():
# Parent codeelse:
# Child code
Tenga en cuenta que en Python, a diferencia de C, la asignación no puede ocurrir dentro de expresiones. Los programadores de C pueden quejarse de esto, pero evita una clase común de problemas encontrados en los programas de C: escribir = en una expresión cuando se pretendía ==.
Me gusta esta respuesta porque en realidad señala por qué tal "característica" podría haberse dejado deliberadamente fuera de Python. Al enseñar programación para principiantes, he visto a muchos cometer este error if (foo = 'bar')al intentar probar el valor de foo.
Jonathan Cross
2
@JonathanCross, esta "característica" se agregará en la 3.8. Es poco probable que se use con tanta moderación como debería, pero al menos no es simple=
John La Rooy
@JohnLaRooy: Mirando los ejemplos, creo que "es poco probable que se use con tanta moderación como debería" fue acertado; De los ~ 10 ejemplos, encuentro que solo dos realmente mejoran el código. (Es decir, como la única expresión en una condición while, para evitar duplicar la línea o tener la condición de bucle en el cuerpo, o en una cadena elif para evitar el anidamiento)
Aleksi Torhamo
39
No, al BDFL no le gustó esa característica.
Desde donde estoy sentado, Guido van Rossum, "Dictador benévolo de por vida", ha luchado mucho para que Python sea lo más simple posible. Podemos objetar algunas de las decisiones que ha tomado; hubiera preferido que dijera 'No Pero el hecho de que no haya un comité diseñando Python, sino un "consejo asesor" de confianza, basado en gran parte en el mérito, filtrando las sensibilidades de un diseñador, ha producido un lenguaje increíblemente agradable, en mi humilde opinión.
¿Sencillo? Esta característica podría simplificar bastante parte de mi código porque podría haberlo hecho más compacto y, por lo tanto, más legible. Ahora necesito dos líneas donde solía necesitar una. Nunca entendí por qué Python rechazó las características que tienen otros lenguajes de programación durante muchos años (y a menudo por una muy buena razón). Especialmente esta característica de la que estamos hablando aquí es muy, muy útil.
Regis mayo
6
Menos código no siempre es más simple o morde legible. Tome una función recursiva, por ejemplo. Su equivalente de bucle suele ser más legible.
FMF
1
No me gusta la versión C, pero realmente extraño tener algo como rust if letcuando tengo una cadena if elif, pero necesito almacenar y usar el valor de la condición en cada caso.
Thayne
1
Tengo que decir que el código que estoy escribiendo ahora (la razón por la que busqué este número) es MUCHO más feo sin esta función. En lugar de usar if seguido de muchos otros ifs, necesito seguir sangrando el siguiente if debajo del último else.
MikeKulls
17
No directamente, según esta vieja receta mía , pero como dice la receta, es fácil construir el equivalente semántico, por ejemplo, si necesita transliterar directamente desde un algoritmo de referencia codificado en C (antes de refactorizar a Python más idiomático, por supuesto; -). Es decir:
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
Por cierto, una forma Pythonic muy idiomática para su caso específico, si sabe exactamente qué valor falso somefuncpuede devolver cuando devuelve un valor falso (por ejemplo 0), es
for a in iter(somefunc, 0):
# use a
así que en este caso específico la refactorización sería bastante fácil ;-).
Si el retorno es de cualquier tipo de valor falsish (0, None, '', ...), una posibilidad es:
import itertools
for a in itertools.takewhile(lambda x: x, iter(somefunc, object())):
# use a
pero es posible que prefieras un generador personalizado simple:
defgetwhile(func, *a, **k):whileTrue:
x = func(*a, **k)
ifnot x: breakyield x
for a in getwhile(somefunc):
# use a
Votaría esto dos veces si pudiera. Esta es una gran solución para esos momentos en los que realmente se necesita algo como esto. Adapté su solución a una clase regex Matcher, que se instancia una vez y luego .check () se usa en la declaración if y .result () se usa dentro de su cuerpo para recuperar la coincidencia, si hubiera una. ¡Gracias! :)
Teekin
16
Sí, pero solo desde Python 3.8 en adelante.
PEP 572 propone Assignment Expressions y ya ha sido aceptado.
# 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]
@MarkRansom Todos saludan a Guido. Bien ... suspiro.
StephenBoesch
@javadba, el tipo ha tenido razón muchas más veces de lo que se ha equivocado. Aprecio que tener una sola persona a cargo de la visión da como resultado una estrategia mucho más coherente que el diseño por comité; Puedo comparar y contrastar con C ++, que es mi pan y mantequilla principal.
Mark Ransom
Siento que tanto ruby como scala (v idiomas diferentes) lo hacen bien significativamente más que python: pero en cualquier caso, este no es el lugar ..
StephenBoesch
4
Gracias a la nueva característica de Python 3.8 será posible hacer tal cosa desde esta versión, aunque no usando el =operador de asignación tipo Ada :=. Ejemplo de los documentos:
# Handle a matched regexif (match := pattern.search(data)) isnotNone:
# Do something with match
Una de las razones por las que las asignaciones son ilegales en determinadas condiciones es que es más fácil cometer un error y asignar Verdadero o 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
En Python 3, True y False son palabras clave, por lo que ya no hay riesgo.
Respuestas:
¿Por qué no probarlo?
>>> def some_func(): ... return 2 ... >>> a = 2 >>> if (a = some_func()): File "<stdin>", line 1 if (a = some_func()): ^ SyntaxError: invalid syntax >>>
Entonces, no.
Actualización: esto es posible (con diferente sintaxis) en Python 3.8
fuente
x = x + 1
requiere tiempo de búsqueda adicional mientras quex += 1
era algo más rápido, pero estoy seguro de que ni siquiera le gustó mucho hacer eso . :-)ACTUALIZACIÓN: la respuesta original está casi al final
Python 3.8 traerá PEP572
https://lwn.net/Articles/757713/
https://www.python.org/dev/peps/pep-0572/#examples-from-the-python-standard-library
Respuesta original
http://docs.python.org/tutorial/datastructures.html
ver también:
http://effbot.org/pyfaq/why-can-ti-use-an-assignment-in-an-expression.htm
fuente
if (foo = 'bar')
al intentar probar el valor defoo
.=
No, al BDFL no le gustó esa característica.
Desde donde estoy sentado, Guido van Rossum, "Dictador benévolo de por vida", ha luchado mucho para que Python sea lo más simple posible. Podemos objetar algunas de las decisiones que ha tomado; hubiera preferido que dijera 'No Pero el hecho de que no haya un comité diseñando Python, sino un "consejo asesor" de confianza, basado en gran parte en el mérito, filtrando las sensibilidades de un diseñador, ha producido un lenguaje increíblemente agradable, en mi humilde opinión.
fuente
if let
cuando tengo una cadena if elif, pero necesito almacenar y usar el valor de la condición en cada caso.No directamente, según esta vieja receta mía , pero como dice la receta, es fácil construir el equivalente semántico, por ejemplo, si necesita transliterar directamente desde un algoritmo de referencia codificado en C (antes de refactorizar a Python más idiomático, por supuesto; -). Es decir:
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
Por cierto, una forma Pythonic muy idiomática para su caso específico, si sabe exactamente qué valor falso
somefunc
puede devolver cuando devuelve un valor falso (por ejemplo0
), esfor a in iter(somefunc, 0): # use a
así que en este caso específico la refactorización sería bastante fácil ;-).
Si el retorno es de cualquier tipo de valor falsish (0,
None
,''
, ...), una posibilidad es:import itertools for a in itertools.takewhile(lambda x: x, iter(somefunc, object())): # use a
pero es posible que prefieras un generador personalizado simple:
def getwhile(func, *a, **k): while True: x = func(*a, **k) if not x: break yield x for a in getwhile(somefunc): # use a
fuente
Sí, pero solo desde Python 3.8 en adelante.
PEP 572 propone Assignment Expressions y ya ha sido aceptado.
Citando la parte de sintaxis y semántica del 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]
En su caso específico, podrá escribir
if a := some_func(): # Use a
fuente
No. La asignación en Python es una declaración, no una expresión.
fuente
Gracias a la nueva característica de Python 3.8 será posible hacer tal cosa desde esta versión, aunque no usando el
=
operador de asignación tipo Ada:=
. Ejemplo de los documentos:# Handle a matched regex if (match := pattern.search(data)) is not None: # Do something with match
fuente
Puede definir una función para que realice la asignación por usted:
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)
fuente
Una de las razones por las que las asignaciones son ilegales en determinadas condiciones es que es más fácil cometer un error y asignar Verdadero o 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
En Python 3, True y False son palabras clave, por lo que ya no hay riesgo.
fuente
== True
del lado correcto de todos modos.El operador de asignación, también conocido informalmente como el operador de la morsa, se creó el 28 de febrero de 2018 en PEP572 .
En aras de la exhaustividad, publicaré las partes relevantes para que pueda comparar las diferencias entre 3.7 y 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)*
fuente