Comment basculer une valeur en Python


128

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


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__


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.


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

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'

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


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

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

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

4

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

def toggle(value):
    return 1 - value

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)

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!

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

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.


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

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

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)

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"

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.