码农行者 码农行者
首页
  • Python

    • 语言特性
    • Django相关
    • Tornado
    • Celery
  • Golang

    • golang学习笔记
    • 对比python学习go
    • 模块学习
  • JavaScript

    • Javascript
  • 数据结构预算法笔记
  • ATS
  • Mongodb
  • Git
云原生
运维
垃圾佬的快乐
  • 数据库
  • 机器学习
  • 杂谈
  • 面试
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

DeanWu

软件工程师
首页
  • Python

    • 语言特性
    • Django相关
    • Tornado
    • Celery
  • Golang

    • golang学习笔记
    • 对比python学习go
    • 模块学习
  • JavaScript

    • Javascript
  • 数据结构预算法笔记
  • ATS
  • Mongodb
  • Git
云原生
运维
垃圾佬的快乐
  • 数据库
  • 机器学习
  • 杂谈
  • 面试
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Python

    • 语言特性

      • Python 提高开发效率的内置小工具
      • 「Python Tips」- 40中常用Python string用法
      • 「Python Tips」- python 魔法方法
      • 「Python Tips」- 命名元祖(namedtuple)使用指南
      • 「Python Tips」 - 10个一行代码的Python小技巧
      • 「Python Tips」 Pickle 的使用
      • 「Python Tips」 - pipenv 试用过程分享
      • 「Python Tips」 - Python 变量作用域详解
      • 「Python Tips」 - 卸载通过pkg安装的python
      • 「Python Tips」 - python 单元测试 unittest 工具使用
      • Python中的类属性和实例属性
      • Python中的类属性和实例属性-补充篇
      • Python中一种轻松实现并发编程的方法
      • Python GIL锁的来龙去脉
      • 「译」如何选择python项目的基础docker镜像
      • Python 段错误(Segmentation fault)排查
      • CentOS 7 部署Selenium相关脚本
      • 「译」python 3 值得尝试的一些技巧
      • 现在是否适合升级到Python3.8
      • Python 双下划线和单下划线的意义总结
        • 1 单前导下划线:_foo
        • 2 单尾随下划线:foo_
        • 3 单下划线:_
          • 定义临时或未使用的变量。
          • python 交互式解释器中最后一次评估的结果存储在“_”中
          • 用作数字分组的视觉分隔符
        • 4 双前导和尾随下划线:_foo_
        • 5 双前导下划线:__bar
        • 资源
    • Django

    • Best.Practices.for.Django

    • Djangorestfulframework

    • Celery

    • Tornado

    • Flask

    • FastApi

    • virtualenv

  • Golang

  • Javascript

  • 开发语言
  • Python
  • 语言特性
DeanWu
2023-04-04
目录

Python 双下划线和单下划线的意义总结

原文: https://towardsdatascience.com/whats-the-meaning-of-single-and-double-underscores-in-python-3d27d57d6bd1

在 Python 中命名变量和函数时,你有没有好奇过单下划线和双下划线的不同含义?

我最近研究了这个主题以复习一些基础知识,并决定将其写下来,因为我确实知道有一天我会再次需要它。

这篇文章详细介绍了下划线的不同使用方式。它将涵盖:

  • 单前导下划线:_foo
  • 单个尾随下划线:foo_
  • 单下划线:_
  • 双前导和尾随下划线(用于定义dunder 方法):bar
  • 双前导下划线:__bar

了解每种语法的含义以及何时应该使用它,将帮助您在可读性和效率方面提高代码质量。

这也将帮助您遵守标准的命名约定。

事不宜迟,让我们来看看一些实际的例子🔍

# 1 单前导下划线:_foo

变量、函数或方法名称前的单个前导下划线表示这些对象在内部使用。

这更像是对程序员的语法提示,并不是由 Python 解释器强制执行的,这意味着这些对象仍然可以从另一个脚本以一种方式访问​​另一种方式。

但是,在某些情况下无法访问具有前导下划线的变量。

module.py考虑以下定义两个变量的文件。

# module.py

public_variable = 2
_private_variable = 1/2
1
2
3
4

如果您从解释器或其他地方使用通配符导入(尽管强烈建议不要这样做),您会注意到具有前导下划线的变量在命名空间中不可用。

>>> from module import *

>>> public_variable
2

>>> _private_variable
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-69-28da499e5a9b> in <module>
----> 1 _private_variable

NameError: name '_private_variable' is not defined
1
2
3
4
5
6
7
8
9
10
11
12

但是,正如我们前面所说,_private_variable如果导入模块并直接调用变量名,仍然可以访问。

>>> import module

>>> module.public_variable
2

>>>module._private_variable
0.5
1
2
3
4
5
6
7

单前导下划线通常在类中用于定义“内部”属性。我在内部周围加上了双引号,因为在 Python 中没有内部属性这样的东西。前导下划线仍然是一个符号,应该这样对待。

如果你有一些不打算从类外部访问的属性,仅仅是因为它们仅在内部用于中间计算,你可以考虑为它们添加一个前导下划线——这将作为一个提示。

让我们看一个例子:考虑一个 Employee 类,它定义了一个_seniority属性,除其他外,该属性是计算薪酬包所需的。

import datetime

class Employee:
     def __init__(self, first_name, last_name, start_date):
         self.first_name = first_name
         self.last_name = last_name
         self.start_date = start_date
         self._seniority = self._get_seniority(start_date)

     def _get_seniority(self):
         today_date = datetime.datetime.today().date()
         seniority = (today_date - start_date).days
         return seniority
     
    def compute_compensation_package(self):
         # use self._seniority here.

        
