Як переключити значення в Python


128

Який найефективніший спосіб перемикання між 0і 1?


Хоча це питання задає питання про те, як найбільш ефективно перемикати значення у двійковій формі, деякі відповіді пояснюють пересування через (довільні) значення, наприклад, stackoverflow.com/a/61041907/537865
божевільний

Відповіді:


273

Рішення за допомогою NOT

Якщо значення булеві, найшвидшим підходом є використання оператора not :

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

Розв’язання з використанням віднімання

Якщо значення чисельні, віднімання від загального є простим і швидким способом перемикання значень:

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

Рішення за допомогою XOR

Якщо значення перемикається між 0 і 1 , ви можете використовувати побітове ексклюзивне або :

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

Методика узагальнюється до будь-якої пари цілих чисел. Крок крок за одним замінюється константою xor за попередньою обчисленою:

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

(Цю ідею подав Нік Коглан, а згодом узагальнив @zxxc.)

Рішення за допомогою словника

Якщо значення доступні, можна скористатися словником:

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

Розв’язання, використовуючи умовний вираз

Найповільніший спосіб - використовувати умовний вираз :

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

Рішення за допомогою itertools

Якщо у вас є більше двох значень, функція itertools.cycle () забезпечує загальний швидкий спосіб перемикання між послідовними значеннями:

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

Зауважте, що в Python 3 next()метод був змінений на __next__(), тому перший рядок тепер буде записаний якtoggle = itertools.cycle(['red', 'green', 'blue']).__next__


Останній приклад здається настільки гладким та інтуїтивним, але не працює в Python 3+ із видаленням .next (). Чи є спосіб змусити його працювати аналогічно в більш пізній версії python?
labarna

2
@labarna У Python 3 функцію .next()замінила глобальна next()функція. Вищенаведеним прикладом буде:toggle = itertools.cycle(...); next(toggle)
elpres

2
toggle = itertools.cycle(['red', 'green', 'blue']) next(toggle)
Максиміліян

7
Приклад XOR можна узагальнити для перемикання між значеннями aта bвикористанням x = x ^ (a ^ b).
zxxc

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

33

Я завжди використовую:

p^=True

Якщо р - булева, це перемикається між істинним та хибним.


1
Ідеально! pДля цього методу не потрібно посилатися двічі !! Ідея, якщо ви перемикаєте значення з довгим довгим посиланням.
ThorSummoner

1
як називається цей оператор?
mix3d

4
Це оператор XOR.
bastelflp

1
@ mix3d Саме він є "побітним ексклюзивом або" (на відміну від "логічного ексклюзиву чи") - wiki.python.org/moin/BitwiseOperators . Логічний XOR загалом не має конкретного оператора в Python, але ви можете знайти його реалізованим у деяких спеціальних випадках, наприклад, у десятковому модулі.
Тейлор

@ mix3d ^=- порозрядне присвоєння xor
wjandrea

23

Ось ще один неінтуїтивний спосіб. Краса полягає в тому, що ти можеш проїхати кілька значень, а не лише два [0,1]

Для двох значень (перемикання)

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

Для кількох значень (скажімо 4)

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

Я також не очікував, що це рішення буде майже швидким

>>> 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
так, це солодке як горіх. дякую всім, це цікаво, дивлячись, як різні люди підходять до проблеми (та інформативно.)

Приємно, це мініатюрна державна машина.
kindall

ну, ваш найцікавіший, але це не те, що мені особисто потрібно для того, про що я просив, так добре, я думаю, проста математика тоді, мабуть, найкраще для мене, чи не повинно бути 1-х там?

Так, але це не повинно відрізняти швидкість.
Блендер

Ай, але це зробило б це неправильно, хоча б і не було? кілька чудових відповідей тут, ТАК скелі!

19

notОператор заперечує змінну (перетворюючи його в логічне значення , якщо воно вже не один). Ви, ймовірно, можете використовувати 1та 0взаємозамінно з Trueі False, тому просто заперечуйте це:

toggle = not toggle

Але якщо ви використовуєте два довільних значення, використовуйте рядок if:

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

