Python 中的调用是如何工作的? [英] How Does Calling Work In Python?

查看:30
本文介绍了Python 中的调用是如何工作的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

For a project I'm working on, I'm implementing a linked-list data-structure, which is based on the idea of a pair, which I define as:

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

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

where self.next_pair and self.prev_pair are pointers to the previous and next links, respectively.

为了设置链表,我有一个看起来像这样的安装函数.

To set up the linked-list, I have an install function that looks like this.

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

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

Over the course of the program, I am building up a dictionary of these linked-lists, and taking links off of some and adding them in others. Between being pruned and re-installed, the links are stored in an intermediate array.

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

Over the course of debugging this program, I have come to realize that my understanding of the way Python passes arguments to functions is flawed. Consider this test case I wrote:

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             

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

When I run this test, I get the following result.

0
0
10
None
10108
0

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

What I don't understand is why the second call to p.next_pair produces a different result (10108) than the first call (0). install does not return a Pair object that can overwrite the one passed in (it returns None), and it's not as though I'm passing install a pointer.

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

My understanding of call-by-value is that the interpreter copies the values passed into a function, leaving the caller's variables unchanged. For example, if I say

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

baz = 2
y = foo(baz)
print y
print baz

然后 32 应该被分别打印出来.事实上,当我在 Python 解释器中测试它时,就会发生这种情况.

Then 3 and 2 should be printed, respectively. And indeed, when I test that out in the Python interpreter, that's what happens.

如果有人能在这里指出正确的方向,我将不胜感激.

I'd really appreciate it if anyone can point me in the right direction here.

推荐答案

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

In Python, everything is an object. Simple assignment stores a reference to the assigned object in the assigned-to name. As a result, it is more straightforward to think of Python variables as names that are assigned to objects, rather than objects that are stored in named locations.

例如:

baz = 2

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

... stores in baz a pointer, or reference, to the integer object 2 which is stored elsewhere. (Since the type int is immutable, Python actually has a pool of small integers and reuses the same 2 object everywhere, but this is an implementation detail that need not concern us much.)

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

When you call foo(baz), foo()'s local variable x also points to the integer object 2 at first. That is, the foo()-local name x and the global name baz are names for the same object, 2. Then x = x + 1 is executed. This changes x to point to a different object: 3.

理解这一点很重要:x 不是一个包含 2 的盒子,2 然后增加到 3.不,x 最初指向 2,然后该指针更改为指向 3.自然,由于我们没有改变baz指向的对象,它仍然指向2.

It is important to understand: x is not a box that holds 2, and 2 is then incremented to 3. No, x initially points to 2 and that pointer is then changed to point to 3. Naturally, since we did not change what object baz points to, it still points to 2.

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

Another way to explain it is that in Python, all argument passing is by value, but all values are references to objects.

这样做的一个违反直觉的结果是,如果一个对象是可变的,则可以通过任何引用对其进行修改,并且所有引用都将看到"更改.例如,考虑这个:

A counter-intuitive result of this is that if an object is mutable, it can be modified through any reference and all references will "see" the change. For example, consider this:

baz = [1, 2, 3]

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

foo(baz)
print baz
>>> [2, 2, 3]

看起来与我们的第一个示例非常不同.但实际上,该参数以相同的方式传递.foo() 在名称 x 下接收一个指向 baz 的指针,然后对其执行一个改变它的操作(在这种情况下,第一个列表的元素指向不同的 int 对象).不同之处在于名称 x 从不指向新对象;x[0] 被修改为指向不同的对象.x 本身仍然指向与 baz 相同的对象.(实际上,在底层,对 x[0] 的赋值变成了一个方法调用:x.__setitem__().)因此 baz看到"对列表的修改.怎么可能不行?

This seems very different from our first example. But in reality, the argument is passed the same way. foo() receives a pointer to baz under the name x and then performs an operation on it that changes it (in this case, the first element of the list is pointed to a different int object). The difference is that the name x is never pointed to a new object; it is x[0] that is modified to point to a different object. x itself still points to the same object as baz. (In fact, under the hood the assignment to x[0] becomes a method call: x.__setitem__().) Therefore baz "sees" the modification to the list. How could it not?

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

You don't see this behavior with integers and strings because you can't change integers or strings; they are immutable types, and when you modify them (e.g. x = x + 1) you are not actually modifying them but binding your variable name to a completely different object. If you change baz to a tuple, e.g. baz = (1, 2, 3), you will find that foo() gives you an error because you can`t assign to elements of a tuple; tuples are another immutable type. "Changing" a tuple requires creating a new one, and assignment then points the variable to the new object.

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

Objects of classes you define are mutable and so your Pair instance can be modified by any function it is passed into -- that is, attributes may be added, deleted, or reassigned to other objects. None of these things will re-bind any of the names pointing to your object, so all the names that currently point to it will "see" the changes.

这篇关于Python 中的调用是如何工作的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