Python: почему этот код работает вечно (бесконечный цикл?)

Я разрабатываю приложение в Google App Engine. Один из моих методов — никогда не завершать, что заставляет меня думать, что он попал в бесконечный цикл. Я смотрел на это, но не могу понять это.

Отказ от ответственности: я использую http://code.google.com/p/gaeunit< href="http://code.google.com/p/gaeunit" rel="nofollow noreferrer">текст ссылки для запуска моих тестов. Может быть, он ведет себя странно?

Это проблемная функция:

def _traverseForwards(course, c_levels):
    ''' Looks forwards in the dependency graph '''
    result = {'nodes': [], 'arcs': []}

    if c_levels == 0:
        return result

    model_arc_tails_with_course = set(_getListArcTailsWithCourse(course))
    q_arc_heads = DependencyArcHead.all()

    for model_arc_head in q_arc_heads:
        for model_arc_tail in model_arc_tails_with_course:
            if model_arc_tail.key() in model_arc_head.tails:
                result['nodes'].append(model_arc_head.sink)
                result['arcs'].append(_makeArc(course, model_arc_head.sink))

                # rec_result = _traverseForwards(model_arc_head.sink, c_levels - 1)

                # _extendResult(result, rec_result)

    return result

Первоначально я думал, что это может быть ошибка рекурсии, но я закомментировал рекурсию, и проблема не устранена. Если эта функция вызывается с c_levels = 0, она работает нормально.

Модели, на которые он ссылается:

class Course(db.Model):
    dept_code = db.StringProperty()
    number = db.IntegerProperty()
    title = db.StringProperty()
    raw_pre_reqs = db.StringProperty(multiline=True)
    original_description = db.StringProperty()

    def getPreReqs(self):
        return pickle.loads(str(self.raw_pre_reqs))

    def __repr__(self):
        return "%s %s: %s" % (self.dept_code, self.number, self.title)

class DependencyArcTail(db.Model):
    ''' A list of courses that is a pre-req for something else '''
    courses = db.ListProperty(db.Key)

    def equals(self, arcTail):
        for this_course in self.courses:
            if not (this_course in arcTail.courses):
                return False

        for other_course in arcTail.courses:
            if not (other_course in self.courses):
                return False

        return True

class DependencyArcHead(db.Model):
    ''' Maintains a course, and a list of tails with that course as their sink '''
    sink = db.ReferenceProperty()
    tails = db.ListProperty(db.Key) 

Вспомогательные функции, на которые он ссылается:

def _makeArc(source, sink):
    return {'source': source, 'sink': sink}

def _getListArcTailsWithCourse(course):
    ''' returns a LIST, not SET 
        there may be duplicate entries
    '''
    q_arc_heads = DependencyArcHead.all()
    result = []
    for arc_head in q_arc_heads:
        for key_arc_tail in arc_head.tails:
            model_arc_tail = db.get(key_arc_tail)
            if course.key() in model_arc_tail.courses:
                result.append(model_arc_tail)

    return result

Я пропустил что-то довольно очевидное здесь, или GAEUnit капризничает?

Кроме того, тест, замедляющий выполнение, содержит не более 5 моделей любого типа в хранилище данных. Я знаю, что это потенциально медленно, но мое приложение делает это только один раз, а затем кэширует его.


person Nick Heiner    schedule 06.06.2010    source источник


Ответы (1)


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

Однако кажется, что это будет действительно медленно. Вы перебираете целые таблицы, а затем выполняете дополнительные запросы к хранилищу данных в каждом вложенном цикле. Маловероятно, что такой запрос будет своевременно выполнен в GAE, если только ваши таблицы не очень-очень маленькие.


Некоторые приблизительные цифры:

Если H = # объектов в DepedencyArcHead и T = среднее # хвостов в каждом DependencyArcHead, тогда:

  • _getListArcTailsWithCourse выполняет около H*T запросов (занижено). В «худшем» случае result, возвращаемое этой функцией, будет содержать H*T элементов.
  • _traverseForwards перебирает все эти результаты H раз и таким образом выполняет еще H*(H*T) запросов.
  • Даже если H и T составляют порядка 10 секунд, вы можете выполнять тысячи запросов. Если они больше, то... (и это игнорирует любые дополнительные запросы, которые вы бы сделали, если бы раскомментировали рекурсивный вызов).

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

person David Underhill    schedule 06.06.2010