Protobuf 訊息

您可以使用 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 類別,並像存取任何其他 Python 物件一樣存取其 name 欄位:

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

讀取及設定 name 欄位時,系統會將值從內建的 Python str 類型轉換為 string 類型,以便與 protobuf 執行階段相容。

執行這些類型轉換所需的時間會對效能造成相當大的影響,因此您應根據應用程式的需求,決定是否要使用 proto-plus 或 protobuf 訊息。

proto-plus 和 protobuf 訊息的用途

Proto-plus 訊息用途
Proto-plus 提供多項人體工學改善功能,可改善 protobuf 訊息,因此非常適合用於編寫可維護、可讀的程式碼。由於這些 API 會公開標準 Python 物件,因此更容易使用和理解。
Protobuf 訊息用途
如果應用程式需要快速處理大型報表,或建構包含大量作業的變更要求 (例如 BatchJobService 或 OfflineUserDataJobService),請使用 protobufs 處理效能敏感的使用案例。

動態切換訊息類型

為應用程式選取合適的訊息類型後,您可能會發現需要針對特定工作流程使用其他類型。在這種情況下,您可以使用用戶端程式庫提供的公用程式,在兩種型別之間動態切換。使用與先前相同的 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 用戶端程式庫常見用途的主要差異。

位元組序列化

Proto-plus 訊息
serialized = type(campaign).serialize(campaign)
deserialized = type(campaign).deserialize(serialized)
Protobuf 訊息
serialized = campaign.SerializeToString()
deserialized = campaign.FromString(serialized)

JSON 序列化

Proto-plus 訊息
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 訊息,以便使用輔助程式:

Proto-plus 訊息
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 訊息公開的列舉是 Python 內建 enum 類型的例項,因此會繼承多種便利方法。

列舉類型擷取

使用 GoogleAdsClient.get_type 方法擷取列舉時,傳回的訊息會因您使用的是 proto-plus 還是 protobuf 訊息而略有不同。例如:

Proto-plus 訊息
val = client.get_type("CampaignStatusEnum").CampaignStatus.PAUSED
Protobuf 訊息
val = client.get_type("CampaignStatusEnum").PAUSED

為了簡化列舉擷取作業,GoogleAdsClient 執行個體提供便利屬性,無論使用哪種訊息類型,都有一致的介面:

val = client.enums.CampaignStatusEnum.PAUSED

列舉值擷取

有時,瞭解特定列舉的值或欄位 ID 很有用,例如 CampaignStatusEnum 上的 PAUSED 對應至 3:

Proto-plus 訊息
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
# Native protobuf enum fields already store the integer value (3):
print(campaign.status)
# Or look up the integer value from the enum name string:
print(status_enum.CampaignStatus.Value("PAUSED"))

擷取列舉名稱

有時知道列舉欄位的名稱很有用。舉例來說,從 API 讀取物件時,您可能想知道整數 3 對應的廣告活動狀態:

Proto-plus 訊息
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
print(status_enum.CampaignStatus.Name(campaign.status))

重複欄位

如 proto-plus 文件所述,重複欄位通常等同於類型清單,也就是說,其行為與 list 幾乎相同。

將值附加至重複的純量欄位

在重複的純量類型欄位 (例如 string 或 int64 欄位) 中新增值時,無論訊息類型為何,介面都相同:

Proto-plus 訊息
ad.final_urls.append("https://www.example.com")
Protobuf 訊息
ad.final_urls.append("https://www.example.com")

這也包括所有其他常見的 list 方法,例如 extend:

Proto-plus 訊息
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"]
)

將訊息類型附加至重複欄位

如果重複欄位不是純量類型,則將其新增至重複欄位時,行為會略有不同:

Proto-plus 訊息
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

指派重複欄位

無論是純量和非純量重複欄位,您都可以透過不同方式將清單指派給欄位:

Proto-plus 訊息
# 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

空白訊息

有時,您可能需要知道訊息例項是否包含任何資訊,或是否已設定任何欄位。

Proto-plus 訊息
# When using proto-plus messages you can check the message for truthiness.
is_empty = not bool(campaign)
Protobuf 訊息
is_empty = campaign.ByteSize() == 0

訊息文案

對於 proto-plus 和 protobuf 訊息,請在 GoogleAdsClient 上使用 copy_from 輔助方法:

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

接著,在 Python REPL 或指令碼中:

import gapic.utils

print(gapic.utils.reserved_names.RESERVED_NAMES)

欄位型態

由於 protobuf 訊息例項上的欄位有預設值,因此不一定能直觀地知道是否已設定欄位。

Proto-plus 訊息
# 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")

protobuf Message 類別介面具有 HasField 方法,可判斷是否已設定訊息的子訊息、oneof 欄位或 optional 純量欄位,即使設為預設值也一樣。(在 proto3 的非選用純量欄位上呼叫 HasField 會引發 ValueError)。

Protobuf 訊息方法

protobuf 訊息介面包含一些不屬於 proto-plus 介面的便利方法,但您可以將 proto-plus 訊息轉換為對應的 protobuf 訊息,藉此存取這些方法:

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

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

Issue Tracker

如果對這些異動有任何疑問,或在遷移至最新版本程式庫時遇到任何問題,請在 Issue Tracker 上提交問題。