How Does Calling Work In Python?
对于我正在进行的项目,我正在实现一个链接列表数据结构,它基于对的概念,我将其定义为:
1 2 3 4 5 6 7 | class Pair: def __init__(self, name, prefs, score): self.name = name self.score = score self.preferences = prefs self.next_pair = 0 self.prev_pair = 0 |
其中,
要设置链接列表,我有一个这样的安装函数。
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | def install(i, pair): flag = 0 try: old_pair = pair_array[i] while old_pair.next_pair != 0: if old_pair == pair: #if pair in remainders: remainders.remove(pair) return 0 if old_pair.score < pair.score: flag = 1 if old_pair.prev_pair == 0: # we are at the beginning old_pair.prev_pair = pair pair.next_pair = old_pair pair_array[i] = pair break else: # we are not at the beginning pair.prev_pair = old_pair.prev_pair pair.next_pair = old_pair old_pair.prev_pair = pair pair.prev_pair.next_pair = pair break else: old_pair = old_pair.next_pair if flag==0: if old_pair == pair: #if pair in remainders: remainders.remove(pair) return 0 if old_pair.score < pair.score: if old_pair.prev_pair==0: old_pair.prev_pair = pair pair.next_pair = old_pair pair_array[i] = pair else: pair.prev_pair = old_pair.prev_pair pair.next_pair = old_pair old_pair.prev_pair = pair pair.prev_pair.next_pair = pair else: old_pair.next_pair = pair pair.prev_pair = old_pair except KeyError: pair_array[i] = pair pair.prev_pair = 0 pair.next_pair = 0 |
在整个程序过程中,我正在建立一个这些链接列表的字典,并从一些列表中去掉链接,然后将它们添加到其他列表中。在修剪和重新安装之间,链接存储在中间数组中。
在调试这个程序的过程中,我逐渐意识到我对Python向函数传递参数的方式的理解是有缺陷的。考虑一下我写的这个测试用例:
1 2 3 4 5 6 7 8 9 10 11 12 | def test_install(): p = Pair(20000, [3, 1, 2, 50], 45) print p.next_pair print p.prev_pair parse_and_get(g) first_run() rat = len(juggler_array)/len(circuit_array) pref_size = get_pref_size() print pref_size print install(3, p) print p.next_pair.name print p.prev_pair |
当我运行此测试时,得到以下结果。
1 2 3 4 5 6 | 0 0 10 None 10108 0 |
我不明白为什么第二次调用
我对按值调用的理解是,解释器复制传递给函数的值,使调用方的变量保持不变。例如,如果我说
1 2 3 4 5 6 7 8 | def foo(x): x = x+1 return x baz = 2 y = foo(baz) print y print baz |
然后分别打印
如果有人能给我指明正确的方向,我会非常感激的。
在Python中,一切都是一个对象。简单分配将对分配对象的引用存储在分配对象名称中。因此,可以更直接地将python变量看作是分配给对象的名称,而不是存储在命名位置的对象。
例如:
1 | baz = 2 |
…在
当调用
重要的是要理解:
另一种解释方法是,在Python中,所有参数传递都是按值传递的,但所有值都是对对象的引用。
这样做的一个反直觉的结果是,如果一个对象是可变的,它可以通过任何引用进行修改,并且所有引用都将"看到"更改。例如,考虑一下:
1 2 3 4 5 6 7 8 | baz = [1, 2, 3] def foo(x): x[0] = x[0] + 1 foo(baz) print baz >>> [2, 2, 3] |
这似乎与我们的第一个示例非常不同。但事实上,这场争论也是以同样的方式通过的。
您不会看到整型和字符串的这种行为,因为您不能更改整型或字符串;它们是不可变的类型,当您修改它们(例如
您定义的类的对象是可变的,因此您的
当向函数传递变量时,python不复制任何内容。它既不是按值调用,也不是按引用调用,但在这两者中,它更类似于按引用调用。您可以将其视为"按值调用,但该值是一个引用"。
如果将可变对象传递给一个函数,那么在函数内部修改该对象将影响它出现的所有地方的对象。(如果将不可变对象传递给函数,如字符串或整数,则根据定义,根本无法修改对象。)
这在技术上不是通过引用传递的原因是,您可以重新绑定一个名称,以便该名称完全引用其他内容。(对于不可变对象的名称,这是您唯一可以对其执行的操作。)重新绑定仅存在于函数内部的名称不会影响可能存在于函数外部的任何名称。
在使用
在第二个示例中,您没有修改任何对象,只是将名称重新绑定到其他对象(本例中的其他整数)。
如果您重写了第一个示例,根据传递给函数的pair对象的信息(无论这是您随后修改的副本,还是您在构造时修改数据的构造函数),在函数内部显式地创建一个新的pair对象,那么您的函数将不具有修改p对象的副作用。参与进来。
编辑:顺便说一下,在python中,不应该使用
型
我建议您忘记实现一个链接列表,只需使用一个python
跟踪链接列表中链接的python循环将以python解释器的速度运行,也就是说,速度很慢。如果您只使用内置的
如果您需要类似于列表的东西,但是插入和删除速度都很快,那么您能让
但是,如果您真的需要链表,我建议您查找由其他人编写和调试的代码,并根据您的需要进行调整。谷歌搜索"python linked list recipe"或"python linked list module"。
我将加入一个稍微复杂的因素:
1 2 3 4 | >>> def foo(x): ... x *= 2 ... return x ... |
对于数字、列表和字符串,使用我知道支持的方法定义稍微不同的函数。
首先,用字符串调用它:
1 2 3 4 5 6 | >>> baz ="hello" >>> y = foo(baz) >>> y 'hellohello' >>> baz 'hello' |
接下来,用列表调用它:
1 2 3 4 5 6 7 | >>> baz=[1,2,2] >>> y = foo(baz) >>> y [1, 2, 2, 1, 2, 2] >>> baz [1, 2, 2, 1, 2, 2] >>> |
对于字符串,参数不会被修改。通过列表,可以修改参数。
如果是我,我会避免在方法中修改参数。