Comment basculer une valeur en Python

128

Quelle est la manière la plus efficace de basculer entre 0et 1?

codeforester
la source
alors que cette question demande comment basculer les valeurs de manière binaire le plus efficacement possible, certaines réponses expliquent le passage en revue des valeurs (arbitraires), par exemple stackoverflow.com/a/61041907/537865
mad le

Réponses:

273

Solution utilisant NOT

Si les valeurs sont booléennes, l'approche la plus rapide consiste à utiliser l' opérateur not :

>>> x = True
>>> x = not x        # toggle
>>> x
False
>>> x = not x        # toggle
>>> x
True
>>> x = not x        # toggle
>>> x
False

Solution utilisant la soustraction

Si les valeurs sont numériques, la soustraction du total est un moyen simple et rapide de basculer les valeurs:

>>> A = 5
>>> B = 3
>>> total = A + B
>>> x = A
>>> x = total - x    # toggle
>>> x
3
>>> x = total - x    # toggle
>>> x
5
>>> x = total - x    # toggle
>>> x
3

Solution utilisant XOR

Si la valeur bascule entre 0 et 1 , vous pouvez utiliser un ou exclusif au niveau du bit :

>>> x = 1
>>> x ^= 1
>>> x
0
>>> x ^= 1
>>> x
1

La technique se généralise à n'importe quelle paire d'entiers. Le pas xor-par-un est remplacé par une constante xor-par-précalculée:

>>> A = 205
>>> B = -117
>>> t = A ^ B        # precomputed toggle constant
>>> x = A
>>> x ^= t           # toggle
>>> x
-117
>>> x ^= t           # toggle
>>> x
205
>>> x ^= t           # toggle
>>> x
-117

(Cette idée a été soumise par Nick Coghlan et plus tard généralisée par @zxxc.)

Solution utilisant un dictionnaire

Si les valeurs sont hachables, vous pouvez utiliser un dictionnaire:

>>> A = 'xyz'
>>> B = 'pdq'
>>> d = {A:B, B:A}
>>> x = A
>>> x = d[x]         # toggle
>>> x
'pdq'
>>> x = d[x]         # toggle
>>> x
'xyz'
>>> x = d[x]         # toggle
>>> x
'pdq'

Solution utilisant une expression conditionnelle

Le moyen le plus lent est d'utiliser une expression conditionnelle :

>>> A = [1,2,3]
>>> B = [4,5,6]
>>> x = A
>>> x = B if x == A else A
>>> x
[4, 5, 6]
>>> x = B if x == A else A
>>> x
[1, 2, 3]
>>> x = B if x == A else A
>>> x
[4, 5, 6]

Solution utilisant itertools

Si vous avez plus de deux valeurs, la fonction itertools.cycle () fournit un moyen générique rapide de basculer entre les valeurs successives:

>>> import itertools
>>> toggle = itertools.cycle(['red', 'green', 'blue']).next
>>> toggle()
'red'
>>> toggle()
'green'
>>> toggle()
'blue'
>>> toggle()
'red'
>>> toggle()
'green'
>>> toggle()
'blue'

Notez que dans Python 3, la next()méthode a été changée en __next__(), donc la première ligne serait maintenant écrite commetoggle = itertools.cycle(['red', 'green', 'blue']).__next__

Raymond Hettinger
la source
Le dernier exemple semble si simple et intuitif, mais ne fonctionne pas dans Python 3+ avec la suppression de .next (). Existe-t-il un moyen de le faire fonctionner de la même manière dans une version ultérieure de python?
labarna
2
@labarna Dans Python 3, le .next()a été remplacé par une next()fonction globale . L'exemple ci-dessus serait:toggle = itertools.cycle(...); next(toggle)
elpres
2
toggle = itertools.cycle(['red', 'green', 'blue']) next(toggle)
Maximilian
7
L'exemple XOR peut être généralisé pour basculer entre les valeurs aet l' butilisation x = x ^ (a ^ b).
zxxc
int(not 0)et int(not 1)... hrmmm
jhrr
33

J'utilise toujours:

p^=True

