Come cambiare un valore in Python


Risposte:


273

Soluzione che utilizza NOT

Se i valori sono booleani, l'approccio più veloce consiste nell'utilizzare l' operatore not :

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

Soluzione usando la sottrazione

Se i valori sono numerici, la sottrazione dal totale è un modo semplice e veloce per alternare i valori:

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

Soluzione che utilizza XOR

Se i comandi di valore tra 0 e 1 , è possibile utilizzare un bit per bit esclusiva-o :

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

La tecnica generalizza a qualsiasi coppia di numeri interi. Il passaggio xor-by-one viene sostituito con un xor-by-precalcolato costante:

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

(Questa idea è stata presentata da Nick Coghlan e successivamente generalizzata da @zxxc.)

Soluzione utilizzando un dizionario

Se i valori sono modificabili, puoi utilizzare un dizionario:

>>> 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'

Soluzione che utilizza un'espressione condizionale

Il modo più lento è usare un'espressione condizionale :

>>> 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]

Soluzione che utilizza itertools

Se hai più di due valori, la funzione itertools.cycle () fornisce un modo rapido e generico per alternare tra i valori successivi:

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

Si noti che in Python 3 il next()metodo è stato modificato in __next__(), quindi la prima riga verrà ora scritta cometoggle = itertools.cycle(['red', 'green', 'blue']).__next__


L'ultimo esempio sembra così semplice e intuitivo, ma non funziona in Python 3+ con la rimozione di .next (). C'è un modo per farlo funzionare in modo simile nella versione successiva di Python?
labarna

2
@labarna In Python 3, .next()è stata sostituita da una next()funzione globale . L'esempio sopra sarebbe:toggle = itertools.cycle(...); next(toggle)
elpres

2
toggle = itertools.cycle(['red', 'green', 'blue']) next(toggle)
Maximilian

7
L'esempio XOR può essere generalizzato per alternare tra valori ae butilizzo x = x ^ (a ^ b).
zxxc

int(not 0)e int(not 1)... hrmmm
jhrr

33

Uso sempre:

p^=True

Se p è un valore booleano, passa da vero a falso.


1
Perfetto! pnon è necessario fare riferimento due volte affinché questo metodo funzioni !! Idea se stai alternando un valore con un riferimento lungo lungo.
ThorSummoner

1
come si chiama questo operatore?
mix3d

4
Questo è l'operatore XOR.
bastelflp

1
@ mix3d Precisamente è "bitwise esclusivo or" (al contrario di "logico esclusivo or") - wiki.python.org/moin/BitwiseOperators . Logical XOR non ha un operatore specifico in Python in generale, ma puoi trovarlo implementato in alcuni casi speciali come nel modulo decimal.
Taylor Edmiston

@ mix3d ^=è l' assegnazione xor bit per bit
wjandrea

23

Ecco un altro modo non intuitivo. Il bello è che puoi scorrere più valori e non solo due [0,1]

Per due valori (commutazione)

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

Per più valori (diciamo 4)

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

Non mi aspettavo che anche questa soluzione fosse quasi la più veloce

>>> 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
sì questo è schweet come un dado. grazie a tutti, è divertente osservare come persone diverse affrontano il problema (e sono istruttive.)

Bello, è una macchina a stati in miniatura.
kindall

beh, il tuo è il più interessante, ma non è quello di cui ho bisogno personalmente per la cosa che stavo chiedendo, quindi ok, penso che quello di matematica semplice sia probabilmente il migliore per me, non dovrebbe essere 1-x lì?

Sì, ma questo non dovrebbe rendere la velocità diversa.
Blender

ai, ma lo farebbe sbagliato anche se non è vero? alcune ottime risposte qui, COSÌ rocce!

19

L' notoperatore nega la tua variabile (convertendola in un booleano se non lo è già). Probabilmente puoi usare 1e in modo 0intercambiabile con Truee False, quindi negalo semplicemente:

toggle = not toggle

Ma se stai usando due valori arbitrari, usa un inline if:

toggle = 'a' if toggle == 'b' else 'b'

1
+1 ma toggle = 0 if toggle else 1è più breve e più generale
luc

Scusa, scambierò le variabili per renderlo più chiaro. Stavo usando l'inline ifper alternare tra due variabili arbitrarie , non solo 1e 0.
Blender

