Як я магу праверыць, што спіс мае адзін і толькі адзін truthy значэнне?

У пітона, у мяне ёсць спіс, які павінен мець <�моцны> адзін і толькі адзін truthy значэнне (гэта значыць, BOOL (значэнне) True ). Ёсць разумны спосаб праверыць гэта? Прама цяпер, я проста ітэрацыя па спісе і ручная праверка:

def only1(l)
    true_found = False
    for v in l:
        if v and not true_found:
            true_found=True
        elif v and true_found:
             return False #"Too Many Trues"
    return true_found

Гэта, здаецца, безгустоўны і не вельмі прарочы. Ці ёсць разумнейшы спосаб зрабіць гэта?

67
Я думаю, што ваша рашэнне цалкам нармальна, і гэта прарочы!
дададзена аўтар wim, крыніца
Common Lisp: (= 1 (кол-калі # 'спіс ідэнтычнасці)) .
дададзена аўтар Kaz, крыніца
Каб быць ясна: вы хочаце, каб праверыць, што ёсць толькі адзін True або толькі адзін truthy значэнне?
дададзена аўтар Marcin, крыніца
<�Код> сума (ЛСТ) == 1
дададзена аўтар Pål GD, крыніца

17 адказы

Той, які не патрабуе імпарту:

def single_true(iterable):
    i = iter(iterable)
    return any(i) and not any(i)

З іншага боку, магчыма, больш чытэльная версія:

def single_true(iterable):
    iterator = iter(iterable)
    has_true = any(iterator) # consume from "i" until first true or it's exhuasted
    has_another_true = any(iterator) # carry on consuming until another true value/exhausted
    return has_true and not has_another_true # True if exactly one true found

гэта:

  • Падобна, каб пераканацца, я мае любая праўдзівае значэнне
  • пазірае з гэтага моманту ў ітэрацыю, каб пераканацца, што няма іншага сапраўднага значэння
207
дададзена
@MatthewScouten Вы амаль правільна - з бясконцымі паслядоўнасцямі, яна будзе працаваць, за выключэннем выпадкаў адмовы, дзе ёсць 2 ісцін. Але ёсць таксама выпадак няўдачы з-за 0 ісцін, якія ніколі не вернуцца б.
дададзена аўтар wim, крыніца
@Lattyware ИМО гэта не чытаецца, таму што яна абапіраецца на схаваныя папярэдняга веды дэталі рэалізацыі любы . Той, хто мае досвед працы з Python ведае, што гэта кароткае замыканне і працуе на бясконцых паслядоўнасцях, і спажывае итератор відавочным чынам. Але гэта цалкам натуральна [памылкова] мяркуюць, што гэта вядзе сябе як тэарэтыка-множнай або матэматычную абстракцыю, напрыклад, працуючы на ​​неўпарадкаваных наборы як канчатковыя мноства паралельна, а не перабор і працаёмкія. Менавіта з гэтай прычыны, што лінія выглядае нязграбнай і заблытанай, пра што сведчыць у каментарах.
дададзена аўтар wim, крыніца
-1 па той жа прычыне, як тут - гэтая лінія ўяўляе сабой блок-псіхічны камень. чытальнасць мае значэнне.
дададзена аўтар wim, крыніца
@Lattyware Я добра ведаю гэтага . Тым не менш, радок кода, які выглядае як любой (I), а не які-небудзь (я) няяўна хаваецца зусім няшмат вытанчанасці, і я думаю, што код у OP з'яўляецца магчыма больш прарочы. Логіка ў вырашальнай ступені залежыць ад пабочных эфектаў любы / усе апярэджання итераторы. Разуменне таго, што лінія таксама залежыць ад ведаючы, што левы бок і будзе ацэньвацца да правага боку. Гэта не адразу відаць, каб убачыць, што я на левай баку адрозніваецца ад я на правай баку. Можа быць, калі б ён быў разбіты на некалькі радкоў ..
дададзена аўтар wim, крыніца
<�Код> Zen пітона # 17: Калі рэалізацыя цяжка растлумачыць, што гэта дрэнная ідэя У апошні раз Джон. Clements адправіў у той жа радку кода быў pagefull каментароў, як «я павінен быў прачытаць гэта па меншай меры ў 4 разы, пакуль я не зразумеў гэта», «я не разумею, як ён чытае. а калі праўда, а не праўда. Дапамажыце мне зразумець «. і "Нават з тлумачэннем я не магу зразумець паводзіны» усё, што ад розных карыстальнікаў.
дададзена аўтар wim, крыніца
Можа быць, нешта накшталт seen_one = любой (I); seen_another = любой (я); вярнуцца seen_one і ня seen_another . Хоць, магчыма, гэта падправіць дзэн # 2 за кошт прынясення ў ахвяру дзэн # 1 ... Уздых :)
дададзена аўтар wim, крыніца
Я выдаліў свой downvote, таму што альтэрнатыва выглядае добра. Инлайна каментары празмернасці, хоць, досыць відавочна, каб быць самастойным каментаваннем коды. <�Я> Гэта першая версія, якая мае патрэбу ў каментары .
дададзена аўтар wim, крыніца
@pnuts Я галасаваў -1, таму што я адчуваў, што адказ меў некаторыя праблемы ў яго першапачатковым выглядзе. Я пакінуў каментар, які тлумачыць, што я адчуваў, быў недахоп, і пасля некаторых абмеркаванняў адказ быў пасля адрэдагаваны аўтарам. Гэта змяненне палепшыла адказ досыць і таму я адмовіўся ад майго downvote. Не маглі б вы растлумачыць, дзе менавіта гэта супярэчыць жаданні SO духу, ці што гэта <�б> жаданы SO дух азначае для вас?
дададзена аўтар wim, крыніца
@pnuts Вы маеце рацыю, што пашыранае абмеркаванне не рэкамендуецца ў каментарах, таму мы можам працягнуць гэта абмеркаванне ў чаце .
дададзена аўтар wim, крыніца
Re: усе дыскусіі пра чытальнасці тут; Я б выказаў здагадку, што прычына многіх людзей знайшлі гэты адказ цяжка зразумець, <�я> не , што яны не разумеюць паводзіны любы , але яны не разумеюць, иТЭР() стварае выдаткоўванай итератор ці не знаёмыя з канцэпцыяй выдаткоўванага итератора ў цэлым. Праблема, як праверка, што спіс мае адзін True значэнне з'яўляецца тое, што поўны пачатковец праграміст можа таксама сутыкнуцца з; гэта зразумела, што такія людзі знаходзяць неабходнасць відавочна думаць пра якія расходуюцца итераторах быць дзіўнымі і цяжка.
дададзена аўтар Mark Amery, крыніца
Асабліва з другой версіяй коды, гэта, безумоўна, правільны адказ. Лягчэй чытаць <�я> і </я> выкарыстоўвае больш Pythony даброць.
дададзена аўтар Robert Grant, крыніца
@eyquem: has_true і ня has_another_true здаецца досыць чытэльным. Хоць спасылка на тлумачэнні, што вы можаце толькі адзін раз ітэрацыю над итератором не пашкодзіць тое, просты акт ітэрацыі «спажывае» итератор ў Python. Хоць я хацеў бы выкарыстаць гэта або итератора ў першым прыкладзе.
дададзена аўтар jfs, крыніца
@wim Ну я не думаю, што большая частка «-1, што разумны» як канструктыўны каментар. У астатнім я баюся, што гэта не месца, і там няма месца.
дададзена аўтар pnuts, крыніца
@wim @wim На працягу некалькіх дзесяткаў слоў О.П. згадаў і <�б> Ёсць <�я> разумны спосаб , каб праверыць гэта? і <�б> Ёсць <�я> разумнешым </я> спосаб зрабіць гэта? тэстаў Хуткасць паказаць Зварот любога (I), а не які-небудзь (I) хуткая/ОЦ. Гэта ў цяперашні час 33+ і вы націснулі на «Адказ на гэтае пытанне не з'яўляецца карысным». Шчыра кажучы, я думаю, што вы мелі рацыі, і дзейнічалі насуперак жаданага С.О. духу, так сказаў так - хоць канструктыўна, што вы мелі другую думка.
дададзена аўтар pnuts, крыніца
+1, але убудаваныя каментары ў другой залішнія, добра падабраныя імёны зменных FTW.
дададзена аўтар Andy Hayden, крыніца
@joneshf Гэта не так. Ён часта выкарыстоўваецца, але гэта ніколі не было ні слова. Калі вы паглядзіце яго, спіс яго большасць слоўнікаў, як «нестандартны» або «няправільны» . Правільнае слова было б адзін з <�я> незалежна або <�я> незалежна .
дададзена аўтар Gareth Latty, крыніца
@wim Гэта <�я> не </я> дэталь рэалізацыі любога() - гэта дакументаваная асаблівасць функцыі і гарантаваная функцыянальнасць любой рэалізацыі, што адпавядае спецыфікацыям Python.
дададзена аўтар Gareth Latty, крыніца
Я б сказаў, што гэта не цяжка растлумачыць, гэта проста не ў вышэйшай ступені чытэльным. Я згодны, што падзяліўшы яго на пару радкоў, магчыма, з инлайн каментар, калі вы хочаце быць супер ясна, можа быць карысным для чытачоў кода, каб яны зразумелі. Улічваючы, што гэта ў функцыі з адпаведным імем, аднак, я не думаю, што <�я> таксама </я> цяжка зразумець, што адбываецца, а рэальнасць любой нетрывіяльнай код ніколі не будзе адразу зразумела, на першы погляд.
дададзена аўтар Gareth Latty, крыніца
Гэта мае поўны сэнс. <�Код> любой() з'яўляецца guarenteed спыніць спажыванне другога яна трапляе True значэнне, і итераторы заўсёды будзе спажывацца. Гэта лёгка лепшы спосаб зрабіць гэта тут. Гэта эфектыўна, кароткія і чытэльным.
дададзена аўтар Gareth Latty, крыніца
Я не думаю, што рашэнне так непрыгожа - я б сцвярджаць, што падзел яго, як гэта робіць яго вельмі ясным і чытэльным, захоўваючы пры гэтым добры метад.
дададзена аўтар Gareth Latty, крыніца
@joneshf "Яго рэпутацыя не паднялася на працягу многіх гадоў, і гэта яшчэ далёка ад ўсеагульнага прызнання . Выкарыстоўвайце незалежна замест ". - КДИ не ў сеткі, але ён таксама пералічвае слова як няправільнае. Калі вы хочаце быць тэхнічнымі, так, слова існуе - але гэта бессэнсоўнае слова, якое не павінна выкарыстоўвацца. Калі я кажу "гэта не слова», што было стэнаграфія - ясна слова сапраўды існуе, гэта проста не добры выкарыстоўваць.
дададзена аўтар Gareth Latty, крыніца
Акрамя таго, некалькі менш рэлевантнасці, <�я> Незалежна </я> не слова. Ва ўсякім выпадку, +1, элегантны і лаканічны адказ.
дададзена аўтар Gareth Latty, крыніца
Я рэдагаваў код, каб разбіць яго на некалькі - з некаторымі дадатковымі каментарамі і відавочнымі імёнамі зменных
дададзена аўтар Jon Clements, крыніца
@MatthewScouten не ... мы спажываючы ад итератора тут ... паспрабуйце запусціць код ...
дададзена аўтар Jon Clements, крыніца
@MatthewScouten ў адпаведнасці з спажываннем Iterable. <�Код> любы будзе паводле дакументацыі вярнуцца Праўда, як толькі будзе знойдзена без фальшывага значэння. Пасля гэтага, мы шукаем сапраўднае значэнне, а таксама калі знойдзены лячыць яго як адмова ... Так што гэта будзе працаваць для пустых спісаў, спісаў/іншых паслядоўнасцяў, і любы ітэрацыі ...
дададзена аўтар Jon Clements, крыніца
@MarkAmery Вы ведаеце, што - я думаю, што вы, верагодна, маюць рацыю, што гэта асноўны пазнавальны каменем перапоны тут (ён быў таксама узняты на аналагічны адказ на іншую пасаду). Я меў на ўвазе, каб вярнуцца на гэты пост у нейкі момант, але да гэтага часу ён не забыўся (хоць вы зрабілі згадаць яго мета на іншы дзень). Калі ў мяне ёсць момант, я буду бачыць, калі я магу распрацаваць што-то значна менш «дзіўна»/«заблытаны»/«адразу няправільна глядзіць» - я не быў цалкам задаволены абнаўленнем я зрабіў да яго першапачаткова так ці інакш. Дзякуй за вашу перспектыву і напамінку.
дададзена аўтар Jon Clements, крыніца
@MathewScouten Пабочных эфектаў разарваць усе тэарэмы! <�Код> х і ня х = False з'яўляецца адзіна правільным, калі х референциально празрыстым.
дададзена аўтар Ben, крыніца
@ Джон Клементс Улічваючы ваш апошні каментар, што вы думаеце не вяртаюцца любой (я), калі любы (я) яшчэ False , каб палепшыць чытальнасць? Я пачытаю ваш першы адказ, каб быць праніклівым і досыць ясна, але гэта не так для ўсіх, па-відаць.
дададзена аўтар eyquem, крыніца
Я думаю, што правільны пытанне, ці з'яўляецца гэта ці не ідыёматычны для дадзенага мовы. Гэта? Я не пітон чалавека, але я магу прыдумаць некалькі канструкцый, якія ідыёматычны ў C, які я знайшоў бы нязручным ў іншых мовах.
дададзена аўтар Euro Micelli, крыніца
Той, хто думае, што гэта не чытаемае рашэнне павінна ўлічваць наступнае: Гэта кароткае і залежыць толькі ад вядомых мадэляў паводзінаў і агульнай канструкцыі Python. Проста таму, што нуб не зразумее, хто ня робіць яго даступным для чытання. Ён таксама з'яўляецца выдатным сродкам выкладання, што павінна быць вядома, паколькі ён падбухторвае непасрэднае цікаўнасць тых, хто не бачыў, як ён працуе.
дададзена аўтар dansalmo, крыніца
@ Джон Клементс: Які чорт? Вы маеце рацыю. але: я бачу, не гарантуе, што любы будзе спажываць толькі частка Iterable. І гэта ніколі не будзе прайсці праверку кода. ніхто не паверыць, пакуль не паспрабуеш.
дададзена аўтар Matthew Scouten, крыніца
Калі б я мог прыняць 2, я б прыняў гэта, як добра. Ён мае перавагу быць кароткімі, эфектыўнымі і разумным. Ён таксама натхніў цікавы і карысная размова. Праблема заключаецца ў тым, што яна выглядае жудасна няправільна выпадковы чытач.
дададзена аўтар Matthew Scouten, крыніца
х і ня х заўсёды Ілжывыя Па-за залежнасці ад й
дададзена аўтар Matthew Scouten, крыніца
Калі бясконцая итерируемыми запаўняе праходы тэст лагічна, гэта ніколі не вернецца. ці не так? Гэта толькі «працуе» для бясконцых итерируемых, калі яны не праходзіць тэст.
дададзена аўтар Matthew Scouten, крыніца
Я даведаўся шмат з гэтым адказам, як аб функцыі ИТЭР (), што я ніколі не выкарыстоўваў раней, і на самай справе цікава, калі выкарыстоўваць для кутніх падобных выпадках, за выключэннем і аб адмысловым ўласцівасці любога, што робіць яго вельмі эфектыўным. Відавочна, што другі прыклад лепш. Я б, аднак, падкрэсліць, больш ролю ИТЭР() , так як я цалкам ігнараваў яго, пакуль я не чырвоныя каментары ..
дададзена аўтар Yohan Obadia, крыніца
@Lattyware Незалежна слова.
дададзена аўтар joneshf, крыніца
@Lattyware, гэта значна больш, абмеркаванне для english.stackexchange, так што я буду трымаць яго кароткім, і не адказваць потым. Па-за залежнасці ад таго, што вы думаеце, гэтае слова. Як аўтарытэтны ў вікіпедыі ёсць фактычныя слоўнікі (г.зн. Merriam-Webster і Оксфард) пералічыць яго ў якасці словы.
дададзена аўтар joneshf, крыніца

Гэта залежыць ад таго, калі вы проста шукаеце для значэння True або таксама шукаюць іншыя каштоўнасці, якія будуць вылічвацца True лагічна (напрыклад, 11 або < код> "прывітанне" ). Калі ранейшы:

def only1(l):
    return l.count(True) == 1

Калі апошні:

def only1(l):
    return sum(bool(e) for e in l) == 1

так як гэта будзе рабіць як падлік і пераўтварэнне ў адной ітэрацыі без неабходнасці стварэння новага спісу.

41
дададзена
Я хацеў бы пазбегнуць, выкарыстоўваючы л у якасці імя зменнай (яна выглядае занадта шмат, як 1 тут), і я хацеў бы перапісаць сума (BOOL (е) пры е ў л ) а сума (1 для е л, калі е)
дададзена аўтар wim, крыніца
@delnan: Дзякуй, зрабіў гэта змена
дададзена аўтар David Robinson, крыніца
У Python 3 :. <�Код> Спіс (карта (BOOL, л)) разлічваць (True)
дададзена аўтар poke, крыніца
Проста паказаць на OP гэта, верагодна, не будзе кароткае замыканне пры выяўленні больш чым адзін «True» значэння, таму іх код можа даць ім больш высокую эфектыўнасць пры пэўных абставінах.
дададзена аўтар NominSim, крыніца
Другая функцыя можа быць запісана ў выглядзе Зварот сумы (BOOL (е) пры е ў л) == 1 . <�Код> Bool падкласы Int і True/False паводзяць сябе, як 1/0 адносна арыфметыкі.
дададзена аўтар delnan, крыніца
Гэта знаходзіць толькі літарныя Праўда, не іншыя праўдзівыя каштоўнасці (напрыклад: пазітыўныя Ints не пустыя кантэйнеры, і г.д.)
дададзена аўтар Matthew Scouten, крыніца
Мне гэта падабаецца. Прыемны і чысты. Для майго выпадку, аднак, мне трэба было не злічыць None як ілжывыя, а астатняе, як True (уключаючы 0). Але гэта невялікая змена, каб зрабіць: only1 = лямбда: сума (е не з'яўляецца Ні для е ў MyList) == 1
дададзена аўтар vtlinh, крыніца

Найбольш шматслоўным рашэнне не заўсёды самае unelegant рашэнне. Таму я дадаю толькі нязначныя змены (для таго, каб захаваць некаторыя залішнія лагічныя ацэнкі):

def only1(l):
    true_found = False
    for v in l:
        if v:
            # a True was found!
            if true_found:
                # found too many True's
                return False 
            else:
                # found the first True
                true_found = True
    # found zero or one True value
    return true_found

Вось некаторыя таймінгі для параўнання:

# file: test.py
from itertools import ifilter, islice

def OP(l):
    true_found = False
    for v in l:
        if v and not true_found:
            true_found=True
        elif v and true_found:
             return False #"Too Many Trues"
    return true_found

def DavidRobinson(l):
    return l.count(True) == 1

def FJ(l):
    return len(list(islice(ifilter(None, l), 2))) == 1

def JonClements(iterable):
    i = iter(iterable)
    return any(i) and not any(i)

def moooeeeep(l):
    true_found = False
    for v in l:
        if v:
            if true_found:
                # found too many True's
                return False 
            else:
                # found the first True
                true_found = True
    # found zero or one True value
    return true_found

Мой выхад:

$ python -mtimeit -s 'import test; l=[True]*100000' 'test.OP(l)' 
1000000 loops, best of 3: 0.523 usec per loop
$ python -mtimeit -s 'import test; l=[True]*100000' 'test.DavidRobinson(l)' 
1000 loops, best of 3: 516 usec per loop
$ python -mtimeit -s 'import test; l=[True]*100000' 'test.FJ(l)' 
100000 loops, best of 3: 2.31 usec per loop
$ python -mtimeit -s 'import test; l=[True]*100000' 'test.JonClements(l)' 
1000000 loops, best of 3: 0.446 usec per loop
$ python -mtimeit -s 'import test; l=[True]*100000' 'test.moooeeeep(l)' 
1000000 loops, best of 3: 0.449 usec per loop

Як можна бачыць, рашэнне OP значна лепш, чым большасць іншых рашэнняў, размешчаных тут. Як і варта было чакаць, лепшыя з іх з'яўляюцца тыя, з паводзінамі кароткага замыкання, асабліва такім рашэннем, якое размясцілі Джон Клементс. Прынамсі, для выпадку двух ранніх True значэнняў у доўгім спісе.

Тут жа для не True значэння на ўсіх:

$ python -mtimeit -s 'import test; l=[False]*100000' 'test.OP(l)' 
100 loops, best of 3: 4.26 msec per loop
$ python -mtimeit -s 'import test; l=[False]*100000' 'test.DavidRobinson(l)' 
100 loops, best of 3: 2.09 msec per loop
$ python -mtimeit -s 'import test; l=[False]*100000' 'test.FJ(l)' 
1000 loops, best of 3: 725 usec per loop
$ python -mtimeit -s 'import test; l=[False]*100000' 'test.JonClements(l)' 
1000 loops, best of 3: 617 usec per loop
$ python -mtimeit -s 'import test; l=[False]*100000' 'test.moooeeeep(l)' 
100 loops, best of 3: 1.85 msec per loop

Я не правяраў статыстычную значнасць, але цікава, на гэты раз падыходы, прапанаваныя F.J. і асабліва, што адзін Джон Клементс зноў, як уяўляецца, відавочна пераўзыходзіць.

37
дададзена
+1 Для вашага адкрыцця лініі. Усе адказы з сума на самой справе горш, чым просты і прамой кода OP .. У
дададзена аўтар wim, крыніца
Эх ... можа быць, гэта не правільны выклік, але я кідаў уніз -1 для выпадкова ў выніку чаго ў дэталёвым прафіляванне, дзе ў нас няма ніякіх падставаў думаць, што гэта апраўдана. Там не было ніякіх указанняў ў пытанні пра тое, што найбольш эфектыўным рашэннем можна было жаданага, таму той факт, што вы апісалі больш эфектыўныя рашэнні, як "вышэй", не клапоцячыся пра іншых фактараў - як чытальнасць - проста здаецца недарэчным.
дададзена аўтар Mark Amery, крыніца
@JonClements, менавіта таму я напісаў <�я> найбольш , зрабіў гэта ясней. (Большасць з дасланых, ня большасьць пратэставаных ...)
дададзена аўтар moooeeeep, крыніца
@MarkAmery я дадаў як раздзел аб чытальнасці і элегантнасці (адзін кароткі па агульным прызнанні) і аб ацэнцы эфектыўнасці. Паколькі пытанне прасіў кемлівасць абодва аспекты павінен быць прадметам разгляду, я думаю. Як я гэта бачу, я падаў адказ для вырашэння гэтых двух адпаведных аспектаў. Калі вы адчуваеце, што гэты адказ не з'яўляецца карысным, не саромейцеся downvote.
дададзена аўтар moooeeeep, крыніца
Умм - гледзячы на ​​ранніх праўдзівых таймінгі - не 0,446 самы хуткі?
дададзена аўтар Jon Clements, крыніца
Я прыняў гэта, таму што гэта тое, што я вырашыў на самай справе выкарыстаць для майго кода, і таму што гэта быў першы адказ з таймінгамі на ім. @ Джон Клементс адказ быў блізкі бягун.
дададзена аўтар Matthew Scouten, крыніца
Я падазраю, што JonClement настолькі хутка, таму што большасць з любы рэалізуецца ў C
дададзена аўтар Matthew Scouten, крыніца

Однострочный адказ, які захоўвае паводзіны кароткага замыкання:

from itertools import ifilter, islice

def only1(l):
    return len(list(islice(ifilter(None, l), 2))) == 1

Гэта будзе значна хутчэй, чым іншыя альтэрнатывы тут для вельмі вялікіх итерируемых, якія маюць два ці больш праўдзівыя каштоўнасці адносна рана.

ifilter(None, itr) gives an iterable that will only yield truthy elements (x is truthy if bool(x) returns True). islice(itr, 2) gives an iterable that will only yield the first two elements of itr. By converting this to a list and checking that the length is equal to one we can verify that exactly one truthy element exists without needing to check any additional elements after we have found two.

Вось некаторыя параўнання тэрмінаў:

  • Setup code:

    In [1]: from itertools import islice, ifilter
    
    In [2]: def fj(l): return len(list(islice(ifilter(None, l), 2))) == 1
    
    In [3]: def david(l): return sum(bool(e) for e in l) == 1
    
  • Exhibiting short-circuit behavior:

    In [4]: l = range(1000000)
    
    In [5]: %timeit fj(l)
    1000000 loops, best of 3: 1.77 us per loop
    
    In [6]: %timeit david(l)
    1 loops, best of 3: 194 ms per loop
    
  • Large list where short-circuiting does not occur:

    In [7]: l = [0] * 1000000
    
    In [8]: %timeit fj(l)
    100 loops, best of 3: 10.2 ms per loop
    
    In [9]: %timeit david(l)
    1 loops, best of 3: 189 ms per loop
    
  • Small list:

    In [10]: l = [0]
    
    In [11]: %timeit fj(l)
    1000000 loops, best of 3: 1.77 us per loop
    
    In [12]: %timeit david(l)
    1000000 loops, best of 3: 990 ns per loop
    

Такім чынам, сума() падыход хутчэй для вельмі маленькіх спісаў, але спіс ўводу становіцца больш мая версія працуе хутчэй, нават калі кароткае замыканне не ўяўляецца магчымым. Калі кароткае замыканне магчыма на вялікім ўваходзе, розніца ў прадукцыйнасці ясная.

19
дададзена
Гэта з'яўляецца дзіўным.
дададзена аўтар Ryan Haining, крыніца
@NominSim калі ласка, паглядзіце на таймінгі, прадугледжаных у маёй абароне: stackoverflow.com/a/16801638/1025391
дададзена аўтар moooeeeep, крыніца
+1, калі вы даць некаторыя timeit эксперыментаванне для аб'ектыўнай пункту гледжання прадукцыйнасці ў параўнанні з рашэннем ОП.
дададзена аўтар moooeeeep, крыніца
Гэта можа быць зроблена без імпарту, як паказана ў маім адказе - але, безумоўна, добра выкарыстоўваць itertools ;)
дададзена аўтар Jon Clements, крыніца
@moooeeeep наіўнасці, калі вы мелі бясконцае Iterable, які мае два True значэнне недзе «на ранняй стадыі», гэта завершыцца, у параўнанні з іншымі адказамі спінінга іх колаў назаўжды, спрабуючы атрымаць рахунак.
дададзена аўтар NominSim, крыніца
+1. Толькі адзін, каб прайграць поўнае намер OP яшчэ кароткага замыкання.
дададзена аўтар NominSim, крыніца
Нав. Узяў мяне тройчы, пакуль іншыя варыянты, каб зразумець. Калi кароткае замыканне важна, я б код OP як гэта значна больш відавочным і прыкладна гэтак жа эфектыўным.
дададзена аўтар delnan, крыніца
Да галасавання за стыль, і захаванне кароткага замыкання. Але гэта цяжэй чытаць.
дададзена аўтар Matthew Scouten, крыніца

Я хацеў, каб зарабіць некрамант знак, так што я абагульніў выдатны адказ на Джона Clements ', захоўваючы перавагі кароткага замыкання логікі і хуткай праверку з любым прэдыкаты.

Дык вось:

N (ісціны) = п

def n_trues(iterable, n=1):
    i = iter(iterable)
    return all(any(i) for j in range(n)) and not any(i)

N(trues) <= n:

def up_to_n_trues(iterable, n=1):
    i = iter(iterable)
    all(any(i) for j in range(n))
    return not any(i)

N(trues) >= n:

def at_least_n_trues(iterable, n=1):
    i = iter(iterable)
    return all(any(i) for j in range(n))

m <= N(trues) <= n

def m_to_n_trues(iterable, m=1, n=1):
    i = iter(iterable)
    assert m <= n
    return at_least_n_trues(i, m) and up_to_n_trues(i, n - m)
13
дададзена
>>> l = [0, 0, 1, 0, 0]
>>> has_one_true = len([ d for d in l if d ]) == 1
>>> has_one_true
True
11
дададзена
@dansalmo: Гэта цяжка быць упэўненым, вядома, але мая тэорыя, што многія n00b пітона праграмістаў - магчыма, тыя, з Java фону, у прыватнасці, - адчуваць сябе больш камфортна з больш сінтаксісам. (Я, я выкарыстаў, каб быць трохі як то 5-10 гадоў таму, але сёння я лічу непрафесійным і недасведчаныя.) +1
дададзена аўтар Jonas Byström, крыніца
Чаму гэта downvoted? Я думаю, што гэта самы просты і найбольш чытаным з усіх.
дададзена аўтар dansalmo, крыніца

Вы можаце зрабіць:

x = [bool(i) fабоi in x]
return x.count(True) == 1

або

x = map(bool, x)
return x.count(True) == 1

Грунтуючыся на @ метадзе JoranBeasley у:

sum(map(bool, x)) == 1
4
дададзена

Гэта падобна на працу і павінны быць у стане справіцца з любой ітэрацыю, а не толькі спіс с. Гэта кароткае схемы кожны раз, калі гэта магчыма, каб максымізаваць эфектыўнасць. Працуе як у Python 2 і 3.

def only1(iterable):
    for i, x in enumerate(iterable):  # check each item in iterable
        if x: break                   # truthy value found
    else:
        return False                  # no truthy value found
    for x in iterable[i+1:]:          # one was found, see if there are any more
        if x: return False            #   found another...
    return True                       # only a single truthy value found

testcases = [  # [[iterable, expected result], ... ]
    [[                          ], False],
    [[False, False, False, False], False],
    [[True,  False, False, False], True],
    [[False, True,  False, False], True],
    [[False, False, False, True],  True],
    [[True,  False, True,  False], False],
    [[True,  True,  True,  True],  False],
]

for i, testcase in enumerate(testcases):
    correct = only1(testcase[0]) == testcase[1]
    print('only1(testcase[{}]): {}{}'.format(i, only1(testcase[0]),
                                             '' if correct else
                                             ', error given '+str(testcase[0])))

выхад:

only1(testcase[0]): False
only1(testcase[1]): False
only1(testcase[2]): True
only1(testcase[3]): True
only1(testcase[4]): True
only1(testcase[5]): False
only1(testcase[6]): False
4
дададзена
+1 так, гэта яшчэ больш яўная
дададзена аўтар wim, крыніца
Мне падабаецца гэты падыход, як наконт пераробка логікі вакол ITER (х для х у my_list, калі х) , а затым з дапамогай кнопак наступны , можа быць, лепш, чым з дапамогай кнопак Карта і list.index
дададзена аўтар wim, крыніца
@wim: Нягледзячы на ​​тое, што я не выкарыстаў падыход, прапанаваны вам, ваш каментар натхніў мяне перагледзець мой першапачатковы адказ і зрабіць яго яшчэ больш інкрыментны характар ​​і пазбаўляецца ад Карта і спіс. індэкс .
дададзена аўтар martineau, крыніца
if sum([bool(x) for x in list]) == 1

(Калі выказаць здагадку, што ўсе вашыя каштоўнасці booleanish.)

Гэта, верагодна, будзе хутчэй, проста сумуючы яго

sum(list) == 1   

хоць гэта можа выклікаць некаторыя праблемы, у залежнасці ад тыпаў дадзеных у спісе.

3
дададзена
@StevenRumbalski Ok: P
дададзена аўтар Doorknob, крыніца
Некаторыя капіталізацыі і пунктуацыя былі б добра тут.
дададзена аўтар Steven Rumbalski, крыніца

@ JonClements` рашэнне прадоўжана у большасці N Праўдзівых значэнняў :

# Extend any() to n true values
def _NTrue(i, n=1):
    for x in xrange(n):
        if any(i): # False for empty
            continue
        else:
            return False
    return True

def NTrue(iterable, n=1):
    i = iter(iterable)
    return any(i) and not _NTrue(i, n)

edit: better version

def test(iterable, n=1): 
    i = iter(iterable) 
    return sum(any(i) for x in xrange(n+1)) <= n 

edit2: include at least m True's and at most n True's

def test(iterable, n=1, m=1): 
    i = iter(iterable) 
    return  m <= sum(any(i) for x in xrange(n+1)) <= n
3
дададзена
Ці ёсць тут не усе (любой (я) для г у xrange (п)), а не які-небудзь (я) праца?
дададзена аўтар Eric, крыніца
Там няма <�я> па меншай меры, адзін True </я> там.
дададзена аўтар moooeeeep, крыніца
Вы хутчэй значыць любой (I) і не ўсе (любыя (I) для х у xrange (п)) ?
дададзена аўтар moooeeeep, крыніца
@moooeeeep я бачу. Я адрэдагаваны, каб адлюстраваць, што (у асноўным праверыць, што сума знаходзіцца ў межах дыяпазону [at_least, at_most])
дададзена аўтар Nisan.H, крыніца
@moooeeeep Ня True і не ўсе (<�п Булевы>) лагічна такі ж, як COUNT (True) <= N ? Ідэя заключаецца ў тым, каб праверыць яшчэ найменшы магчымы набор і перапынак на першым няспраўным стане.
дададзена аўтар Nisan.H, крыніца
@Eric што б вярнуць толькі Справядліва для дакладна п праўдзівых х. Яна падказала мне ідэю сумы па любы з, хоць.
дададзена аўтар Nisan.H, крыніца
Не, я маю на ўвазе больш за ўсё. Яна вяртае ісціну, калі ў большасці існуе N праўдзівага шматзначнае значэнне: напрыклад, 3 у Ісціны спісе 1000 атрымае iterable.count (True) = 3 , Ntrue (ітэрацыю, 1) = False , Ntrue (ітэрацыю, 2) = False , Ntrue (ітэрацыю, 3) = True , Ntrue (ітэрацыю, 4) = True ... Гэта ў асноўным пашырае і не любы (я) частку ў , а не які-небудзь (я), а не які-небудзь (я) і не ...
дададзена аўтар Nisan.H, крыніца

Калі ёсць толькі адзін True , то даўжыня True павінны быць адзін:

def only_1(l): return 1 == len(filter(None, l))
3
дададзена
Можа быць, вы маглі б растлумачыць свой адказ?
дададзена аўтар Linus Caldwell, крыніца
def only1(l)
    sum(map(lambda x: 1 if x else 0, l)) == 1

Explanation: The map function maps a list to another list, doing True => 1 and False => 0. We now have a list of 0s and 1s instead of True or False. Now we simply sum this list and if it is 1, there was only one True value.

2
дададзена

Для паўнаты сакэ і прадэманстраваць перадавыя выкарыстанне патоку кіравання Пайтону для для ітэрацыі цыклу, можна пазбегнуць дадатковага ўліку ў прынятым адказе, што робіць гэта крыху хутчэй.:

def one_bool_true(iterable):
    it = iter(iterable)
    for i in it:
        if i:
            break
    else:            #no break, didn't find a true element
        return False
    for i in it:     # continue consuming iterator where left off
        if i: 
            return False
    return True      # didn't find a second true.

Прыведзеныя вышэй у просты паток кіравання дазваляе выкарыстоўваць складаныя функцыі ў Python завес: функцыя яшчэ . Семантыка, што калі вы скончыце ітэрацыя итератора, што вы спажываеце без залому -ную з яго, вы затым увядзіце яшчэ блок.

Вось прыняты адказ, які выкарыстоўвае трохі больш ўліку.

def only1(l):
    true_found = False
    for v in l:
        if v:
            # a True was found!
            if true_found:
                # found too many True's
                return False 
            else:
                # found the first True
                true_found = True
    # found zero or one True value
    return true_found

часу гэтыя:

import timeit
>>> min(timeit.repeat(lambda: one_bool_true([0]*100 + [1, 1])))
13.992251592921093
>>> min(timeit.repeat(lambda: one_bool_true([1, 1] + [0]*100)))
2.208037032979064
>>> min(timeit.repeat(lambda: only1([0]*100 + [1, 1])))
14.213872335107908
>>> min(timeit.repeat(lambda: only1([1, 1] + [0]*100)))
2.2482982632641324
>>> 2.2482/2.2080
1.0182065217391305
>>> 14.2138/13.9922
1.0158373951201385

Такім чынам, мы бачым, што прыняты адказ зойме трохі больш часу (ледзь-ледзь больш, чым адзін-паўтара працэнта).

Натуральна, з дапамогай убудаванага ў любы , напісаны на C, нашмат хутчэй (гл адказ Джона Клемента для рэалізацыі - гэта кароткая форма):

>>> min(timeit.repeat(lambda: single_true([0]*100 + [1, 1])))
2.7257133318785236
>>> min(timeit.repeat(lambda: single_true([1, 1] + [0]*100)))
2.012824866380015
1
дададзена

Гэта тое, што вы шукаеце?

sum(l) == 1
0
дададзена
Гэта трывае няўдачу ў спісе: [2], так як аўтар не ўдакладніў, што элементы павінны быць толькі ісціна і хлусня, або 1 і 0
дададзена аўтар vtlinh, крыніца
import collections

def only_n(l, testval=True, n=1):
    counts = collections.Counter(l)
    return counts[testval] == n

Лінейнае час. Выкарыстоўвае убудаваны клас лічыльніка, які з'яўляецца тое, што вы павінны выкарыстоўваць для праверкі лічыльнікаў.

Перачытваючы свой пытанне, ён выглядае, як вы на самой справе хочаце, каб праверыць, што ёсць толькі адзін truthy значэнне, а не адзін True значэнне. Паспрабуйце гэта:

import collections

def only_n(l, testval=True, coerce=bool, n=1):
    counts = collections.Counter((coerce(x) for x in l))
    return counts[testval] == n

У той час як вы можаце атрымаць больш лепшую прадукцыйнасць справы, нічога не мае лепшую прадукцыйнасць у горшым выпадку. Гэта таксама кароткае і лёгка чытаць.

Вось версія аптымізаваная для лепшага выпадку прадукцыйнасці:

import collections
import itertools

def only_n(l, testval=True, coerce=bool, n=1):
    counts = collections.Counter()
    def iterate_and_count():
        for x in itertools.imap(coerce,l):
            yield x
            if x == testval and counts[testval] > n:
               break
    counts.update(iterate_and_count())
    return counts[testval] == n

Горшы паказчык выпадак мае высокі да (як у O (кп + C) ), але гэта зусім цэлым.

Here's an ideone to experiment with performance: http://ideone.com/ZRrv2m

0
дададзена

Як наконт:

len([v for v in l if type(v) == bool and v])

Калі вы жадаеце лічыць Булевы Праўдзівыя значэння.

0
дададзена

Вось тое, што павінна працаваць на што-небудзь truthy, хоць ён не мае кароткага замыкання. Я знайшоў яго, шукаючы чысты спосаб забараніць ўзаемавыключальныя аргументы:

if sum(1 for item in somelist if item) != 1:
    raise ValueError("or whatever...")
0
дададзена