Si p est un booléen, cela bascule entre vrai et faux.

renger
la source
1
Parfait! pn'a pas besoin d'être référencé deux fois pour que cette méthode fonctionne !! Idée si vous basculez une valeur avec une longue référence longue.
ThorSummoner
1
comment s'appelle cet opérateur?
mix3d
4
C'est l'opérateur XOR.
bastelflp
1
@ mix3d Précisément, c'est "ou exclusif au niveau du bit" (par opposition à "ou exclusif logique") - wiki.python.org/moin/BitwiseOperators . Logical XOR n'a pas d' opérateur spécifique en Python en général, mais vous pouvez le trouver implémenté dans certains cas particuliers comme dans le module décimal.
Taylor Edmiston
@ mix3d ^=est l' assignation de xor bit à bit
wjandrea
23

Voici une autre manière non intuitive. La beauté est que vous pouvez parcourir plusieurs valeurs et pas seulement deux [0,1]

Pour deux valeurs (basculement)

>>> x=[1,0]
>>> toggle=x[toggle]

Pour plusieurs valeurs (disons 4)

>>> x=[1,2,3,0]
>>> toggle=x[toggle]

Je ne m'attendais pas à ce que cette solution soit presque la plus rapide aussi

>>> stmt1="""
toggle=0
for i in xrange(0,100):
    toggle = 1 if toggle == 0 else 0
"""
>>> stmt2="""
x=[1,0]
toggle=0
for i in xrange(0,100):
    toggle=x[toggle]
"""
>>> t1=timeit.Timer(stmt=stmt1)
>>> t2=timeit.Timer(stmt=stmt2)
>>> print "%.2f usec/pass" % (1000000 * t1.timeit(number=100000)/100000)
7.07 usec/pass
>>> print "%.2f usec/pass" % (1000000 * t2.timeit(number=100000)/100000)
6.19 usec/pass
stmt3="""
toggle = False
for i in xrange(0,100):
    toggle = (not toggle) & 1
"""
>>> t3=timeit.Timer(stmt=stmt3)
>>> print "%.2f usec/pass" % (1000000 * t3.timeit(number=100000)/100000)
9.84 usec/pass
>>> stmt4="""
x=0
for i in xrange(0,100):
    x=x-1
"""
>>> t4=timeit.Timer(stmt=stmt4)
>>> print "%.2f usec/pass" % (1000000 * t4.timeit(number=100000)/100000)
6.32 usec/pass
Abhijit
la source
1
ouais c'est schweet comme une noix. merci à tous, c'est amusant de voir comment différentes personnes abordent le problème (et informatif.)
Bien, c'est une machine à états miniature.
kindall
Eh bien, le vôtre est le plus intéressant, mais ce n'est pas ce dont j'ai personnellement besoin pour ce que je demandais, alors d'accord, je pense que le simple calcul mathématique est probablement le meilleur pour moi, ne devrait-il pas y avoir 1-x?
Oui, mais cela ne devrait pas rendre la vitesse différente.
Blender
ai, mais cela ferait mal mais n'est-ce pas? quelques bonnes réponses ici, tellement rock!
19

L' notopérateur nie votre variable (en la convertissant en booléen si ce n'est déjà fait). Vous pouvez probablement utiliser 1et de 0manière interchangeable avec Trueet False, alors annulez-le:

toggle = not toggle

Mais si vous utilisez deux valeurs arbitraires, utilisez un inline if:

toggle = 'a' if toggle == 'b' else 'b'
Mixeur
la source
1
+1 mais toggle = 0 if toggle else 1est plus court et plus général
luc
Désolé, je vais permuter les variables pour que ce soit plus clair. J'utilisais l'inline ifpour basculer entre deux variables arbitraires , pas seulement 1et 0.
Blender
14

Juste entre 1 et 0, faites ceci

1-x 

x peut prendre 1 ou 0

SP
la source
Puisque (en Python 2.x, de toute façon) Trueet Falsesont en fait des entiers, bien que ceux avec une __str__()méthode étonnamment verbeuse , xpeuvent également être Trueou Falseici. Vous obtiendrez cependant 1 ou 0.
kindall
12

