Протобуф-сообщения

С помощью параметра конфигурации use_proto_plus вы можете указать, хотите ли вы, чтобы библиотека возвращала сообщения proto-plus или сообщения protobuf . Подробную информацию о том, как установить этот параметр, смотрите в документации по конфигурации .

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

Сообщения Proto-plus и protobuf

Конвейер генератора кода интегрирует proto-plus как способ улучшить эргономику интерфейса сообщений protobuf, заставляя их вести себя больше как собственные объекты Python. Однако это означает, что использование proto-plus приводит к снижению производительности.

Прото-плюс производительность

Одним из основных преимуществ proto-plus является то, что он преобразует сообщения protobuf и известные типы в собственные типы Python с помощью процесса, называемого маршалингом типов .

Маршалинг происходит при доступе к полю в экземпляре сообщения proto-plus, в частности, когда поле читается или устанавливается, например, в определении protobuf:

syntax = "proto3";

message Dog {
  string name = 1;
}

Когда это определение преобразуется в класс proto-plus, оно будет выглядеть примерно так:

import proto

class Dog(proto.Message):
    name = proto.Field(proto.STRING, number=1)

Затем вы можете инициализировать класс Dog и получить доступ к его полю name , как к любому другому объекту Python:

dog = Dog()
dog.name = "Scruffy"
print(dog.name)

При чтении и настройке поля name значение преобразуется из собственного типа str Python в string тип, чтобы значение было совместимо со средой выполнения protobuf.

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

Варианты использования сообщений proto-plus и protobuf

Варианты использования сообщений Proto-plus
Proto-plus предлагает ряд эргономических улучшений по сравнению с сообщениями protobuf, поэтому они идеально подходят для написания поддерживаемого и читаемого кода. Поскольку они предоставляют собственные объекты Python, их проще использовать и понимать.
Варианты использования сообщений Protobuf
Используйте protobufs для случаев использования, чувствительных к производительности, особенно в приложениях, которым необходимо быстро обрабатывать большие отчеты или которые создают запросы на изменение с большим количеством операций, например с помощью BatchJobService или OfflineUserDataJobService .

Динамическое изменение типов сообщений

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

from google.ads.googleads import util

# Proto-plus message type
dog = Dog()

# Protobuf message type
dog = util.convert_proto_plus_to_protobuf(dog)

# Back to proto-plus message type
dog = util.convert_protobuf_to_proto_plus(dog)

Различия в интерфейсе сообщений Protobuf

Интерфейс proto-plus подробно документирован , но здесь мы выделим некоторые ключевые различия, влияющие на распространенные случаи использования клиентской библиотеки Google Рекламы.

Сериализация байтов

Прото-плюс сообщения
serialized = type(campaign).serialize(campaign)
deserialized = type(campaign).deserialize(serialized)
Протобуф-сообщения
serialized = campaign.SerializeToString()
deserialized = campaign.FromString(serialized)

Сериализация JSON

Прото-плюс сообщения
serialized = type(campaign).to_json(campaign)
deserialized = type(campaign).from_json(serialized)
Протобуф-сообщения
from google.protobuf.json_format import MessageToJson, Parse

serialized = MessageToJson(campaign)
deserialized = Parse(serialized, campaign)

Маски полей

Вспомогательный метод маски поля, предоставляемый api-core, предназначен для использования экземпляров сообщений protobuf. Поэтому при использовании сообщений proto-plus преобразуйте их в сообщения protobuf, чтобы использовать помощник:

Прото-плюс сообщения
from google.api_core.protobuf_helpers import field_mask

campaign = client.get_type("Campaign")
protobuf_campaign = util.convert_proto_plus_to_protobuf(campaign)
mask = field_mask(None, protobuf_campaign)
Протобуф-сообщения
from google.api_core.protobuf_helpers import field_mask

campaign = client.get_type("Campaign")
mask = field_mask(None, campaign)

Перечисления

Перечисления, предоставляемые сообщениями proto-plus, являются экземплярами собственного типа enum Python и, следовательно, наследуют ряд удобных методов.

Получение типа перечисления

При использовании метода GoogleAdsClient.get_type для получения перечислений возвращаемые сообщения немного различаются в зависимости от того, используете ли вы сообщения proto-plus или protobuf. Например:

Прото-плюс сообщения
val = client.get_type("CampaignStatusEnum").CampaignStatus.PAUSED
Протобуф-сообщения
val = client.get_type("CampaignStatusEnum").PAUSED

Чтобы упростить получение перечислений, в экземплярах GoogleAdsClient есть удобный атрибут, который имеет единообразный интерфейс независимо от того, какой тип сообщения вы используете:

val = client.enums.CampaignStatusEnum.PAUSED

Получение значения перечисления

Иногда полезно знать значение или идентификатор поля данного перечисления, например, PAUSED в CampaignStatusEnum соответствует 3 :

Прото-плюс сообщения
campaign = client.get_type("Campaign")
campaign.status = client.enums.CampaignStatusEnum.PAUSED
# To read the value of campaign status
print(campaign.status.value)
Протобуф-сообщения
campaign = client.get_type("Campaign")
status_enum = client.enums.CampaignStatusEnum
campaign.status = status_enum.PAUSED
# To read the value of campaign status
print(status_enum.CampaignStatus.Value(campaign.status))

