왜리스트에 사전과 같은 안전한 "get"메소드가 없는가?


264

왜리스트에 사전과 같은 안전한 "get"메소드가 없는가?

>>> d = {'a':'b'}
>>> d['a']
'b'
>>> d['c']
KeyError: 'c'
>>> d.get('c', 'fail')
'fail'

>>> l = [1]
>>> l[10]
IndexError: list index out of range

1
리스트는 사전과 다른 목적으로 사용됩니다. get ()은 일반적인 사용 사례에 필요하지 않습니다. 그러나 사전의 경우 get ()이 매우 유용합니다.
mgronber

42
예를 들어 l[10:11]대신 슬라이스를 요청하면 IndexError를 발생시키지 않고 목록에서 빈 하위 목록을 항상 가져올 수 있습니다 l[10]. ()이 하위 목록에는 원하는 요소가있을 것입니다.
jsbueno

56
여기 일부와는 반대로, 나는 안전한 아이디어를지지한다 .get. 그것은 l[i] if i < len(l) else default더 읽기 쉽고 간결하며, i다시 계산하지 않고도 표현할 수있게 해줍니다.
Paul Draper

6
오늘 나는 이것이 존재하기를 바랐다. 목록을 반환하는 비싼 함수를 사용하지만 첫 번째 항목 만 원했거나 None존재하지 않는 경우 에만 사용했습니다 . 말을하는 것이 좋았 기 x = expensive().get(0, None)때문에 쓸모없는 값 비싼 값을 임시 변수에 넣을 필요가 없었습니다.
Ryan Hiebert

2
@ 라이언 내 대답은 당신을 도울 수 있습니다 stackoverflow.com/a/23003811/246265
Jake

답변:


112

궁극적으로 .geta dict는 예외가 발생하지 않고 키가 있는지 확인하고 값을 반환하는 것이 비효율적 인 연관 컬렉션 (값과 이름이 연관되어 있음) 이기 때문에 아마도 안전한 방법 이 없을 것입니다. len메소드가 매우 빠르기 때문에 목록 요소에 액세스하는 예외를 피하기 위해 . 이 .get방법을 사용하면 사전에서 37 번째 항목에 직접 액세스하지 않고 이름과 관련된 값을 쿼리 할 수 ​​있습니다 (목록에서 요구하는 것과 더 유사 함).

물론 이것을 직접 구현할 수 있습니다.

def safe_list_get (l, idx, default):
  try:
    return l[idx]
  except IndexError:
    return default

__builtins__.list생성자에 monkeypatch 할 수도 __main__있지만 대부분의 코드에서 사용하지 않기 때문에 덜 널리 퍼질 것입니다. 이 코드를 자신의 코드로 만든 목록과 함께 사용하려면 하위 클래스를 작성 list하고 get메소드를 추가하면 됩니다.


24
파이썬은 다음과 같은 원숭이 패치 내장 타입을 허용하지 않습니다list
Imran

7
@CSZ : .get존재하지 않는 데이터를 가져올 때 예외를 피하는 효율적인 방법-목록에없는 문제를 해결합니다. 유효한 목록 색인이 무엇인지 아는 것은 매우 사소하고 효율적이지만 사전의 키 값에 대해 이것을 수행하는 좋은 방법은 없습니다.
Nick Bastin

10
나는 이것이 사전에 키가 있는지 또는 항목을 반환하는지 검사하는 것이 전혀 효율성에 있다고 생각하지 않습니다 O(1). 검사만큼 기본적으로 빠르지는 len않지만 복잡한 관점에서 보면 모두 빠릅니다 O(1). 정답은 일반적인 사용법 / 의미론입니다.
Mark Longair

3
@Mark : 모든 O (1)가 동일하게 생성되는 것은 아닙니다. 또한 dict모든 경우가 아니라 가장 좋은 경우 O (1)입니다.
Nick Bastin

