您是否应该在Python的非库代码中将下划线_用作“访问修饰符指示符”?

时间:2019-06-05 11:25:59

标签: python semantics

简介


因此,我一直在对下划线字符(_)进行一些研究。我知道它的大多数用例及其语义,因此我将它们放到下面作为回顾,最后我将得出一个问题,这个问题更多是关于两个用例的概念性问题。

用例


  1. 要将上一个评估存储在解释器中(在解释器之外has no special semantics,并且未定义为单个字符)
  2. In internationalization context,您将在其中导入别名为gettext的诸如_之类的函数
  3. 在十进制分组中以提高可见性(特别是进行3组分组,例如1_000_000)-请注意,仅从Python 3.6起,该功能才可用。

    示例:

    1_000_000 == 10**6  # equals True
    x = 1_000
    print(x)  # yields 1000
    
  4. 要“忽略”某些值,尽管我不会将其称为“忽略”,因为这些值仍会被评估并绑定到_,就像它是常规标识符一样。通常,我会找到比这更好的设计,因为我发现这是code smell。多年来,我很少使用这种方法,因此,我想每当您认为需要使用它时,就可以肯定地改进设计以免使用它。

    示例:

    for _ in range(10):
        # do stuff without _
        ping_a_server()
    
    # outside loop that still exists and it still has the last evaluated value
    print(_)  # would yield 9
    
  5. 跟踪标识符(used by convention以避免名称与内置标识符或保留字冲突):

    示例

    class Automobile:
    
        def __init__(self, type_='luxury', class_='executive'):
            self.car_type = type_
            self.car_class = class_
    
    noob_car = Automobile(type_='regular', class_='supermini')
    luxury = Automobile()
    
  6. 作为访问修饰符but only as a convetion,因为Python没有真正意义上的access modifiers

    单个下划线


    Acts as a weak "internal use" indicator。星号(_)会忽略所有以from M import *开头的标识符

    示例:

    a.py
    
    _bogus = "I'm a bogus variable"
    __bogus = "I'm a bogus with 2 underscores"
    ___triple = 3.1415
    class_ = 'Plants'
    type_ = 'Red'
    regular_identifier = (x for x in range(10))
    
    b.py
    from a import *
    print(locals())  # will yield all but the ones starting with _
    

    重要的概念观察

    我讨厌人们称其为私有(不包括 实际上在Python中没有什么是私有的)。 如果以类推的话,这将等效于Java's protected,因为在Java中,protected的意思是“ 派生的类和/或在同一包中”。因此,由于在模块级别,任何带有下划线_的标识符都具有与常规标识符不同的语义(而且我是从Python的角度讲语义,而不是从我们的角度来看CONSTANTS和{{1} }的含义不同,但对于Python而言,它们是同一件事),并且在谈论开始导入时会被导入机制忽略,这实际上表明您应该在该模块内或在其定义的类内使用这些标识符。或其派生子类。

    双下划线


    无需赘述,这将调用名称处理机制 当在类中的标识符上使用时,这会更困难,但是 同样,对于人们来说,从类中访问属性 该基类的子类。

问题


因此,我一直在阅读this book,专门针对初学者,在变量部分,作者说了类似的话:

  

变量可以以下划线_开头,尽管我们通常避免这样做,除非我们编写库代码供他人使用。

这让我想起了什么... 在私有项目中,甚至在没有被其他项目用作依赖项的开源项目中,将内容标记为非公开有意义吗? < / p>

例如,我有一个 open source web app ,我定期将更改推送到该位置。它主要用于教育目的,因为我想编写简洁的标准化代码,并将我在此过程中获得的任何新技能付诸实践。现在我想到了上述问题:使用标记事物为非公开的标识符有意义吗?

为了便于讨论,我们可以说,在将来,将有500个人积极地为该Web应用程序做出贡献,并且它在代码方面变得非常活跃。我们可以假设很多人将直接使用那些“受保护的”和“私有”的标识符(即使建议这样做,也不是所有人中的500名都知道最佳实践),但是因为这是一个非图书馆项目,所以< / strong>,这意味着它不是其他项目中的依赖项,也不是其他人使用的依赖项,可以在某种程度上让他们放心,因为这些方法不会在代码重构中消失,因为进行重构的开发人员可能会注意到该项目中的所有呼叫者,并将相应地进行重构(或者他不会注意到,但是测试会告诉他)。

显然,这在图书馆代码中是有意义的,因为所有人都取决于您的图书馆,所有可能的未来人都取决于您的图书馆,或者间接依赖您的图书馆的人(其他人将您的图书馆嵌入他们的图书馆并公开他们的图书馆)。库等),请注意,具有单尾划线双尾划线的标识符是一种实现细节,可以随时更改。因此,他们应该始终使用您的公共API。

如果没有人可以从事这个项目,我将其私有化,并且我将是唯一一个从事该项目的人怎么办?或一小部分人。 在此类项目中使用访问修饰符指示符有意义吗?

1 个答案:

答案 0 :(得分:5)

您的观点似乎是基于以下假设:私有或公共(或python中的等效建议)基于读写代码的开发人员。那是错误的。

即使您仅单独编写一个仅会使用的应用程序,如果设计正确,也会将其划分为模块,并且这些模块将公开接口并具有私有实现。

您同时编写了模块和使用该模块的代码,这并不意味着没有部分应该私有以保持封装。因此,是的,不管模块上开发人员的数量或依赖于该项目的开发人员数量如何,使用领先的下划线将模块的各个部分标记为私有都是有意义的。

编辑

我们真正讨论的是encapsulation,它是python和任何其他语言的软件工程通用的概念。

这个想法是将整个应用程序分成几个部分(我正在谈论的模块,可能是python包,但也可能是设计中的其他东西),并确定执行目标所需的几种功能中的哪一个是在那里实现(称为Single Responsibility Principle)。

这是design by contract的好方法,这意味着确定模块要向软件的其他部分公开的抽象,并将不包含在其中的所有内容隐藏为实现细节。其他模块不应仅依靠公开的功能来依靠您的实现,这样,只要您想提高性能,支持新功能,改善可维护性或任何其他原因,就可以自由更改。

现在,所有这些理论都与语言无关,与应用程序无关,这意味着每次设计软件时,都必须依靠语言提供的功能来设计模块和构建封装。

Python,据我所知,是少数几个(即使不是唯一的)Python之一,做出了有意的选择(我认为不好),它不强制执行封装,但允许开发人员访问所有内容,正如您所知已经发现了。

但是,这并不意味着上述概念不适用,而只是不能在语言级别上强制实施,并且必须以更宽松的方式实施(作为简单建议)。

封装实现并自由使用每条可用信息是否意味着一个坏主意?显然不是,建立架构仍然是一个很好的SOLID原则。

所有这些都不是真正必要的,它们只是良好的原则,经过时间和经验的证明,它们可以创建高质量的软件。

您应该在小型应用程序中使用没有人使用它吗?如果您希望事情按原样完成,那么应该。

有必要吗?嗯,您可以在没有它的情况下使用它,但是您可能会发现,此后您会花费更多的精力。

如果我不是编写库而是编写完整的应用程序,那该怎么办?那么,这是否意味着您不应该以一种干净整洁的方式编写它?