With the `use_proto_plus` configuration parameter, you can specify whether you
want the library to return [proto-plus messages](https://proto-plus-python.readthedocs.io/en/latest/) or
[protobuf messages](https://developers.google.com/protocol-buffers/docs/pythontutorial). For details on how
to set this parameter, see the [configuration docs](https://developers.google.com/google-ads/api/docs/client-libs/python/configuration).

This section describes the performance implications of each option so you can
choose the best approach for your application.

## Proto-plus versus protobuf messages

The code generator pipeline integrates [proto-plus](https://proto-plus-python.readthedocs.io/en/latest/) as a way to
improve the ergonomics of the protobuf message interface by making them behave
more like standard Python objects. However, this means that using proto-plus
introduces performance overhead.

## Proto-plus performance

One of the core benefits of proto-plus is that it converts
[protobuf messages](https://proto-plus-python.readthedocs.io/en/latest/marshal.html#wrapper-types) and
[well-known types](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf) to built-in
Python types through a process called [type marshaling](https://proto-plus-python.readthedocs.io/en/latest/marshal.html).

Marshaling occurs when a field is accessed on a proto-plus message instance,
specifically when a field is either read or set, for example, in a protobuf
definition:

    syntax = "proto3";

    message Dog {
      string name = 1;
    }

When this definition is converted to a proto-plus class, it looks like this:

    import proto


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

You can then initialize the `Dog` class and access its `name` field as you would
any other Python object:

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

When reading and setting the `name` field, the value is converted from a
built-in Python `str` type to a [`string`](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
type so that the value is compatible with the protobuf runtime.

The time spent performing these type conversions has a large enough performance
impact that you should decide, based on your application's needs, whether to use
proto-plus or protobuf messages.

## Use cases for proto-plus and protobuf messages

Proto-plus message use cases
:   Proto-plus offers a number of ergonomic improvements over protobuf messages,
    so they're ideal for writing maintainable, readable code. Since they expose
    standard Python objects, they're easier to use and understand.

Protobuf message use cases
:   Use protobufs for performance-sensitive use cases, specifically in apps that
    need to process large reports quickly, or that build mutate requests with a
    large number of operations, for example with
    [`BatchJobService`](https://developers.google.com/google-ads/api/reference/rpc/v26/BatchJobService) or
    [`OfflineUserDataJobService`](https://developers.google.com/google-ads/api/reference/rpc/v26/OfflineUserDataJobService).

## Switch message types dynamically

After selecting the appropriate message type for your app, you might find that
you need to use the other type for a specific workflow. In this case, you can
switch between the two types dynamically using utilities offered by the client
library. Using the same `Dog` message class from earlier:

    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 message interface differences

The proto-plus interface is [documented in detail](https://proto-plus-python.readthedocs.io/en/latest/), and the
following sections highlight key differences that affect common use cases for
the Google Ads client library.

### Bytes serialization

Proto-plus messages
:

    ```python
    serialized = type(campaign).serialize(campaign)
    deserialized = type(campaign).deserialize(serialized)
    ```

Protobuf messages
:

    ```python
    serialized = campaign.SerializeToString()
    deserialized = campaign.FromString(serialized)
    ```

### JSON serialization

Proto-plus messages
:

    ```python
    serialized = type(campaign).to_json(campaign)
    deserialized = type(campaign).from_json(serialized)
    ```

Protobuf messages
:

    ```python
    from google.protobuf.json_format import MessageToJson, Parse

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

### Field masks

The field mask helper method provided by [`api-core`](https://github.com/googleapis/python-api-core) is
designed to use protobuf message instances. When using proto-plus messages,
convert them to protobuf messages to use the helper:

Proto-plus messages
:

    ```python
    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 messages
:

    ```python
    from google.api_core.protobuf_helpers import field_mask

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

### Enums

Enums exposed by proto-plus messages are instances of Python's built-in
[`enum`](https://docs.python.org/3/library/enum.html) type and therefore inherit a number of convenience
methods.

#### Enum type retrieval

When using the `GoogleAdsClient.get_type` method to retrieve enums, the messages
that are returned are slightly different depending on whether you're using
proto-plus or protobuf messages. For example:

Proto-plus messages
:

    ```python
    val = client.get_type("CampaignStatusEnum").CampaignStatus.PAUSED
    ```

Protobuf messages
:

    ```python
    val = client.get_type("CampaignStatusEnum").PAUSED
    ```

To make retrieving enums simpler, there's a convenience attribute on
`GoogleAdsClient` instances that has a consistent interface regardless of which
message type you're using:

    val = client.enums.CampaignStatusEnum.PAUSED

#### Enum value retrieval

Sometimes it's useful to know the value, or field ID, of a given enum, for
example, `PAUSED` on `CampaignStatusEnum` corresponds to `3`:

Proto-plus messages
:

    ```python
    campaign = client.get_type("Campaign")
    campaign.status = client.enums.CampaignStatusEnum.PAUSED
    # To read the value of campaign status
    print(campaign.status.value)
    ```

Protobuf messages
:

    ```python
    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"))
    ```

#### Enum name retrieval

Sometimes it's useful to know the name of an enum field. For example, when
reading objects from the API you might want to know which campaign status the
integer `3` corresponds to:

Proto-plus messages
:

    ```python
    campaign = client.get_type("Campaign")
    campaign.status = client.enums.CampaignStatusEnum.PAUSED
    # To read the name of campaign status
    print(campaign.status.name)
    ```

Protobuf messages
:

    ```python
    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))
    ```

### Repeated fields

As described in the [proto-plus docs](https://proto-plus-python.readthedocs.io/en/latest/fields.html#repeated-fields), repeated fields are
generally equivalent to typed lists, which means that they behave almost
identically to a `list`.

#### Append values to repeated scalar fields

When adding values to repeated
[scalar type](https://developers.google.com/protocol-buffers/docs/proto3#scalar) fields, for example `string`
or `int64` fields, the interface is the same regardless of message type:

Proto-plus messages
:

    ```python
    ad.final_urls.append("https://www.example.com")
    ```

Protobuf messages
:

    ```python
    ad.final_urls.append("https://www.example.com")
    ```

This includes all other common `list` methods as well, for example `extend`:

Proto-plus messages
:

    ```python
    ad.final_urls.extend(
        ["https://www.example.com", "https://www.example.com/2"]
    )
    ```

Protobuf messages
:

    ```python
    ad.final_urls.extend(
        ["https://www.example.com", "https://www.example.com/2"]
    )
    ```

#### Append message types to repeated fields

If the repeated field is not a
[scalar type](https://developers.google.com/protocol-buffers/docs/proto3#scalar), the behavior when adding
them to repeated fields is slightly different:

Proto-plus messages
:

    ```python
    frequency_cap = client.get_type("FrequencyCapEntry")
    frequency_cap.cap = 100
    campaign.frequency_caps.append(frequency_cap)
    ```

Protobuf messages
:

    ```python
    # The add method initializes a message and adds it to the repeated field
    frequency_cap = campaign.frequency_caps.add()
    frequency_cap.cap = 100
    ```

#### Assign repeated fields

For both scalar and non-scalar repeated fields, you can assign lists to the
field in different ways:

Proto-plus messages
:

    ```python
    # In proto-plus it's possible to use assignment.
    urls = ["https://www.example.com"]
    ad.final_urls = urls
    ```

Protobuf messages
:

    ```python
    # 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
    ```

### Empty messages

Sometimes it's useful to know whether a message instance contains any
information, or has any of its fields set.

Proto-plus messages
:

    ```python
    # When using proto-plus messages you can check the message for truthiness.
    is_empty = not bool(campaign)
    ```

Protobuf messages
:

    ```python
    is_empty = campaign.ByteSize() == 0
    ```

### Message copy

For both proto-plus and protobuf messages, use the `copy_from` helper method on
the `GoogleAdsClient`:

    client.copy_from(campaign, other_campaign)

### Empty message fields

The process for setting empty message fields is the same regardless of the
message type you're using. You copy an empty message into the field in question.
See the [Message copy](https://developers.google.com/google-ads/api/docs/client-libs/python/protobuf-messages#copy) section as well as the
[Empty message fields](https://developers.google.com/google-ads/api/docs/client-libs/python/empty-message-fields) guide. The following example shows
how to set an empty message field:

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

### Field names that are reserved words

When using proto-plus messages, field names automatically appear with a trailing
underscore if the name is also a reserved word in Python. The following example
shows how to work with an [`Asset`](https://developers.google.com/google-ads/api/reference/rpc/v26/Asset) instance:

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

The [full list of reserved names](https://github.com/googleapis/gapic-generator-python/blob/master/gapic/utils/reserved_names.py) is constructed in the
[gapic generator](https://github.com/googleapis/gapic-generator-python) module. It can be accessed programmatically
as well.

First, install the module:

    python -m pip install gapic-generator

Then, in a Python REPL or script:

    import gapic.utils

    print(gapic.utils.reserved_names.RESERVED_NAMES)

> [!NOTE]
> **Note:** The augmentation of fields whose names are reserved words does not affect GAQL queries. When requesting the `Asset.type` field in a `Search` or `SearchStream` request, don't include a trailing underscore in the query string.

### Field presence

Because the fields on protobuf message instances have default values, it's not
always intuitive to know whether a field has been set or not.

Proto-plus messages
:

    ```python
    # Use the "in" operator.
    has_field = "name" in campaign
    ```

Protobuf messages
:

    ```python
    campaign = client.get_type("Campaign")
    # Determines whether "name" is set and not just an empty string.
    campaign.HasField("name")
    ```

The protobuf [`Message`](https://googleapis.dev/python/protobuf/latest/google/protobuf/message.html#google.protobuf.message.Message) class interface has a `HasField`
method that determines whether a sub-message, `oneof` field, or `optional`
scalar field on a message has been set, even if it was set to a default value.
(Calling `HasField` on non-optional scalar fields in `proto3` raises a
`ValueError`.)

### Protobuf message methods

The protobuf message interface includes some convenience methods that are not
part of the proto-plus interface; however, you can access them by converting a
proto-plus message to its protobuf counterpart:

    # 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

If you have any questions about these changes or any problems migrating to the
latest version of the library, [file an issue](https://github.com/googleads/google-ads-python/issues) on the
issue tracker.