【问题标题】:Why can I not invoke this function at module-level?为什么我不能在模块级别调用此函数?
【发布时间】:2020-12-18 02:28:10
【问题描述】:

问题 [python 3.8.6]

def __foo():
    pass

class MyClass:
    @staticmethod
    def bar():
        __foo()

MyClass.bar()

结果:

NameError: name '_MyClass__foo' is not defined

观察

  • 如果我用foo() 替换__foo(),它运行良好。

  • 我意识到 python 正在 MyClass 中寻找 __foo()。在 C++ 中,我可以使用 ::__foo() 在模块级别显式调用函数。 python中是否有等价物?

问题

  • 你能解释我为什么会收到这个错误吗?

  • 我如何保留__foo() 并从类内部调用它?

分辨率

我对 python 中的命名约定有一个误解。

类中的

私有方法和符号有双下划线前缀,但模块级别的相同符号有单下划线前缀。 p>

【问题讨论】:

  • @juanpa.arrivillaga 将函数的范围限制在模块中。如果我将它添加为后缀,它也可以工作。我需要在 python 中找到正确命名的规则。
  • 这并没有将函数的范围限制在模块中......为什么你认为它会这样做? 传统上 API 的非公开部分使用单 下划线。
  • Thx - 单下划线也可以。您能否指出一些描述所有下划线约定的文档?谢谢。
  • 你不能。在使用星号导入时,您可以防止某些名称被引入,但是,您通常应该避免使用星号导入。按照惯例,您应该只使用一个下划线(它将自动从带星号的导入中排除,并且大多数基于 python 的工具,例如制表符完成,理解这一点并且不会显示它)
  • 我也在尝试使用下划线来记录文档。阅读代码,了解哪些符号只能在模块内使用以及哪些符号应由模块外的应用程序使用是很有用的。

标签: python scope


【解决方案1】:

因为带有两个下划线作为前缀(而不是后缀)的 any 名称在类定义语句中被破坏了。来自the docs

无法访问的“私有”实例变量,除非从 Python 中不存在对象内部。但是,有一个约定 后面是大多数 Python 代码:一个以 下划线(例如_spam)应被视为 API(无论是函数、方法还是数据成员)。它应该 被视为实施细节,如有更改,恕不 注意。

由于类私有成员有一个有效的用例(即 避免名称与子类定义的名称发生名称冲突),有 对这种机制的支持有限,称为名称修改。 任何 __spam 形式的标识符(至少两个前导下划线,在 大多数尾随下划线)在文本上替换为 _classname__spam,其中 classname 是当前类名,去掉了前导下划线。 这种修改是不加考虑的 到标识符的句法位置,只要它出现 在类的定义中。

(强调)

最正确的事情是不要用两个下划线来命名你的函数。或者,您可以执行以下操作:

def __foo():
    pass

def bar():
    __foo()


class MyClass:
    bar = staticmethod(bar)


MyClass.bar()

或者老实说,将bar 保留为模块级函数。

但同样,使用两个下划线没有多大意义。如果您想表明该函数不是模块的公共 api 的一部分,那么您应该使用 single 下划线:

def _foo():
    pass

【讨论】:

  • 太棒了!谢谢 - 不知道 python 使用 __ 来破坏方法名称。我将使用单下划线。
  • @kfmfe04 是的,如果你不知道的话肯定会让人困惑
  • 是的 - 我被甩了,因为我在类中使用双下划线作为私有静态方法。认为该规则也适用于模块级别 - 我错了!
  • @kfmfe04 理解非常重要,python 没有私有方法。正如文档中引用的部分所述。但是,要表示“这是私有的”,请在方法名称中使用 单 下划线
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多