4
사람들이 여기서 요점을 놓치고 있다고 생각합니다. 논의는 효율성에 관한 것이 아닙니다. 조기 최적화로 중단하십시오. 프로그램이 너무 느리면 학대를 .get()받거나 코드 (또는 환경)의 다른 곳에 문제가있는 것입니다. 이러한 방법을 사용하는 요점은 코드 가독성입니다. "vanilla"기술은이 작업을 수행해야하는 모든 장소에서 4 줄의 코드가 필요합니다. 이 .get()기술은 하나만 필요하며 후속 메소드 호출 (예 :)과 쉽게 연결될 수 있습니다 my_list.get(2, '').uppercase().
Tyler Crompton

67

첫 번째 요소를 원하면 작동합니다. my_list.get(0)

>>> my_list = [1,2,3]
>>> next(iter(my_list), 'fail')
1
>>> my_list = []
>>> next(iter(my_list), 'fail')
'fail'

나는 그것이 당신이 요구 한 것이 아니라 다른 사람들을 도울 수 있다는 것을 알고 있습니다.


7
기능적인 프로그래밍에 비해 덜 파이썬 적
Eric

next(iter(my_list[index:index+1]), 'fail')0이 아닌 모든 인덱스를 허용합니다. FP가 적지 만 더 많은 Pythonic이며 거의 더 읽기 쉽습니다 my_list[index] if index < len(my_list) else 'fail'.
alphabetasoup

47

아마도 목록 의미론에는 그다지 의미가 없기 때문일 것입니다. 그러나 서브 클래 싱을 통해 쉽게 자신 만의 것을 만들 수 있습니다.

class safelist(list):
    def get(self, index, default=None):
        try:
            return self.__getitem__(index)
        except IndexError:
            return default

def _test():
    l = safelist(range(10))
    print l.get(20, "oops")

if __name__ == "__main__":
    _test()

5
이것은 OP에 가장 많은 해답입니다. 파이썬에서 안전한 작업 인 하위 목록을 추출 할 수도 있습니다. mylist = [1, 2, 3]이 주어지면 예외를 유발하지 않고 mylist [8 : 9]로 9 번째 요소를 추출 할 수 있습니다. 그런 다음 목록이 비어 있는지 테스트하고 비어 있지 않은 경우 반환 된 목록에서 단일 요소를 추출하십시오.
jose.angel.jimenez

1
이것은 비 파이썬 적 일 라이너 해킹이 아닌 받아 들일만한 대답이어야합니다. 특히 사전과의 대칭을 보존하기 때문입니다.
Eric

1
좋은 get방법 이 필요하기 때문에 자신의 목록을 하위 클래스로 분류하는 것에 대한 비판은 없습니다 . 가독성이 중요합니다. 그리고 추가적인 불필요한 클래스마다 가독성이 떨어집니다. try / except서브 클래스를 작성하지 않고이 방법을 사용하십시오 .
Jeyekomon

@Jeyekomon 서브 클래 싱을 통해 상용구를 줄이는 것은 완벽하게 Pythonic입니다.
Keith

42

.get을 사용하는 대신 목록을 사용하는 것이 좋습니다. 사용법 차이입니다.

>>> l = [1]
>>> l[10] if 10 < len(l) else 'fail'
'fail'

15
-1로 최신 요소를 얻으려고하면 실패합니다.
pretobomba

원형으로 연결된 목록 개체에는 작동하지 않습니다. 또한 구문은 내가 "스캔 블록"이라고 부르는 것을 유발합니다. 코드를 통해 스캔하여 수행하는 작업을 볼 때 잠시 동안 속도가 느려집니다.
Tyler Crompton

인라인 / else가 2.6과 같은 오래된 파이썬에서 작동하지 않는 경우 (또는 2.5입니까?)
Eric

3
@ TylerCrompton : 파이썬에는 원형으로 연결된 목록이 없습니다. 직접 작성 한 경우 .get메소드를 자유롭게 구현할 수 있습니다 (이 경우 인덱스의 의미를 설명하는 방법 또는 실패하는 이유는 확실하지 않습니다).
Nick Bastin

범위를 벗어난 음수 지수를 처리하는 대안은 다음과 같습니다.lst[i] if -len(lst) <= i < len(l) else 'fail'
마이크

17

이 시도:

>>> i = 3
>>> a = [1, 2, 3, 4]
>>> next(iter(a[i:]), 'fail')
4
>>> next(iter(a[i + 1:]), 'fail')
'fail'

1
나는 이것을 좋아하지만, 먼저 새로운 하위 목록을 만들어야합니다.
Rick은

