135 votes

Peut-on avoir une affectation dans une condition ?

Est-il possible d'avoir une affectation dans une condition ?

Par exemple.

if (a=some_func()):
    # Use a

158voto

Jason Hall Points 15935

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():

85voto

gnibbler Points 103484

MISE À JOUR - La réponse originale se trouve en bas de page.

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 notation NAME := 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

Réponse originale

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

38voto

Kevin Little Points 5406

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.

18voto

timgeb Points 5966

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

17voto

Alex Martelli Points 330805

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.com

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.

Powered by:

X