첫 번째 항목을 반환하는 Python 관용구 또는 없음


273

나에게 일어나지 않는 간단한 방법이 있다고 확신합니다.

목록을 반환하는 많은 메소드를 호출하고 있습니다. 목록이 비어있을 수 있습니다. 목록이 비어 있지 않으면 첫 번째 항목을 반환하고 싶습니다. 그렇지 않으면 None을 반환하고 싶습니다. 이 코드는 작동합니다 :

my_list = get_list()
if len(my_list) > 0: return my_list[0]
return None

나는 이것을하기위한 간단한 한 줄의 관용구가 있어야하는 것처럼 보이지만, 내 삶을 위해 나는 그것을 생각할 수 없다. 있습니까?

편집하다:

여기서 한 줄짜리 식을 찾고있는 이유는 엄청나게 간결한 코드를 좋아하지 않기 때문에 다음과 같은 많은 코드를 작성해야하기 때문입니다.

x = get_first_list()
if x:
    # do something with x[0]
    # inevitably forget the [0] part, and have a bug to fix
y = get_second_list()
if y:
    # do something with y[0]
    # inevitably forget the [0] part AGAIN, and have another bug to fix

내가하고 싶은 일은 분명히 기능을 통해 달성 될 수 있습니다 (아마도 가능할 것입니다).

def first_item(list_or_none):
    if list_or_none: return list_or_none[0]

x = first_item(get_first_list())
if x:
    # do something with x
y = first_item(get_second_list())
if y:
    # do something with y

파이썬에서 간단한 표현식이 할 수있는 일에 자주 놀랐 기 때문에 질문을 게시했으며 간단한 표현식이 있으면 트릭을 수행 할 수있는 경우 함수를 작성하는 것이 어리석은 일이라고 생각했습니다. 그러나 이러한 답변을 보면 함수 간단한 솔루션 인 것 같습니다 .


28
BTW, 파이썬 2.6+에 당신이 사용할 수있는 next(iter(your_list), None)대신 first_item(your_list)가정 your_list하지 않습니다 None( get_first_list()그리고 get_second_list()항상 반복자를 반환해야합니다).
jfs

1
난 당신 말은 생각 next(iter(your_list))당신이에 두 번째 인수를 제공하는 경우 때문에 iter, 당신은 첫 번째 인수는 호출임을를 말하는 것입니다.
Robert Rossney

7
아니, None여기에 두 번째 매개 변수입니다 next()하지 iter(). if 가 비어있는 next()대신에 주어진다면 두 번째 매개 변수를 반환합니다 . StopIterationyour_list
jfs

@JFSebastian이 제안한 솔루션은 중복 질문이 있습니다. stackoverflow.com/a/18533669/144408
shahjapan 2016 년

답변:


205

파이썬 2.6 이상

next(iter(your_list), None)

만약 your_list할 수 있습니다 None:

next(iter(your_list or []), None)

파이썬 2.4

def get_first(iterable, default=None):
    if iterable:
        for item in iterable:
            return item
    return default

예:

x = get_first(get_first_list())
if x:
    ...
y = get_first(get_second_list())
if y:
    ...

다른 옵션은 위 함수를 인라인하는 것입니다.

for x in get_first_list() or []:
    # process x
    break # process at most one item
for y in get_second_list() or []:
    # process y
    break

피할 break수 있습니다 :

for x in yield_first(get_first_list()):
    x # process x
for y in yield_first(get_second_list()):
    y # process y

어디:

def yield_first(iterable):
    for item in iterable or []:
        yield item
        return

1
마지막 옵션은 거의 내가 찾는 것입니다. 명확하고 작동하며 새 함수를 정의 할 필요가 없습니다. 휴식 시간이 필요하지 않은 경우 "정확히"라고 말하고 싶습니다. 생략 할 위험이 크지 않기 때문입니다. 그러나이 접근법에는 진실의 고리가 있습니다.
Robert Rossney

