美文网首页
Python基础知识必备:最强字符——下划线,你的编程小帮手

Python基础知识必备:最强字符——下划线,你的编程小帮手

作者: Python热衷者 | 来源:发表于2020-12-02 16:10 被阅读0次

    刚开始学Python的时候会发现,Python里会出现各种各样的下划线 “_”,而且位置都不相同,有时候在名称后面,有时候在前面,有时候还会在数字中间......这些下划线都分别代表了什么意思?

    这里我们将介绍的五种不同情况:

    图片1.png

    一.单下划线(_)

    单下划线通常在3种情况下使用:
    各位有什么不同的意见可以在下面评论,相互讨论一下,需要各种书籍教程也可以私信我。

    1.在解析程序中

    按照习惯,有时候单个独立下划线是用作一个名字,来表示某个变量是临时的或无关紧要的。

    _名称指向交互式解释器会话中,最后执行的语句结果。这首先是由标准CPython解释器完成的,其他解析器也紧随其后。

    图片2.png

    在下面的代码示例中,我将汽车元组拆分为单独的变量,但我只对颜色和里程值感兴趣。 但是,为了使拆分表达式成功运行,我需要将包含在元组中的所有值分配给变量。 在这种情况下,“_”作为占位符变量可以派上用场:

    >>> car = ('red', 'auto', 12, 3812.4)
    >>> color, _, _, mileage = car
    >>> color'red'
    >>> mileage3812.4
    >>> _12

    2.作为名称

    这与上一点有些关联,_ 用作一次性的名称。这为了让阅读代码的人知道,这里分配了某个名称,但是不打算使用。例如,在下面的循环中,我们不需要访问正在运行的索引,我们可以使用“_”来表示它只是一个临时值:

    >>> for _ in range(32):... print('Hello, World.')

    3.i18n

    我们可以看到 _ 还可以用作函数。在这种情况下,它通常用于执行国际化和本地化字符串转换查找的函数的名称。这似乎源于并遵循 C 语言的相关规则。如在Django文档中所见:

    from django.http import HttpResponsefrom django.utils.translation import gettext as _def my_view(request): output = _("Welcome to my site.") return HttpResponse(output)

    在任何还将 _ 用作 i18n 查找和翻译的代码块中,都应避免使用 _ 作为一次性使用的名称。

    二.名称前加单下划线(例如_total)

    名称前的单个下划线用于指定程序员将名称视为“私有”。这可以视为一种约定,下划线前缀的含义是告知其他程序员:以单个下划线开头的变量或方法仅供内部使用。。 正如Python文档所述:

    带有下划线的名称(例如 _spam)应被视为 API 的非公开部分(无论是函数、方法还是数据成员)。它应被视为实现细节,如有更改,恕不另行通知。

    *之所以说是一种约定,是因为它实际上对解析程序而言有着某种意义;如果我们从 <module / package> import *,除非以模块/软件包的 all 列表明确包含它们,否则不会导入以 _开头的名称。看看下面的例子:

    class Test: def init(self):
    self.foo = 11 self._bar = 23

    图片3.png

    三. 名称后的单下划线(例如 total_)

    一个变量的最合适的名称已经被一个关键字所占用,可以附加一个下划线来解决命名冲突:例如,如果你想命名某种格式,为了避免掩盖 Python 的内置格式,你可以将其命名为format_。

    >>> def make_object(name, class):

    SyntaxError: "invalid syntax"

    >>> def make_object(name, class_):... pass

    四. 数字字面中的单下划线(例如 100_000)

    PEP 515 指数建议扩展 Python 的语法,以便下划线可以用作整体、浮点和复杂数字文本中数字分组的可视分隔符。

    我们可以执行以下操作::

    十进制数按千分组amount = 10_000_000.0

    按字对十六进制地址进行分组addr = 0xCAFE_F00D
    用二进制文字将位分组为半字节flags = 0b_0011_1111_0100_1110
    相同,用于字符串转换flags = int('0b_1111_0000', 2)

    五. 姓名前的双下划线(例如__total)

    在名称(特别是方法名称)前使用双下划线(__)不是约定,只是对解析程序有特殊的意义。双下划线前缀会导致Python解释器重写属性名称,以避免子类中的命名冲突。

    >>> class A(object):... def _internal_use(self):... pass... def __method_name(self):... pass...
    >>> dir(A())['_A__method_name', ..., '_internal_use']

    这听起来很抽象。 因此,我组合了一个小小的代码示例来予以说明:

    class Test: def init(self): self.foo = 11 self._bar = 23 self.__baz = 23

    让我们用内置的dir()函数来看看这个对象的属性:

    >>> t = Test()
    >>> dir(t)['_Test__baz', 'class', 'delattr', 'dict', 'dir','doc', 'eq', 'format', 'ge', 'getattribute','gt', 'hash', 'init', 'le', 'lt', 'module','ne', 'new', 'reduce', 'reduce_ex', 'repr','setattr', 'sizeof', 'str', 'subclasshook','weakref', '_bar', 'foo']

    以上是这个对象属性的列表。 让我们来看看这个列表,并寻找我们的原始变量名称foo,_bar和__baz - 我保证你会注意到一些有趣的变化。

    -self.foo变量在属性列表中显示为未修改为foo。
    -self._bar的行为方式相同 - 它以_bar的形式显示在类上。 就像我之前说过的,在这种情况下,前导下划线仅仅是一个约定。 给程序员一个提示而已。
    -然而,对于self.__baz而言,情况看起来有点不同。 当你在该列表中搜索__baz时,你会看不到有这个名字的变量。

    图片4.png

    __baz出什么情况了?

    如果你仔细观察,你会看到此对象上有一个名为_Test__baz的属性。 这就是Python解释器所做的名称修饰。 它这样做是为了防止变量在子类中被重写。

    让我们创建另一个扩展Test类的类,并尝试重写构造函数中添加的现有属性:

    class ExtendedTest(Test):
    def init(self):
    super().init() self.foo = 'overridden'
    self._bar = 'overridden'
    self.__baz = 'overridden'

    现在,你认为foo,_bar和__baz的值会出现在这个ExtendedTest类的实例上吗? 我们来看一看:

    >>> t2 = ExtendedTest()
    >>> t2.foo'overridden'
    >>> t2._bar'overridden'
    >>> t2.__bazAttributeError: "'ExtendedTest' object has no attribute '__baz'"

    等一下,当我们尝试查看t2 .__ baz的值时,为什么我们会得到AttributeError? 名称修饰被再次触发了! 事实证明,这个对象甚至没有__baz属性:

    >>> dir(t2)['_ExtendedTest__baz', '_Test__baz', 'class', 'delattr','dict', 'dir', 'doc', 'eq', 'format', 'ge','getattribute', 'gt', 'hash', 'init', 'le','lt', 'module', 'ne', 'new', 'reduce','reduce_ex', 'repr', 'setattr', 'sizeof', 'str','subclasshook', 'weakref', '_bar', 'foo', 'get_vars']

    正如你可以看到__baz变成_ExtendedTest__baz以防止意外修改:

    >>> t2._ExtendedTest__baz'overridden'

    但原来的_Test__baz还在:

    >>> t2._Test__baz42

    双下划线名称修饰对程序员是完全透明的。 下面的例子证实了这一点:

    class ManglingTest:
    def init(self):
    self.__mangled = 'hello' def get_mangled(self):
    return self.__mangled

    >>> ManglingTest().get_mangled()'hello'
    >>> ManglingTest().__mangledAttributeError: "'ManglingTest' object has no attribute '__mangled'"

    名称修饰是否也适用于方法名称? 是的,也适用。名称修饰会影响在一个类的上下文中,以两个下划线字符("dunders")开头的所有名称:

    class MangledMethod:
    def __method(self): return 42 def call_it(self):
    return self.__method()

    >>> MangledMethod().__method()AttributeError: "'MangledMethod' object has no attribute '__method'"
    >>> MangledMethod().call_it()42

    这是另一个也许令人惊讶地运用名称修饰的例子:

    _MangledGlobal__mangled = 23class MangledGlobal: def test(self): return __mangled

    >>> MangledGlobal().test()23

    六. 在名称之前和之后加上双下划线(例如init

    这些是 Python 使用的特殊方法名称。对于我们来说,这只是一个约定,即 Python 系统使用与用户定义的名称不冲突的名称的一种方式。然后,我们通常会覆盖这些方法并为 Python 调用它们时定义所需的行为。例如,在编写类时init重写方法。

    没有什么可以阻止我们编写自己的特殊方法名称(但是最好别这么做):

    >>> class C(object):... def mine(self):... pass...
    >>> dir(C)... [..., 'mine', ...]

    尽量不要使用这种命名方式,只需要让Python定义的特殊名称遵循该约定即可。

    大家有什么不同观点,可以在下面踊跃评论。也可以在一起相互讨论一下,也可以私聊我进行讨论。

    相关文章

      网友评论

          本文标题:Python基础知识必备:最强字符——下划线,你的编程小帮手

          本文链接:https://www.haomeiwen.com/subject/atquwktx.html