Question: I am learning the coding style through reading the Django source code:  

In the pagination.py [Pagination](https://docs.djangoproject.com/en/1.11/topics/pagination/#django.core.paginator.Paginator), the source code define a private method to instantiate the class Page. 

```
def page(self, number):
    """
    Returns a Page object for the given 1-based page number.
    """
    number = self.validate_number(number)
    bottom = (number - 1) * self.per_page
    top = bottom + self.per_page
    if top + self.orphans >= self.count:
        top = self.count
    return self._get_page(self.object_list[bottom:top], number, self)

def _get_page(self, *args, **kwargs):
    """
    Returns an instance of a single page.

    This hook can be used by subclasses to use an alternative to the
    standard :cls:`Page` object.
    """
    return Page(*args, **kwargs)

```

I think it more readable if instantiate it with method page as

```
def page(self, number):
    """
    Returns a Page object for the given 1-based page number.
    """
    number = self.validate_number(number)
    bottom = (number - 1) * self.per_page
    top = bottom + self.per_page
    if top + self.orphans >= self.count:
        top = self.count
    return Page(self.object_list[bottom:top], number, self)

```

What's the advantage to define separate `_get_page` method in extra step?

Answer: 
It is already in the comment of the function:

```
def _get_page(self, *args, **kwargs):
    """
    Returns an instance of a single page.

    **This hook can be used by subclasses to use an alternative to the
 standard :cls:`Page` object.
 """**
    return Page(*args, **kwargs)
```

Imagine that later you write your own paging method (with a more advanced `Page` object), then you will need not only to fix all *your* code by using the new `Page` object (and perhaps some still need to use the "*old*" page object), but you would have to patch some parts of the Django codebase as well.

By introducing a method that acts as a wrapper around the `Page` constructor, we can *monkey patch* the construction of a page.

For example we can define our own speciale `Page` class:

```
class SpecialPage(Page):

    # ...
    pass

```

and then we can add a **`monkey_patching.py`** file to any of the applications, and write:

```
import django.core.paginator

def _new_get_page(self, *args, **kwargs):
    return SpecialPage(*args, **kwargs)

django.core.paginator._get_page = _new_get_page

```

Typically by creating "levels of indirection", one allows a user to fix certain parts at those indirection levels, and thus can inject their own implementation into Django parts.

Subclassing is the most straightforward fix, but not per se the only one. Perhaps you want to preprocess certain arguments, or post-process the `Page` object, or fire some "signals" to all kind of functions that do something with the `Page` that is created. Perhaps for a page those are not very logical things, but for other object constructions (like model instances), adding those levels of indirection are more sensical.
The advantage is, like the docstring says (that you also show in your question):

> 
> This hook can be used by subclasses to use an alternative to the standard :cls:`Page` object.
> 
> 
> 

If we just want to change the `Page` class, we override `_get_page()` but don't need to repeat the whole logic that builds `top` and `bottom` parameters in the `page()` method ([DRY principle](http://django.wikispaces.asu.edu/DRY+Principle)).

We would override the `page()` method only if we want to change some part of the logic calculated in there.