

当我学习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的工作原理,并且可以真正帮助您了解其中的一些极端情况。