【问题标题】:using function attributes to store results for lazy (potential) processing使用函数属性存储延迟(潜在)处理的结果
【发布时间】:2010-10-07 11:34:22
【问题描述】:

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

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,有的话我会的)

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

【问题讨论】:

    标签: python function-attributes


    【解决方案1】:

    只需让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!'
    

    【讨论】:

    • 这是我想到的一件事,但是专门检测一个空的生成器是很乏味的,以至于想要包裹在像 peek 这样的函数中。我想知道它是否为空,这将涉及一个标志(啊!)或每次我想检查时重新发明peek
    • 如果我只想在发生碰撞时采取行动,它会起作用。 +1
    • 您只需使用else 子句检测空案例:我将其添加到我的答案中,以供参考。在 Python 中永远不需要像 peek 这样的函数:使用它可能表明有问题。
    • 好眼光。我总是在这里问最愚蠢的问题。我知道。谢谢你说服我。
    • 实际上,对于被接受的心理感到抱歉,但是设置一个循环来抛出一个中断来躲避循环上的 else 子句,这不应该是一个循环来做我想做的事情这样做实在是太乏味了。它混淆了代码的意图。
    【解决方案2】:

    这与我们在"pythonic way to rewrite an assignment in an if statement" 中讨论的非常相似,但是这个版本每次调用只处理一个位置参数或一个关键字参数,因此人们总是可以检索缓存的实际值(不仅仅是 Pythonian 中的 True 值布尔意义与否)。

    我从来没有真正关心过你的提议,即接受多个关键字并根据你是否希望所有结果通过 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() 中的代码,并且也可以在其他地方使用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-19
      • 1970-01-01
      • 1970-01-01
      • 2019-06-14
      相关资源
      最近更新 更多