使用函数属性来存储延迟(潜在)处理的结果

发布于 2024-09-26 07:55:03 字数 1333 浏览 3 评论 0原文

我正在做一些碰撞检测,我非常想在两个不同的上下文中使用相同的函数。在一种情况下,我希望它是这样的

def detect_collisions(item, others):
    return any(collides(item, other) for other in others)

,而在另一种情况下,我希望它是

def get_collisions(item, others):
    return [other for other in others if collides(item, other)]

我真的很讨厌在这里编写两个函数的想法。仅仅保持他们的名字简单是一个问题,而使碰撞检测的接口复杂化则是另一个问题。所以我在想:

def peek(gen):
    try:
        first = next(gen)
    except StopIteration:
        return False
    else:
        return it.chain((first,), gen)

def get_collisions(item, others):
    get_collisions.all = peek(other for other in others if collides(item, other))
    return get_collisions.all

现在,当我只想进行检查时,我可以说:

if get_collisions(item, others):
    # aw snap

或者

if not get_collisions(item, others):
    # w00t

在我真正想检查它们的其他上下文中,我可以这样做:

if get_collisions(item, others):
    for collision in get_collisions.all:
        # fix it

并且在这两种情况下,我不再进行任何处理比我需要的。

我认识到这比前两个函数的代码更多,但它还有以下优点:

  1. 将碰撞检测接口保留为树,节点位于顶层而不是中层。这看起来更简单。

  2. 使用方便的查看功能来连接自己。如果我再使用它一次,那么我实际上编写的代码就会更少。 (回应 YAGNI,如果我有的话,我会的)

所以。如果你是众所周知的杀人狂,知道我住在哪里,那么如果我写了上面的代码,我会期待你来访吗?如果是这样,您会如何处理这种情况?

I'm doing some collision detection and I would very much like to use the same function in two different contexts. In one context, I would like for it to be something like

def detect_collisions(item, others):
    return any(collides(item, other) for other in others)

and in another, I would like it to be

def get_collisions(item, others):
    return [other for other in others if collides(item, other)]

I really hate the idea of writing two functions here. Just keeping their names straight is one turnoff and complicating the interface to the collision detecting is another. so I was thinking:

def peek(gen):
    try:
        first = next(gen)
    except StopIteration:
        return False
    else:
        return it.chain((first,), gen)

def get_collisions(item, others):
    get_collisions.all = peek(other for other in others if collides(item, other))
    return get_collisions.all

Now when I just want to do a check, I can say:

if get_collisions(item, others):
    # aw snap

or

if not get_collisions(item, others):
    # w00t

and in the other context where I actually want to examine them, I can do:

if get_collisions(item, others):
    for collision in get_collisions.all:
        # fix it

and in both cases, I don't do any more processing than I need to.

I recognize that this is more code than the first two functions but it also has the advantage of:

  1. Keeping my interface to the collision detection as a tree with the node at the top level instead of a mid level. This seems simpler.

  2. Hooking myself up with a handy peek function. If I use it one other time, then I'm actually writing less code. (In response to YAGNI, if I have it, I will)

So. If you were the proverbial homicidal maniac that knows where I live, would I be expecting a visit from you if I wrote the above code? If so, how would you approach this situation?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

∞梦里开花 2024-10-03 07:55:03

只需让 get_collisions 返回一个生成器:

def get_collisions(item, others):
    return (other for other in others if collides(item, other))

然后,如果您想进行检查:

for collision in get_collisions(item, others):
    print 'Collision!'
    break
else:
    print 'No collisions!'

Just make get_collisions return a generator:

def get_collisions(item, others):
    return (other for other in others if collides(item, other))

Then, if you want to do a check:

for collision in get_collisions(item, others):
    print 'Collision!'
    break
else:
    print 'No collisions!'
月竹挽风 2024-10-03 07:55:03

这与我们在

我从来没有真正关心过您接受多个关键字并具有不同命名函数的建议,具体取决于您是否希望通过 any()all() 处理所有结果 - 但是喜欢使用关键字参数的想法,它允许单个函数同时在两个或多个位置使用。这就是我最终得到的结果:

# can be called with a single unnamed value or a single named value
def cache(*args, **kwargs):
    if len(args)+len(kwargs) == 1:
        if args:
            name, value = 'value', args[0]  # default attr name 'value'
        else:
            name, value = kwargs.items()[0]
    else:
        raise NotImplementedError('"cache" calls require either a single value argument '
                                  'or a name=value argument identifying an attribute.')
    setattr(cache, name, value)
    return value

# add a sub-function to clear the cache attributes (optional and a little weird)
cache.clear = lambda: cache.func_dict.clear()

# you could then use it either of these two ways
if get_collisions(item, others):
    # no cached value

if cache(collisions=get_collisions(item, others)):
    for collision in cache.collisions:
        # fix them

通过将所有丑陋的细节放在一个单独的函数中,它不会以某种方式影响 get_collisions() 中的代码,并且也可以在其他地方使用。

This is very similar to what we were discussing in "pythonic way to rewrite an assignment in an if statement", but this version only handles one positional or one keyword argument per call so one can always retrieve the actual valued cached (not just whether is a True value in the Pythonian boolean sense or not).

I never really cared for your proposal to accept multiple keywords and have differently named functions depending on whether you wanted all the results put through any() or all() -- but liked the idea of using keyword arguments which would allow a single function to be used in two or more spots simultaneously. Here's what I ended up with:

# can be called with a single unnamed value or a single named value
def cache(*args, **kwargs):
    if len(args)+len(kwargs) == 1:
        if args:
            name, value = 'value', args[0]  # default attr name 'value'
        else:
            name, value = kwargs.items()[0]
    else:
        raise NotImplementedError('"cache" calls require either a single value argument '
                                  'or a name=value argument identifying an attribute.')
    setattr(cache, name, value)
    return value

# add a sub-function to clear the cache attributes (optional and a little weird)
cache.clear = lambda: cache.func_dict.clear()

# you could then use it either of these two ways
if get_collisions(item, others):
    # no cached value

if cache(collisions=get_collisions(item, others)):
    for collision in cache.collisions:
        # fix them

By putting all the ugly details in a separate function, it doesn't affect the code in get_collisions() one way or the other, and is also available for use elsewhere.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文