如何在Python中重构方法以使其成为私有的?

时间:2011-11-02 11:40:27

标签: python private

我知道Python中没有真正私有的东西,有时你需要遵循:

  • 阻止人们调用某些方法或在他们执行时警告,但允许从其他模块调用它们
  • 检测这些方法的使用位置以及在控制台上发出警告甚至是异常,这样您就可以开始将方法标记为私有而不会破坏现有代码。
  • 隐藏IDE自动完成的私有方法(可选) - 可能在其名称前使用一个下划线。 现在我想知道是否有人找到了一种美妙的方式来获得这种行为,也许有人使用annotations

请记住,这必须与现有代码库一起使用,因此它应该支持逐步重新考虑现有代码。

最后一点几乎已经解决,关于它的唯一问题是我应该使用一个下划线还是两个下划线?

4 个答案:

答案 0 :(得分:7)

Python具有“同意成人”的理念:使用下划线将前缀标记为私有的前缀方法。不要从外部调用带有前导下划线的任何方法。如果你这样做,那就是你自己。你可以自由地这样做,但你已被警告过。

要在现有代码库中采用此对话,请将原始方法重命名为带有前导下划线的名称,并添加一个包含引发警告的原始名称的包装器。

您的IDE应具有可配置的自动完成功能。如果没有,请使用Emacs:)

答案 1 :(得分:1)

隐藏自动完成功能将完全取决于您的编辑器以及它如何处理自动完成功能。我的编辑器没有自动完成,所以我不需要使用它。

标准的python约定是对方法名称加前缀和下划线。这告诉用户该方法是私有的,不应该使用。

您可以在方法名称前使用双下划线;这将调用名称mangling。

有关详情,请参阅本页9.7: http://docs.python.org/tutorial/classes.htm

但它仍然不是私密的;它可以被称为。

远程提升异常会查看检查对象和框架信息。有大量的q和关于这个网站。

TL;博士 你不能私有化,但你可以更难找到

答案 2 :(得分:0)

可能在内部类上定义您的私有方法,并使用self._private.method在内部访问它:

class PublicObject(object):

    class PrivateObject(object):
        def __init__(self, public):
            self.public = public

        def private1(self):
            print "a private method, not visible outside the public wrapper on", id(self.public)

    def __init__(self):
        self._private = self.PrivateObject(self)

    def public1(self):
        print "a public method, which invokes a private one"
        return self._private.private1()

a = PublicObject()

print dir(a) # only shows public1, not private1; won't show private1 in most IDEs autocomplete

a.public1()
a.private1() # raises an exception
a._private.private1() # still possible to call private methods, but obvious when you are doing so

如果您希望ProtectedObject在继承链中具有可见性,请创建类似的_protected属性并使用__getattr__访问super(PublicObject,self.public)._protected上的属性和方法。

顺便说一句,你所要求的实际上是在Python哲学之外,并且通过复合对象添加这个间接将会产生性能成本。

答案 3 :(得分:0)

您可以通过调用warnings.warn来装饰私有方法。

我不确定你是什么意思,阻止人,但允许模块调用方法。一个是在交互式提示下发生的区别,但另一个不是吗?如果是这样,您可以通过检查sys.path[0]的值来测试是否从交互式提示符运行python。

当python运行脚本时,sys.path[0]等于脚本的目录。当python运行交互式会话时,sys.path[0]被设置为空字符串''。所以, 警告人们,但不是脚本,你可以做到

import warnings
import functools

def warn_private(func):
    if not sys.path[0]:
        @functools.wraps(func)
        def wrapper(self,*args,**kwargs):
            warnings.warn('{f} is private'.format(f=func.__name__))
            return func(self,*args,**kwargs)
        return wrapper
    else:
        return func

class Foo(object):
    @warn_private
    def _bar(self):
        pass