¿Es posible tener cesión en condición?
Por ej.
if (a=some_func()):
# Use a
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
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 . :-)
Python 3.8 traerá PEP572
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.
https://lwn.net/Articles/757713/
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.
https://www.python.org/dev/peps/pep-0572/#examples-from-the-python-standard-library
Ejemplos de la biblioteca estándar de Python
site.py env_base solo se usa en estas líneas, poniendo su asignación en el if lo mueve como el "encabezado" del bloque.
Actual:
env_base = os.environ.get("PYTHONUSERBASE", None) if env_base: return env_baseMejorado:
if env_base := os.environ.get("PYTHONUSERBASE", None): return env_base _pydecimal.pyEvite anidado si y elimine un nivel de sangría.
Actual:
if self._is_special: ans = self._check_nans(context=context) if ans: return ansMejorado:
if self._is_special and (ans := self._check_nans(context=context)): return anscopy.py El código parece más regular y evita múltiples if anidados. (Consulte el Apéndice A para conocer el origen de este ejemplo).
Actual:
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)Mejorado:
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 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 sMejorado:
s = _format_time(self._hour, self._minute, self._second, self._microsecond, timespec) if tz := self._tzstr(): s += tz return ssysconfig.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:
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)] = 0Mejorado:
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)] = 0Simplificar 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-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 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 code else: # Child code
http://docs.python.org/tutorial/datastructures.html
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 ==.
ver también:
http://effbot.org/pyfaq/why-can-ti-use-an-assignment-in-an-expression.htm
if (foo = 'bar')al intentar probar el valor de foo.
=
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.
if letcuando 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 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:
def getwhile(func, *a, **k):
while True:
x = func(*a, **k)
if not x: break
yield x
for a in getwhile(somefunc):
# use a
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
No. La asignación en Python es una declaración, no una expresión.
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
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)
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.
== Truedel 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)*