파이썬 목록 이해-반복되는 평가를 피하고 싶다
대략적인 목록 이해력이 있습니다.
[f(x) for x in l if f(x)]
여기서 l은 목록이고 f (x)는 목록을 반환하는 값 비싼 함수입니다.
f (x)가 비어 있지 않을 때마다 f (x)를 두 번 평가하는 것을 피하고 싶습니다. 목록 이해력 내에 출력을 저장하는 방법이 있습니까?
최종 조건을 제거하고 전체 목록을 생성 한 다음 정리할 수 있지만 이는 낭비적인 것 같습니다.
편집 :
두 가지 기본 접근 방식이 제안되었습니다.
내부 생성기 이해 :
[y for y in (f(x) for x in l) if y]
또는 메모.
나는 내부 생성기 이해가 언급 된 문제에 대해 우아하다고 생각합니다. 사실 저는 명확하게하기 위해 질문을 단순화했습니다.
[g(x, f(x)) for x in l if f(x)]
이보다 복잡한 상황에서는 메모가 더 깨끗한 최종 결과를 낳는다 고 생각합니다.
해결책 (반복되는 x 값이있는 경우 가장 좋은 방법)은 함수 f 를 메모 하는 것입니다 . 즉, 함수가 호출되는 인수를 저장하고 저장하는 래퍼 함수를 만들고 동일한 값이 요청되면 반환하는 것입니다. .
정말 간단한 구현은 다음과 같습니다.
storage = {}
def memoized(value):
if value not in storage:
storage[value] = f(value)
return storage[value]
[memoized(x) for x in l if memoized(x)]
그런 다음 목록 이해에서이 함수를 사용합니다. 이 접근 방식은 이론적 및 실제적 두 가지 조건에서 유효합니다. 첫 번째는 함수 f 가 결정적이어야한다는 것입니다. 즉, 동일한 입력이 주어지면 동일한 결과를 반환하고 다른 하나는 객체 x 를 사전 키로 사용할 수 있다는 것입니다. 첫 번째 것이 유효하지 않은 경우 정의에 따라 매번 f를 다시 계산해야하며 두 번째 것이 실패하면 약간 더 강력한 접근 방식을 사용할 수 있습니다.
인터넷에서 많은 메모 구현을 찾을 수 있으며 새 버전의 파이썬에도 포함되어 있다고 생각합니다.
참고로 작은 L을 변수 이름으로 사용하지 마십시오. 일부 터미널에서 i 또는 a 1과 혼동 될 수 있으므로 나쁜 습관입니다.
편집하다:
언급했듯이 생성기 이해를 사용하는 가능한 솔루션 (쓸모없는 중복 임시 생성을 방지하기 위해)은 다음 식입니다.
[g(x, fx) for x, fx in ((x,f(x)) for x in l) if fx]
f의 계산 비용, 원래 목록의 중복 수 및 처리시 메모리를 고려하여 선택에 가중치를 부여해야합니다. 메모 화는 공간 속도를 절충합니다. 즉, 각 결과를 추적하여 저장하므로 큰 목록이있는 경우 메모리 점유 측면에서 비용이 많이들 수 있습니다.
[y for y in (f(x) for x in l) if y]
할 것입니다.
메모 데코레이터를 사용해야합니다. 여기에 흥미로운 링크가 있습니다.
링크 및 '코드'의 메모 사용 :
def memoize(f):
""" Memoization decorator for functions taking one or more arguments. """
class memodict(dict):
def __init__(self, f):
self.f = f
def __call__(self, *args):
return self[args]
def __missing__(self, key):
ret = self[key] = self.f(*key)
return ret
return memodict(f)
@memoize
def f(x):
# your code
[f(x) for x in l if f(x)]
[y for y in [f(x) for x in l] if y]
업데이트 된 문제의 경우 다음이 유용 할 수 있습니다.
[g(x,y) for x in l for y in [f(x)] if y]
아니. 이를 수행하는 ( 깨끗한 ) 방법이 없습니다. 좋은 구식 루프에는 잘못된 것이 없습니다.
output = []
for x in l:
result = f(x)
if result:
output.append(result)
읽기 어려운 경우 언제든지 함수로 래핑 할 수 있습니다.
이전 답변에서 알 수 있듯이 이중 이해를 사용하거나 메모를 사용할 수 있습니다. 합리적인 크기의 문제는 취향의 문제입니다 (그리고 최적화를 숨기므로 메모가 더 깔끔해 보인다는 데 동의합니다). 그러나 매우 큰 목록을 검토하는 경우 에는 큰 차이가 있습니다. Memoization은 계산 한 모든 단일 값을 저장하고 빠르게 기억을 날려 버릴 수 있습니다. 생성기를 사용한 이중 이해 (대괄호가 아닌 둥근 괄호)는 유지하려는 것만 저장합니다.
실제 문제를 해결하려면 :
[g(x, f(x)) for x in series if f(x)]
최종 값을 계산하려면 x
및 f(x)
. 문제 없습니다. 둘 다 다음과 같이 전달하십시오.
[g(x, y) for (x, y) in ( (x, f(x)) for x in series ) if y ]
Again: this should be using a generator (round parens), not a list comprehension (square brackets). Otherwise you will build the whole list before you start filtering the results. This is the list comprehension version:
[g(x, y) for (x, y) in [ (x, f(x)) for x in series ] if y ] # DO NOT USE THIS
You can use memoization. It is a technique which is used in order to avoid doing the same computation twice by saving somewhere the result for each calculated value. I saw that there is already an answer that uses memoization, but I would like to propose a generic implementation, using python decorators:
def memoize(func):
def wrapper(*args):
if args in wrapper.d:
return wrapper.d[args]
ret_val = func(*args)
wrapper.d[args] = ret_val
return ret_val
wrapper.d = {}
return wrapper
@memoize
def f(x):
...
Now f
is a memoized version of itself. With this implementation you can memoize any function using the @memoize
decorator.
There have been a lot of answers regarding memoizing. The Python 3 standard library now has a lru_cache
, which is a Last Recently Used Cache. So you can:
from functools import lru_cache
@lru_cache()
def f(x):
# function body here
This way your function will only be called once. You can also specify the size of the lru_cache
, by default this is 128. The problem with the memoize decorators shown above is that the size of the lists can grow well out of hand.
Use map()
!!
comp = [x for x in map(f, l) if x]
f
is the function f(X)
, l
is the list
map()
will return the result of f(x)
for each x in the list.
Starting Python 3.8
, and the introduction of assignment expressions (PEP 572) (:=
operator), it's possible to use a local variable within a list comprehension in order to avoid calling twice the same function:
In our case, we can name the evaluation of f(x)
as a variable y
while using the result of the expression to filter the list but also as the mapped value:
[y for x in l if (y := f(x))]
Here is my solution:
filter(None, [f(x) for x in l])
How about defining:
def truths(L):
"""Return the elements of L that test true"""
return [x for x in L if x]
So that, for example
> [wife.children for wife in henry8.wives]
[[Mary1], [Elizabeth1], [Edward6], [], [], []]
> truths(wife.children for wife in henry8.wives)
[[Mary1], [Elizabeth1], [Edward6]]
ReferenceURL : https://stackoverflow.com/questions/15812779/python-list-comprehension-want-to-avoid-repeated-evaluation
'programing' 카테고리의 다른 글
Windows에서 forever 모듈을 사용하여 Node.js 애플리케이션을 중지하는 방법은 무엇입니까? (0) | 2021.01.14 |
---|---|
Git : 태그가 어떤 브랜치에 있는지 확인하는 방법은 무엇입니까? (0) | 2021.01.14 |
특정 문자 앞의 문자열의 마지막 부분을 얻는 방법은 무엇입니까? (0) | 2021.01.14 |
빛나는 R에 대한 툴팁? (0) | 2021.01.14 |
postgresql : 스키마 내의 테이블 이름을 바꾸는 방법 (0) | 2021.01.14 |