아, 나는 이것을 전혀 좋아하지 않는다. 목록의 항목 중 하나라도 False로 평가되면 해당 값은 삭제되고 교체됩니다. ""목록에 빈 문자열이 있으면 해당 문자열 이 삭제되고 빈 목록으로 바뀝니다 []. 0이 있으면로 교체하십시오 []. 당신이있는 경우 False거기에 또한 대체했다. 등 특정 상황에서이 문제를 해결할 수도 있지만 이는 개발하기에 나쁜 습관입니다.
steveha

4
@steveha : 항목에 포함 된 항목에 대해 아무 것도 알려주지 bool(lst) 않는지 여부를 알려줍니다 . 예를 들어 표현 은 return 이며, return 과 동일 합니다 . len(lst) > 0lstbool([False]) == True;[False] or [][False][0] or [] [0]
jfs

@RobertRossney : 진술 yield_first()을 피하기 위해 추가 했습니다 break.
jfs

1
@ThomasAhle : 둘 다 맞습니다. 비 사례에 대한 솔루션을 제공하기 위해 답변을 업데이트했습니다.
jfs

218

가장 좋은 방법은 다음과 같습니다.

a = get_list()
return a[0] if a else None

한 줄로도 할 수 있지만 프로그래머가 읽기가 훨씬 어렵습니다.

return (get_list()[:1] or [None])[0]

죄송합니다. 이것은 Python 2.4 앱이라고 언급해야합니다.
Robert Rossney

15
@Adam : 미래는 사용할 수 있습니다next(iter(your_list), None)
jfs

@JFSebastian은 실제로 next(iter(your_list))충분하다
Brad Johnson

13
@BradleagheJohnson : 잘못되었습니다. NoneOP가 요청한대로 리턴 하는 대신 StopIteration을 발생 시킵니다. 시도하십시오 : next(iter([])).
jfs

72
(get_list() or [None])[0]

작동합니다.

BTW list내장 변수 를 덮어 쓰기 때문에 변수를 사용하지 않았습니다 list().

편집 : 나는 조금 더 간단하지만 여기서 잘못된 버전을 가지고있었습니다.


4
이것은 영리하고 효과적이지만, efotinis가 제안한 "return foo [0] if foo else None"은 유지 보수를 위해 따르는 것이 조금 더 쉽다고 생각합니다.
Jay

3
문제는 한 줄 해결책을 요구했기 때문에 나는 그것을 배제했습니다.
재귀

get_list ()가 None을 반환하면 이러한 표현식은 작동하지 않습니다. "TypeError : 'NoneType'개체를 설명 할 수 없습니다." 또는 "TypeError : 지원되지 않는 피연산자 유형 : + : 'NoneType'및 'list'."
Robert Rossney

4
문제에서 메소드는 목록을 반환하지만 None은 하나가 아닙니다. 따라서 요구 사항을 감안할 때 여전히 둘 다 작동한다고 생각합니다.
재귀

get_list ()가 더 이상 첨자 화되거나 추가되지 않으므로 None을 반환하면 지금 작동합니다.
Robert

32

가장 파이썬적인 관용적 방법은 list가 iterable 이기 때문에 iterator에서 next ()를 사용하는 것 입니다. @JFSebastian이 2011 년 12 월 13 일 코멘트에 언급 한 것과 같습니다.

next(iter(the_list), None)the_list비어 있으면 None을 반환합니다 . 참조 다음 () 파이썬 2.6+

또는 the_list비어 있지 않다는 것을 알고 있다면 :

iter(the_list).next()iterator.next () 참조 파이썬 2.2 이상


1
이것은 return l[0] if l else None리스트가 libcomp 인 경우 와 같이 변수에리스트를 할당 할 필요가 없다는 장점이 있습니다 . 목록이 실제로 genexpr 인 경우 다음과 같이 전체 목록을 구성 할 필요가 없으므로 훨씬 좋습니다.next(iter(x for x in range(10) if x % 2 == 0), None)
RubenLaguna

the_list비어 있지 않다는 것을 알고 있다면을 쓸 것 the_list[0]입니다.
kaya3

12

목록 이해에서 첫 번째 항목 (또는 없음)을 뽑으려고하는 경우 발전기로 전환하여 다음과 같이 할 수 있습니다.

next((x for x in blah if cond), None)

장점 : blah를 색인 할 수없는 경우 작동합니다. Con : 익숙하지 않은 구문입니다. ipython에서 해킹하고 필터링하는 동안 유용합니다.


