Est-il possible d'avoir une affectation dans une condition?
Par ex.
if (a=some_func()):
# Use a
Est-il possible d'avoir une affectation dans une condition?
Par ex.
if (a=some_func()):
# Use a
Réponses:
Pourquoi ne pas l'essayer?
>>> def some_func():
... return 2
...
>>> a = 2
>>> if (a = some_func()):
File "<stdin>", line 1
if (a = some_func()):
^
SyntaxError: invalid syntax
>>>
Donc non.
Mise à jour: c'est possible (avec une syntaxe différente) en Python 3.8
x = x + 1nécessite un temps de recherche supplémentaire alors que x += 1c'était un peu plus rapide, mais je suis sûr qu'il n'a même pas aimé faire ça . :-)
Python 3.8 apportera PEP572
Résumé
Il s'agit d'une proposition pour créer un moyen d'assigner des variables dans une expression en utilisant la notation NAME: = expr. Une nouvelle exception, TargetScopeError est ajoutée, et il y a une modification à l'ordre d'évaluation.
https://lwn.net/Articles/757713/
Le «désordre PEP 572» a été le sujet d'une session 2018 du Sommet du langage Python dirigée par le dictateur bienveillant pour la vie (BDFL) Guido van Rossum. PEP 572 cherche à ajouter des expressions d'affectation (ou "affectations en ligne") au langage, mais il a vu une discussion prolongée sur plusieurs énormes threads sur la liste de diffusion python-dev - même après plusieurs tours sur les idées python. Ces fils étaient souvent controversés et étaient clairement volumineux au point que beaucoup les ont probablement simplement exclus. Lors du sommet, Van Rossum a donné un aperçu de la proposition de fonctionnalité, qu'il semble enclin à accepter, mais il a également voulu discuter de la façon d'éviter ce type d'explosion de fils à l'avenir.
https://www.python.org/dev/peps/pep-0572/#examples-from-the-python-standard-library
Exemples de la bibliothèque standard Python
site.py env_base n'est utilisé que sur ces lignes, placer son affectation sur if le déplace comme "en-tête" du bloc.
Actuel:
env_base = os.environ.get("PYTHONUSERBASE", None) if env_base: return env_baseAmélioré:
if env_base := os.environ.get("PYTHONUSERBASE", None): return env_base _pydecimal.pyÉvitez si et supprimez un niveau d'indentation.
Actuel:
if self._is_special: ans = self._check_nans(context=context) if ans: return ansAmélioré:
if self._is_special and (ans := self._check_nans(context=context)): return anscopy.py Le code semble plus régulier et évite plusieurs if imbriqués. (Voir l'annexe A pour l'origine de cet exemple.)
Actuel:
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)Amélioré:
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 n'est utilisé que pour s + = tz, déplacer son affectation à l'intérieur du if aide à montrer sa portée.
Actuel:
s = _format_time(self._hour, self._minute, self._second, self._microsecond, timespec) tz = self._tzstr() if tz: s += tz return sAmélioré:
s = _format_time(self._hour, self._minute, self._second, self._microsecond, timespec) if tz := self._tzstr(): s += tz return ssysconfig.py L'appel de fp.readline () dans la condition while et l'appel de .match () sur les lignes if rendent le code plus compact sans
ce qui rend la compréhension plus difficile.
Actuel:
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)] = 0Amélioré:
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)] = 0Simplification des compréhensions de liste Une compréhension de liste peut cartographier et filtrer efficacement en capturant la condition:
results = [(x, y, x/y) for x in input_data if (y := f(x)) > 0]De même, une sous-expression peut être réutilisée dans l'expression principale, en lui donnant un nom lors de la première utilisation:
stuff = [[y := f(x), x/y] for x in range(5)]Notez que dans les deux cas, la variable y est liée dans la portée contenant (c'est-à-dire au même niveau que les résultats ou les trucs).
Capture des valeurs de condition Les expressions d'affectation peuvent être utilisées à bon escient dans l'en-tête d'une instruction 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)En particulier avec la boucle while, cela peut supprimer le besoin d'avoir une boucle infinie, une affectation et une condition. Il crée également un parallèle fluide entre une boucle qui utilise simplement un appel de fonction comme condition, et une qui l'utilise comme condition mais utilise également la valeur réelle.
Fork Un exemple du monde UNIX de bas niveau:
if pid := os.fork(): # Parent code else: # Child code
http://docs.python.org/tutorial/datastructures.html
Notez qu'en Python, contrairement à C, l'affectation ne peut pas se produire à l'intérieur des expressions. Les programmeurs C peuvent se plaindre à ce sujet, mais cela évite une classe courante de problèmes rencontrés dans les programmes C: taper = dans une expression lorsque == était prévu.
regarde aussi:
http://effbot.org/pyfaq/why-can-ti-use-an-assignment-in-an-expression.htm
if (foo = 'bar')en voulant tester la valeur defoo .
=
Non, le BDFL n'aimait pas cette fonctionnalité.
D'où je suis assis, Guido van Rossum, "Dictateur bienveillant pour la vie", s'est battu pour garder Python aussi simple que possible. Nous pouvons chipoter avec certaines des décisions qu'il a prises - j'aurais préféré qu'il dise "Non Mais le fait qu'il n'y ait pas eu de comité de conception de Python, mais plutôt un "conseil consultatif" de confiance, basé en grande partie sur le mérite, filtrant à travers les sensibilités d' un concepteur, a produit un sacré langage sympa, à mon humble avis.
if letquand j'ai une chaîne if elif, mais j'ai besoin de stocker et d'utiliser la valeur de la condition dans chaque cas.
Pas directement, selon ma vieille recette - mais comme le dit la recette, il est facile de construire l'équivalent sémantique, par exemple si vous avez besoin de translittérer directement à partir d'un algorithme de référence codé C (avant de refactoriser vers Python plus idiomatique, bien sûr; -). C'est à dire:
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, une forme pythonique très idiomatique pour votre cas spécifique, si vous savez exactement quelle valeur falsifiée somefuncpeut renvoyer quand elle renvoie une valeur fausse (par exemple 0), est
for a in iter(somefunc, 0):
# use a
donc dans ce cas précis, le refactoring serait assez simple ;-).
Si le retour pourrait être une sorte de valeur falsish (0, None, '', ...), une possibilité est:
import itertools
for a in itertools.takewhile(lambda x: x, iter(somefunc, object())):
# use a
mais vous préférerez peut-être un simple générateur personnalisé:
def getwhile(func, *a, **k):
while True:
x = func(*a, **k)
if not x: break
yield x
for a in getwhile(somefunc):
# use a
Oui, mais uniquement à partir de Python 3.8 et au-delà.
La PEP 572 propose des expressions d'affectation et a déjà été acceptée.
Citant la partie syntaxe et sémantique du 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]
Dans votre cas particulier, vous pourrez écrire
if a := some_func():
# Use a
Non. L'affectation en Python est une instruction, pas une expression.
Grâce à la nouvelle fonctionnalité de Python 3.8, il sera possible de faire une telle chose à partir de cette version, bien que n'utilisant pas un =opérateur d'assignation de type Ada :=. Exemple tiré de la documentation:
# Handle a matched regex
if (match := pattern.search(data)) is not None:
# Do something with match
Vous pouvez définir une fonction pour effectuer l'affectation à votre place:
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)
L'une des raisons pour lesquelles les affectations sont illégales dans les conditions est qu'il est plus facile de faire une erreur et d'attribuer Vrai ou Faux:
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 et False sont des mots-clés, donc plus de risque.
== Truedu bon côté de toute façon.
L'opérateur d'affectation - également connu de manière informelle sous le nom d'opérateur de morse - a été créé le 28 février 2018 dans PEP572 .
Par souci d'exhaustivité, je posterai les parties pertinentes afin que vous puissiez comparer les différences entre 3.7 et 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)*