Python에서 []없이 목록 이해


85

목록에 참여 :

>>> ''.join([ str(_) for _ in xrange(10) ])
'0123456789'

join 반복 가능해야합니다.

분명히 join의 인수는 [ str(_) for _ in xrange(10) ]이고 목록 이해력 입니다.

이것 좀봐:

>>>''.join( str(_) for _ in xrange(10) )
'0123456789'

이제 join의 인수는 str(_) for _ in xrange(10), no []이지만 결과는 동일합니다.

왜? str(_) for _ in xrange(10)목록이나 반복 가능 항목도 생성 합니까 ?


1
나는 그것이 joinC로 작성되었을 가능성이 높기 때문에 목록 이해보다 훨씬 빠르게 실행 된다고 생각합니다 . 테스트 시간!
Joel Cornett 2012 년

분명히, 나는 당신의 질문을 완전히 잘못 읽었습니다. 그것은 ... 나를 위해 발전기를 돌려 것 같다
조엘 코 네트에게

18
참고 : _특별한 의미가 없으며 일반 변수 이름입니다. 종종 버리는 이름으로 사용되지만 이것은 경우가 아닙니다 (변수를 사용하고 있음). 나는 (적어도 이런 식으로) 코드에서 그것을 사용하지 않을 것입니다.
rplnt

답변:


67
>>>''.join( str(_) for _ in xrange(10) )

이를 생성기 표현식 이라고하며 PEP 289에 설명되어 있습니다.

생성기 표현식과 목록 이해의 주요 차이점은 전자가 메모리에 목록을 생성하지 않는다는 것입니다.

표현식을 작성하는 세 번째 방법이 있습니다.

''.join(map(str, xrange(10)))

