itsource

파이썬에서 두 개의 발전기(또는 다른 반복 가능한 것)를 결합하는 방법은 무엇입니까?

mycopycode 2023. 7. 6. 22:13
반응형

파이썬에서 두 개의 발전기(또는 다른 반복 가능한 것)를 결합하는 방법은 무엇입니까?

다음 코드를 변경하고 싶습니다.

for directory, dirs, files in os.walk(directory_1):
    do_something()

for directory, dirs, files in os.walk(directory_2):
    do_something()

다음 코드로:

for directory, dirs, files in os.walk(directory_1) + os.walk(directory_2):
    do_something()

오류가 발생했습니다.

+: 'property' 및 'property'에 대해 지원되지 않는 피연산자 유형

파이썬에서 두 발전기에 가입하는 방법은?

itertools.chain() 해야 합니다.여러 반복 가능한 항목과 각 항목의 산출량은 대략 다음과 같습니다.

def chain(*iterables):
    for it in iterables:
        for element in it:
            yield element

사용 예:

from itertools import chain

g = (c for c in 'ABC')  # Dummy generator, just for example
c = chain(g, 'DEF')  # Chain the generator and a string
for item in c:
    print(item)

출력:

A
B
C
D
E
F

코드의 예:

from itertools import chain

def generator1():
    for item in 'abcdef':
        yield item

def generator2():
    for item in '123456':
        yield item

generator3 = chain(generator1(), generator2())
for item in generator3:
    print item

Python(3.5 이상)에서는 다음을 수행할 수 있습니다.

def concat(a, b):
    yield from a
    yield from b

간단한 예:

from itertools import chain
x = iter([1,2,3])      #Create Generator Object (listiterator)
y = iter([3,4,5])      #another one
result = chain(x, y)   #Chained x and y

iter tools.chain과 함께.다음과 같은 작업을 수행할 수 있습니다.

def genny(start):
  for x in range(start, start+3):
    yield x

y = [1, 2]
ab = [o for o in itertools.chain.from_iterable(genny(x) for x in y)]
print(ab)

여기서는 중첩된 생성자 식을 사용하고 있습니다.fors:

range_a = range(3)
range_b = range(5)
result = (item
    for one_range in (range_a, range_b)
    for item in one_range)
assert list(result) == [0, 1, 2, 0, 1, 2, 3, 4]

for ... in ...왼쪽에서 오른쪽으로 평가됩니다.다음의 식별자for새 변수를 설정합니다.하는 동안에one_range다음에 사용되는for ... in ...,그item두 번째 것부터 "최종" 할당식에 사용되며, "최종" 할당식에는 (처음에) 하나만 사용됩니다.

관련 질문:어떻게 하면 목록에서 플랫 목록을 만들있습니까?

2020년 업데이트:Python 3 및 Python 2 모두에서 작동합니다.

import itertools

iterA = range(10,15)
iterB = range(15,20)
iterC = range(20,25)

첫 번째 선택지

for i in itertools.chain(iterA, iterB, iterC):
    print(i)

# 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

대체 옵션, python 2.6에 도입됨

for i in itertools.chain.from_iterable( [iterA, iterB, iterC] ):
    print(i)

# 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

iter tools.chain()은 기본입니다.

이터툴.체인.from_iterable은 반복 가능한 항목이 있는 경우 유용합니다.예를 들어 다음과 같은 하위 디렉터리별 파일 목록[ ["src/server.py", "src/readme.txt"], ["test/test.py"] ].

또한 언팩 연산자를 사용할 수 있습니다.*:

concat = (*gen1(), *gen2())

참고: '지루하지 않은' 반복 작업에 가장 효율적으로 사용할 수 있습니다.다른 종류의 이해와 함께 사용할 수도 있습니다.제너레이터 콘캣에 대한 선호되는 방법은 @Uduse의 답변을 사용하는 것입니다.

생성기를 별도로 유지하면서 동시에 반복하려면 zip()을 사용할 수 있습니다.

참고: 두 발전기 중 짧은 쪽에서 반복이 멈춥니다.

예:

for (root1, dir1, files1), (root2, dir2, files2) in zip(os.walk(path1), os.walk(path2)):

    for file in files1:
        #do something with first list of files

    for file in files2:
        #do something with second list of files

(거부자:Python 3만 해당!)