15

jose.angel.jimenez의 크레딧


"oneliner"팬들을 위해…


목록의 첫 번째 요소를 원하거나 목록이 비어있는 경우 기본값을 원하면 다음을 시도하십시오.

liste = ['a', 'b', 'c']
value = (liste[0:1] or ('default',))[0]
print(value)

보고 a

liste = []
value = (liste[0:1] or ('default',))[0]
print(value)

보고 default


다른 요소의 예…

liste = ['a', 'b', 'c']
print(liste[0:1])  # returns ['a']
print(liste[1:2])  # returns ['b']
print(liste[2:3])  # returns ['c']

기본 폴백으로…

liste = ['a', 'b', 'c']
print((liste[0:1] or ('default',))[0])  # returns a
print((liste[1:2] or ('default',))[0])  # returns b
print((liste[2:3] or ('default',))[0])  # returns c

테스트 Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23:13)


1
짧은 대안 : value, = liste[:1] or ('default',). 괄호가 필요한 것 같습니다.
qräbnö

13

가장 좋은 방법은 목록을 dict로 변환 한 다음 get 메소드로 액세스하는 것입니다.

>>> my_list = ['a', 'b', 'c', 'd', 'e']
>>> my_dict = dict(enumerate(my_list))
>>> print my_dict
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}
>>> my_dict.get(2)
'c'
>>> my_dict.get(10, 'N/A')

20
합리적인 해결 방법이지만 "할 수있는 최선의 방법"은 거의 없습니다.
tripleee

3
매우 비효율적입니다. 참고 : 그 대신에 다음을 zip range len사용할 수 있습니다.dict(enumerate(my_list))
Marian

3
이것은 최선의 것이 아니며, 당신이 할 수있는 최악의 일입니다.
erikbwork

3
성능을 고려하면 최악입니다 ... 성능에 관심이 있다면 파이썬과 같은 해석 언어로 코딩하지 않습니다. 나는이 솔루션을 다소 우아하고 강력하며 pythonic 사전을 사용하여 찾습니다. 어쨌든 초기 최적화는 악의적이므로 dict을 가지고 나중에 병목 현상을 보자.
Eric

7

그래서 나는 이것에 대해 더 많은 연구를했으며 이것에 대해 특별한 것이 없다는 것이 밝혀졌습니다. list.index (value)를 찾았을 때 흥분되어 지정된 항목의 색인을 반환하지만 특정 색인에서 값을 얻는 데 필요한 것은 없습니다. 따라서 safe_list_get 솔루션을 사용하고 싶지 않다면 꽤 좋습니다. 시나리오에 따라 작업을 수행 할 수있는 1 개의 라이너 if 문이 있습니다.

>>> x = [1, 2, 3]
>>> el = x[4] if len(x) > 4 else 'No'
>>> el
'No'

'아니오'대신 '없음'을 사용할 수도 있습니다.

>>> x = [1, 2, 3]
>>> i = 2
>>> el_i = x[i] if len(x) == i+1 else None

또한 목록에서 첫 번째 또는 마지막 항목을 가져 오려면 작동합니다.

end_el = x[-1] if x else None

이것들을 함수로 만들 수도 있지만 IndexError 예외 솔루션을 여전히 좋아했습니다. 나는 safe_list_get솔루션 의 불완전한 버전을 실험 하여 조금 더 간단하게 만들었습니다 (기본값 없음).

def list_get(l, i):
    try:
        return l[i]
    except IndexError:
        return None

가장 빠른 것을 확인하기 위해 벤치마킹하지 않았습니다.


1
pythonic이 아닙니다.
Eric

@ 에릭 어느 스 니펫? 나는 다시 시도함으로써 가장 의미있는 것을 제외하고는 시도를 생각합니다.
radtek

독립형 함수는 파이썬이 아닙니다. 예외는 실제로 좀 더 파이썬적인 것이지만 프로그래밍 언어에서 일반적인 패턴이기 때문에 그렇지 않습니다. 훨씬 더 pythonic은 내장 유형 list을 서브 클래스 화 하여 확장하는 새로운 객체입니다 . 그렇게하면 생성자가 list목록처럼 동작하는 것을 가져갈 수 있고 새 인스턴스는처럼 동작합니다 list. IMHO가 인정해야 할 Keith의 답변을 아래에서 참조하십시오.
Eric