11

OP의 솔루션은 거의 거기에 있으며 Pythonic을 더 만들려면 몇 가지가 있습니다.

하나, 목록의 길이를 얻을 필요가 없습니다. 파이썬에서 빈 목록은 if 검사에서 False로 평가됩니다. 간단히 말해

if list:

또한 예약어와 겹치는 변수에 할당하는 것은 매우 나쁜 생각입니다. "list"는 파이썬에서 예약 된 단어입니다.

그래서 그것을

some_list = get_list()
if some_list:

여기서 많은 솔루션이 놓친 정말 중요한 점은 모든 Python 함수 / 메소드가 기본적으로 None을 반환 한다는 입니다. 아래에서 다음을 시도하십시오.

def does_nothing():
    pass

foo = does_nothing()
print foo

함수를 조기에 종료하기 위해 None을 반환하지 않는 한 명시 적으로 None을 반환 할 필요는 없습니다. 간결하게, 첫 번째 항목을 반환하십시오.

some_list = get_list()
if some_list:
    return list[0]

마지막으로, 이것은 아마도 암시 적이지만 명시 적이어야합니다 (명시 적이 암시 적보다 낫기 때문에 ) 함수가 다른 함수에서 목록을 얻지 않아야합니다. 그냥 매개 변수로 전달하십시오. 최종 결과는

def get_first_item(some_list): 
    if some_list:
        return list[0]

my_list = get_list()
first_item = get_first_item(my_list)

내가 말했듯이 OP는 거의 거기에 있었고 몇 번의 터치만으로 원하는 Python 풍미를 얻을 수 있습니다.


1
왜 파이썬에서 '목록'이 예약어입니까? 또는 요컨대, 'list'는 파이썬에서 reserver 단어라고 어디에서 말합니까? 또는 요컨대, 'list'가 파이썬에서 예약어인지 어떻게 확인 했습니까? 'list'라는 변수를 선언 할 수 있다는 점을 고려하면 분명히 예약어 가 아닙니다.
라세 V. 칼슨

2
요점을 알았어. Python에서 실제로 예약 된 단어는 tinyurl.com/424663 에서 찾을 수 있지만 내장 함수 및 유형을 예약 된 것으로 간주하는 것이 좋습니다. list ()는 실제로 목록을 생성합니다. dict, range 등도
마찬가지입니다

마지막 두 줄을 결합하여 임시 변수를 제거합니다 (추가 my_list가 필요하지 않은 경우). 가독성을 향상시킵니다.
Svante

이것은 내가 끌어 당기는 대답과 거의 같습니다.
Robert Rossney

1
get_first_item기본적으로 없음으로 설정된 default매개 변수 (예 :)를 승인해야합니다 dict.get. 따라서 함수 인터페이스 my_list는 비어있는 경우 발생하는 사항을 명시 적으로 통신 합니다.
jfs

3
for item in get_list():
    return item

간결하고 아름답습니다.
gotgenes

비극적으로, 그것은 작동하지 않습니다; get_list ()가 None을 반환하면 "TypeError : 'NoneType'객체를 반복 할 수 없습니다"예외가 발생합니다.
Robert Rossney

"get_list의 항목에 대한 () 또는 [] :"해결하려면
itsadok

5
이 질문은 get_list가 목록을 반환한다고 가정합니다. len과 getitem 이 결과로 호출됩니다.
A. Coady 2018 년

2

솔직히 말해서, 나는 더 좋은 관용구가 있다고 생각하지 않습니다. 당신은 명확하고 간결합니다- "더 나은"것이 필요하지 않습니다. 어쩌면, 그러나 이것은 변경할 수있는 정말 취향의 문제입니다 if len(list) > 0:으로 if list:False로 항상을 평가합니다 빈리스트 -.

관련 참고에 따르면, 파이썬은 Perl 이 아닙니다 (말장난 이 아닙니다 !), 가장 멋진 코드를 얻을 필요는 없습니다.
실제로, 파이썬에서 본 최악의 코드는 매우 시원했고 완전히 유지할 수 없었습니다.

