The private objects and attributes are easily accessed using [`inspect.getclosurevars`][1]:

    >>> c = CounterController()
    >>> import inspect
    >>> inspect.getclosurevars(c.add_one_endpoint)
    ClosureVars(nonlocals={'counter_controller': <__main__.CounterController.<locals>.CounterControllerPrivate object at 0x10a047cf8>}, globals={}, builtins={}, unbound={'add_one'})
    >>> _.nonlocals['counter_controller']
    <__main__.CounterController.<locals>.CounterControllerPrivate object at 0x10a047cf8>
    >>> _.counter
    0

So you might consider whether it's worth spending a lot of effort on this approach. Personally I would spend the effort on API documentation instead.


  [1]: https://docs.python.org/3/library/inspect.html#inspect.getclosurevars