【问题标题】:how to replace self in a class method?如何在类方法中替换self?
【发布时间】:2017-10-23 07:56:27
【问题描述】:

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

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 <] ??

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

【问题讨论】:

    标签: python-3.x class self


    【解决方案1】:

    最简单的做法是直接对a 属性进行操作:

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

    这具有就地更改self 的行为。

    使用__iadd__的其他解决方案

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

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

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

    【讨论】:

    • 感谢您使用 iadd 的建议(事实上,您和 Martijn 在下面的回答都或多或少相同)。我可以根据 iaddadd 定义为 def add__(self,other): "r = self.deepcopy()" 然后 "r.__iadd__( other)" 最后是 "return r" 。 __iadd 现在可以正确更新 self 并且示例类的行为会始终如一......或者我认为......(见下文)
    • 您应该避免从另一个调用__add____iadd__ 之一,因为它们被认为是独立的方法。最佳做法是直接使用这些所谓的魔术方法中的基础数据(此处为 self.a)。
    【解决方案2】:

    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

    【讨论】:

      【解决方案3】:

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

      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__ 需要返回 self 否则不起作用!

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

      【讨论】:

      • 但显然这在问题 #1047034 的答案之一中得到了解释:这是因为(据我了解)x += y 被称为 x = x.__iad__(y)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-01-08
      • 2013-08-24
      • 1970-01-01
      • 2015-03-03
      • 1970-01-01
      • 2019-12-06
      • 2021-04-03
      相关资源
      最近更新 更多