Approche trigonométrique , juste parce que sinet les cosfonctions sont cool.

entrez la description de l'image ici

>>> import math
>>> def generator01():
...     n=0
...     while True:
...         yield abs( int( math.cos( n * 0.5 * math.pi  ) ) )
...         n+=1
... 
>>> g=generator01() 
>>> g.next()
1
>>> g.next()
0
>>> g.next()
1
>>> g.next()
0
Dani Herrera
la source
Oh mon Dieu! Je <3 vous.
Rishabh Agrahari
2
@RishabhAgrahari, oui mec, j'étais le gagnant du défi Raymond Hettinger ;)
dani herrera
7

Étonnamment, personne ne mentionne le bon vieux module de division 2:

In : x = (x + 1)  % 2 ; x
Out: 1

In : x = (x + 1)  % 2 ; x
Out: 0

In : x = (x + 1)  % 2 ; x
Out: 1

In : x = (x + 1)  % 2 ; x
Out: 0

Notez que cela équivaut à x = x - 1, mais l'avantage de la technique modulo est que la taille du groupe ou la longueur de l'intervalle peut être plus grande que 2 éléments, vous donnant ainsi un schéma d'entrelacement similaire au round-robin sur lequel boucler.

Maintenant, juste pour 2, le basculement peut être un peu plus court (en utilisant un opérateur binaire):