원하는 구문과 유사한 구문을 가진 것은 스플랫 연산자를 사용하여 두 개의 생성기를 확장하는 것입니다.

for directory, dirs, files in (*os.walk(directory_1), *os.walk(directory_2)):
    do_something()

설명:

이것은 효과적으로 두 발전기를 3-튜플의 N-튜플로 단일 레벨 평탄화를 수행합니다.os.walk다음과 같이 표시됩니다.

((directory1, dirs1, files1), (directory2, dirs2, files2), ...)

그런 다음 for-loop이 이 N-tup을 통해 반복됩니다.

물론 외부 괄호를 괄호로 바꾸기만 하면 N-튜플 대신 3-튜플 목록을 얻을 수 있습니다.

for directory, dirs, files in [*os.walk(directory_1), *os.walk(directory_2)]:
    do_something()

이는 다음과 같은 결과를 낳습니다.

[(directory1, dirs1, files1), (directory2, dirs2, files2), ...]

프로:

이 접근 방식의 장점은 어떤 것도 가져올 필요가 없고 코드도 많지 않다는 것입니다.

단점:

단점은 두 개의 제너레이터를 컬렉션에 덤프한 다음 해당 컬렉션을 반복하여 두 개의 패스를 효과적으로 수행하고 잠재적으로 많은 메모리를 사용한다는 것입니다.

생성기(gen1 및 gen2)가 필요하고 두 결과를 모두 요구하는 추가 계산을 수행하려고 합니다.이러한 함수/계산 결과를 맵 방법을 통해 반환할 수 있으며, 이 방법은 루프할 수 있는 생성기를 반환합니다.

이 시나리오에서는 람다 함수를 통해 함수/계산을 구현해야 합니다.까다로운 부분은 지도와 람다 함수 안에서 우리가 하고자 하는 것입니다.

제안된 솔루션의 일반적인 형태:

def function(gen1,gen2):
        for item in map(lambda x, y: do_somethin(x,y), gen1, gen2):
            yield item

나는 사용자 "wjandrea"의 댓글에서 제안된 것처럼, 가장 좋은 해결책은 다음과 같습니다.

def concat_generators(*gens):
    for gen in gens:
        yield from gen

그것은 반환된 유형을 바꾸지 않고 정말로 파이썬입니다.

이전과 이후에 knows 디렉토리에서 파일 경로 목록을 가져오려면 다음 작업을 수행할 수 있습니다.

for r,d,f in os.walk(current_dir):
    for dir in d:
        if dir =='after':
                after_dir = os.path.abspath(os.path.join(current_dir, dir))
                for r,d,f in os.walk(after_dir): 
                    after_flist.append([os.path.join(r,file)for file in f if file.endswith('json')])
                              
        elif dir =='before': 
                before_dir = os.path.abspath(os.path.join(current_dir, dir))
                for r,d,f in os.walk(before_dir):
                    before_flist.append([os.path.join(r,file)for file in f if file.endswith('json')])

더 나은 답이 있다는 것을 압니다. 이것은 제가 느낀 간단한 코드입니다.

모든 제너레이터를 목록에 넣을 수 있습니다.제너레이터를 결합할 수는 없지만 목록을 결합할 수는 있습니다.단점은 실제로 메모리에 3개의 목록을 만들었다는 것입니다. 하지만 장점은 이것이 매우 읽기 쉽고 가져오기가 필요하지 않으며 한 줄로 된 관용어라는 것입니다.

운영을 위한 솔루션.

for directory, dirs, files in list(os.walk(directory_1)) + list(os.walk(directory_2)):
    do_something()
a = range(20)
b = range(10,99,3)
for v in list(a) + list(b):
    print(v) 

한 번만 수행하면 되고 모듈을 하나 더 가져오지 않으려면 간단한 해결 방법이 있습니다.

그냥 하기:

for dir in directory_1, directory_2:
    for directory, dirs, files in os.walk(dir):
        do_something()

두 생성기를 모두 "가입"하려면 다음을 수행합니다.

for directory, dirs, files in (
        x for osw in [os.walk(directory_1), os.walk(directory_2)] 
               for x in osw
        ):
    do_something()

언급URL : https://stackoverflow.com/questions/3211041/how-to-join-two-generators-or-other-iterables-in-python

반응형