美文网首页
实例就地操作类属性

实例就地操作类属性

作者: 南瓜豆腐foobar | 来源:发表于2019-07-18 17:29 被阅读0次

    Python 中,类实例可以引用类的属性,就地操作符可以就地修改一个可变对象。那么通过实例引用一个可变类型的类属性,并进行就地操作,会产生什么结果?我开始的推测是类的属性会被改变但不会创建实例的属性,那实际的结果是什么呢?

    试一下:

    class T:
        li = []
    t = T()
    t.li += 'abc'
    print(t.__dict__)  # t 的属性中有 li,值为 ['a', 'b', 'c']
    print(T.__dict__)  # T 的属性中有 li,值为 ['a', 'b', 'c']
    

    果然翻车了,先说下我的思路:

    1. t.li 是引用的类属性 li,即 t.li 指向的是 T.li 的地址;
    2. 对列表的原地操作,只会改变列表的值,但不会改变其内存地址

    基于上以上两点的认知,所以我推测不会创建实例 tli 属性。但事实很打脸,为此我查了下官方文档。文档在赋值语句的介绍中指出:

    如果该对象为类实例并且属性引用在赋值运算符的两侧都出现,则右侧表达式 a.x 可以访问实例属性或(如果实例属性不存在)类属性。 左侧目标 a.x 将总是设定为实例属性,并在必要时创建该实例属性。 因此,a.x 的两次出现不一定指向相同的属性:如果右侧表达式指向一个类属性,则左侧表达式会创建一个新的实例属性作为赋值的目标:

    class Cls:
        x = 3             # class variable
    inst = Cls()
    inst.x = inst.x + 1   # writes inst.x as 4 leaving Cls.x as 3
    

    文档中说的很清楚,那么回到我们上面的测试:

    t.li += 'abc'
    

    这条语句可以理解为 t.li = t.li + list('abc'),这样可以解释为什么会创建 t 实例的属性,但是无法解释类 T 的属性 li 的值为什么会改变。继续查文档,在标准库的 operater 模块中,有关于原地操作符的介绍:

    Many operations have an "in-place" version. Listed below are functions providing a more primitive access to in-place operators than the usual syntax does; for example, the statement x += y is equivalent to x = operator.iadd(x, y).

    大概的意思:很多操作符都有一个”就地操作“版本。下列的函数提供了一种比常规语法更原始的方式实现就地操作,比如 x +=y 等效于 x = operator.iadd(x, y)

    好了,现在研究下 operator.iadd

    import operator
    li = []
    new_li = operator.iadd(li, 'abc')
    print(new_li)  # ['a', 'b', 'c']
    print(li)  # ['a', 'b', 'c']
    print(new_li == li)  # True
    print(li is new_li)  # True
    

    可以看到原地操作符在操作可变数据类型时,会更新原数据,并且返回值是更新后的原数据。那么 t.li += 'abc' 等效于 t.li = operator.iadd(t.li, 'abc'),这样,结合赋值语句的特性,就解释了结果。

    相关文章

      网友评论

          本文标题:实例就地操作类属性

          本文链接:https://www.haomeiwen.com/subject/kvfrlctx.html