python - 为什么使用外部函数传递和更改类自变量可以操作可迭代对象,但不能操作变量?

标签 python class methods self

我在程序中遇到了一个非常难以追踪的错误,其中类自迭代被外部函数操纵,并发现一些自变量可以更改,而有些则不能。 是否可以使用外部函数像 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 是否会带来任何性能或其他问题? 即,如果我想操作自变量,是否必须使用方法?

最佳答案

Python 的参数传递对于所有对象都是相同的 - 传递原始对象(不是“副本”,不是“引用”,不是“指向”的指针 - 它是传递的对象本身),无论对象的类型如何,是否可变等。然后,这些对象将作为局部变量绑定(bind)到其匹配参数的名称。

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

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

现在,当您改变您的参数之一时,由于您正在处理传递给函数的对象,因此这些更改非常明显地在函数外部可见。

这里:

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})

这里:

def external_2(instance):
    # this one mutates `instance` - it's actually
    # syntactic sugar for 
    # `instance.__setattr__("my_var", instance.__getattribute__("my_var") + 1))`
    instance.my_var += 1

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

关于python - 为什么使用外部函数传递和更改类自变量可以操作可迭代对象,但不能操作变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51875174/

相关文章:

python - 在Python中同时迭代2个不同的字典

python RQ : pattern for callback

java - 如何将字符串传递给不同的方法

python - 在python中操作时间序列数据: summing series and aggregating over a time period

python - 使用keras mobilenet模型

android - 如何查看我的通讯录中的号码是否已注册到 Whatsapp?

python - 最佳实践 - 访问对象变量

c++ - 方法/构造函数及其返回值

方法中的 Java 隐式 "this"参数?

java - 无法访问java中另一个类中方法的返回实例