如何在Python中调用工作?

Pse*_*ias 5 python programming-languages

对于我正在研究的项目,我正在实现一个链表数据结构,它基于一对的概念,我定义为:

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
Run Code Online (Sandbox Code Playgroud)

where self.next_pairself.prev_pair是分别指向上一个和下一个链接的指针.

要设置链表,我有一个看起来像这样的安装功能.

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
Run Code Online (Sandbox Code Playgroud)

在程序的过程中,我正在建立这些链接列表的字典,并从一些链接中删除并在其他链接中添加它们.在修剪和重新安装之间,链接存储在中间阵列中.

在调试这个程序的过程中,我逐渐意识到我对Python将参数传递给函数的方式的理解是有缺陷的.考虑一下我写的这个测试用例:

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             
Run Code Online (Sandbox Code Playgroud)

当我运行此测试时,我得到以下结果.

0
0
10
None
10108
0
Run Code Online (Sandbox Code Playgroud)

我不明白为什么第二次调用p.next_pair产生不同于10108第一次调用(0)的结果().install不会返回一个Pair可以覆盖传入的对象(它返回None)的对象,并且它不像是在传递install指针.

我对call-by-value的理解是解释器复制传递给函数的值,使调用者的变量保持不变.例如,如果我说

def foo(x):
     x = x+1
     return x

baz = 2
y = foo(baz)
print y
print baz
Run Code Online (Sandbox Code Playgroud)

然后3,2应分别打印.事实上,当我在Python解释器中测试时,就会发生这种情况.

如果有人能指出我正确的方向,我真的很感激.

And*_*ter 8

将变量传递给函数时,Python不会复制任何内容.它既不是按值调用也不是逐个引用,但在这两个中它更类似于引用调用.您可以将其视为"按值调用,但值是参考".

如果将可变对象传递给函数,则在函数内修改该对象将影响对象出现的任何位置.(如果将不可变对象传递给函数,如字符串或整数,则根据定义,您根本无法修改该对象.)

这在技术上不是通过引用传递的原因是您可以重新绑定名称,以便名称完全引用其他内容.(对于不可变对象的名称,这是您可以对它们执行的唯一操作.)重新绑定仅存在于函数内的名称不会影响函数外部可能存在的任何名称.

在第一个包含Pair对象的示例中,您正在修改对象,因此您可以在函数外部看到效果.

在第二个示例中,您没有修改任何对象,只是将名称重新绑定到其他对象(在本例中为其他整数). baz是一个指向整数对象的名称(在Python中,一切都是对象,甚至是整数),其值为2.传递bazfoo(x),名称在堆栈中xfoo函数内部本地创建,并x设置为指针传递给函数的函数 - 与指针相同的指针baz.但是,xbaz不一样的东西,他们只包含指向同一个对象.在该x = x+1行上,x反弹指向一个值为3的整数对象,该指针是从函数返回的并用于将整数对象绑定到y的指针.

如果你重写了你的第一个例子,根据传递给它的Pair对象的信息在你的函数中显式创建一个新的Pair对象(无论这是一个你修改的副本,还是你做了一个修改构造数据的构造函数)那么你的函数就没有修改传入的对象的副作用.

编辑:顺便说一句,在Python中你不应该使用0占位符来表示"我没有价值" - 使用None.同样,你不应该像你似乎在做的那样0用来表示.但是所有的,并且在布尔表达式中进行评估,所以无论你使用哪一个,你都可以说出代替的东西.Falseflag0NoneFalseFalseif not flagif flag == 0


kin*_*all 8

在Python中,一切都是对象.简单赋值存储对分配给的名称中指定对象的引用.因此,将Python变量视为分配给对象的名称而不是存储在命名位置的对象更为直接.

例如:

baz = 2
Run Code Online (Sandbox Code Playgroud)

...存储在baz指向或引用的整数对象中2,存储在别处.(由于类型int是不可变的,Python实际上有一个小整数池,并且在2任何地方都重用相同的对象,但这是一个不需要太多关注的实现细节.)

当你调用时foo(baz),foo()的局部变量x也首先指向整数对象2.也就是说,foo()-local name x和全局名称baz是同一对象的名称2.然后x = x + 1执行.这变为x指向不同的对象:3.

重要的是要明白:x是不是持有箱2,并2进行增量来3.不,x最初指向2并指针然后更改为指向3.当然,由于我们没有改变对象baz指向的内容,它仍然指向2.

解释它的另一种方法是在Python中,所有参数传递都是按值进行的,但所有值都是对象的引用.

一个反直觉的结果是,如果一个对象是可变的,它可以通过任何引用进行修改,所有引用都将"看到"该更改.例如,考虑一下:

baz = [1, 2, 3]

def foo(x):
   x[0] = x[0] + 1

foo(baz)
print baz
>>> [2, 2, 3]
Run Code Online (Sandbox Code Playgroud)

似乎从我们的第一个例子很不同.但实际上,论证以同样的方式传递.foo()接收一个指向baz名称下的指针x,然后对其进行更改它的操作(在这种情况下,列表的第一个元素指向另一个int对象).不同之处在于名称x永远不会指向新对象; 它被x[0]修改为指向不同的对象.x本身仍然指向同一个对象baz.(实际上,在引擎盖下,赋值x[0]变为方法调用:x.__setitem__().)因此baz"看到"对列表的修改.怎么可能不行?

您没有看到整数和字符串的这种行为,因为您无法更改整数或字符串; 它们是不可变类型,当你修改它们时(例如x = x + 1)你实际上并没有修改它们,而是将变量名绑定到一个完全不同的对象.如果你改成baz一个元组,例如baz = (1, 2, 3),你会发现它foo()会给你一个错误,因为你不能分配一个元组的元素; 元组是另一种不可变类型."更改"元组需要创建一个新元组,然后赋值将变量指向新对象.

您定义的类的对象是可变的,因此您的Pair实例可以通过传入的任何函数进行修改 - 也就是说,可以添加,删除或重新分配属性到其他对象.这些东西都不会重新绑定指向您对象的任何名称,因此当前指向它的所有名称都将"看到"更改.