programing

"if x: return x" 문구를 피하는 피톤식 방법

kingscode 2022. 10. 29. 12:02
반응형

"if x: return x" 문구를 피하는 피톤식 방법

다른 4가지 메서드를 순차적으로 호출하여 특정 상태를 확인하고 Truthy를 반환할 때마다 즉시 반환하는 메서드가 있습니다(다음 메서드는 체크하지 않음)하는 방법이 있습니다.

def check_all_conditions():
    x = check_size()
    if x:
        return x

    x = check_color()
    if x:
        return x

    x = check_tone()
    if x:
        return x

    x = check_flavor()
    if x:
        return x
    return None

많은 수하물 코드 같은데.두 줄의 if 스테이트먼트 대신 다음과 같은 것을 하고 싶습니다.

x and return x

하지만 그건 잘못된 Python이야.심플하고 우아한 솔루션을 놓치고 있는 건가요?덧붙여서, 이 4가지 체크 방법은 비용이 많이 들 수 있기 때문에 여러 번 전화하고 싶지 않습니다.

마르틴의 훌륭한 대답 대신, 당신은 체인으로 묶을 수 있습니다.or 값,즉 truthy 값이 None값이: truthy:

def check_all_conditions():
    return check_size() or check_color() or check_tone() or check_flavor() or None

데모:

>>> x = [] or 0 or {} or -1 or None
>>> x
-1
>>> x = [] or 0 or {} or '' or None
>>> x is None
True

루프를 사용할 수 있습니다.

conditions = (check_size, check_color, check_tone, check_flavor)
for condition in conditions:
    result = condition()
    if result:
        return result

이를 통해 조건의 수를 가변으로 만들 수 있다는 추가 이점이 있습니다.

+(Python 3 버전, Python 2 버전 사용)를 사용하여 일치하는 첫 번째 값을 얻을 수 있습니다.

try:
    # Python 2
    from future_builtins import map, filter
except ImportError:
    # Python 3
    pass

conditions = (check_size, check_color, check_tone, check_flavor)
return next(filter(None, map(lambda f: f(), conditions)), None)

하지만 이게 더 읽기 쉽다면 논쟁의 여지가 있다.

또 다른 옵션은 제너레이터 식을 사용하는 것입니다.

conditions = (check_size, check_color, check_tone, check_flavor)
checks = (condition() for condition in conditions)
return next((check for check in checks if check), None)

바꾸지 마세요

다양한 답변에서 알 수 있듯이 이를 수행하는 다른 방법이 있습니다.원래 코드만큼 명확한 것은 없습니다.

timgeb와 사실상 같은 답변이지만, 더 나은 포맷을 위해 괄호를 사용할 수 있습니다.

def check_all_the_things():
    return (
        one()
        or two()
        or five()
        or three()
        or None
    )

Colly의 법칙에 따르면 다음 두 가지 문제를 구분하여 이 코드를 보다 읽기 쉽게 만들 수 있습니다.

  • 어떤 것을 확인해야 합니까?
  • 한 가지가 사실로 돌아왔나요?

두 가지 기능으로 나뉩니다.

def all_conditions():
    yield check_size()
    yield check_color()
    yield check_tone()
    yield check_flavor()

def check_all_conditions():
    for condition in all_conditions():
        if condition:
            return condition
    return None

이를 통해 다음을 피할 수 있습니다.

  • 복잡한 논리 구조
  • 아주 긴 줄
  • 반복

...선형으로 읽기 쉬운 흐름을 유지합니다.

또, 고객의 상황에 따라서는, 보다 좋은 기능명을 생각해 낼 수 있기 때문에, 한층 더 읽기 쉬워집니다.

이것은 Martijns의 첫 번째 예시의 변형입니다.또한 단락을 허용하기 위해 "콜러블 컬렉션" 스타일을 사용합니다.

루프를 할 수 .any

conditions = (check_size, check_color, check_tone, check_flavor)
return any(condition() for condition in conditions) 

:any는 부울값을 반환하기 때문에 체크의 정확한 반환값이 필요한 경우 이 솔루션은 작동하지 않습니다. any 하지 않다14,'red','sharp','spicy'됩니다.True.

