파이썬에서 숫자 목록과 음수 대응 항목 생성


31

파이썬에서 숫자 목록과 부정적인 대응 항목을 생성하는 편리한 단일 라이너가 있습니까?

예를 들어 숫자 6 ~ 9와 -6 ~ -9의 목록을 생성한다고 가정 해보십시오.

내 현재 접근 방식은 다음과 같습니다

l = [x for x in range(6,10)]
l += [-x for x in l]

간단한 "한 줄짜리"는 다음과 같습니다.

l = [x for x in range(6,10)] + [y for y in range(-9, -5)]

그러나 두 개의 목록을 생성 한 다음 서로 결합하는 것은 불편 해 보입니다.


3
양수가 음수 앞에 와야합니까?
Erich

2
@Erich 아니요, 제 경우에는 순서가 중요하지 않습니다.
17:01에

6
주문이 중요하지 않은 경우에도 목록이어야합니까? 세트가 OK (정렬되지 않은) 또는 제너레이터 또는 튜플 (둘 다 정렬 된 것)입니까?
JG

@ JG 나중에 그림을 만들고 싶을 수도 있습니다. 스 캐터 등을 사용하는 것으로 추정됩니다 scalar or array-like, shape.
UPE

2
이러한 답변의 대부분은 골프 방지 솔루션처럼 읽습니다. 정렬, 생성기 함수, itertools. 대부분의 '답변'보다 제공 한 코드를 유지하는 것이 좋습니다.
Peilonrayz

답변:


44

순서가 중요한지 잘 모르겠지만 튜플을 만들어 목록 이해에 풀 수 있습니다.

nums = [y for x in range(6,10) for y in (x,-x)]
print(nums)
[6, -6, 7, -7, 8, -8, 9, -9]

50

멋지고 읽기 쉬운 함수를 만듭니다.

def range_with_negatives(start, end):
    for x in range(start, end):
        yield x
        yield -x

용법:

list(range_with_negatives(6, 10))

그것이 당신이 무엇이든 편리한 원 라이너를 얻는 방법입니다. 매직 프로 해커처럼 보이려고하지 마십시오.


20
목록 / 딕트 이해에 대한 편견은 SO에서 매우 일반적이며, 일반적으로 그것들이 "판독 가능하지 않다"고 말함으로써 정당화됩니다. 구조의 고유 한 가독성은 ( 사용 방법 이 아닌 ) 크게 주관적입니다. 개인적으로 나는 이해력이 읽기 쉽고,이 해결책은 훨씬 덜하다고 생각합니다 (비교 : "50 세 이상의 모든 남성의 혈압을 기록하십시오"대 "모든 사람을 통과하십시오. 그렇다면 남성입니까? 그렇다면 ...") ). 나는 이런 이유 때문에 그것들을 사용한다. 왜냐하면 "무슨 것처럼 보이려고"하지 않기 때문이다. 이것이 문제라면 여러 줄에 걸쳐 긴 이해가 어려워 질 수 있습니다.
jez

2
그럴 수 있지. 주관성이 들어오는 곳하지만 그건 : 나는 개인적으로 발전기 기능으로, 정신적 긴장의 관점에서 읽을 수로 적어도 목록 빌려 솔루션을 찾을 수 있습니다. 그러나 이름 y을 으로 바꾸어 Datanovice의 답변에 대한 가독성 (또는 오히려 가독성)을 향상시킬 것 signedValue입니다. 함수 정의 접근법의 실제 추가 가치는 chain약간 다른 숫자 인수가있는 Barmar의 한 줄짜리 문제를 피하면서 질문이 요청한 엄격한 순서 (양수, 음수)로 결과를 제공하는 것입니다. 두 번 하드 코딩됩니다.
jez

2
나는 목록 이해에 대한 일반적인 감정에 동의하지만, 이와 같은 기능을 정의하는 것도 매우 유용한 기술입니다. (재귀 생성기를 작성하고 최상위 결과를 다른 것에 전달함으로써 재귀 알고리즘에서 결과를 수집하는 것이 특히 강력합니다 list.) 몇 년 동안 이러한 결정을 내리는 최선의 방법을 체계화하려고 시도한 후 나에게 의미가있는 유일한 일반적인 원칙 : 프로그램에 무언가 분명하고 좋은 이름이 있다면, 그렇게 할 기회를 가지십시오 (그리고 기능은 우리가하는 방법 중 하나입니다).
Karl Knechtel

