【问题标题】:Private methods in PythonPython 中的私有方法
【发布时间】:2013-06-16 02:50:39
【问题描述】:

我想在我的类中有一个函数,我将只在这个类的方法中使用它。我不会在这些方法的实现之外调用它。在 C++ 中,我会使用在类的私有部分中声明的方法。在 Python 中实现这样一个函数的最佳方法是什么?

我正在考虑在这种情况下使用静态装饰器。我可以使用没有任何装饰器和self 字的函数吗?

【问题讨论】:

  • Python 中没有真正的“私有”方法。请参阅this question - 更具体地说,this answer
  • 我在询问这样做的好风格
  • 静态方法和“私有”方法是两个不同的东西......你能解释一下你真正想要什么吗?你想要一个既是静态又是私有的方法吗?
  • 好的方式是在方法名前加上下划线。

标签: python oop static-methods


【解决方案1】:

Python 没有私有方法或属性的概念。这完全取决于您如何实现您的课程。但是您可以使用伪私有变量(名称修改);任何以__(两个下划线)开头的变量都将成为伪私有变量。

来自the documentation

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

class A:
    def __private(self):
       pass

所以__private 现在实际上变成了_A__private

静态方法示例:

>>> class A:
...     @staticmethod         #not required in py3.x
...     def __private():
...         print 'hello'
...
>>> A._A__private()
hello

【讨论】:

  • 我可以只做 def __private(): pass without self 吗?然后在我的课堂上调用它 __private() 。我可以在课外访问此功能吗?
  • @freude 您已将其定义为静态方法,然后将其称为:A._A__private()
  • 在这种情况下静态是什么意思?
  • __ 的目标是避免与子类发生冲突。它通过名称修改来完成此操作,将类名合并到变量中。事实上,这会使变量更难找到和访问是一个副作用。
  • 你确实需要一个装饰器。 Python 会特别对待类方法的第一个参数,所以除非你应用 @staticmethod 或类似的装饰器,否则你会得到一个错误。
【解决方案2】:

Python 不像许多其他语言那样具有“私有”的概念。它建立在同意成人原则的基础上,即您的代码的用户将负责任地使用它。按照惯例,以单下划线或双下划线开头的属性将被视为内部实现的一部分,但它们实际上并未对用户隐藏。但是,双下划线会导致属性名称的名称混乱。

另外,请注意 self 只是按照惯例特殊,而不是语言的任何特性。 Instance methods,当作为实例的成员调用时,会隐式地将实例作为第一个参数传递,但在方法本身的实现中,从技术上讲,该参数可以命名为您想要的任何任意东西。 self 只是为了便于理解代码的约定。因此,在方法的签名中不包含self 除了导致将隐式实例参数分配给签名中的下一个变量名之外,没有实际的功能影响。

这对于 class methods 当然是不同的,它接收类对象本身的实例作为隐式的第一个参数,而 静态方法,它不接收隐式论据。

【讨论】:

    【解决方案3】:

    这里有很多很棒的东西可以使用前导下划线进行混淆。就个人而言,我从语言设计决策中受益匪浅,因为它减少了理解和使用新模块所需的时间。

    但是,如果您决心实现私有属性/方法并且您愿意成为 unpythonic,您可以按照以下方式做一些事情:

    from pprint import pprint
    
    
    # CamelCase because it 'acts' like a class
    def SneakyCounter():
    
        class SneakyCounterInternal(object):
    
            def __init__(self):
                self.counter = 0
    
            def add_two(self):
                self.increment()
                self.increment()
    
            def increment(self):
                self.counter += 1
    
            def reset(self):
                print 'count prior to reset: {}'.format(self.counter)
                self.counter = 0
    
        sneaky_counter = SneakyCounterInternal()
    
        class SneakyCounterExternal(object):
    
            def add_two(self):
                sneaky_counter.add_two()
    
            def reset(self):
                sneaky_counter.reset()
    
        return SneakyCounterExternal()
    
    
    # counter attribute is not accessible from out here
    sneaky_counter = SneakyCounter()
    
    sneaky_counter.add_two()
    sneaky_counter.add_two()
    sneaky_counter.reset()
    
    # `increment` and `counter` not exposed (AFAIK)
    pprint(dir(sneaky_counter))
    

    很难想象您想要这样做的情况,但这是可能的。

    【讨论】:

    • 不错的技巧)这是一个很好的例子,如何不使用 python,但很有教育意义)
    【解决方案4】:

    Python 只是不做私有的。如果您愿意,您可以遵循约定并在名称前加上 单下划线,但其他编码人员应以绅士†的方式尊重这一点

    † 或绅士风度

    【讨论】:

      【解决方案5】:

      你只是不这样做:

      • Pythonic 方法是不使用docstrings 记录这些方法/成员,仅使用“真实”代码 cmets。并且约定是在它们后面附加一个单下划线或双下划线;

      • 然后你可以在你的成员前面使用双下划线,因此它们是类的本地(主要是名称修饰,即类外成员的真实名称变为:instance.__classname_membername)。在使用继承时避免冲突或在类的子级之间创建“私有空间”很有用。

      • 据我所知,可以使用元类“隐藏”变量,但这违反了 Python 的整个哲学,所以我不会对此进行详细介绍。

      【讨论】:

      • 双下划线激活名称修改。这绝对和私有不一样
      • 这正是我所说的:你不这样做,但你使用约定!
      • 您可以通过元类混淆属性访问,您可以制作只读属性并使用装饰器或元类实现私有/setter/getter 模式,但唯一的方法是将某些东西混淆到某人真正无法从 Python 代码中得到它的方法是将其隐藏在 C 扩展中。
      猜你喜欢
      • 2013-01-31
      • 1970-01-01
      • 2019-02-10
      • 2019-08-26
      • 2011-03-24
      • 2017-05-23
      • 2011-12-12
      • 2017-05-29
      相关资源
      最近更新 更多