1
내가 아는 것처럼 생성기는 ( str(_) for _ in xrange(10) ). 그러나 나는 혼란 스러웠습니다. 왜에서 ()생략 할 수 join있습니까? 즉, 코드는` ''.join ((str (_) for _ in xrange (10))), 맞습니까?
Alcott 2012 년

1
@Alcott 튜플에 대한 나의 이해는 실제로 괄호가 아닌 쉼표로 구분 된 표현식 목록에 의해 정의된다는 것입니다. 괄호는 할당의 값을 시각적으로 그룹화하거나 튜플이 함수 호출과 같이 다른 쉼표로 구분 된 목록으로 이동하는 경우 값을 실제로 그룹화하기 위해서만 있습니다. 이것은 종종 tup = 1, 2, 3; print(tup). 이를 염두에두고 for표현식의 일부로 사용 하면 생성기가 생성되고 괄호는 잘못 작성된 루프와 구별하기 위해 존재합니다.
Eric Ed Lohmar

132

다른 응답자들은 생성기 표현식 (목록 이해와 유사한 표기법이 있지만 주변 대괄호가 없음)을 발견했다고 답했습니다 .

일반적으로 genexp (정말로 알려진대로)는 목록 이해보다 메모리 효율적이고 빠릅니다.

그러나 ''.join()목록 이해는 더 빠르고 메모리 효율적입니다. 그 이유는 조인 이 데이터를 두 번 통과해야하므로 실제로 실제 목록이 필요하기 때문입니다. 하나를 주면 즉시 작업을 시작할 수 있습니다. 대신 genexp를 제공하면 genexp를 실행하여 메모리에 새 목록을 작성할 때까지 작업을 시작할 수 없습니다.

~ $ python -m timeit '"".join(str(n) for n in xrange(1000))'
1000 loops, best of 3: 335 usec per loop
~ $ python -m timeit '"".join([str(n) for n in xrange(1000)])'
1000 loops, best of 3: 288 usec per loop

itertools.imapmap을 비교할 때 동일한 결과가 유지됩니다 .

~ $ python -m timeit -s'from itertools import imap' '"".join(imap(str, xrange(1000)))'
1000 loops, best of 3: 220 usec per loop
~ $ python -m timeit '"".join(map(str, xrange(1000)))'
1000 loops, best of 3: 212 usec per loop

4
@lazyr 두 번째 타이밍이 너무 많은 작업을하고 있습니다. listcomp 주위에 genexp를 래핑하지 말고 genexp를 직접 사용하십시오. 당신이 이상한 타이밍을 가지고있는 것은 당연합니다.
Raymond Hettinger 2012 년

11
''.join()문자열을 만들기 위해 반복자를 2 번 통과해야하는 이유를 설명해 주 시겠습니까?
ovgolovin

27
@ovgolovin 첫 번째 패스는 연결된 문자열에 대해 올바른 메모리 양을 할당 할 수 있도록 문자열의 길이를 합산하는 것이고 두 번째 패스는 개별 문자열을 할당 된 공간에 복사하는 것입니다.
Lauritz V. Thaulow

20
@lazyr 그 추측이 맞습니다. 이것이 바로 str.join이하는 일입니다. :-)
Raymond Hettinger

4
때때로 나는 SO에 대한 특정 답변을 "좋아하는"능력을 정말로 놓친다.
Air

5

두 번째 예제는 목록 이해가 아닌 생성기 표현식을 사용합니다. 차이점은 목록 이해력을 사용하면 목록이 완전히 빌드되고에 전달된다는 것 .join()입니다. 생성기 표현식을 사용하면 항목이 하나씩 생성되고 .join(). 후자는 메모리를 덜 사용하고 일반적으로 더 빠릅니다.

발생하면 목록 생성자는 생성기 표현식을 포함하여 모든 이터 러블을 기꺼이 소비합니다. 그래서:

[str(n) for n in xrange(10)]

다음에 대한 "구문 설탕"입니다.

list(str(n) for n in xrange(10))

즉, 목록 이해는 목록으로 변환되는 생성기 표현식입니다.


2
후드 아래에서 동등하다고 확신합니까? Timeit 말한다 : [str(x) for x in xrange(1000)]: 262 usec, list(str(x) for x in xrange(1000)): 304 usec.
Lauritz V. Thaulow

2
@lazyr 당신이 맞습니다. 목록 이해가 더 빠릅니다. 그리고 이것이 파이썬 2.x에서 목록 이해가 누출되는 이유입니다. 이것은 GVR이 썼던 것입니다 : ""이것은 목록 이해의 원래 구현의 인공물입니다; 수년 동안 파이썬의 "더러운 작은 비밀"중 하나였습니다. 목록 이해력을 눈부시게 빠르게 만들기위한 의도적 인 타협으로 시작되었으며 초보자에게는 일반적인 함정은 아니지만 분명히 때때로 사람들을 찔렀
ovgolovin

3
@ovgolovin listcomp가 더 빠른 이유는 join 이 작업을 시작하기 전에 목록을 만들어야하기 때문입니다. 당신이 언급하는 "누수"는 속도 문제가 아니라 단지 루프 유도 변수가 listcomp 외부에 노출된다는 것을 의미합니다.
Raymond Hettinger

1
@RaymondHettinger 그렇다면이 단어는 " 목록 이해를 맹목적으로 빠르게 만들기 위한 의도적 인 타협 으로 시작했다"는 의미는 무엇 입니까? 내가 이해했듯이 누출과 속도 문제의 연결이 있습니다. GVR은 또한 다음과 같이 썼습니다. "제너레이터 표현식의 경우이 작업을 수행 할 수 없습니다. 생성기 표현식은 별도의 실행 프레임이 필요한 생성기를 사용하여 구현됩니다. 따라서 생성기 표현식 (특히 짧은 시퀀스를 반복하는 경우) 은 목록 이해보다 효율성이 떨어졌습니다 . "
ovgolovin

4
@ovgolovin listcomp 구현 세부 사항에서 str.join이 수행하는 방식에 대해 잘못된 도약을했습니다. str.join 코드의 첫 번째 줄 중 하나는 seq = PySequence_Fast(orig, "");str.join ()을 호출 할 때 반복기가 목록이나 튜플보다 느리게 실행되는 유일한 이유입니다. 더 논의하고 싶다면 채팅을 시작할 수 있습니다. (저는 PEP 289의 작성자이고 LIST_APPEND opcode의 작성자이며 list () 생성자를 최적화 한 사람입니다. 문제에 대한 친숙 함).
Raymond Hettinger 2012 년

5

언급했듯이 생성기 표현식 입니다.

문서에서 :

인수가 하나만있는 호출에서는 괄호를 생략 할 수 있습니다. 자세한 내용은 호출 섹션을 참조하십시오 .


4

괄호 안에 있지만 대괄호가 아닌 경우 기술적으로 는 생성기 표현식입니다. 생성기 표현식은 Python 2.4에서 처음 도입되었습니다.

http://wiki.python.org/moin/Generators

조인 이후 부분 ( str(_) for _ in xrange(10) )은 그 자체로 생성기 표현식입니다. 다음과 같이 할 수 있습니다.

mylist = (str(_) for _ in xrange(10))
''.join(mylist)

위의 두 번째 경우에서 쓴 것과 똑같은 의미입니다.

생성기에는 매우 흥미로운 속성이 있지만 그중에서도 필요하지 않을 때 전체 목록을 할당하지 않는다는 점은 중요합니다. 대신 조인과 같은 함수는 생성기 표현식에서 항목을 한 번에 하나씩 "펌핑"하여 작은 중간 부분에서 작업을 수행합니다.

특정 예에서 목록과 생성기는 아마도 크게 다르게 수행되지 않지만 일반적으로 가능할 때마다 생성기 표현식 (및 생성기 함수)을 사용하는 것을 선호합니다. 대부분 생성기가 전체 목록보다 느린 경우가 극히 드뭅니다. 구체화.


1

그것은 목록 이해력이 아니라 생성기입니다. 생성기도 반복 가능하지만 전체 목록을 먼저 만든 다음 조인에 전달하는 대신 xrange의 각 값을 하나씩 전달하므로 훨씬 더 효율적일 수 있습니다.


0

두 번째 join호출에 대한 인수 는 생성기 표현식입니다. iterable을 생성합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.