그건 그렇고, 여기에서 본 대부분의 솔루션은 list [0]이 False로 평가 될 때 고려하지 않습니다 (예 : 빈 문자열 또는 0).이 경우 모두 올바른 요소가 아닌 None을 반환합니다.


파이썬에서는 쓰기 if lst:보다는 좋은 스타일로 간주됩니다 if len(lst) > 0:. 또한 파이썬 키워드를 변수 이름으로 사용하지 마십시오. 눈물로 끝납니다. L또는 lst일부 목록의 변수 이름으로 사용하는 것이 일반적 입니다. 이 경우 실제로 list변수 이름으로 사용할 것을 제안하는 것이 아니라 "일부 목록"을 의미한다고 확신 합니다. 그리고 "lst [0]이 False로 평가 될 때"에 대해 +1입니다.
steveha

예, 더 나은 명확성을 위해 동일한 종류의 OP 이름을 유지하고있었습니다. 그러나 당신은 확실히 맞습니다 : 파이썬 키워드를 재정의하지 않도록 항상주의를 기울여야합니다.
rob

2

첫 번째 항목을 반환하는 파이썬 관용구 또는 없음?

가장 파이썬적인 접근 방식은 가장 많이 찬성 된 답변으로, 질문을 읽을 때 가장 먼저 떠 올랐습니다. 비어있는 목록이 함수에 전달 된 경우이를 사용하는 방법은 다음과 같습니다.

def get_first(l): 
    return l[0] if l else None

그리고 get_list함수 에서 목록이 반환되면 :

l = get_list()
return l[0] if l else None

다른 방법으로 여기에 설명했습니다.

for

내가 이것을 할 수있는 영리한 방법을 생각하기 시작했을 때, 이것이 내가 생각한 두 번째 것입니다.

for item in get_list():
    return item

이것은 함수가 여기에서 끝나고 빈 목록을 반환 None하면 암시 적으로 반환 한다고 가정합니다 get_list. 아래의 명시 적 코드는 정확히 동일합니다.

for item in get_list():
    return item
return None

if some_list

다음은 암시 적 사용하는 제안 된 (잘못된 변수 이름을 수정했습니다) None. 발생하지 않을 수도있는 반복 대신 논리적 확인을 사용하므로 위의 방법보다 선호됩니다. 이것은 무슨 일이 일어나고 있는지 즉시 이해하기 쉬워야합니다. 그러나 가독성과 유지 관리 성을 위해 글을 쓰고 있다면 return None끝에 명시 를 추가해야합니다 .

some_list = get_list()
if some_list:
    return some_list[0]

or [None]제로 인덱스 슬라이스 및 선택

이것은 또한 가장 투표가 많은 답변입니다.

return (get_list()[:1] or [None])[0]

슬라이스는 불필요하며 메모리에 추가 항목 목록을 만듭니다. 다음은 성능이 더 우수해야합니다. 설명하기 위해, or첫 번째 요소 False가 부울 컨텍스트에있는 경우 두 번째 요소를 get_list리턴하므로 빈 목록을 리턴하면 괄호에 포함 된 표현식은 'None'이 포함 된 목록을 리턴하며 0인덱스에 의해 액세스됩니다 .

return (get_list() or [None])[0]

다음은 사실을 사용하고 첫 번째가 True부울 컨텍스트에 있고 두 번째 항목이 my_list를 두 번 참조하므로 두 번째 항목을 반환 하므로 삼항 표현식보다 우수하지 않습니다 (기술적으로는 한 줄짜리가 아닙니다).

my_list = get_list() 
return (my_list and my_list[0]) or None

next

그런 다음 우리는 내장의 다음 영리한 사용을 next하고iter

return next(iter(get_list()), None)

설명하기 위해 메소드 iter가있는 반복자를 리턴합니다 .next. ( .__next__Python 3에서) 그런 다음 내장 함수 next는 해당 .next메소드를 호출 하고 반복자가 소진되면 기본 값을 반환합니다 None.

중복 삼항 식 ( a if b else c) 및 순환

아래가 제안되었지만 논리가 일반적으로 부정 대신 긍정적으로 더 잘 이해되므로 반대가 바람직합니다. get_list결과가 어떤 방식으로 메모되지 않으면가 두 번 호출 되므로 성능이 저하됩니다.

