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

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

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

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

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

Производительность 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 значение преобразуется из собственного типа Python str в 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 Ads.

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

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

JSON-сериализация

Прото-плюс сообщения
serialized = type(campaign).to_json(campaign)
deserialized = type(campaign).from_json(serialized)
Сообщения Protobuf
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)
Сообщения Protobuf
from google.api_core.protobuf_helpers import field_mask

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

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

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

Извлечение типа Enum

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

Прото-плюс сообщения
val = client.get_type("CampaignStatusEnum").CampaignStatus.PAUSED
Сообщения Protobuf
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)
Сообщения Protobuf
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)
Сообщения Protobuf
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")
Сообщения Protobuf
ad.final_urls.append("https://www.example.com")

Это также включает в себя все другие общие методы list , например extend :

Прото-плюс сообщения
ad.final_urls.extend(["https://www.example.com", "https://www.example.com/2"])
Сообщения Protobuf
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)
Сообщения Protobuf
# 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
# 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
Сообщения Protobuf
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
Сообщения Protobuf
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())

Отслеживание проблем

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