42

가장 간단한 해결책은 *unpacking 연산자를 사용하여 두 범위를 목록으로 압축 해제하는 것입니다 .

>>> [*range(6, 10), *range(-9, -5)]
[6, 7, 8, 9, -9, -8, -7, -6]

이것은 가장 짧은 대답 일뿐 만 아니라 단일 목록 만 구성하고 두 개 이상의 함수 호출을 포함하지 않기 때문에 가장 성능이 range좋습니다.

timeit모듈을 사용 하여이 질문의 모든 답변을 테스트하여 이것을 확인했습니다 .

답변 ID 방법 시간 결과
-------------------------------------------------- ------------------------------------------------
(문제) [x 범위 (6,10)의 x에 대한 x] + [범위 범위의 y에 대한 y (-9, -5)] 루프 당 0.843 usec
(이 답변) [* range (6, 10), * range (-9, -5)] 루프 당 0.509 usec
61348876 [범위 (x, -x)에서 y의 경우 (6,10)의 x에 대한 y] 루프 당 0.754 usec
61349149 list (range_with_negatives (6, 10)) 루프 당 0.795 usec
61348914 list (itertools.chain (range (6, 10), range (-9, -5))) 0.709 루프 당 usec
61366995 [itertools.product ((-1, 1), range (6, 10))의 부호 x, 부호 x x] 루프 당 0.899 usec
61371302 list (range (6, 10)) + list (range (-9, -5)) 루프 당 0.729 usec
61367180 list (range_with_negs (6, 10)) 루프 당 1.95 usec

(내 컴퓨터에서 Python 3.6.9를 사용하여 적시에 테스트 (평균 사양))


2
나는 당신이 가지고있는 모든 것이 <10 항목을 가진 예일 때 성능이 관련이 있다고 가정하는데 너무 열중하지는 않지만 이것이 가장 간단한 해결책입니다.
JollyJoker

하드 코딩없이 음수 값의 시작과 끝을 어떻게 알 수 있습니까?
알도 카니

1
@aldokkani[*range(x, y), *range(-y + 1, -x + 1)]
RoadrunnerWMC

20

itertools.chain()두 범위를 연결하는 데 사용할 수 있습니다 .

import itertools
list(itertools.chain(range(6, 10), range(-9, -5)))

1
순서의 명확성을 위해 'range (-6, -10, -1)'을 사용하고 (6과 10을 변수로 대체)
Maarten Fabré

7

itertools.product직교 곱인을 사용할 수 있습니다 .

[sign*x for sign, x in product((-1, 1), range(6, 10))]
[-6, -7, -8, -9, 6, 7, 8, 9]

곱셈을 사용하기 때문에 속도가 느릴 수 있지만 읽기 쉬워야합니다.

순전히 기능적인 솔루션을 원한다면 다음 itertools.starmapoperator.mul같이 가져올 수도 있습니다 .

from itertools import product, starmap
from operator import mul

list(starmap(mul, product((-1, 1), range(6, 10))))

그러나 이것은 읽기 어렵습니다.


5
나는의 사용을 발견 product, starmap그리고 opertaor.mul불필요하게 둔각 중첩 된 지능형리스트와 비교,하지만 난 곱셈을 사용하는 제안을 승인. [x * sign for sign in (1, -1) for x in range(6, 10)]보다 10 % 느리고 [y for x in range(6, 10) for y in (x, -x)]순서가 중요한 경우 튜플 기반 접근 방식을 정렬하는 것보다 3 배 이상 빠릅니다.
접근하는

이것은 깔끔한 아이디어이지만 문제 세부 사항에 너무 구체적 일 수 있습니다. 다른 사람들이 제공하는 기술은 더 일반적으로 적용됩니다.
Karl Knechtel

@ApproachingDarknessFish 나는 동의 starmap하고 mul피해야한다,하지만 난 생각 product차종 더 읽기를 같이 그룹 반복자와 그 요소가 별도로. 목록 이해의 이중 루프는 예기치 않은 순서로 인해 혼란 스러울 수 있습니다.
Frank Vel

4