1
+1 , але toggle = 0 if toggle else 1коротше і більш загальне
Luc

Вибачте, я підміняю змінні, щоб зробити це зрозумілішим. Я використовував вбудований рядок ifдля перемикання між двома довільними змінними, а не просто 1та 0.
Блендер

14

Тільки між 1 і 0, зробіть це

1-x 

x може приймати 1 або 0


Оскільки (в будь-якому випадку Python 2.x, як Trueі раніше ) і Falseє фактично цілими числами, хоча і з дивно багатослівним __str__()методом, xтакож може бути Trueабо Falseтут. Однак ви отримаєте 1 або 0 назад.
kindall

12

Тригонометричний підхід , просто тому, що sinі cosфункції класні.

введіть тут опис зображення

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

Боже мій! Я <3 ти.
Рішабх Аграхарі

2
@RishabhAgrahari, так, людина, я був переможцем виклику Реймонда Хеттінгера ;)
dani herrera

7

Дивно, але ніхто не згадує старий хороший модуль поділу 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

Зауважте, що він еквівалентний x = x - 1 , але перевага модульної техніки полягає в тому, що розмір групи або довжина інтервалу може бути більшим, ніж просто 2 елементи, що дає вам схожу схему переплетення круглолітових, щоб перемикати.

Тепер лише для 2-х перемикань може бути трохи коротшим (за допомогою бітового оператора):

x = x ^ 1

Я не впевнений, наскільки «пітонічним» є ця (C-подібна) арифметична модуль (я, е, чи застосовується «пітонічна»?). Я думаю, це просто арифметика, працює скрізь, де у вас є бінарне.
Яуген Якимович

Очевидно, що машина з кінцевим станом з кортежем типу x = (1,2,3,0); token = 0; token = x [token] надзвичайно захоплюючий, оскільки може бути навіть більш загальним, ніж просто групова робота.
Яуген Якимович

7

один із способів перемикання - це використання декількох призначень

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

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

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

Використання 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

Найпростіший спосіб переключення між 1 і 0 - відняти від 1.

def toggle(value):
    return 1 - value

4

Використання обробника винятків

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

Гаразд, я найгірший:

введіть тут опис зображення

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

Як щодо уявного перемикання, яке зберігає не тільки поточний перемикач, але і пару інших значень, пов’язаних з ним?

toggle = complex.conjugate

Збережіть значення + або - зліва та будь-яке неподписане значення праворуч:

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

Нуль працює також:

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

Легко отримуйте поточне значення перемикання ( Trueі Falseпредставляє + і -), значення LHS (реальне) або значення RHS (уявне):

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

Легко поміняйте місцями LHS та RHS (але врахуйте, що знак обох значень не повинен бути важливим):

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

Легко міняйте LHS та RHS, а також перемикайте одночасно:

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

Захищає від помилок:

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

Виконайте зміни до LHS та RHS:

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

... але будьте обережні, маніпулюючи RHS:

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

2

Змінні a і b можуть мати будь-які два значення, наприклад 0 і 1, або 117 і 711, або "голови" і "хвости". Математика не використовується, лише швидка підміна значень кожного разу, коли потрібен перемикач.

a = True   
b = False   

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

1

Я використовую функцію abs, дуже корисну для циклів

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

х буде 0.


0

Давайте зробимо кілька злому кадрів. Увімкніть змінну на ім'я. Примітка. Це може не працювати з кожним виконанням Python.

Скажімо, у вас є змінна "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

Якщо ви маєте справу з цілою змінною, ви можете збільшити 1 і обмежити набір 0 і 1 (mod)

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

0

Перемикання між -1 та +1 може бути отримане шляхом вбудованого множення; використовується для обчислення pi способом "Лейбніц" (або подібним):

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

0

Ви можете використовувати з indexв listс.

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"

Плюси : відсутність додаткових бібліотек, власний пояснювальний код та робота з довільними типами даних.

Мінуси : не дублювати-зберігати. toggleValues(["one","two","duped", "three", "duped", "four"], "duped") завжди повернеться"three"

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.