最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

if statement - How to avoid repeated checks for an optional callback in a Python for loop? - Stack Overflow

programmeradmin0浏览0评论

Let’s say I have a function download() that takes an optional on_progress callback. Inside the function, I iterate over some data and call on_progress(i) if the callback is provided. My current implementation looks like this:

def download(on_progress: Callable | None):
    data = requests.get("exampleurl")
    for i in data.iter_content():
        if on_progress:
            on_progress(i)

However, the if on_progress check inside the loop feels inefficient and repetitive. Is there a more Pythonic way to avoid this check on every iteration while still allowing the on_progress callback to be optional?

Let’s say I have a function download() that takes an optional on_progress callback. Inside the function, I iterate over some data and call on_progress(i) if the callback is provided. My current implementation looks like this:

def download(on_progress: Callable | None):
    data = requests.get("exampleurl.com")
    for i in data.iter_content():
        if on_progress:
            on_progress(i)

However, the if on_progress check inside the loop feels inefficient and repetitive. Is there a more Pythonic way to avoid this check on every iteration while still allowing the on_progress callback to be optional?

Share Improve this question edited Feb 6 at 8:22 mkrieger1 23.2k7 gold badges63 silver badges79 bronze badges asked Feb 5 at 21:57 IzaanIzaan 212 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 3

There are a lot of approaches, but one way would be to use a no-op function as the default argument:

def _NO_OP(x: str) -> None:
    pass

def download(on_progress: Callable[[str], None] | None):
    data = requests.get("exampleurl.com")
    if on_progress is None:
        on_progress = _NO_OP

    for i in data.iter_content():
        on_progress(i)

Of course, from an efficiency standpoint, probably this is slightly less efficient in the case where there isn't a on_progress callable passed by the caller, because a cheap if condition probably costs less than a function call (even if the function itself is just a no-op). Although the code is cleaner.

Frankly, efficiency is really not going to be an issue. I would frankly just keep what you have, maybe change it to if on_progress is not None: ...

You can create a wrapper function that adds progress monitoring only when needed:

from collections.abc import Callable

def normal_behaviour(*args):
    '''
    Implement here what always needs to be done
    '''

def progress_wrapper(func, on_progress):
    def with_progress(args):
        on_progress(args)
        return func(args)
    if not on_progress:
        return func
    return with_progress

def download(on_progress: Callable | None=None):
    data = requests.get('http://example.com')
    for i in data.iter_content():
        progress_wrapper(normal_behaviour, on_progress)(i)

In the above example, if you want to measure progress the normal loop behaviour is wrapped with a progress measuring function. If not, the normal function is returned and there won't be any checks within the loop.

You can eliminate the check inside the loop by using a default value for on_progress (like a no-op function). This way, the loop doesn't need to check whether on_progress is provided:

def download(on_progress: Callable | None = lambda x: None):
    data = requests.get("http://example.com")
    for i in data.iter_content():
        on_progress(i)

In this version, on_progress defaults to a no-op function (lambda x: None), so the function always calls it without needing the if check.

发布评论

评论列表(0)

  1. 暂无评论