1
@Eric 나는 질문을 OOP에 국한된 것이 아니라 "목록이 왜 dict.get()붙잡기보다는 목록 색인 참조에서 기본값을 반환하는 것과 유사하지 IndexError않은가? 그래서 실제로 언어 / 라이브러리 기능에 관한 것이다. FP 문맥과 비교) 또한, 아마도 python을 WWGD (FP Python에 대한 그의 염려가 잘 알려져 있음)로 사용하고 PEP8 / 20을 만족시킬 필요는 없습니다.
cowbert

1
el = x[4] if len(x) == 4 else 'No'– 당신은 의미 len(x) > 4합니까? x[4]if의 범위를 벗어났습니다 len(x) == 4.
마이크

4

사전은 조회 용입니다. 출품작의 존재 여부를 묻는 것이 합리적입니다. 리스트는 일반적으로 반복됩니다. L [10]이 있는지 묻는 것이 아니라 L의 길이가 11인지 묻는 것이 일반적입니다.


예, 동의합니다. 그러나 방금 페이지 "/ group / Page_name"의 상대 URL을 구문 분석했습니다. '/'로 나누고 PageName이 특정 페이지와 같은지 확인하고 싶었습니다. 길이를 추가로 확인하거나 예외를 포착하거나 자체 함수를 작성하는 대신 [url.split ( '/'). get_from_index (2, None) == "lalala"]와 같이 작성하는 것이 편안합니다. 아마도 당신은 맞습니다. 그것은 단순히 비정상적인 것으로 간주됩니다. 어쨌든 나는 여전히 이것에 동의하지 않는다 =)
CSZ

@Nick Bastin : 아무 잘못이 없습니다. 그것은 코딩의 단순성과 속도에 관한 것입니다.
CSZ 2019

키가 연속적인 정수인 경우 목록을보다 공간 효율적인 사전으로 사용하려는 경우에도 유용합니다. 물론 네거티브 인덱싱의 존재는 이미 그 일을 중단합니다.
안티몬

-1

유스 케이스는 기본적으로 고정 길이의 배열과 행렬을 수행 할 때만 관련이 있기 때문에 사전에 얼마나 오래 있는지 알 수 있습니다. 이 경우 일반적으로 None 또는 0으로 채우기 전에 실제로 작성하므로 실제로 사용할 인덱스가 이미 존재합니다.

당신은 이것을 말할 수 있습니다 : 사전에 .get ()이 꽤 자주 필요합니다. 풀 타임 프로그래머로 10 년이 지난 후에 저는 목록에 필요하다고 생각하지 않습니다. :)


의견에 대한 나의 예는 어떻습니까? 더 간단하고 읽기 쉬운 것은 무엇입니까? (url.split ( '/'). getFromIndex (2) == "lalala") OR (결과 = url.split (); len (result)> 2 및 결과 [2] == "lalala"). 그리고 네, 나는 그러한 함수를 스스로 쓸 수 있다는 것을 알고 있습니다 =) 그러나 그런 함수가 내장되어 있지 않다는 것에 놀랐습니다.
CSZ

1
당신의 경우에 당신이 잘못하고 있다고 말하십시오. URL 처리는 경로 (패턴 일치) 또는 객체 순회를 통해 수행되어야합니다. 그러나 특정 사례에 답변하려면 : 'lalala' in url.split('/')[2:]. 그러나 여기에서 솔루션의 문제는 두 번째 요소 만 보는 것입니다. URL이 '/ monkeybonkey / lalala'인 경우 어떻게합니까? TrueURL이 유효하지 않아도을 얻을 수 있습니다.
Lennart Regebro

두 번째 요소 만 필요했기 때문에 두 번째 요소 만 사용했습니다. 그러나 예, 조각은 좋은 대안으로 보입니다
CSZ

@CSZ : 그러나 첫 번째 요소는 무시되므로이 경우 건너 뛸 수 있습니다. :) 내가 의미하는 바를 보아라. 예는 실제 생활에서 잘 작동하지 않는다.
Lennart Regebro
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.