Получение имени перечисления

Иногда полезно знать имя поля перечисления. Например, при чтении объектов из API вам может потребоваться узнать, какому статусу кампании соответствует int 3 :

Прото-плюс сообщения
campaign = client.get_type("Campaign")
campaign.status = client.enums.CampaignStatusEnum.PAUSED
# To read the name of campaign status
print(campaign.status.name)
Протобуф-сообщения
campaign = client.get_type("Campaign")
status_enum = client.enums.CampaignStatusEnum
# Sets the campaign status to the int value for PAUSED
campaign.status = status_enum.PAUSED
# To read the name of campaign status
status_enum.CampaignStatus.Name(campaign.status)

Повторяющиеся поля

Как описано в документации proto-plus , повторяющиеся поля обычно эквивалентны типизированным спискам, а это означает, что они ведут себя почти идентично list .

Добавляйте значения к повторяющимся скалярным полям

При добавлении значений в повторяющиеся поля скалярного типа , например поля string или int64 , интерфейс один и тот же независимо от типа сообщения:

Прото-плюс сообщения
ad.final_urls.append("https://www.example.com")
Протобуф-сообщения
ad.final_urls.append("https://www.example.com")

Сюда также входят все другие распространенные методы list , например, extend :

Прото-плюс сообщения
ad.final_urls.extend(["https://www.example.com", "https://www.example.com/2"])
Протобуф-сообщения
ad.final_urls.extend(["https://www.example.com", "https://www.example.com/2"])

Добавляйте типы сообщений в повторяющиеся поля

Если повторяющееся поле не скалярного типа , поведение при добавлении их в повторяющиеся поля немного отличается:

Прото-плюс сообщения
frequency_cap = client.get_type("FrequencyCapEntry")
frequency_cap.cap = 100
campaign.frequency_caps.append(frequency_cap)
Протобуф-сообщения
# The add method initializes a message and adds it to the repeated field
frequency_cap = campaign.frequency_caps.add()
frequency_cap.cap = 100

Назначение повторяющихся полей

Как для скалярных, так и для нескалярных повторяющихся полей можно назначать списки полю разными способами:

Прото-плюс сообщения
# In proto-plus it's possible to use assignment.
urls = ["https://www.example.com"]
ad.final_urls = urls
Протобуф-сообщения
# Protobuf messages do not allow assignment, but you can replace the
# existing list using slice syntax.
urls = ["https://www.example.com"]
ad.final_urls[:] = urls

Пустые сообщения

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

Прото-плюс сообщения
# When using proto-plus messages you can simply check the message for
# truthiness.
is_empty = bool(campaign)
is_empty = not campaign
Протобуф-сообщения
is_empty = campaign.ByteSize() == 0

Копия сообщения

Как для сообщений proto-plus, так и для protobuf мы рекомендуем использовать вспомогательный метод copy_from в GoogleAdsClient :

client.copy_from(campaign, other_campaign)

Пустые поля сообщений

Процесс установки пустых полей сообщений одинаков, независимо от типа сообщения, которое вы используете. Вам просто нужно скопировать пустое сообщение в соответствующее поле. См. раздел «Копирование сообщения» , а также руководство по пустым полям сообщений . Вот пример того, как установить пустое поле сообщения:

client.copy_from(campaign.manual_cpm, client.get_type("ManualCpm"))

Имена полей, которые являются зарезервированными словами

При использовании сообщений proto-plus имена полей автоматически появляются с завершающим подчеркиванием, если имя также является зарезервированным словом в Python. Вот пример работы с экземпляром Asset :

asset = client.get_type("Asset")
asset.type_ = client.enums.AssetTypeEnum.IMAGE

Полный список зарезервированных имен формируется в модуле генератора gapic . Доступ к нему можно получить и программно.

Сначала установите модуль:

python -m pip install gapic-generator

Затем в REPL или скрипте Python:

import gapic.utils
print(gapic.utils.reserved_names.RESERVED_NAMES)

Присутствие на местах

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

Прото-плюс сообщения
# Use the "in" operator.
has_field = "name" in campaign
Протобуф-сообщения
campaign = client.get_type("Campaign")
# Determines whether "name" is set and not just an empty string.
campaign.HasField("name")

Интерфейс класса Message protobuf имеет метод HasField , который определяет, установлено ли поле в сообщении, даже если для него установлено значение по умолчанию.

Методы сообщений Protobuf

Интерфейс сообщений protobuf включает в себя некоторые удобные методы, которые не являются частью интерфейса proto-plus; однако получить к ним доступ просто, преобразовав сообщение proto-plus в его аналог protobuf:

# Accessing the ListFields method
protobuf_campaign = util.convert_protobuf_to_proto_plus(campaign)
print(campaign.ListFields())

# Accessing the Clear method
protobuf_campaign = util.convert_protobuf_to_proto_plus(campaign)
print(campaign.Clear())

Трекер проблем

Если у вас есть какие-либо вопросы по поводу этих изменений или проблем при переходе на последнюю версию библиотеки, сообщите о проблеме на нашем трекере.