return None if not get_list() else get_list()[0]

더 나은 역 :

return get_list()[0] if get_list() else None

더 좋은 방법 get_list은 한 번만 호출 되는 로컬 변수를 사용하는 것 입니다. 먼저 권장되는 Pythonic 솔루션이 있습니다.

l = get_list()
return l[0] if l else None

2

숙어에 대해서는 이라는 itertools 레시피nth있습니다.

itertools 레시피에서 :

def nth(iterable, n, default=None):
    "Returns the nth item or a default value"
    return next(islice(iterable, n, None), default)

원 라이너를 원한다면 다음과 같은 레시피를 구현하는 라이브러리를 설치하십시오 more_itertools.

import more_itertools as mit

mit.nth([3, 2, 1], 0)
# 3

mit.nth([], 0)                                             # default is `None`
# None

라는 첫 번째 항목 만 반환하는 다른 도구를 사용할 수 있습니다 more_itertools.first.

mit.first([3, 2, 1])
# 3

mit.first([], default=None)
# None

이 itertools는 목록뿐만 아니라 모든 iterable에 대해 일반적으로 확장됩니다.



1

호기심으로 두 가지 솔루션에 대한 타이밍을 실행했습니다. return 문을 사용하여 for 루프를 조기에 종료하는 솔루션은 Python 2.5.1을 사용하는 컴퓨터에서 약간 더 비쌉니다. 반복 가능 구성과 관련이 있다고 생각합니다.

import random
import timeit

def index_first_item(some_list):
    if some_list:
        return some_list[0]


def return_first_item(some_list):
    for item in some_list:
        return item


empty_lists = []
for i in range(10000):
    empty_lists.append([])

assert empty_lists[0] is not empty_lists[1]

full_lists = []
for i in range(10000):
    full_lists.append(list([random.random() for i in range(10)]))

mixed_lists = empty_lists[:50000] + full_lists[:50000]
random.shuffle(mixed_lists)

if __name__ == '__main__':
    ENV = 'import firstitem'
    test_data = ('empty_lists', 'full_lists', 'mixed_lists')
    funcs = ('index_first_item', 'return_first_item')
    for data in test_data:
        print "%s:" % data
        for func in funcs:
            t = timeit.Timer('firstitem.%s(firstitem.%s)' % (
                func, data), ENV)
            times = t.repeat()
            avg_time = sum(times) / len(times)
            print "  %s:" % func
            for time in times:
                print "    %f seconds" % time
            print "    %f seconds avg." % avg_time

이것이 내가 얻은 타이밍입니다.

empty_lists :
  index_first_item :
    0.748353 초
    0.741086 초
    0.741191 초
    평균 0.743543 초
  return_first_item :
    0.785511 초
    0.822178 초
    0.782846 초
    평균 0.796845 초
전체 목록 :
  index_first_item :
    0.762618 초
    0.788040 초
    0.786849 초
    평균 0.779169 초
  return_first_item :
    0.802735 초
    0.878706 초
    0.808781 초
    평균 0.830074 초
mixed_lists :
  index_first_item :
    0.791129 초
    0.743526 초
    0.744441 초
    평균 0.759699 초
  return_first_item :
    0.784801 초
    0.785146 초
    0.840193 초
    평균 0.803380 초

0
try:
    return a[0]
except IndexError:
    return None

1
일반적으로 예외는 흐름 제어에 사용되지 않습니다.
Matt Green

나는이 코드를 더 길게 만들고 예외 처리를 추가하는 것이 내가 찾고있는 관용구라고 생각하지 않습니다.
Robert Rossney

3
그러나 이것은 일반적인 파이썬 관용구입니다! 성능상의 이유로 사용하지 않을 수 있습니다. 당신은 더 나은 성능을 얻을 수 if len(a) > 0:있지만 이것은 좋은 스타일로 간주됩니다. 이것이 문제를 얼마나 잘 표현 하는지를 주목하라 : "목록의 헤드를 추출하려고 시도하고, 그래도 해결되지 않으면 None"를 리턴 하라. 구글 "EAFP"를 검색하거나 이쪽을 봐 : python.net/~goodger/projects/pycon/2007/idiomatic/...
steveha