쓰려고 요?if x: return x두두 한??

def check_all_conditions():
    x = check_size()
    if x: return x

    x = check_color()
    if x: return x

    x = check_tone()
    if x: return x

    x = check_flavor()
    if x: return x

    return None

이것은 당신이 가지고 있던 것보다 덜 반복적이지만 IMNSHO는 꽤 부드럽게 읽힌다.

이 목적을 위해 만들어진 빌트인에 대해 언급하지 않은 것이 매우 놀랍습니다.

def check_all_conditions():
    return any([
        check_size(),
        check_color(),
        check_tone(),
        check_flavor()
    ])

이 구현이 가장 명확하지만 첫 번째 구현이 다음과 같은 경우에도 모든 체크를 평가합니다.True.


첫 번째 실패한 체크에서 정지해야 할 경우 를 사용하여 목록을 단순한 값으로 변환하는 것을 고려하십시오.

def check_all_conditions():
    checks = [check_size, check_color, check_tone, check_flavor]
    return reduce(lambda a, f: a or f(), checks, False)

reduce(function, iterable[, initializer]): 으로 두 왼쪽에서 오른쪽으로 두 개의 인수의 함수를 누적하여 반복 가능한 항목에 적용하여 반복 가능한 값을 단일 값으로 줄입니다.왼쪽 인수 x는 누적값, 오른쪽 인수 y는 반복가능으로부터의 갱신값입니다.됩니다.

고객님의 경우:

  • lambda a, f: a or f()는, 어큐뮬레이터의 입니다.a 현재 체크 " " " " 입니다.f()- 아, 아, 아! - 아, 아, 아!Truea- 아, 아, 아! - 아, 아, 아!True,f()평가되지 않습니다.
  • checks」)이 되어 있습니다.f람다에서 항목)
  • False 않으면 않고 「이렇게 된다」가 됩니다.그렇지 않으면 체크가 이루어지지 않고 결과는 항상 다음과 같습니다.True

any ★★★★★★★★★★★★★★★★★」reduce기능 프로그래밍을 위한 기본 도구입니다.이것들, 그것도 훌륭하게 훈련해 주셨으면 합니다!

동일한 코드 구조를 원하는 경우 3진문을 사용할 수 있습니다!

def check_all_conditions():
    x = check_size()
    x = x if x else check_color()
    x = x if x else check_tone()
    x = x if x else check_flavor()

    return x if x else None

이거 보면 되게 선명하고 좋은 것 같아요.

데모:

실행 중인 스크린샷

저는 @phil-frost에 이어 @wayne-werner's가 가장 좋은 답입니다.

흥미로운 점은 함수가 많은 다른 데이터 유형을 반환한다는 사실에 대해 아무도 언급하지 않았다는 것입니다. 그러면 추가 작업을 수행하기 위해 x 유형 자체를 검사해야 합니다.

그래서 저는 @PhilFrost의 응답을 하나의 유형을 유지한다는 아이디어와 혼합하고 싶습니다.

def all_conditions(x):
    yield check_size(x)
    yield check_color(x)
    yield check_tone(x)
    yield check_flavor(x)

def assessed_x(x,func=all_conditions):
    for condition in func(x):
        if condition:
            return x
    return None

「 」라는 점에 해 주세요.x되지만, '인수'도 있습니다.all_conditions서 모든 함수가 '체크함수', '체크 함수는 '체크'로 표시됩니다.x 후 반송하다True ""False여 . . . .funcall_conditions「」를 사용할 수 .assessed_x(x) 를 통해 를 전달할 수 있습니다func.

★★★★★★★★★★★★★★★★★★★★★★★.x하나의 체크가 통과되는 즉시, 하지만 항상 같은 타입이 됩니다.

.check_True ""False수표 되다

if check_size(x):
    return x
#etc

