programing

다른 것이 가장 많이 이루어질 때 if-ellif-ellif-ellif-ellip 진술을 하는 가장 효율적인 방법은?

newsource 2023. 9. 12. 20:03

다른 것이 가장 많이 이루어질 때 if-ellif-ellif-ellif-ellip 진술을 하는 가장 효율적인 방법은?

if-ellif-ellif-ellif-ellip-ellip-ellip-ellip-ellip-ellip-ellip-ellip-ellip-ellip-ellip-else에서 99%

if something == 'this':
    doThis()
elif something == 'that':
    doThat()
elif something == 'there':
    doThere()
else:
    doThisMostOfTheTime()

이 구조물은 많이 완성되지만, 다른 구조물에 부딪히기 전에 모든 조건에 걸쳐 진행되기 때문에 피토닉은 말할 것도 없고 효율적이지도 않다는 느낌이 듭니다.반면에 그러한 조건 중에 충족되는 것이 있는지 알아야 하기 때문에 어쨌든 테스트를 해봐야 합니다.

이것이 더 효율적으로 이루어질 수 있는지, 혹은 이것이 단지 가능한 최선의 방법인지 아는 사람이 있습니까?

암호는...

options.get(something, doThisMostOfTheTime)()

할 것 더 좀 더 빨라야 할 것 같지만, 사실은 더 느립니다.if...elif...else구성합니다. 함수를 호출해야 하기 때문입니다. 함수는 엄격한 루프에서 상당한 성능 오버헤드가 될 수 있습니다.

이러한 예를 생각해 보십시오.

1.py

something = 'something'

for i in xrange(1000000):
    if something == 'this':
        the_thing = 1
    elif something == 'that':
        the_thing = 2
    elif something == 'there':
        the_thing = 3
    else:
        the_thing = 4

2.py

something = 'something'
options = {'this': 1, 'that': 2, 'there': 3}

for i in xrange(1000000):
    the_thing = options.get(something, 4)

3.py

something = 'something'
options = {'this': 1, 'that': 2, 'there': 3}

for i in xrange(1000000):
    if something in options:
        the_thing = options[something]
    else:
        the_thing = 4

4.py

from collections import defaultdict

something = 'something'
options = defaultdict(lambda: 4, {'this': 1, 'that': 2, 'there': 3})

for i in xrange(1000000):
    the_thing = options[something]

...사용하는 CPU의 양을 기록합니다...

1.py: 160ms
2.py: 170ms
3.py: 110ms
4.py: 100ms

...에서 사용자 시간을 사용합니다.

옵션 #4에는 개별 키가 누락될 때마다 새 항목을 추가해야 하는 추가 메모리 오버헤드가 있습니다. 따라서 개별 키가 누락될 경우가 무한히 많을 것으로 예상한다면 옵션 #3을 사용하는 것이 좋습니다. 이는 원래 구성에서 여전히 크게 개선된 것입니다.

사전을 만듭니다.

options = {'this': doThis,'that' :doThat, 'there':doThere}

이제 다음 항목만 사용합니다.

options.get(something, doThisMostOfTheTime)()

한다면something서을수다다수에는 없습니다.options그 때 지시합니다.dict.get을합니다를 합니다.doThisMostOfTheTime

몇 가지 타이밍 비교:

스크립트:

from random import shuffle
def doThis():pass
def doThat():pass
def doThere():pass
def doSomethingElse():pass
options = {'this':doThis, 'that':doThat, 'there':doThere}
lis = range(10**4) + options.keys()*100
shuffle(lis)

def get():
    for x in lis:
        options.get(x, doSomethingElse)()

def key_in_dic():
    for x in lis:
        if x in options:
            options[x]()
        else:
            doSomethingElse()

def if_else():
    for x in lis:
        if x == 'this':
            doThis()
        elif x == 'that':
            doThat()
        elif x == 'there':
            doThere()
        else:
            doSomethingElse()

결과:

>>> from so import *
>>> %timeit get()
100 loops, best of 3: 5.06 ms per loop
>>> %timeit key_in_dic()
100 loops, best of 3: 3.55 ms per loop
>>> %timeit if_else()
100 loops, best of 3: 6.42 ms per loop

위해서10**5존재하지 않는 키 및 100개의 유효한 키:

>>> %timeit get()
10 loops, best of 3: 84.4 ms per loop
>>> %timeit key_in_dic()
10 loops, best of 3: 50.4 ms per loop
>>> %timeit if_else()
10 loops, best of 3: 104 ms per loop

는 에서 를 하려면 하려면 를 을 사용합니다.key in options가장 효율적인 방법은 다음과 같습니다.

if key in options:
   options[key]()
else:
   doSomethingElse()

피피를 사용할 수 있습니까?

원래의 코드를 유지하되 파이피로 실행하면 50배의 속도를 낼 수 있습니다.

싸이톤:

matt$ python
Python 2.6.8 (unknown, Nov 26 2012, 10:25:03)
[GCC 4.2.1 Compatible Apple Clang 3.0 (tags/Apple/clang-211.12)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> from timeit import timeit
>>> timeit("""
... if something == 'this': pass
... elif something == 'that': pass
... elif something == 'there': pass
... else: pass
... """, "something='foo'", number=10000000)
1.728302001953125

Pypypy:

matt$ pypy
Python 2.7.3 (daf4a1b651e0, Dec 07 2012, 23:00:16)
[PyPy 2.0.0-beta1 with GCC 4.2.1] on darwin
Type "help", "copyright", "credits" or "license" for more information.
And now for something completely different: ``a 10th of forever is 1h45''
>>>>
>>>> from timeit import timeit
>>>> timeit("""
.... if something == 'this': pass
.... elif something == 'that': pass
.... elif something == 'there': pass
.... else: pass
.... """, "something='foo'", number=10000000)
0.03306388854980469

여기 동적 조건을 가진 if를 사전으로 변환한 예가 있습니다.

selector = {lambda d: datetime(2014, 12, 31) >= d : 'before2015',
            lambda d: datetime(2015, 1, 1) <= d < datetime(2016, 1, 1): 'year2015',
            lambda d: datetime(2016, 1, 1) <= d < datetime(2016, 12, 31): 'year2016'}

def select_by_date(date, selector=selector):
    selected = [selector[x] for x in selector if x(date)] or ['after2016']
    return selected[0]

이것은 방법이지만, 파이썬을 유창하게 하지 못하는 사람들에게 읽기 쉬운 방법이 아니기 때문에 가장 파이썬적인 방법은 아닐 수도 있습니다.

python 3.10에 소개된 match statement로 시도했습니다.

5.py

something = 'something'
for i in range(10000000):
    match something:
        case "this":
            the_thing = 1
        case "that":
            the_thing = 2
        case "there":
            the_thing = 3
        case _:
            the_thing = 4

은 3:3.10.0 과 는 으로 과 는 으로
y : 1.4s
y : 0.9s
y : 0.7s
y : 0.7s
y : 1.0s
저는 1.py 과 비슷한 것을 얻을 줄 알았는데 꽤 빠르네요.

사람들은 다음에 대해 경고합니다.exec보안상의 이유로 하지만 이는 이상적인 경우입니다.
그것은 쉬운 상태 기계입니다.

Codes = {}
Codes [0] = compile('blah blah 0; nextcode = 1')
Codes [1] = compile('blah blah 1; nextcode = 2')
Codes [2] = compile('blah blah 2; nextcode = 0')

nextcode = 0
While True:
    exec(Codes[nextcode])

사전 및 람다 함수를 사용하여 스위치 케이스 유형으로 if-elif-lif-limit을 모방할 수 있습니다.

예를 들어,

x = 5
y = 5
operator = 'add'

def operation(operator, x, y): 
 return {
   'add': lambda: x+y,
   'sub': lambda: x-y,
   'mul': lambda: x*y,
   'div': lambda: x/y
 }.get(operator, lambda: None)()

result = operation(operator, x, y)
print(result)

최근 저는 기능 실행 시간을 2.5시간에서 ~2분으로 단축하는 "nested if other"의 접근 방법을 발견했습니다.Baam! 시작합니다.

이전 코드
빈 = 람다 x:"x==0이면 알 수 없음(x>75이면 "높음"(x>50이면 "중간", x<=75이면 "낮음")(x>25이면 "중간", x<=50이면 "낮음")

col.apply(빈) 시간 ~2.5시간

코드 최적화

Define Dictionary alternative to nest if else
 def dict_function(*args):
'Pass in a list of tuples, which will be key/value pairs'
ret = {}
for k,v in args:
    for i in k:
        ret[i] = v
return ret
Dict = dict_function(([0],"Unknown"),(range(1,25),"Low"),(range(25,50),"Medium_Low"),(range(50,75),"Medium"),(range(75,100),"High"))

col.apply(lambda x:Dict[x])

dict_function은 주어진 범위에 대해 여러 key_value 쌍을 만듭니다.시간~2분.

성능에 관해서는 아니지만 최근에도 같은 문제가 있었지만 함수를 만들어 수동으로 딕트에 추가하는 "API"가 싫습니다.저는 다음과 같은 API를 원합니다.functools.singledispatch, 유형이 아닌 값을 기준으로 디스패치합니다.그래서...

def value_dispatch(func):
    """value-dispatch function decorator.
    Transforms a function into a function, that dispatches its calls based on the
    value of the first argument.
    """
    funcname = getattr(func, '__name__')
    registry = {}

    def dispatch(arg):
        """return the function that matches the argument"""
        return registry.get(arg, func)

    def register(arg):
        def wrapper(func):
            """register a function"""
            registry[arg] = func
            return func
        return wrapper

    def wrapper(*args, **kwargs):
        if not args:
            raise ValueError(f'{funcname} requires at least 1 positional argument')
        return dispatch(args[0])(*args, **kwargs)

    wrapper.register = register
    wrapper.dispatch = dispatch
    wrapper.registry = registry
    return wrapper

다음과 같이 사용:

@value_dispatch
def handle_something():
    print("default")

@handle_something.register(1)
def handle_one():
    print("one")

handle_something(1)
handle_something(2)

추신: 참고용으로 Gitlab에서 스니펫을 만들었습니다.

언급URL : https://stackoverflow.com/questions/17166074/most-efficient-way-of-making-an-if-elif-elif-else-statement-when-the-else-is-don