Est-il possible d'avoir une affectation dans une condition ?
Par exemple.
if (a=some_func()):
# Use a
Est-il possible d'avoir une affectation dans une condition ?
Par exemple.
if (a=some_func()):
# Use a
Pourquoi ne pas l'essayer ?
>>> def some_func():
... return 2
...
>>> if (a = some_func()):
File "<stdin>", line 1
if (a = some_func()):
^
SyntaxError: invalid syntax
Donc, non.
Mise à jour : Ceci est possible (avec une syntaxe différente) dans Python 3.8
if a := some_func():
Python 3.8 apportera PEP572
Résumé
Il s'agit d'une proposition de création d'un moyen d'assigner aux variables dans une expression en utilisant la notationNAME := expr
. Une nouvelle exception,TargetScopeError
est ajouté, et il y a un changement à l'évaluation l'ordre.
https://lwn.net/Articles/757713/
Le "désordre PEP 572" était le sujet d'une session du Python Language Summit de 2018. dirigée par le dictateur bienveillant pour la vie (BDFL) Guido van Rossum. Le PEP 572 cherche à ajouter des expressions d'assignation (ou " assignations en ligne ") au langage, mais elle a fait l'objet d'une discussion prolongée sur de multiples sur la liste de diffusion python-dev - même après de multiples tours de table sur python-ideas. sur python-ideas. Ces fils de discussion étaient souvent litigieux et étaient clairement volumineux au point que beaucoup les ont probablement simplement ignorés. À l'adresse sommet, Van Rossum a donné un aperçu de la proposition de fonctionnalité, qu'il semble qu'il semble enclin à accepter, mais il a également voulu discuter de la façon dont d'éviter ce genre 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, mettre son affectation sur le if le déplace comme "en-tête" du bloc.
Actuel :
env_base = os.environ.get("PYTHONUSERBASE", None) if env_base: return env_base
Amélioré :
if env_base := os.environ.get("PYTHONUSERBASE", None): return env_base _pydecimal.py
Évitez les "if" imbriqués et supprimez un niveau d'indentation.
Actuel :
if self._is_special: ans = self._check_nans(context=context) if ans: return ans
Amélioré :
if self._is_special and (ans := self._check_nans(context=context)): return ans
copy.py Le code semble plus régulier et évite les multiples 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.py
tz n'est utilisé que pour s += tz, le déplacement de son affectation à l'intérieur du if permet de 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 s
Amélioré :
s = _format_time(self._hour, self._minute, self._second, self._microsecond, timespec) if tz := self._tzstr(): s += tz return s
sysconfig.py Appeler fp.readline() dans la condition while et appeler .match() sur les lignes if rend le code plus compact sans
ce qui le rend plus difficile à comprendre.
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)] = 0
Amé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)] = 0
Simplifier les compréhensions de liste Une compréhension de liste peut mapper 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 le contenant (c'est-à-dire au même niveau que results ou stuff).
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 la nécessité de disposer d'un fichier boucle infinie, d'une affectation et d'une condition. Cela crée également un parallèle fluide entre une boucle qui utilise simplement un appel de fonction comme comme condition, et une boucle qui l'utilise comme condition mais qui utilise également la valeur réelle.
Fourchette Un exemple tiré 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 au C, l'affectation ne peut pas se faire à l'intérieur d'une à l'intérieur d'une expression. Les programmeurs C peuvent râler râler, mais cela permet d'éviter une problèmes rencontrés dans les programmes C : taper = dans une expression alors que == était prévu.
Voir aussi :
http://effbot.org/pyfaq/why-can-t-i-use-an-assignment-in-an-expression.htm
Non, le BDFL n'a pas aimé cette fonctionnalité.
De mon point de vue, Guido van Rossum, "Dictateur bienveillant à vie", s'est battu pour que Python reste aussi simple que possible. Nous pouvons ergoter sur certaines des décisions qu'il a prises - j'aurais préféré qu'il dise "non" plus souvent. Mais le fait qu'il n'y ait pas eu de comité pour la conception de Python, mais plutôt un "conseil consultatif" de confiance, basé en grande partie sur le mérite, filtrant par le biais de un a produit un langage d'enfer, à mon avis.
Oui, mais seulement à partir de Python 3.8 et plus.
PEP 572 propose Expressions d'affectation et a déjà été accepté.
En citant le Syntaxe et sémantique dans le cadre 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 spécifique, vous serez en mesure d'écrire
if a := some_func():
# Use a
Pas directement, par cette vieille recette de moi -- 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é en C (avant de le refactorer en 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 faussaire somefunc
peut retourner lorsqu'il renvoie une valeur fausse (par exemple 0
), est
for a in iter(somefunc, 0):
# use a
donc dans ce cas précis, le remaniement serait assez facile;-).
Si le retour pouvait être tout une sorte de valeur fictive (0, None
, ''
...), une possibilité est :
import itertools
for a in itertools.takewhile(lambda x: x, iter(somefunc, object())):
# use a
mais vous pouvez préférer 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
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.