76

x次のコードは、とが異なる値であるかどうかをチェックしy(変数xy、は値、、またはzのみを持つことができます)、そうである場合は 3 番目の文字に設定します。abcz

if x == 'a' and y == 'b' or x == 'b' and y == 'a':
    z = 'c'
elif x == 'b' and y == 'c' or x == 'c' and y == 'b':
    z = 'a'
elif x == 'a' and y == 'c' or x == 'c' and y == 'a':
    z = 'b'

より簡潔で、読みやすく、効率的な方法でこれを行うことは可能ですか?

4

11 に答える 11

62
z = (set(("a", "b", "c")) - set((x, y))).pop()

あなたのコードの 3 つのケースのうちの 1 つが成り立つと仮定しています。この場合、セットset(("a", "b", "c")) - set((x, y))は によって返される単一の要素で構成されpop()ます。

編集:コメントで Raymond Hettinger が示唆しているように、タプルのアンパックを使用して、セットから単一の要素を抽出することもできます。

z, = set(("a", "b", "c")) - set((x, y))
于 2012-01-09T17:23:39.130 に答える
47

このstrip方法は、私にとってすぐに実行できるもう1つのオプションです。

z = 'abc'.strip(x+y) if x!=y else None
于 2012-01-09T19:15:29.473 に答える
18
z = (set('abc') - set(x + y)).pop()

これが機能することを示すすべてのシナリオを次に示します。

>>> (set('abc') - set('ab')).pop()   # x is a/b and y is b/a
'c'
>>> (set('abc') - set('bc')).pop()   # x is b/c and y is c/b
'a'
>>> (set('abc') - set('ac')).pop()   # x is a/c and y is c/a
'b'
于 2012-01-09T17:24:21.510 に答える
15

問題の 3 つの項目が"a""b"and"c"ではなく1、 、2and3である場合は、バイナリ XOR を使用することもできます。

z = x ^ y

zより一般的には、3 つの数値の残りの 1 つに設定したい場合、このセットから2 つの数値を指定するab、次のように使用できます。cxy

z = x ^ y ^ a ^ b ^ c

もちろんa ^ b ^ c、数値が固定されている場合は事前計算できます。

このアプローチは、元の手紙でも使用できます。

z = chr(ord(x) ^ ord(y) ^ 96)

例:

>>> chr(ord("a") ^ ord("c") ^ 96)
'b'

このコードを読んだ人がすぐにそれが何を意味するのか理解できるとは思わないでください:)

于 2012-01-09T18:02:04.027 に答える
13

Sven Marnach と FJ によるソリューションは美しいと思いますが、私の小さなテストでは速くはありません。これは、事前に計算された を使用した Raymond の最適化バージョンsetです。

$ python -m timeit -s "choices = set('abc')" \
                   -s "x = 'c'" \
                   -s "y = 'a'" \
                      "z, = choices - set(x + y)"
1000000 loops, best of 3: 0.689 usec per loop

これは元のソリューションです。

$ python -m timeit -s "x = 'c'" \
                   -s "y = 'a'" \
                      "if x == 'a' and y == 'b' or x == 'b' and y == 'a':" \
                      "    z = 'c'" \
                      "elif x == 'b' and y == 'c' or x == 'c' and y == 'b':" \
                      "    z = 'a'" \
                      "elif x == 'a' and y == 'c' or x == 'c' and y == 'a':" \
                      "    z = 'b'"
10000000 loops, best of 3: 0.310 usec per loop

6 つの比較すべてを試行する必要があるため、これはステートメントの最悪の入力であることに注意してください。ifと のすべての値でテストするとx、次の結果yが得られます。

x = 'a', y = 'b': 0.084 usec per loop
x = 'a', y = 'c': 0.254 usec per loop
x = 'b', y = 'a': 0.133 usec per loop
x = 'b', y = 'c': 0.186 usec per loop
x = 'c', y = 'a': 0.310 usec per loop
x = 'c', y = 'b': 0.204 usec per loop

ベースのsetバリアントは、さまざまな入力に対して同じパフォーマンスを示しますが、一貫して2 ~ 8 倍遅くなります。その理由は、ifベースのバリアントがはるかに単純なコードを実行するためです。つまり、ハッシュと比較して等価テストです。

どちらのタイプのソリューションも価値があると思います。セットのような「複雑な」データ構造を作成すると、パフォーマンスが低下する一方で、読みやすさと開発速度が大幅に低下することを知っておくことが重要です。コードを変更すると、複雑なデータ型も大幅に改善されます。セットベースのソリューションを 4 つ、5 つ、... の変数に拡張するのは簡単ですが、if ステートメントはすぐにメンテナンスの悪夢に変わります。

于 2012-01-09T17:39:58.777 に答える
8

辞書を使用して、このオプションを試してください。

z = {'ab':'c', 'ba':'c', 'bc':'a', 'cb':'a', 'ac':'b', 'ca':'b'}[x+y]

もちろん、x+yキーがマップに存在しない場合はKeyError、処理する必要がある が生成されます。

ディクショナリが 1 回事前計算され、将来の使用のために保存されている場合、評価ごとに新しいデータ構造を作成する必要がなく、文字列の連結とディクショナリのルックアップのみが必要になるため、アクセスははるかに高速になります。

lookup_table = {'ab':'c', 'ba':'c', 'bc':'a', 'cb':'a', 'ac':'b', 'ca':'b'}
z = lookup_table[x+y]
于 2012-01-09T17:45:01.803 に答える
8
z = 'a'*('a' not in x+y) or 'b'*('b' not in x+y) or 'c'

以下のハックで、条件付き代入を使用する

z = 'a' if ('a' not in x+y) else 'b' if ('b' not in x+y) else 'c'

しかし、おそらくdictソリューションの方が高速です...時間を計る必要があります。

于 2012-01-09T17:44:02.893 に答える
2

私はそれが次のように見えるべきだと思います:

z = (set(("a", "b", "c")) - set((x, y))).pop() if x != y else None
于 2012-01-09T17:38:34.033 に答える
1

リスト内包表記を使用して、他の場合と同様に、コード内の 3 つのケースのいずれかが保持されていると仮定します。

l = ['a', 'b', 'c']
z = [n for n in l if n not in [x,y]].pop()

または、受け入れられた回答のように、タプルを利用して展開し、

z, = [n for n in l if n not in [x,y]]
于 2012-01-09T18:57:03.513 に答える
0

これが機能するかどうかを確認してください

if a not in xy
    z= 'a'
if b not in xy
    z='b'
if c not in xy
    z='c'
于 2012-01-10T00:19:29.513 に答える