first_name = "John"
last_name = "Doe"
start_date = datetime.date(2021, 12, 1)
employee = Employee(first_name, last_name, start_date)

>>> employee._seniority
53
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

如您所见,_seniority可以从类的外部访问该属性。

# 2 单尾随下划线:foo_

在某些情况下,您想要使用的变量名实际上是 Python 中的保留关键字,例如class, def, type,object等。

为避免这种冲突,您可以添加尾随下划线作为命名约定。

class_ = "A"
1

# 3 单下划线:_

在某些情况下,您会看到 python 开发人员使用单下划线。主要有下边几种:

# 定义临时或未使用的变量。

示例 #1

如果您不使用 for 循环的运行索引,您可以轻松地将其替换为单个下划线。

for _ in range(100):
   do_some_work()
1
2

示例 #2

如果你的函数返回一个包含五个元素的元组,但你只需要使用其中的两个(例如第一个和第四个),你可以使用下划线来命名剩下的三个。

cpu, _, _, memory, _ = extract_laptop_specs(laptop)
1

# python 交互式解释器中最后一次评估的结果存储在“_”中

In [1]: a = 2

In [2]: a
Out[2]: 2

In [3]: _
Out[3]: 2
1
2
3
4
5
6
7

# 用作数字分组的视觉分隔符

根据PEP 515 (opens new window),现在可以将下划线添加到数字文字中以提高长数字的可读性。

下面是一个示例,您可以将十进制数按千位进行分组。

In [2]: 1_000
Out[2]: 1000
  
In [3]: 1_000_000
Out[3]: 1000000

In [4]: 1_000_000_000
Out[4]: 1000000000
1
2
3
4
5
6
7
8

# 4 双前导和尾随下划线:foo

双前导和尾部下划线用于定义特殊的通用类方法,称为dunder 方法( D ouble Under score methods的缩写)。

Dunder 方法是您仍然可以覆盖的保留方法。它们具有特殊的行为并且被称为不同的。例如:

  • __init__ 用作类的构造函数
  • __call__ 用于使对象可调用
  • __str__ 用于定义当我们将对象传递给函数时屏幕上打印的内容print。

如您所见,Python 引入了这种命名约定来区分模块的核心方法和用户定义的方法。

如果您想了解更多有关 dunder 方法的信息,可以查看此链接 (opens new window)。

# 5 双前导下划线:__bar

双前导下划线通常用于名称修饰。

名称重整是解释器更改属性名称以避免子类中的命名冲突的过程。

让我们看下面的类来说明:

class Car:
    def __init__(self):
        self.color = "red"
        self._speed = 70
        self.__brand = "bmw"
        
car = Car()
1
2
3
4
5
6
7

car现在让我们使用内置方法检查对象的属性dir。

>>> print(dir(car))

['_Car__brand', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', 
 '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
 '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', 
 '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', 
 '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_speed', 'color']
1
2
3
4
5
6
7

我们注意到color和_speed可用但不可用__brand。

但是,有一个_Car__brand属性代替。这就是名称重整:解释器在属性前加上一个“_”加上类名。这样做是为了避免属性的值__brand在子类中被覆盖。

让我们创建一个子类并尝试覆盖以前的属性:

class ExtendedCar(Car):
    def __init__(self):
        super(ExtendedCar, self).__init__()
        self.color = "green"
        self._speed = 80
        self.__brand = "audi"
        
extended_car = ExtendedCar()
1
2
3
4
5
6
7
8

现在让我们检查对象的属性extended_car:

>>> print(dir(extended_car))

['_Car__brand', '_ExtendedCar__brand', '__class__', '__delattr__', '__dict__', 
 '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
 '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__',
 '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
 '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
 '__weakref__', '_speed', 'color']
1
2
3
4
5
6
7
8

如我们所见,添加了一个新属性:_ExtendedCar_brand而_Car_brand来自父类的 仍然存在。

如果我们尝试访问这两个属性:

>>> extended_car._Car__brand
'bmw'

>>> extended_car._ExtendedCar__brand
'audi'
1
2
3
4
5

我们注意到属性的值__brand(来自父类)没有被覆盖,即使ExtendedCar类的定义会建议这样做。

# 资源

我从来不知道单下划线和双下划线在许多不同的上下文中会有同样多的含义。

了解它们很有趣,并且一如既往,这是我的资源列表,供您深入研究该主题。

  • https://towardsdatascience.com/5-different-meanings-of-underscore-in-python-3fafa6cd0379
  • https://www.python.org/dev/peps/pep-0515/
  • https://www.python.org/dev/peps/pep-0008/
  • 一篇关于 Dunders 的法语博文:https 😕/he-arc.github.io/livre-python/dunders/index.html
  • https://www.section.io/engineering-education/dunder-methods-python/
上次更新: 2023/04/04, 18:43:11
现在是否适合升级到Python3.8
Django 简单入门及最佳实践

← 现在是否适合升级到Python3.8 Django 简单入门及最佳实践→

最近更新
01
chromebox/chromebook 刷bios步骤
03-01
02
redis 集群介绍
11-28
03
go语法题二
10-09
更多文章>
Theme by Vdoing | Copyright © 2015-2024 DeanWu | 遵循CC 4.0 BY-SA版权协议
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式