我正在学习python.我试图了解如何设计一个公开api的库.我希望避免暴露将来可能发生变化的内部方法.我正在寻找一种简单而pythonic的方式来做到这一点.我有一个包含一堆类的库.这些类的一些方法在类内部使用.我不想将这些方法暴露给客户端代码.
假设我的库(fe mylib)包含一个C带有两个方法的类,一个C.public()方法被认为是从客户端代码和C.internal()方法中使用的方法,用于对库代码进行一些工作.我想将自己提交给公共api(C.public()),但我希望C.internal()将来可以更改方法,例如添加或删除参数.
以下代码说明了我的问题:
mylib/c.py:
class C:
def public(self):
pass
def internal(self):
pass
Run Code Online (Sandbox Code Playgroud)
mylib/f.py:
class F:
def build():
c = C()
c.internal()
return c
Run Code Online (Sandbox Code Playgroud)
mylib/__init__.py:
from mylib.c import C
from mylib.f import F
Run Code Online (Sandbox Code Playgroud)
client/client.py:
import mylib
f = mylib.F()
c = f.build()
c.public()
c.internal() # I wish to hide this from client code
Run Code Online (Sandbox Code Playgroud)
我想到了以下解决方案:
仅记录公共API,警告文档中的用户不要使用私有库api.和平相处,希望客户只使用公共API.如果下一个库版本中断客户端代码是客户端错误:).
使用某种形式的命名约定,fe为每个方法添加前缀"_",(它保留用于受保护的方法并向ide引发警告),也许我可以使用其他前缀.
使用对象组合来隐藏内部方法.例如,库可以返回仅PC嵌入C对象的客户端对象.
mylib/pc.py:
class PC:
def __init__(self, c):
self.__c__
def public(self):
self.__cc__.public()
Run Code Online (Sandbox Code Playgroud)
但这看起来有点做作.
任何建议表示赞赏:-)
更新
有人提出这个问题是重复的,Python在类中有"私有"变量吗?
这是类似的问题,但我对范围有点不同.我的范围是一个库而不是一个类.我想知道是否有一些关于标记(或强制)的约定,它们是库的公共方法/类/函数.例如,我使用__init__.py导出公共类或函数.我想知道是否有一些关于导出类方法的约定,或者我是否只能依赖文档.我知道我可以使用"_"前缀来标记受保护的方法.据我所知,protected方法是可以在类层次结构中使用的方法.
我发现了一个关于使用装饰器@api Sphinx Public API文档标记公共方法的问题,但它大约在3年前.有一个普遍接受的解决方案,所以如果有人正在阅读我的代码,了解什么是库公共API的方法,以及打算在库内部使用的方法?希望我澄清了我的问题.谢谢大家!
您无法真正隐藏对象的方法和属性。如果你想确保你的内部方法不被暴露,包装是最好的方法:
class PublicC:
def __init__(self):
self._c = C()
def public(self):
self._c.public()
Run Code Online (Sandbox Code Playgroud)
据我所知,通常不鼓励使用双下划线作为前缀,以防止与 python 内部发生冲突。
不鼓励的是
__myvar__带有双下划线前缀+后缀的名称...这种命名风格被许多 python 内部使用,应该避免 - Anentropic
如果您更喜欢子类化,则可以覆盖内部方法并引发错误:
class PublicC(C):
def internal(self):
raise Exception('This is a private method')
Run Code Online (Sandbox Code Playgroud)
如果你想使用一些Python魔法,你可以看看__getattribute__。在这里,您可以检查用户尝试检索的内容(函数或属性),并AttributeError在客户端想要使用内部/黑名单方法时提出。
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
Run Code Online (Sandbox Code Playgroud)
我认为这会导致最少的代码开销,但也需要一些维护。您还可以反转检查和白名单方法。
...
whitelist = ['public']
def __getattribute__(self, name):
if name not in PublicC.whitelist:
...
Run Code Online (Sandbox Code Playgroud)
这可能更适合您的情况,因为白名单可能不会像黑名单那样频繁更改。
最终,这取决于你。正如您自己所说:一切都与文档有关。
另一条评论:
也许您还想重新考虑您的班级结构。您已经有一个工厂类F了C。让我们F拥有所有的内部方法。
class F:
def build(self):
c = C()
self._internal(c)
return c
def _internal(self, c):
# work with c
Run Code Online (Sandbox Code Playgroud)
在这种情况下,您不必包装或子类化任何内容。如果没有硬性设计限制使这成为不可能,我会推荐这种方法。