x = x ^ 1
Yauhen Yakimovich
la source
Je ne sais pas comment "pythonique" est cette arithmétique modulo (de type C) (i, e, si "pythonique" s'applique?). Je suppose que c'est juste de l'arithmétique, fonctionne partout ailleurs où vous avez du binaire.
Yauhen Yakimovich
Evidemment, la machine à états finis avec un tuple comme x = (1,2,3,0); token = 0; token = x [token] est extrêmement excitant, car il peut être encore plus général qu'une simple opération de groupe.
Yauhen Yakimovich
7

une façon de basculer est d'utiliser l'affectation multiple

>>> a = 5
>>> b = 3

>>> t = a, b = b, a
>>> t[0]
3

>>> t = a, b = b, a
>>> t[0]
5

Utilisation d'itertools:

In [12]: foo = itertools.cycle([1, 2, 3])

In [13]: next(foo)
Out[13]: 1

In [14]: next(foo)
Out[14]: 2

In [15]: next(foo)
Out[15]: 3

In [16]: next(foo)
Out[16]: 1

In [17]: next(foo)
Out[17]: 2
hugo24
la source
4

Le moyen le plus simple de basculer entre 1 et 0 est de soustraire de 1.

def toggle(value):
    return 1 - value
Lapin
la source
4

Utilisation du gestionnaire d'exceptions

>>> def toogle(x):
...     try:
...         return x/x-x/x
...     except  ZeroDivisionError:
...         return 1
... 
>>> x=0
>>> x=toogle(x)
>>> x
1
>>> x=toogle(x)
>>> x
0
>>> x=toogle(x)
>>> x
1
>>> x=toogle(x)
>>> x
0

Ok, je suis le pire:

entrez la description de l'image ici

import math
import sys

d={1:0,0:1}
l=[1,0]

def exception_approach(x):
    try:
        return x/x-x/x
    except  ZeroDivisionError:
        return 1

def cosinus_approach(x):
    return abs( int( math.cos( x * 0.5 * math.pi  ) ) )

def module_approach(x):
    return  (x + 1)  % 2

def subs_approach(x):
    return  x - 1

def if_approach(x):
    return 0 if x == 1 else 1

def list_approach(x):
    global l
    return l[x]

def dict_approach(x):
    global d
    return d[x]

def xor_approach(x):
    return x^1

def not_approach(x):
    b=bool(x)
    p=not b
    return int(p)

funcs=[ exception_approach, cosinus_approach, dict_approach, module_approach, subs_approach, if_approach, list_approach, xor_approach, not_approach ]

f=funcs[int(sys.argv[1])]
print "\n\n\n", f.func_name
x=0
for _ in range(0,100000000):
    x=f(x)
Dani Herrera
la source
3

Que diriez-vous d'une bascule imaginaire qui stocke non seulement la bascule actuelle, mais quelques autres valeurs qui lui sont associées?

toggle = complex.conjugate

Stockez toute valeur + ou - à gauche et toute valeur non signée à droite:

>>> x = 2 - 3j
>>> toggle(x)
(2+3j)

Zéro fonctionne aussi:

>>> y = -2 - 0j
>>> toggle(y)
(-2+0j)

Récupérez facilement la valeur de bascule actuelle ( Trueet Falsereprésentent + et -), la valeur LHS (réelle) ou la valeur RHS (imaginaire):

>>> import math
>>> curr = lambda i: math.atan2(i.imag, -abs(i.imag)) > 0
>>> lhs = lambda i: i.real
>>> rhs = lambda i: abs(i.imag)
>>> x = toggle(x)
>>> curr(x)
True
>>> lhs(x)
2.0
>>> rhs(x)
3.0

Échangez facilement LHS et RHS (mais notez que le signe des deux valeurs ne doit pas être important):

>>> swap = lambda i: i/-1j
>>> swap(2+0j)
2j
>>> swap(3+2j)
(2+3j)

Échangez facilement LHS et RHS et basculez également en même temps:

>>> swaggle = lambda i: i/1j
>>> swaggle(2+0j)
-2j
>>> swaggle(3+2j)
(2-3j)

Protège contre les erreurs:

>>> toggle(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor 'conjugate' requires a 'complex' object but received a 'int'

Effectuer les modifications de LHS et RHS:

>>> x += 1+2j
>>> x
(3+5j)

... mais soyez prudent en manipulant le RHS:

>>> z = 1-1j
>>> z += 2j
>>> z
(1+1j) # whoops! toggled it!
Rick soutient Monica
la source
2

Les variables a et b peuvent être N'IMPORTE QUELLE deux valeurs, comme 0 et 1, ou 117 et 711, ou "heads" et "tails". Aucun calcul n'est utilisé, juste un échange rapide des valeurs chaque fois qu'une bascule est souhaitée.

a = True   
b = False   

a,b = b,a   # a is now False
a,b = b,a   # a is now True
user2948775
la source
1

J'utilise la fonction abs, très utile sur les boucles

x = 1
for y in range(0, 3):
    x = abs(x - 1)

x sera égal à 0.

Proteo5
la source
0

Faisons un peu de piratage de cadre. Basculer une variable par son nom. Remarque: cela peut ne pas fonctionner avec tous les environnements d'exécution Python.

Disons que vous avez une variable «x»

>>> import inspect
>>> def toggle(var_name):
>>>     frame = inspect.currentframe().f_back
>>>     vars = frame.f_locals
>>>     vars[var_name] = 0 if vars[var_name] == 1 else 1

>>> x = 0
>>> toggle('x')
>>> x
1
>>> toggle('x')
>>> x
0
Brantley Harris
la source
0

Si vous avez affaire à une variable entière, vous pouvez incrémenter 1 et limiter votre ensemble à 0 et 1 (mod)

X = 0  # or X = 1
X = (X + 1)%2
Italo Nesi
la source
0

La commutation entre -1 et +1 peut être obtenue par multiplication en ligne; utilisé pour le calcul de pi la manière 'Leibniz' (ou similaire):

sign = 1
result = 0
for i in range(100000):
    result += 1 / (2*i + 1) * sign
    sign *= -1
print("pi (estimate): ", result*4)
John Bograd
la source
0

Vous pouvez utiliser le indexof lists.

def toggleValues(values, currentValue):
    return values[(values.index(currentValue) + 1) % len(values)]

> toggleValues( [0,1] , 1 )
> 0
> toggleValues( ["one","two","three"] , "one" )
> "two"
> toggleValues( ["one","two","three"] , "three")
> "one"

Avantages : Aucune bibliothèque supplémentaire, code auto-explicatif et utilisation de types de données arbitraires.

Inconvénients : pas de duplication-sauvegarde. toggleValues(["one","two","duped", "three", "duped", "four"], "duped") reviendra toujours"three"

furieux
la source