How to create a public api library so as not to expose the internals?

I am learning python. I am trying to figure out how to create a library that provides a public api. I want to avoid disclosing internal methods that may change in the future. I am looking for a simple and pythonic way to do this. I have a library containing a bunch of classes. Some methods of these classes are used inside classes. I do not want to disclose these methods to client code.

Suppose my library (fe mylib ) contains a class C with two methods: the C.public() method, which is supposed to be used from client code, and the C.internal() method, used to do some work in the library code. I want to pass myself to the public api ( C.public() ), but I expect to change the C.internal() method in the future, for example, adding or removing parameters.

The following code illustrates my question:

mylib/c.py :

 class C: def public(self): pass def internal(self): pass 

mylib/f.py :

 class F: def build(): c = C() c.internal() return c 

mylib/__init__.py :

 from mylib.c import C from mylib.f import F 

client/client.py :

 import mylib f = mylib.F() c = f.build() c.public() c.internal() # I wish to hide this from client code 

I thought of the following solutions:

  • document only public api, warning the user in the documentation not to use the private api library. Live in peace, hoping that customers will only use the public api. If the next version of the library violates the client code, this is a client error :).

  • use some form of naming convention, i.e. the prefix of each method with "_" (it is reserved for protected methods and raises a warning in ide), maybe I can use other prefixes.

  • use the composition of objects to hide internal methods. For example, a library can only return to PC clients an object that inserts C objects.

mylib/pc.py :

 class PC: def __init__(self, c): self.__c__ def public(self): self.__cc__.public() 

But it looks a little far-fetched.

Any suggestion is appreciated :-)

Update

It has been suggested that this question is duplicated. Does Python have "private" variables in classes?

This is a similar question, but I'm a little different from scope. My scope is not one class. I am wondering if there is any convention on labeling (or forcing) that is publicly available library methods / classes / functions. For example, I use __init__.py to export public classes or functions. I am wondering if there is some kind of agreement on exporting class methods or if I can only rely on the documentation. I know that I can use the prefix "_" to mark protected methods. As far as I know, a protected method is a method that can be used in a class hierarchy.

I found a question about marking a public method using the @api Sphinx Public API documentation decorator, but that was about 3 years ago. There is a generally accepted solution, so if someone is reading my code, find out what methods are for the public api library and methods intended for internal use in the library? I hope I clarified my questions. Thanks everyone!

+5
source share
2 answers

You cannot hide the methods and attributes of objects. If you want to be sure that your internal methods are not displayed, completing the migration is the way to go:

 class PublicC: def __init__(self): self._c = C() def public(self): self._c.public() 

Double underscoring as a prefix is ​​usually discouraged, as far as I know, to prevent collision with python's internal components.

Fault-tolerant are __myvar__ names with double underscore + suffix ... this naming style is used by many python internal components and should be avoided - Anentropic

If you prefer subclassing, you can overwrite internal methods and cause errors:

 class PublicC(C): def internal(self): raise Exception('This is a private method') 

If you want to use python magic, you can take a look at __getattribute__ . Here you can check what your user is trying to get (function or attribute), and raise an AttributeError if the client wants to use an internal / blacklist.

 class C(object): def public(self): print "i am a public method" def internal(self): print "i should not be exposed" class PublicC(C): blacklist = ['internal'] def __getattribute__(self, name): if name in PublicC.blacklist: raise AttributeError("{} is internal".format(name)) else: return super(C, self).__getattribute__(name) c = PublicC() c.public() c.internal() # --- output --- i am a public method Traceback (most recent call last): File "covering.py", line 19, in <module> c.internal() File "covering.py", line 13, in __getattribute__ raise AttributeError("{} is internal".format(name)) AttributeError: internal is internal 

I assume this causes the least code overhead, but also requires some maintenance. You can also change the validation methods and whitelists .

 ... whitelist = ['public'] def __getattribute__(self, name): if name not in PublicC.whitelist: ... 

This might be better for your case, since the whitelist probably won't change as often as the blacklist.

In the end, it is up to you. As you said yourself: all about documentation.

Another note:

You might also want to rethink your class structure. You already have a factory F class for C Let F have all the internal methods.

 class F: def build(self): c = C() self._internal(c) return c def _internal(self, c): # work with c 

In this case, you do not need to wrap or subclasses. If there are no strict design constraints to make this impossible, I would recommend this approach.

+2
source

I thought of the following solutions:

  • the document is only a public api, warning the user in the documentation not to use the private api library. Live in peace, hoping that customers will only use the public api. If the next version of the library violates the client code, a client error :).

  • use some form of naming convention, i.e. the prefix of each method with "_", (it is reserved for protected methods and raises a warning in ide), maybe I can use other prefixes.

  • use the composition of objects to hide internal methods. For example, a library can return only a PC object that embeds C objects to clients.

You did it right with the first two points.

Pythonic's way is to name internal methods, starting with a single underscore ' _ ', so all Python developers know that this method exists, but its use is not recommended and will not use it. (Until they decide to make some monkey fixes, but you should not care about this scenario.) For newbie developers, you can explicitly indicate not to use methods starting with underscore. Also, just do not provide public documentation for your "private" methods, use it only for internal reference.

You might want to take a look at name mangling ", but this is less common.

Hiding internal elements using object composition or methods such as __getattribute__ etc. is not generally recommended in Python.

You might want to look at the source code of some popular libraries to find out how they manage this, for example. Django, Twisted etc.

+2
source

Source: https://habr.com/ru/post/1245078/


All Articles