range객체 를 결합하여 정말 가깝습니다 . 그러나 더 쉬운 방법이 있습니다.

>>> list(range(6, 10)) + list(range(-9, -5))
[6, 7, 8, 9, -9, -8, -7, -6]

즉, 각 range개체를 목록으로 변환 한 다음 두 목록을 연결하십시오.

itertools를 사용하는 또 다른 접근법 :

>>> list(itertools.chain(range(6, 10), range(-9, -5)))
[6, 7, 8, 9, -9, -8, -7, -6]

itertools.chain()일반화 된 것과 같습니다 +: 두 개의 목록을 추가하는 대신 하나의 반복자를 연쇄 적으로 연결하여 "슈퍼 반복기"를 만듭니다. 그런 다음 그것을 전달 list()하면 메모리에 원하는 모든 숫자가 포함 된 구체적인 목록이 나타납니다.


3
이 답변은 [x for x in ...]더 나은 철자가 되는 통찰력에만 유용 list(...)합니다.
Karl Knechtel

2

또 다른 가능성으로 무게.

가독성을 원한다면 원래의 원 라이너는 꽤 좋았지 만 음수 범위가 덜 명확하다고 생각하는 것처럼 범위를 동일하게 변경합니다.

[x for x in range(6, 10)] + [-x for x in range(6, 10)]

2

IMO는 itertools.chain두 가지 다른 답변으로 제시된 접근 방식 이 지금까지 제공된 방법 중 가장 깨끗합니다.

그러나 귀하의 경우 값의 순서는 중요하지 않으므로 다음 두 가지 명시 적 range객체 를 정의하지 않아도 되므로 다음 range을 사용하여 음수 색인에 필요한 모든 비정형 수학을 수행하지 마십시오 itertools.chain.from_iterable.

>>> import itertools
>>> list(itertools.chain.from_iterable((x, -x) for x in range(6, 10)))
[6, -6, 7, -7, 8, -8, 9, -9]

조금 장황하지만 읽을 수 있습니다.

또 다른 유사한 옵션은 일반과 함께 튜플 / 인수 압축 풀기를 사용하는 것입니다 chain.

>>> list(itertools.chain(*((x, -x) for x in range(6, 10))))
[6, -6, 7, -7, 8, -8, 9, -9]

더 간결하지만 빠른 스캔에서 튜플 포장 풀기가 더 어려워집니다.


2

이것은 어디에 철학 에 따라 주제에 대한 변형입니다 ( @Derte Trdelnik답변 참조 )itertools

반복자 빌딩 블록 [...]은 단독으로 또는 조합하여 유용합니다.

새로운 함수를 정의하는 동안 일반적인 함수로 만들 수도 있습니다.

def interleaved_negatives(it):
    for i in it:
        yield i
        yield -i

특정 range반복자에 적용하십시오 .

list(interleaved_negatives(range(6, 10)))

0

지정한 순서를 유지하려면 조건부와 함께 Python의 내장 범위 생성기를 사용할 수 있습니다.

def range_with_negs(start, stop):
    for value in range(-(stop-1), stop):      
        if (value <= -start) or (value >= start):
            yield value

어느 것이 당신에게 출력을 제공합니다 :

In [1]: list(range_with_negs(6, 10))
Out[1]: [-9, -8, -7, -6, 6, 7, 8, 9]

또한 전체 범위의 시작으로 0과 함께 작동합니다.


2
이 값이 큰 경우 비효율적입니다 start.
Solomon Ucko

0

작업을 수행하는 다른 방법이있을 수 있습니다.

주어진 변수 : 1. start = 6 2. stop = 10

다른 방법으로 시도해 볼 수도 있습니다.

def mirror_numbers(start,stop):
  if start<stop:
    val=range(start,stop)
    return [j if i < len(val) else -j for i,j in enumerate([x for x in val]*2) ]

mirror_numbers(6,10)

-1

난 그냥 대칭처럼.

a = 6 b = 10

숫자 = [범위 (a, b) 범위에서 y에 대해 (-(a + b-1), 0)에서 x에 대해 x + y]]

결과는 -9, -8, ..., 8, 9 여야합니다.

나는 숫자 표현이 향상 될 수 있다고 믿습니다. "in"과 "range"를 따르는 것은 여전히 ​​불균형 해 보입니다.

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