목록에서 항목 찾기
16329 단어 python
번호를 찾다
만약 당신이 어떤 기준에 부합되는 첫 번째 숫자를 찾고 싶다면, 당신은 어떻게 할 것입니까?가장 간단한 방법은 하나의 순환을 짜서 하나하나 숫자를 검사하고 정확한 숫자를 찾은 후에 되돌아오는 것이다.
만약 우리가 첫 번째 숫자를 42와 43으로 나누려고 한다면 (즉 1806).만약 우리가 미리 정의된 요소 집합이 없다면 (이 예에서 1부터 시작하는 모든 숫자를 검사하기를 원합니다.) "while 순환"을 사용할 수 있습니다.
# find_item.py
def while_loop():
item = 1
# You don't need to use parentheses, but they improve readability
while True:
if (item % 42 == 0) and (item % 43 == 0):
return item
item += 1
이것은 매우 간단하다.목록에서 숫자 찾기
만약 우리가 검사할 항목 목록이 있다면, 우리는 'for 순환' 을 사용할 것입니다.나는 내가 찾는 숫자가 10000보다 적다는 것을 알고 있기 때문에 그것을 상한선으로 삼자.
# find_item.py
def for_loop():
for item in range(1, 10000):
if (item % 42 == 0) and (item % 43 == 0):
return item
Python 3.8을 사용하여 벤치마크 테스트를 완료하는 두 가지 솔루션을 비교해 보겠습니다.$ python -m timeit -s "from find_item import while_loop" "while_loop()"
2000 loops, best of 5: 134 usec per loop
$ python -m timeit -s "from find_item import for_loop" "for_loop()"
2000 loops, best of 5: 103 usec per loop
'While loop'은'for loop'(134/103)보다 30%가량 느리다≈1.301).순환은 최적화를 거쳐 원소 집합에서 교체할 수 있다.수동으로 교체를 시도하는 것은 (예를 들어 색인 변수를 통해 목록의 요소를 인용하는 것) 비교적 느리고 공학적인 해결 방안이 될 것이다.
Python2 거꾸로 서술
Python2에서
range
, filter
또는 zip
등 함수는 매우 절박하기 때문에 항상 초기화할 때 전체 집합을 만든다.모든 이 요소들은 메모리에 불러와서 코드의 실행 시간과 메모리 사용률을 증가시킵니다.이런 행위를 피하기 위해서는 게으름 등가물, 예를 들어 xrange
, ifilter
또는 izip
을 사용해야 한다.호기심 때문에
for_loop()
함수가 파이톤 2.7.18 (파이톤 2의 최신 버전) 을 사용할 때 얼마나 느린지 봅시다.$ pyenv shell 2.7.18
$ python -m timeit -s "from find_item import for_loop" "for_loop()"
10000 loops, best of 3: 151 usec per loop
이것은 Python3(151/103)에서 같은 함수를 실행하는 것보다 50% 가까이 느리다≈1.4660). 파이썬 버전을 업데이트하는 것이 가장 간단한 성능 승자 중 하나입니다!pyenv가 무엇인지, Python 버전을 빠르게 전환하는 방법을 알고 싶으면, Python 도구 this section of my PyCon 2020 workshop 를 보십시오.
'while 순환'과'for 순환'의 비교로 돌아갑시다.우리가 찾으려는 요소가 목록의 시작인지 끝인지, 그게 무슨 상관입니까?
def while_loop2():
item = 1
while True:
if (item % 98 == 0) and (item % 99 == 0):
return item
item += 1
def for_loop2():
for item in range(1, 10000):
if (item % 98 == 0) and (item % 99 == 0):
return item
이번에 우리는 9702호를 찾고 있는데, 그것은 우리의 목록의 마지막에 있다.이제 성능을 평가해 보겠습니다.$ python -m timeit -s "from find_item import while_loop2" "while_loop2()"
500 loops, best of 5: 710 usec per loop
$ python -m timeit -s "from find_item import for_loop2" "for_loop2()"
500 loops, best of 5: 578 usec per loop
거의 차이가 없다.'While loop'이 이번에 22% 정도 느려졌어요(710/578).≈1.223). 나는 또 몇 번의 테스트(최대 10만 건에 육박)를 했는데 결과는 항상 비슷했다(속도가 20~30% 느리다).무한 목록에서 숫자를 찾습니다
지금까지 우리가 교체하고 싶은 항목의 집합은 10000개에 불과하다.그런데 저희가 상한선을 모르면요?이런 상황에서 우리는
itertools
라이브러리의 count 함수를 사용할 수 있다.from itertools import count
def count_numbers():
for item in count(1):
if (item % 42 == 0) and (item % 43 == 0):
return item
count(start=0, step=1)
는 start
매개 변수로부터 계수를 시작하여 매번 교체에 step
를 추가합니다.나의 예에서, 나는 start 매개 변수를 1로 바꾸어야 한다. 이렇게 하면 그의 작업 원리는 앞의 예와 같다.count
의 작업 원리는 우리가 시작할 때 제작한'while 순환'과 거의 같다.속도가 어때요?$ python -m timeit -s "from find_item import count_numbers" "count_numbers()"
2000 loops, best of 5: 109 usec per loop
이것은 거의 "for loop"버전과 같다.무한계수기가 필요하다면 count
아주 좋은 대체품이다.그럼 리스트는요?
교체 프로젝트 목록의 전형적인 해결 방안은 목록 이해를 사용하는 것이다.그러나 우리는 숫자를 찾은 후 바로 교체에서 물러나기를 원하는데, 이것은 목록의 이해에 있어서 결코 쉽지 않다.이것은 전체 시리즈를 훑어볼 수 있는 아주 좋은 도구이지만, 이 예에서는 그렇지 않다.
상황이 얼마나 나쁜지 봅시다.
def list_comprehension():
return [item for item in range(1, 10000) if (item % 42 == 0) and (item % 43 == 0)][0]
$ python -m timeit -s "from find_item import list_comprehension" "list_comprehension()"
500 loops, best of 5: 625 usec per loop
이것은 정말 엉망이다. 그것은 다른 해결 방안보다 몇 배 느리다.우리가 첫 번째나 마지막 요소를 검색하든지 간에 모두 같은 시간이 필요하다.우리는 이곳에서 사용할 수 없다count
.그러나 사용 목록은 우리가 정확한 방향을 가리키는 것으로 이해된다. 첫 번째 요소를 찾은 다음 교체를 멈춰야 한다.그 물건은 발전기다!우리는 생성기 표현식을 사용하여 조건에 맞는 첫 번째 요소를 얻을 수 있다.
생성기 표현식을 사용하여 항목 찾기
def generator():
return next(item for item in count(1) if (item % 42 == 0) and (item % 43 == 0))
전체 코드는 목록 이해와 매우 유사해 보이지만, 우리는 실제로 사용할 수 있다. count
생성기 표현식은 다음 요소로 되돌아갈 충분한 코드만 실행합니다.네가 전화할 때마다next()
, 그것은 지난번에 멈춘 곳에서 계속 일하고, 다음 항목을 잡고, 돌려주고, 다시 멈춘다.$ python -m timeit -s "from find_item import generator" "generator()"
2000 loops, best of 5: 110 usec per loop
이것은 우리가 지금까지 찾은 최상의 해결 방안을 찾는 데 소요된 시간과 거의 같다.나는 이 문법이 더 읽기 쉽다는 것을 발견했다. 우리가 거기에 너무 많이 넣지 않으면 if
!발전기는 또 다른 장점이 있는데, 즉, '정지' 와 '회복' 계수를 할 수 있다는 것이다.우리는 조건에 부합되는 다음 요소를 얻을 때마다 여러 번 호출할 수 있다.만약 우리가 42와 43으로 나눌 수 있는 세 개의 숫자를 얻고 싶다면, 다음은 생성기 표현식을 사용하면 이 점을 쉽게 할 수 있다.
def generator_3_items():
gen = (item for item in count(1) if (item % 42 == 0) and (item % 43 == 0))
return [next(gen), next(gen), next(gen)]
for loop 버전과 비교하려면 다음과 같이 하십시오.def for_loop_3_items():
items = []
for item in count(1):
if (item % 42 == 0) and (item % 43 == 0):
items.append(item)
if len(items) == 3:
return items
두 버전에 대한 벤치마크 테스트를 실시해 보겠습니다.$ python -m timeit -s "from find_item import for_loop_3_items" "for_loop_3_items()"
1000 loops, best of 5: 342 usec per loop
$ python -m timeit -s "from find_item import generator_3_items" "generator_3_items()"
1000 loops, best of 5: 349 usec per loop
성능에 있어서 이 두 가지 기능은 거의 완전히 같다.그럼 너는 언제 하나를 다른 것으로 대체할 거니?'For 순환'은 더 복잡한 코드를 작성할 수 있게 해 준다.중첩된 "if"문장이나 부작용이 있는 다행 코드를 생성기 표현식에 넣을 수 없습니다.하지만 간단한 필터만 하면 생성기가 더 쉽게 읽을 수 있다.결론
생성기 표현식과
next()
를 결합하는 것은 특정 조건에 따라 하나 이상의 원소를 얻는 좋은 방법이다.그것의 메모리 효율은 높고, 속도는 빠르며, 읽기 쉽다. 네가 간단하게 유지하기만 하면.생성기 표현식의'if문장'수가 증가하면 읽기가 훨씬 어려워집니다.복잡한 선별 조건이 많을지도 모른다.
next()
선별 조건은 'for loop' 이 성능을 희생하지 않는 더 적합한 선택이다.
Reference
이 문제에 관하여(목록에서 항목 찾기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/switowski/find-item-in-a-list-526g텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)