我在学习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
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
我想到了以下解决方案:
仅记录公共API,警告文档中的用户不要使用私有库api。和平相处,希望客户只使用公共API。如果下一个库版本中断客户端代码是客户端错误:)。
使用某种形式的命名约定,例如:使用“_”为每个方法添加前缀,(它保留给受保护的方法并向ide引发警告),也许我可以使用其他前缀。
使用对象组合来隐藏内部方法。
例如,库可以仅返回客户端PC
对象
嵌入C
个对象。
mylib/pc.py
:
class PC:
def __init__(self, c):
self.__c__
def public(self):
self.__cc__.public()
但这看起来有点做作。
任何建议都表示赞赏: - )
更新
有人建议此问题与Does Python have “private” variables in classes?
重复这是类似的问题,但我对范围有点不同。我的范围是一个库而不是一个类。我想知道是否有一些关于标记(或强制)的约定,它们是库的公共方法/类/函数。例如,我使用__init__.py
导出公共类或函数。我想知道是否有一些关于导出类方法的约定,或者我是否只能依赖文档。
我知道我可以使用“_”前缀来标记受保护的方法。据我所知,protected方法是可以在类层次结构中使用的方法。
我发现了一个关于使用装饰器@api
Sphinx Public API documentation标记公共方法的问题,但大约3年前。有一个普遍接受的解决方案,所以如果有人正在阅读我的代码,了解什么是库公共API的方法,以及打算在库内部使用的方法?
希望我澄清了我的问题。
谢谢大家!
答案 0 :(得分:2)
您无法真正隐藏对象的方法和属性。如果您想确保不暴露内部方法,可以选择包装:
class PublicC:
def __init__(self):
self._c = C()
def public(self):
self._c.public()
据我所知,通常不鼓励使用双下划线作为前缀来防止与python内部冲突。
不鼓励使用带有双下划线前缀+后缀的
__myvar__
名称...这种命名风格被许多python内部使用,应该避免 - Anentropic
如果您更喜欢子类化,则可以覆盖内部方法并引发错误:
class PublicC(C):
def internal(self):
raise Exception('This is a private method')
如果你想使用一些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
我认为这会导致代码开销最少,但也需要一些维护。您还可以反向支票和白名单方法。
...
whitelist = ['public']
def __getattribute__(self, name):
if name not in PublicC.whitelist:
...
这可能对您的情况更好,因为白名单可能不会像黑名单那样频繁更改。
最终,这取决于你。正如你自己说的:这都是关于文档的。
另一句话:
也许你也想重新考虑你的班级结构。您已为F
拥有工厂类C
。让F
拥有所有内部方法。
class F:
def build(self):
c = C()
self._internal(c)
return c
def _internal(self, c):
# work with c
在这种情况下,您不必包装或子类化任何东西。如果没有硬设计约束来实现这一点,我建议采用这种方法。
答案 1 :(得分:2)
我想到了以下解决方案:
仅记录公共API,警告文档中的用户不要使用 私人图书馆api。希望客户只使用 公众api。如果下一个库版本中断客户端代码是 客户过错:)。
使用某种形式的命名约定,例如:用“_”为每个方法添加前缀, (它保留用于受保护的方法并向ide发出警告), 也许我可以使用其他前缀。
使用对象组合来隐藏内部方法。比如说 库可以只返回客户端嵌入C的PC对象 对象
前两分你的表现非常正确。
Pythonic的方法是命名以单下划线“_
”开头的内部方法,这样所有的Python开发人员都知道这个方法就在那里,但不建议使用它,也不会使用它。 (直到他们决定做一些猴子修补,但你不应该关心这种情况。)对于新手开发人员,你可能想明确提到不使用以下划线开头的方法。另外,不要为您的“私人”方法提供公共文档,仅将其用于内部参考。
您可能需要查看“name mangling”,但不太常见。
在Python中,通常不鼓励使用对象组合或类似__getattribute__
等方法隐藏内部。
您可能需要查看一些常用库的源代码,以了解他们如何管理这些内容,例如: Django,Twisted等。