引擎盖下的Python

https://d3vl3jxeh4ou3u.cloudfront.net/IISTD%20Hood%20Open.jpg?AWSAccessKeyId=AKIAJNCWKHG7HVI6CO4A&Expires=2082756065&Signature=NAF0lqkzTdtT0cWKcse5I8zRbfk%3D

当我学习python时,我曾经感到沮丧,因为我不明白为什么变量会中断。 我有时觉得自己正在编写好的代码,但我永远无法真正理解python的行为。 有时我会把它当作一个谜。 但是,我是一个很好奇的人,不能仅仅以良好的方式安居乐业,或者称其为魔术。 如果您不知道我在说什么,或者您从未在python中看到过奇怪的行为……请准备好,因为我们要破坏python……很多……

所以……如果您已经使用python编程了一段时间,那么您可能对默认参数有所了解。


中号是愚蠢的,所以我要使用空格而不是空格。如果您想运行代码,不便之处,敬请原谅。

def fizz(x = 5):

….print(x)

此功能将打印5。这并不难理解此功能的行为。

如果您这样写怎么办?

def buzz(y,x = []):

….x.append(y)

…。返回x

如果您要说:

test1 =嗡嗡声(1)

test2 =嗡嗡声(2)

您期望什么:

test1 = [1]

test2 = [2]

好吧,事实证明您得到了这个:

test1 = [1]

test2 = [1,2]

您可能会问:“那里发生了什么?”,但这是python中一个长期存在的问题,称为可变默认参数。 现在,在您开始大惊小怪之前,因为我对您说胡言乱语。 不用担心 我会解释。

python中的所有内容都是一个对象。 这对语言有深远的影响。 这意味着您无法在python中完成您无法在其他语言中完成的工作。 让我们看一个示例,该示例您无法使用大多数其他语言来完成,但是可以使用python。 请注意,如果您在求职面试中这样做……您不会得到这份工作……这很愚蠢,但事实证明python是不同的……我认为您可以在javascript中这样做,因为您具有函数作用域,但是无论如何……让我们继续前进,我们正在谈论python…

对于x范围(10):

….def fizz(x):

……..返回

该函数将起作用…定义10次是没有意义的,因为只有最后一个实例可以存活,但是它在python中起作用。 这样做的目的是解释python中的所有内容都是一个对象,因为这很重要…

现在,我们准备了简短的内容,以确保我们知道python中的所有对象都是对象。 让我们从以前解决问题。

def buzz(y,x = None):

…如果x ==无:

……..x = []

…x.append(y)

…返回x

am! 就像那个问题解决了一样。

test1 =嗡嗡声(1)

test2 =嗡嗡声(2)

现在,我们得到…

[1]

[2]

但是,如果您像我…您就像是的,那太好了,但是为什么这样做有效…

好吧,事实证明,这与范围有关。

我将向您展示另一个解决方案,您将了解它的工作原理以及工作原理。

def fizz(* args,** kwargs):

….def buzz(y,x = [])

……x.append(y)

……返回x

…传回嗡嗡声(* args,** kwargs)

现在,您可能认为这很奇怪…

让我们看一下python是如何真正对待这些函数的。

我要和你谈一个图书馆。 它允许您查看python字节码。 这将使我们能够反汇编功能以查看内部。 这将帮助我们了解发生了什么。

如果我们使用名为dis …的模块,则可以反汇编代码。 让我们看看它是什么样子…我们将从不良行为开始。

从dis导入dis

def buzz(y,x = []):

….x.append(y)

…。返回x

如果我们通过以下方式反汇编此函数:

打印(dis(嗡嗡声))

这是我们得到的:

6 0 LOAD_FAST 1(x)
2 LOAD_METHOD 0(追加)
4 LOAD_FAST 0(y)
6 CALL_METHOD 1
8个POP_TOP

7 10 LOAD_FAST 1(x)
12 RETURN_VALUE

现在,在您惊慌失措之前,让我们一起经历一下。

LOAD_FAST表示您正在加载局部变量,

LOAD_METHOD将append引入函数范围内……这意味着函数现在可以使用该方法了。 Python不知道该对象是否具有该方法……它只是尝试使用它。

然后,将值y从堆栈中弹出,并将其放在x变量的末尾并返回。

现在,我们得到讨论的不良行为的原因是X仅被加载一次……因此,如果您多次调用该函数。 X已经进入范围…

让我们将其与解决问题的反汇编功能进行比较。

def buzz(y,x = None):

…如果x ==无:

……..x = []

…x.append(y)

…返回x

变成:

13 0 LOAD_FAST 1(x)
2 LOAD_CONST 0(无)
4 COMPARE_OP 2(==)
6 POP_JUMP_IF_FALSE 12

14 8 BUILD_LIST 0
10 STORE_FAST 1(x)

15 >> 12 LOAD_FAST 1(x)
14 LOAD_METHOD 0(追加)
16 LOAD_FAST 0(y)
18 CALL_METHOD 1
20个POP_TOP

16 22 LOAD_FAST 1(x)
24 RETURN_VALUE

现在,您可以看到与之前所做的不同的主要区别……它每次都会重新构建列表……因此,可以消除不良行为。 有趣的是……因为我们让python每当它修复错误行为时都必须重建列表。

现在,让我们看一下最后一个函数,看看那里发生了什么。 让我们看看是否可以弄清楚它为什么起作用。

19 0 BUILD_LIST 0
2 BUILD_TUPLE 1
4 LOAD_CONST 1(<代码对象的嗡嗡声,位于0x7f29417a8540,文件“
python_bc_lesson.py”,第19行>)
6 LOAD_CONST 2(’fizz。 .buzz’)
8 MAKE_FUNCTION 1
10 STORE_FAST 2(嗡嗡声)

22 12 LOAD_FAST 2(嗡嗡声)
14 LOAD_FAST 0(参数)
16 LOAD_FAST 1(kwargs)
18 CALL_FUNCTION_EX 1
20 RETURN_VALUE

在0x7f29417a8540处反汇编<code object buzz,文件“ python_bc_lesson.py”,行
19>:
20 0 LOAD_FAST 1(x)
2 LOAD_METHOD 0(追加)
4 LOAD_FAST 0(y)
6 CALL_METHOD 1
8个POP_TOP

21 10 LOAD_FAST 1(x)

啊哈! 记得之前我曾告诉过您python中的所有内容都是一个对象的事……好吧,我们可以看到python使用MAKE_FUNCTION每次实际上创建一个新函数。 如果我们每次都创建一个新的buzz函数,那么它不会有不良行为,因为它每次都会重新创建一次。

希望这对您有所帮助。 我非常希望您开始使用dis函数来学习有关python字节码的更多信息。 我想发表更多这样的帖子,并且我收到了一些制作视频的要求。 如果您希望我制作一些视频教程,请鼓掌。 如有任何疑问,请在下面询问。 如果您对字节码的含义有任何疑问。 我建议您继续学习有关python 字节码的更多信息,而不是问每件事意味着什么,它可以帮助您了解python的工作原理,并且可以真正帮助您了解其中的一些极端情况。