2

I've been reading through some Google App Engine SDK source code, and I've noticed Google often writes a private class method (prepending the method's name with an _), but immediately after finishing the method code block, they create a public variable with the same name, and assign the private method to the variable.

Why do they do this?

Example code:

@classmethod
@utils.positional(3)
def _get_by_id(cls, id, parent=None, **ctx_options):
  """Returns an instance of Model class by ID.

  This is really just a shorthand for Key(cls, id, ...).get().

  Args:
    id: A string or integer key ID.
    parent: Optional parent key of the model to get.
    namespace: Optional namespace.
    app: Optional app ID.
    **ctx_options: Context options.

  Returns:
    A model instance or None if not found.
  """
  return cls._get_by_id_async(id, parent=parent, **ctx_options).get_result()
get_by_id = _get_by_id
Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
Michael Gradek
  • 2,628
  • 3
  • 29
  • 35
  • 1
    It could be to still be able to do `self._whatever()` when `self.whatever()` is overridden, but it's hard to determine intent from here. – user2357112 Nov 28 '16 at 21:19

1 Answers1

3

This is almost certainly a hold-over from a time when Python had no decorator syntax yet. Decorators where introduced in Python 2.4 (see PEP 318), and before this time you had to manually apply a decorator function to an existing, already defined function object.

The code would originally have been written like this:

def _get_by_id(cls, id, parent=None, **ctx_options):
  """Returns an instance of Model class by ID.

  This is really just a shorthand for Key(cls, id, ...).get().

  Args:
    id: A string or integer key ID.
    parent: Optional parent key of the model to get.
    namespace: Optional namespace.
    app: Optional app ID.
    **ctx_options: Context options.

  Returns:
    A model instance or None if not found.
  """
  return cls._get_by_id_async(id, parent=parent, **ctx_options).get_result()
get_by_id = classmethod(utils.positional(3)(_get_by_id))

This manually applied the classmethod and utils.positional(3) decorator functions.

Generally, the undecorated underscore name was retained for easier testing. This doesn't make that much sense with a classmethod, but it could have been a pattern in the codebase that was followed anywhere a decorator was used.

This is not the only hold-over; the Google Python styleguide on Properties is similarly archaic, see Google Style Guide properties for getters and setters.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343