использование атрибутов функции для хранения результатов для отложенной (потенциальной) обработки - PullRequest
2 голосов
/ 07 октября 2010

Я занимаюсь определением столкновений и очень хотел бы использовать одну и ту же функцию в двух разных контекстах.В одном контексте я хотел бы, чтобы это было что-то вроде

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, если он у меня будет, я буду)

Итак.Если бы вы были маньяком-убийцей из пословиц, который знает, где я живу, я бы ожидал от вас визита, если бы написал вышеприведенный код?Если да, как бы вы подошли к этой ситуации?

Ответы [ 2 ]

4 голосов
/ 07 октября 2010

Просто заставьте 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!'
0 голосов
/ 07 октября 2010

Это очень похоже на то, что мы обсуждали в "питонском способе переписать присваивание в операторе if" , но эта версия обрабатывает только один позиционный или один аргумент ключевого слова на вызов, поэтому всегда можно получить кешированное действительное значение (не только значение True в булевом смысле 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() так или иначе, а также доступно для использования в другом месте.

...