14

Solo tra 1 e 0, fallo

1-x 

x può richiedere 1 o 0


Poiché (in Python 2.x, in ogni caso) Truee Falsesono effettivamente numeri interi, sebbene quelli con un __str__()metodo sorprendentemente dettagliato , xpossono anche essere Trueo Falsequi. Tuttavia, otterrai 1 o 0 indietro.
kindall

12

Approccio trigonometrico , solo perché le funzioni sine cossono fantastiche.

inserisci qui la descrizione dell'immagine

>>> 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 mio Dio! Io <3 te.
Rishabh Agrahari

2
@RishabhAgrahari, yea man, I was the Raymond Hettinger challenge winner ;)
dani herrera

7

Sorprendentemente nessuno menziona la buona vecchia divisione modulo 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

Nota che è equivalente a x = x - 1, ma il vantaggio della tecnica modulo è che la dimensione del gruppo o la lunghezza dell'intervallo può essere maggiore di soli 2 elementi, dandoti così uno schema di interleaving round-robin su cui eseguire il loop.

Ora solo per 2, la commutazione può essere un po 'più breve (usando l'operatore bit-wise):

x = x ^ 1

Non sono sicuro di quanto "pitonico" sia questo modulo (tipo C) aritmetico (i, e, se si applica "pitonico"?). Immagino sia solo aritmetica, funziona ovunque tu abbia il binario.
Yauhen Yakimovich

Ovviamente la macchina a stati finiti con tupla come x = (1,2,3,0); token = 0; token = x [token] è estremamente eccitante, poiché può essere anche più generale di una semplice operazione di gruppo.
Yauhen Yakimovich

7

un modo per alternare è utilizzare l'assegnazione multipla

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

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

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

Utilizzando 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

Il modo più semplice per passare da 1 a 0 è sottrarre da 1.

def toggle(value):
    return 1 - value

4

Utilizzo del gestore di eccezioni

>>> 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, sono il peggiore:

inserisci qui la descrizione dell'immagine

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

Che ne dici di un interruttore immaginario che memorizza non solo l'interruttore corrente, ma un paio di altri valori ad esso associati?

toggle = complex.conjugate

Memorizza qualsiasi valore + o - a sinistra e qualsiasi valore senza segno a destra:

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

Anche Zero funziona:

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

Recupera facilmente il valore di commutazione corrente ( Truee Falserappresenta + e -), il valore LHS (reale) o il valore RHS (immaginario):

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

Scambia facilmente LHS e RHS (ma nota che il segno di entrambi i valori non deve essere importante):

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

Scambia facilmente LHS e RHS e attiva contemporaneamente:

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

Protegge dagli errori:

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

Eseguire modifiche a LHS e RHS:

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

... ma fai attenzione a manipolare l'RHS:

>>> z = 1-1j
>>> z += 2j
>>> z
(1+1j) # whoops! toggled it!

2

Le variabili aeb possono essere QUALSIASI due valori, come 0 e 1, o 117 e 711, o "testa" e "croce". Non viene utilizzata la matematica, solo un rapido scambio dei valori ogni volta che si desidera un interruttore.

a = True   
b = False   

a,b = b,a   # a is now False
a,b = b,a   # a is now True

1

Uso la funzione abs, molto utile sui loop

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

x sarà 0.


0

Facciamo un po 'di hacking dei frame. Commuta una variabile in base al nome. Nota: questo potrebbe non funzionare con tutti i runtime di Python.

Supponi di avere una variabile "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

Se hai a che fare con una variabile intera, puoi incrementare 1 e limitare il tuo set a 0 e 1 (mod)

X = 0  # or X = 1
X = (X + 1)%2

0

Il passaggio tra -1 e +1 può essere ottenuto mediante moltiplicazione in linea; utilizzato per il calcolo di pi alla maniera 'Leibniz' (o simile):

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

0

Puoi fare uso indexdi 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"

Pro : nessuna libreria aggiuntiva, codice autoesplicativo e utilizzo di tipi di dati arbitrari.

Contro : non duplica-salva. toggleValues(["one","two","duped", "three", "duped", "four"], "duped") tornerà sempre"three"

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.