「」라고 가정했을 .x ( 수 할 수 .check어쨌든 그걸 수정하는 건 아니겠죠

@timgeb's tim @ 。 동안 저는 을 표현하고 .None return이 필요 .or되고 첫 none-zero,, none-zero, none-empty, none-none이 반환됩니다.★★★★★★★★★★★★★★★★★★★★★★★·None됩니다.None,,,, !!

나의 ★★★★★★★★★★★★★★★★★.check_all_conditions()을 사용하다

def check_all_conditions():
    return check_size() or check_color() or check_tone() or check_flavor()

「」를 사용합니다.timeitnumber=10**7나는 많은 제안들의 실행 시간을 살펴보았다.비교하기 위해 나는 단지 그것을 사용했을 뿐이다.random.random() 또는 "return"을 반환하는 None난수를 바탕으로 합니다.전체 코드는 다음과 같습니다.

import random
import timeit

def check_size():
    if random.random() < 0.25: return "BIG"

def check_color():
    if random.random() < 0.25: return "RED"

def check_tone():
    if random.random() < 0.25: return "SOFT"

def check_flavor():
    if random.random() < 0.25: return "SWEET"

def check_all_conditions_Bernard():
    x = check_size()
    if x:
        return x

    x = check_color()
    if x:
        return x

    x = check_tone()
    if x:
        return x

    x = check_flavor()
    if x:
        return x
    return None

def check_all_Martijn_Pieters():
    conditions = (check_size, check_color, check_tone, check_flavor)
    for condition in conditions:
        result = condition()
        if result:
            return result

def check_all_conditions_timgeb():
    return check_size() or check_color() or check_tone() or check_flavor() or None

def check_all_conditions_Reza():
    return check_size() or check_color() or check_tone() or check_flavor()

def check_all_conditions_Phinet():
    x = check_size()
    x = x if x else check_color()
    x = x if x else check_tone()
    x = x if x else check_flavor()

    return x if x else None

def all_conditions():
    yield check_size()
    yield check_color()
    yield check_tone()
    yield check_flavor()

def check_all_conditions_Phil_Frost():
    for condition in all_conditions():
        if condition:
            return condition

def main():
    num = 10000000
    random.seed(20)
    print("Bernard:", timeit.timeit('check_all_conditions_Bernard()', 'from __main__ import check_all_conditions_Bernard', number=num))
    random.seed(20)
    print("Martijn Pieters:", timeit.timeit('check_all_Martijn_Pieters()', 'from __main__ import check_all_Martijn_Pieters', number=num))
    random.seed(20)
    print("timgeb:", timeit.timeit('check_all_conditions_timgeb()', 'from __main__ import check_all_conditions_timgeb', number=num))
    random.seed(20)
    print("Reza:", timeit.timeit('check_all_conditions_Reza()', 'from __main__ import check_all_conditions_Reza', number=num))
    random.seed(20)
    print("Phinet:", timeit.timeit('check_all_conditions_Phinet()', 'from __main__ import check_all_conditions_Phinet', number=num))
    random.seed(20)
    print("Phil Frost:", timeit.timeit('check_all_conditions_Phil_Frost()', 'from __main__ import check_all_conditions_Phil_Frost', number=num))

if __name__ == '__main__':
    main()

결과는 다음과 같습니다.

Bernard: 7.398444877040768
Martijn Pieters: 8.506569201346597
timgeb: 7.244275416364456
Reza: 6.982133448743038
Phinet: 7.925932800076634
Phil Frost: 11.924794811353031

위의 Martijns의 첫 번째 예에서는 루프 내의 if를 회피할 수 있습니다.

Status = None
for c in [check_size, check_color, check_tone, check_flavor]:
  Status = Status or c();
return Status

이 방법은 조금 틀에서 벗어나지만, 최종 결과는 간단하고 읽기 쉬우며 보기 좋다고 생각합니다.

은 '다'입니다.raise함수 중 하나가 truthy로 평가되고 결과가 반환되는 경우 예외입니다.수 .

def check_conditions():
    try:
        assertFalsey(
            check_size,
            check_color,
            check_tone,
            check_flavor)
    except TruthyException as e:
        return e.trigger
    else:
        return None

필요한 것은,assertFalsey 중 되었을 때 : "truthy"는 과 같습니다.

def assertFalsey(*funcs):
    for f in funcs:
        o = f()
        if o:
            raise TruthyException(o)

위의 내용은 평가할 함수에 대한 인수를 제공하도록 수정될 수 있다.

'우리에게 필요한 것'이 있을 예요.TruthyException그 자체입니다.는 " " 를 제공합니다.object예외를 경우:

class TruthyException(Exception):
    def __init__(self, obj, *args):
        super().__init__(*args)
        self.trigger = obj

물론 원래의 기능을 보다 일반적인 것으로 변경할 수 있습니다.

def get_truthy_condition(*conditions):
    try:
        assertFalsey(*conditions)
    except TruthyException as e:
        return e.trigger
    else:
        return None

result = get_truthy_condition(check_size, check_color, check_tone, check_flavor)

하면 '어느 쪽인가', '어느 쪽인가', '어느 쪽인가', '어느 쪽인가', '인가'를 하고 있기 때문에 조금 수 있습니다.if스테이트먼트 및 예외 처리. 이 되지 않기 수 것으로 한 에 대한 히트는 할 필요가 .True수천 배의 가치를 지닌다.

피토닉 방식은 reduce(누군가 이미 말한 대로) 또는 retertools(아래 그림처럼) 중 하나를 사용하는 이지만, 단순히 오퍼레이터의 단락을 사용하는 것만으로 더 선명한 코드가 생성되는 것 같습니다.

from itertools import imap, dropwhile

def check_all_conditions():
    conditions = (check_size,\
        check_color,\
        check_tone,\
        check_flavor)
    results_gen = dropwhile(lambda x:not x, imap(lambda check:check(), conditions))
    try:
        return results_gen.next()
    except StopIteration:
        return None

Python 3.8이 필요한 경우 "할당식"의 새로운 기능을 사용하여 if-else 체인을 다소 덜 반복적으로 만들 수 있습니다.

def check_all_conditions():
    if (x := check_size()): return x
    if (x := check_color()): return x
    if (x := check_tone()): return x
    if (x := check_flavor()): return x
    
    return None

「」를 사용합니다.max:

def check_all_conditions():
    return max(check_size(), check_color(), check_tone(), check_flavor()) or None

과거에 딕트를 사용한 스위치/케이스 스테이트먼트의 흥미로운 실장을 몇 가지 본 적이 있기 때문에 이 답변에 도달했습니다.미친 짓이다using_complete_sentences_for_function_names, (그래서)check_all_conditions.status(1) (1을 참조해 주세요.

def status(k = 'a', s = {'a':'b','b':'c','c':'d','d':None}) :
  select = lambda next, test : test if test else next
  d = {'a': lambda : select(s['a'], check_size()  ),
       'b': lambda : select(s['b'], check_color() ),
       'c': lambda : select(s['c'], check_tone()  ),
       'd': lambda : select(s['d'], check_flavor())}
  while k in d : k = d[k]()
  return k

하면 각 Select를 .check_FUNCTION 즉 피한다, 피하다check_FUNCTION() if check_FUNCTION() else next이치노이치노력하다 loopdict 될 시킵니다.

로는 실행 할 수 , 일부 .k ★★★★★★★★★★★★★★★★★」s ★★k='c',s={'c':'b','b':None}는 테스트 수를 줄이고 원래 처리 순서를 반대로 합니다.

timeitfellows는 스택에 레이어 한두 개를 추가하는 비용과 dict lookup에 드는 비용에 대해 불평할 수 있지만, 당신은 코드의 아름다움에 더 관심이 있는 것 같습니다.

또는 다음과 같은 간단한 구현이 있습니다.

def status(k=check_size) :
  select = lambda next, test : test if test else next
  d = {check_size  : lambda : select(check_color,  check_size()  ),
       check_color : lambda : select(check_tone,   check_color() ),
       check_tone  : lambda : select(check_flavor, check_tone()  ),
       check_flavor: lambda : select(None,         check_flavor())}
  while k in d : k = d[k]()
  return k
  1. pep8이 아니라 문장 대신 하나의 간결한 서술어를 사용한다는 의미입니다.OP가 코딩 규약을 따르고 있거나 기존 코드 베이스 중 하나를 사용하거나 코드 베이스의 간결한 용어를 고려하지 않을 수 있습니다.

언급URL : https://stackoverflow.com/questions/36117583/pythonic-way-to-avoid-if-x-return-x-statements

반응형