Конкатенация строк Python 2.5 (с использованием cStringIO) - PullRequest
3 голосов
/ 22 февраля 2012

Я использую Google Appengine с Python 2.5, и у меня есть функция, которая вызывает узкое место. Я передаю ему список из 200 экземпляров Model, полученных из хранилища данных, и он возвращает его в формате json, который я затем передаю клиенту.

Изначально я использовал + = для объединения всех значений вместе, но для ответа сервера JSON потребовалось около 30 секунд. Я выполнил несколько проверок и код, прежде чем эта функция запустилась менее чем за секунду Это последний оператор перед тем, как сервер ответит JSON, и время, необходимое для достижения клиента, составляет в среднем 1 секунду (в моей локальной сети). Эта функция выполняется в среднем 30 секунд.

Я прочитал эту статью и попытался использовать метод cStringIO (я также использовал метод соединения со списком, но он занимал столько же времени, а cStringIO использует меньше памяти, поэтому я остался с этим) , Однако это заняло примерно то же время, что и + = конкатенация (иногда дольше). Может кто-нибудь увидеть какие-либо проблемы с моим кодом, которые могут сделать его медленнее?

РЕДАКТИРОВАТЬ: Босс говорит, что это должно быть сделано таким образом. Никаких библиотек json (возьмите его с собой).

РЕДАКТИРОВАТЬ 2: Фамилия, модель:

class LastName(db.Model): 
    entry = db.ReferenceProperty(AlumniEntry, collection_name='last_names') 
    last_name = db.StringProperty(indexed=False) 
    last_name_search = db.StringProperty()

AlumniEntry - это Model, который запрашивается. Я передаю список, который я получаю от ds, к get_json_from_alumnus() (параметр alumnus).

def get_json_from_alumnus(alumnus, search, total=0):
    if len(alumnus) > 0:
        from cStringIO import StringIO
        concat_file = StringIO()

        concat_file.write('{ "alumnus": [')
        i = 0
        for alumni in alumnus:
            if alumni.author:
                author = alumni.author.nickname()
            else:
                author = 'Anonymous'

            concat_file.write('{ ')
            concat_file.write('"author": "')
            concat_file.write(author)
            concat_file.write('", ')
            concat_file.write('"title": "')
            concat_file.write(alumni.title)
            concat_file.write('", ')
            concat_file.write('"first_name": "')
            concat_file.write(alumni.first_name)
            concat_file.write('", ')

            concat_file.write(' "last_names": [')
            j = 0
            for lname in alumni.last_names:
                concat_file.write('{ "last_name": "')
                concat_file.write('lname.last_name')
                concat_file.write('" }')
                if not j == alumni.last_names.count() - 1:
                    #last_names += ','
                    concat_file.write(',')
                j +=1
            concat_file.write('], ')

            concat_file.write(' "addresses": [')
            j = 0
            for address in alumni.addresses:
                if address.street == '' and address.city == '' and address.state == '' and address.zip_code == '':
                    break

                concat_file.write('{ "address":{ "street" : "')
                concat_file.write(address.street)
                concat_file.write('", ')
                concat_file.write('"city" : "')
                concat_file.write(address.city)
                concat_file.write('", ')
                concat_file.write('"state" : "')
                concat_file.write(address.state)
                concat_file.write('", ')
                concat_file.write('"zip_code" : "')
                concat_file.write(address.zip_code)
                concat_file.write('" } }')

                if not j == alumni.addresses.count() - 1:
                    concat_file.write(',')
                j += 1
            concat_file.write('], ')

            concat_file.write(' "numbers": [')
            j = 0
            for phone_number in alumni.phone_numbers:
                concat_file.write('{ "phone_number": "')
                concat_file.write(phone_number.number)
                concat_file.write('" }')
                if not j == alumni.phone_numbers.count() - 1:
                    concat_file.write(',')
                j += 1
            concat_file.write('], ')

            concat_file.write(' "emails": [')
            j = 0
            for email in alumni.emails:
                concat_file.write('{ "email": "')
                concat_file.write(email.email)
                concat_file.write('" }')
                if not j == alumni.emails.count() - 1:
                    concat_file.write(',')
                j += 1
            concat_file.write('], ')

            concat_file.write('"grad_year": "')
            concat_file.write(alumni.grad_year)
            concat_file.write('", ')
            concat_file.write('"elementary": "')
            concat_file.write(alumni.elementary)
            concat_file.write('", ')
            concat_file.write('"entered": "')
            concat_file.write(str(alumni.entered.strftime('%B %d %Y')))
            concat_file.write('", ')
            concat_file.write('"key": "')
            concat_file.write(str(alumni.key()))
            concat_file.write('" ')
            concat_file.write('}')

            if not  i == len(alumnus) - 1:
                concat_file.write(',')
            i += 1
        concat_file.write('], "total" : "')
        concat_file.write(str(total))
        concat_file.write('" }')
    else:
        concat_file.write('{ "alumnus": "No Alumni Entered Yet!" }' if not search else '{ "alumnus": "No Matches!" }')

    return concat_file.getvalue()

Ответы [ 3 ]

7 голосов
/ 22 февраля 2012

Я подозреваю эту строку в вашем коде:

if not j == alumni.last_names.count() - 1:

(и несколько похожих строк).

Вы не опубликовали свою модель, но мне она выглядела как alumni.last_namesможет быть запрос?Выполнение запроса для каждой сущности было бы очень плохой идеей и вполне могло бы повлиять на ваши затраты.Конкатенация нескольких тысяч строк с использованием cStringIO не займет около 30 секунд.

Легко узнать, выполняете ли вы слишком много запросов с использованием Appstats: http://code.google.com/appengine/docs/python/tools/appstats.html (вы можете даже попробовать этов сервере приложений dev).

PS.Единственное число - фактически выпускник, и множественное число - выпускники.: -)

5 голосов
/ 22 февраля 2012

str.join() и интерполяция строк обычно дают намного лучшую производительность, чем повторная конкатенация.Дайте этим попробовать, и пусть силы, которые будут милосердны к вашей душе.

2 голосов
/ 22 февраля 2012

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

Для получения дополнительной информации, пожалуйста, обратитесь к этому связанному вопросу .

Изменить: Если использование стороннего модуля json не может быть и речи, то я бы попытался сократить количество вызовов write, используя строку форматирования как можно дольше.

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

...