你可能没有意识到它的所有含义

python中的下划线如何添加(七爪源码如何在)(1)

下划线或下划线 (_) 在 Python 中有很多用途,在开始本文之前,我对它们的某些方面都有所误解。我做了假设,阅读了不准确的信息,甚至从我参加的在线 Python 课程中收到了一个不完整的故事。

在本文中,我将演示下划线的用法,并就如何处理它给出我非常偏颇的建议。

在本文中,我将介绍 _ 的以下用法:

1. 下划线作为临时变量

_ 可用于在循环和赋值语句中代替“虚拟”变量,其中函数返回多个值,并且您希望忽略其中一个或多个值。

在循环中使用

如果您需要 Python 执行五次,最明显的方法是在 for 循环中使用虚拟变量,如此处所示。当我使用虚拟变量时,我巧妙地称它为……虚拟变量。下面显示的这种语法的缺点是我必须创建一个变量名,并希望任何阅读我的代码的人都清楚我永远不会再使用 dummy。

在这个简短的代码片段中很容易看出这一点,但如果循环更长、更复杂,就不会那么明显了。

for dummy in range(5): print("Hello world")

我可以使用 _ 代替。 下面的代码片段使用 _ 来实现相同的结果,同时向阅读代码的任何人发出信号,即我无意在代码中的任何位置使用循环控制变量。

for _ in range(5): print("Hello world")

我喜欢这种语法并鼓励其他人使用它。 然而,我没有意识到 Python 会像跟踪任何其他变量一样跟踪 _ 的值! 考虑下面的片段; 它打印整数 0 到 4:

for _ in range(5): print(_)

下划线甚至可以在嵌套循环中使用,但是跟踪 _ 变量的值会变得更加混乱。 考虑下面的这个循环。 你认为这段代码会产生什么? 你认为这段代码完成后会在 _ 中存储什么?

for _ in ['A','B','C']: print(_, end=": ") for _ in range(4): print(_, end=",") # what will this line print? print() # newline

这是上面代码的输出:

A: 0,1,2,3, B: 0,1,2,3, C: 0,1,2,3,

_ 可以跟踪它的作用域并根据使用的位置包含不同的值,你可能并不感到惊讶。 这让我很惊讶。 当嵌套的 for 循环完成时,您认为 _ 代表什么值? 它会包含一个有效值吗?

for _ in ['A','B','C']: pass for _ in range(4): passprint(_) # what will this line print?

令我惊讶的是上面的代码打印了 3。我预计它是无效的或打印 C。这种困惑使我得到了我的第一个建议:

切勿使用下划线作为函数或操作的输入。 如果您需要跟踪变量,请花时间给它一个描述性名称。

忽略返回值

一些 Python 函数返回多个值,但通常我不需要所有这些值。 _ 表示我忽略了该值。 假设我们有一个函数可以返回数据集的均值、众数和标准差,但我们现在只关心标准差。 我们可以这样写:

_, _, stdev = myfunction(large_dataset)

上面的代码告诉任何阅读我的代码的人,我只打算使用 myfunction 返回的三个值之一。 我更喜欢这种语法,而不是声明两个永远不会使用的附加变量。 然而,在幕后,Python 存储了 _ 的值——但是哪个呢? 当一行为 _ 分配两个不同的值时,您认为下面的代码会打印什么?

_, _, stdev = myfunction(large_dataset) print(_) # what will this line print?

上面的代码将显示 myfunction 返回的第二个值。 在我像这样在 Python REPL 中测试它之前,我不知道会发生什么:

>>> _, _, stdev = (1, 2, 3) >>> print(_) 2

这种困惑使我稍微修改了我之前给出的建议。

使用下划线忽略函数返回的值。 切勿使用下划线作为函数或操作的输入。 如果您需要跟踪变量,请花时间给它一个描述性名称。

2.下划线表示类属性是私有的

使用下划线信号给那些阅读和使用代码的人,您打算将变量设为私有,但遗憾的是 Python 并没有强制执行您的意图。 考虑以下荒谬的 Python 类,它定义了两个局部变量,一个以单个下划线开头,另一个以两个下划线开头。

class MyClass: def __init__(self): self._myprivate = 12 self.__myreallyprivate = 42

使用此类的任何人都可以轻松地直接访问这些变量,即使我希望这些变量是私有的。 Python 以不同的方式处理单下划线和双下划线(我稍后会解释),但在 Python 中无法像在 Java 或 C 中那样真正将类变量设为私有。 以下两行代码将表明这两个变量都可以直接读写:

instance = MyClass() print(dir(instance))

类实例的 dir 列表显示 _myprivate(单下划线)和 __myreallyprivate(双下划线)之间的唯一区别是 __myreallyprivate 被稍微混淆了。 我可以像这样覆盖作者最初打算私有的两个变量:

instance._myprivate = 'overwritten!!' instance._MyClass__myreallyprivate = 'overwritten!!'

您可能想知道如果使用简单的目录或阅读 Python 文档很容易检测到 __myreallyprivate 混淆的目的可能是什么。 当您创建具有相同局部变量名称的子类时,这种混淆会派上用场。

我将通过一个更简单的超类和子类示例来说明这一点,它们都实现了变量 __private。 考虑以下类定义:class Parent: def __init__(self): self.__private = 42class Child(Parent): def __init__(self): super().__init__() self.__private = 52

The child class above will have two obfuscated variables. I can display those variables with the following code:

mychild = Child() print(mychild._Parent__private) # prints 42 print(mychild._Child__private) # prints 52

这些关于类变量名中的单下划线和双下划线的观察让我得到了几条建议:

如果您是课程的作者:

如果您是其他人写过的课程的消费者:

3.下划线表示函数是私有的

与类变量一样,_ 用于表示将函数设为私有的意图,但 Python 也不强制执行此操作。 考虑一个名为 my_functions.py 的 Python 文件,其函数定义如下:

def _private(): return 'Hello world'

如果我使用 import my_functions 之类的通用导入语句导入这个 Python 模块,那么我仍然可以轻松访问我尝试标记为私有的模块。 这根本不是函数 _private 的作者想要的。

>>> import my_functions >>> my_functions._private 'Hello world'

任何函数 - 甚至是私有函数 - 都可以显式导入,如下所示。 同样,这根本不是函数 _private 的作者想要的。

>>> from my_functions import _private >>> _private() 'Hello world'

我什至可以为私有函数创建一个别名,所以我不会经常提醒我正在使用私有函数。

>>> from my_functions import _private as p >>> p() 'Hello world'

有趣的是,只有在使用通配符时才会导入私有函数,但 PEP8 风格指南不鼓励使用通配符,因为它会在命名空间中产生冲突或混乱。

>>> from my_functions import * >>> _private() Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name '_private' is not defined

不管 Python 允许什么和不允许什么,我的最后一条建议是:

尽管 Python 不会阻止您这样做,但请避免导入已标记为私有的函数。

最后的想法

我所有关于 _ 的建议都集中在在编写代码时表明你的意图以及在使用他们的代码时尊重他人的意图。 不过,尊重其他作者的意图根本不是礼貌。 这个建议是关于保护自己的。

关注七爪网,获取更多APP/小程序/网站源码资源!

,