如何在类方法中替换self?

时间:2017-10-23 07:56:27

标签: python-3.x class self

我有一个简单的类编码为:

class test():
    def __init__(self, a):
        self.a = a

    def __add__(self, other):
        # returns a test object which is the sum of self and other
        return test(self.a + other.a)

    def double(self):
        print()
        print ('meanwhile inside test.double() ...')
        operand = test(self.a)     # a new test object similar to self
        print('    self:   ', self)
        print('    operand:', operand)
        self += operand
        print(' -> self:   ', self)

    def __str__(self):
        return '[> %r <]' % self.a

S1 = test(1)
S2 = test(2)
S = S1 + S2
print('sums do work : S = S1 + S2 =', S1, '+', S2, '=', S)

S.double()
print()
print('but S doubled =', S, '??')

输出结果为:

sums do work : S = S1 + S2 = [> 1 <] + [> 2 <] = [> 3 <]

meanwhile inside test.double() ...
    self:    [> 3 <]
    operand: [> 3 <]
 -> self:    [> 6 <]

but S doubled = [> 3 <] ??

那么,我怎样才能实现这种行为(即,当从被调用的方法返回时,自我实例将被正确更新)而不必复制所有属性(在实际代码中这些属性很多,并且每次都需要添加属性以重新检查代码以确保复制完成)?

3 个答案:

答案 0 :(得分:0)

self只是一个变量,是对实例的引用。将它绑定到另一个对象只会将设置为一个引用,而不是对该实例的任何其他引用。您无法替换其他参考。返回新实例和double()执行此操作的文档(以便调用方必须重新绑定其引用),或者更新self.a

返回新对象:

class test():
    # ...

    def double(self):
        return self + self  # no need to create a new instance here

S = S.double()

或就地更新:

class test():
    # ...

    def double(self):
        self.a *= 2

您选择哪一个取决于您是否希望test()个实例是可变的。不可变对象将始终返回操作的新实例,可变对象应该就地应用操作。

您可能想要了解Python名称,我建议Ned Batchelder's Facts and myths about Python names and values

答案 1 :(得分:0)

最简单的方法是直接在a属性上操作:

def double(self):
    self.a *= 2

这样做就是在原地改变self

使用__iadd__

的其他解决方案

使用self +=目前不会就地更新self,因为__add__函数(正确)会返回新的test对象。如果由于某种原因希望不直接对a方法中的double属性进行操作,则可以添加被调用的__iadd__方法(而不是__add__存在)使用+=时:

def __iadd__(self, other):
    self.a += other.a
def double(self):
    self += self

此外,我遗漏了operarand的创建,因为完全允许other实际上与self相同。

答案 2 :(得分:0)

所以最后这段代码按预期工作

from copy import deepcopy

class test():
    def __init__(self, a):
        self.a = a

    def __iadd__(self, other):
        # this is the actual implementation of the sum of 2 objects
        self.a += other.a
        return self # <== required see comment below

    def __add__(self, other):
        # note: in the code I'm writing this is a relatively complex 
        # operation. So I would like to avoid to have it written in 
        # two places. So the actual implementation for the sum happens
        # in __iadd__
        r = deepcopy(self)
        r += other # <== implemented as r = r.__iadd__(other) behind the scenes
        return r   #     this is why the return self in __iadd__ is necessary !

    def double(self):
        print()
        print ('meanwhile inside test.double() ...')
        operand = test(self.a)     # a new test object similar to self
        print('    self:   ', self)
        print('    operand:', operand)
        self += operand
        print(' -> self:   ', self)

    def __str__(self):
        return '[> %r <]' % (self.a)


S1 = test(1)
S2 = test(2)
S = S1 + S2
print('sums do work : S = S1 + S2 =', S1, '+', S2, '=', S)

S.double()
print()
print('and S doubled =', S, '!!')

这似乎按预期工作......

sums do work : S = S1 + S2 = [> 1 <] + [> 2 <] = [> 3 <]

meanwhile inside test.double() ...
    self:    [> 3 <]
    operand: [> 3 <]
 -> self:    [> 6 <]

and S doubled = [> 6 <] !!

注1:__iadd __需要返回自我,否则无效!

注2:我不确定我是否做得对(即在答案集中添加更多问题)......但我认为如果我在上面的问题中添加这个问题,那么讨论的流程就很难了跟随。