2

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

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

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

4

2 回答 2

4

只需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!'
于 2010-10-07T11:52:45.313 回答
0

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

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

于 2010-10-07T19:54:05.147 回答