Python,为什么传递和更改带有外部函数的类selfvariable用于操作iterable而不是变量

2024-06-01 02:14:31 发布

您现在位置:Python中文网/ 问答频道 /正文

我在我的程序中遇到了一个很难追踪的bug,其中一个类selfiterable被一个外部函数操纵,结果发现有些自变量可以更改,有些不能。 有没有可能在不传递整个类的情况下操作单个自变量(比如带有外部函数的int)?在

下面是一些示例代码:

class TestClass(object):
    def __init__(self):
        self.my_var = 0
        self.my_str = "Foo"
        self.my_tuple = (1, 2, 3)
        self.my_list = [1, 2, 3]
        self.my_dict = {"one": 1, "two": 2, "three": 3}
        self.manipulate_1()
        self.manipulate_2()

    def manipulate_1(self):
        external_1(self.my_var, self.my_list, self.my_str, self.my_tuple, self.my_dict)
        print(self.my_var)
        print(self.my_list)
        print(self.my_str)
        print(self.my_tuple[0])
        print(self.my_dict["one"])
        #prints 0, 15, Foo, 1, 15
    def manipulate_2(self):
        external_2(self)
        print("\n" + str(self.my_var))
        # prints 1

def external_1(instance_var, instance_list, instance_str, instance_tuple, instance_dict):
    instance_var += 1
    del instance_list[0]
    del instance_list[0]
    instance_list[0] = 15
    instance_str = "Bar"
    list(instance_tuple)[0] = 15
    instance_dict.update({"one": 15})


def external_2(instance):
    instance.my_var += 1


a = TestClass()

可以通过删除条目来操作列表,只需将其作为参数传递,而变量只能在传递self时进行操作。在

有没有办法操纵一个自变量。如果没有,传递self是否会带来任何性能问题或其他问题? 一、 如果我想操作一个自变量,是否必须使用方法?在


Tags: instance函数selfvarmydefonedict
2条回答

Python的参数传递对所有对象都是一样的-传递原始对象(不是“副本”,不是“引用”,不是“指针”-传递的是对象本身),而不管对象的类型,是否可变等等。然后这些对象作为局部变量绑定到其匹配参数的名称上。在

您观察到的差异实际上是完全不同操作之间的差异的结果:重新绑定(本地)名称和更改对象。在

由于参数是局部变量(实际上是局部名称),在函数体中重新绑定参数只会使该名称指向另一个对象,不会影响原始参数(除了减少引用计数器)。很明显,这在函数本身之外没有任何影响。在

现在当你改变你的一个参数,因为你正在处理你传递给函数的对象,这些变化很明显,在函数之外是可见的。在

这里:

def external_1(instance_var, instance_list, instance_str, instance_tuple, instance_dict):
    # this one rebinds the local name `instance_var`
    # to a new `int` object. Doesn't affect the object
    # previously bound to `instance_var`
    instance_var += 1

    # those three statement mutate `instance_list`, 
    # so the effect is visible outside the function
    del instance_list[0]
    del instance_list[0]
    instance_list[0] = 15

    # this one rebinds the local name `instance_str`
    # to the literal string "Bar". Same as for `instance_var`
    instance_str = "Bar"

    # this one creates a list from `instance_tuple`, 
    # mutate this list, and discard it. IOW it eats a 
    # couple processor cycles for nothing.  
    list(instance_tuple)[0] = 15

    # and this one mutates `instance_dict` so the
    # effect is visible outside the function
    instance_dict.update({"one": 15})

在这里:

^{pr2}$

正如我已经在评论中多次提到的,所有这些(以及更多的)is explained in full details in Ned Batchelder's reference article。在

请参阅附录。

这是正常和预期的行为。这是因为发送引用和发送值之间的差异。

使用external_1(self.my_var, self.my_list)

  • 您发送self.my_var,它是。这意味着external_1只接收一个值。该值是函数的局部值,因此类无法知道它是否已更改。请尝试此操作以查看此操作:

    def external_1(实例\变量,实例\列表): 实例_var+=1 print('这将打印一个:',instance\u var) 删除实例\u列表[0] 删除实例列表[0]

  • 变量'self.my_列表is an *reference*. This means you're sending the address of where to find the list. So the functionexternal_1`将转到该地址并更改那里的列表值。

使用external_2(self)发送对整个类的external_2的引用。所以它的作用与self.my_list完全相同。在

如果你还不完全理解,别担心,我花了不少时间才理解这些参考(或指针)。关于它们如何工作的教程和视频数以百万计。在

附录:

@bruno说得对,我没有从技术意义上正确地解释它,以及Python实际上是如何处理所有变量的。我只是想把发生的事情作为一个概述来解释,从C世界来看,我是如何理解它的。在

相关问题 更多 >