@steveha 데이터에 따라 다릅니다. len (a)가 일반적으로> 0이고 len (a) <1이 드문 상태 인 경우 예외를 포착하면 실제로 성능이 향상됩니다.
limscoder 2016 년

@limscoder, 나는 당신에 동의합니다. 성능상의 이유로 사용하지 않을 의 세부 사항은 다루지 않았습니다. 나는 당신이 이것을 사용하지 않을 것이라고 말하지 않았습니다.
steveha

0
def head(iterable):
    try:
        return iter(iterable).next()
    except StopIteration:
        return None

print head(xrange(42, 1000)  # 42
print head([])               # None

BTW : 일반적인 프로그램 흐름을 다음과 같이 다시 작성하겠습니다.

lists = [
    ["first", "list"],
    ["second", "list"],
    ["third", "list"]
]

def do_something(element):
    if not element:
        return
    else:
        # do something
        pass

for li in lists:
    do_something(head(li))

(가능할 때마다 반복 피하기)


0

이건 어때요:

(my_list and my_list[0]) or None

참고 : 이것은 객체 목록에는 잘 작동하지만 아래 주석에 따라 숫자 또는 문자열 목록의 경우 잘못된 답변을 반환 할 수 있습니다.


어때요 ([0,1] and 0) or None!.
Kenly

즉 :( 안전하지 않은 현재의 형태로 번호 컬렉션과 함께 사용할 ... 좋은 포인트입니다
VitalyB

와 같은 것 (['','A'] and '') or None.
Kenly

경우 my_list[0]와 같은 평가 False결과는해야한다 None.
Kenly

2
my_list[0] if len(my_list) else None
니콜라스 해밀턴

0

이것이 어떻게 pythonic인지 확실하지 않지만 라이브러리에 첫 번째 기능이있을 때까지 소스에 이것을 포함시킵니다.

first = lambda l, default=None: next(iter(l or []), default)

그것은 단지 한 줄 (검은 색에 부합)이며 의존성을 피합니다.


-1

그리고 / 또는 속임수 사용하기 :

a = get_list()
return a and a[0] or None

-1

아마도 가장 빠른 해결책은 아니지만 아무도이 옵션을 언급하지 않았습니다.

dict(enumerate(get_list())).get(0)

get_list()돌아올 None수 있다면 다음을 사용할 수 있습니다.

dict(enumerate(get_list() or [])).get(0)

장점 :

-한 줄

get_list()한 번만 전화 해

-이해하기 쉬운


-1

내 유스 케이스는 로컬 변수의 값만 설정하는 것이 었습니다.

개인적으로 시도해보고 스타일 클리너를 읽는 것을 발견했습니다

items = [10, 20]
try: first_item = items[0]
except IndexError: first_item = None
print first_item

목록을 자르는 것보다

items = [10, 20]
first_item = (items[:1] or [None, ])[0]
print first_item

-1

여러 사람들이 다음과 같이 할 것을 제안했습니다.

list = get_list()
return list and list[0] or None

많은 경우에 작동하지만 list [0]이 0, False 또는 빈 문자열이 아닌 경우에만 작동합니다. list [0]이 0, False 또는 빈 문자열이면이 메서드는 None을 잘못 반환합니다.

내 코드에서이 버그를 너무 많이 만들었습니다!


3
이것은 독립형 답변이 아닌 결함있는 답변 아래에있는 주석이어야합니다.
IJ 케네디

-2

Extract Method를 사용할 수 있습니다 . 즉, 해당 코드를 호출 한 메소드로 추출하십시오.

나는 그것을 더 많이 압축하려고하지 않을 것입니다. 하나의 라이너는 자세한 버전보다 읽기가 어렵습니다. 추출 방법을 사용하면 하나의 라이너입니다.)



-3

C 스타일의 삼항 연산자에 해당하는 관용적 인 파이썬이 아닙니다

cond and true_expr or false_expr

즉.

list = get_list()
return list and list[0] or None

a [0]이 숫자 0이거나 빈 문자열 (또는 false로 평가되는 것)이면 a [0]이 아닌 대신 None을 반환합니다.
Adam Rosenfield
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.