Python:覆盖__new__中的__init__参数

arknldoa  于 2022-12-25  发布在  Python
关注(0)|答案(4)|浏览(131)

我有一个__new__方法,如下所示:

class MyClass(object):
   def __new__(cls, *args):
      new_args = []
      args.sort()
      prev = args.pop(0)
      while args:
         next = args.pop(0)
         if prev.compare(next):
            prev = prev.combine(next)
         else:
            new_args.append(prev)
            prev = next
         if some_check(prev):
            return SomeOtherClass()
      new_args.append(prev)
      return super(MyClass, cls).__new__(cls, new_args)

   def __init__(self, *args):
       ...

但是,此操作失败,并显示弃用警告:

DeprecationWarning: object.__new__() takes no parameters

在处理args时,可以选择创建SomeOtherClass,这就是为什么在__new__而不是__init__中处理它们的原因
new_args传递到__init__的最佳方法是什么?
否则,我将不得不在__init__中重复处理args(没有some_check)

ryevplcw

ryevplcw1#

我最终采用的解决方案是修改__new__中新创建的对象,并完全删除__init__方法:

def __new__(cls, *args):
   ... # as above
   new_self = super(MyClass, cls).__new__(cls)
   new_self.args = new_args
   return new_self

#def __init__(self, *args):
#    self.args = args
t5zmwmid

t5zmwmid2#

既然您甚至不必创建MyClass对象,为什么不将new放入一个单独的函数new(*args)中,该函数在必要时返回MyClassSomeOtherClass对象?
这样会更简洁一些,因为您知道无论在何处放置MyClass(),都会返回一个MyClass对象,而不是可能的SomeOtherClass,这可能会有点混乱。

j2datikz

j2datikz3#

编辑:找到了一个更好的解决方案-以下问题的表现不够一致。

我已经解决了我自己的问题,因为我偶然发现了一些出乎意料的简单行为:

return cls(*new_args)

代替

return super(MyClass, cls).__new__(cls, *new_args)

只要new_args与最初给__new__的参数不同,它就不会像我预期的那样进入无限递归。

o75abkj4

o75abkj44#

嗯......这个适合我

>>> class B:
    def __new__(cls, *args):
        def is_str(x): return isinstance(x, str)

        print("__new__ parameters are: ", args)

        if any(map(is_str, args)):
            new_args = list(map(int, args))
            ret = cls(*new_args)
            print('ret args', ret.args)
            ret._skip_init = True
            return ret

        return super().__new__(cls)

    def __init__(self, *args):
        if getattr(self, '_skip_init', False):
            print("init skipped")
            return
        print("__init__ parameters are: ", args)
        self.args = args

        
>>> b = B('1', '2', '3', '4')
__new__ parameters are:  ('1', '2', '3', '4')
__new__ parameters are:  (1, 2, 3, 4)
__init__ parameters are:  (1, 2, 3, 4)
ret args (1, 2, 3, 4)
init skipped
>>> 
>>> 
>>> b.args
(1, 2, 3, 4)
    • 编辑:**

或者这里有一个更好更一般的答案:

class B:
    def __new__(cls, *args, run_init=False):
        if not run_init:
            # process your argument here
            # change the argument as you want
            args = [5, 7, 4, 2, 3]  # changing the argument
            
            self = cls(*args, run_init=True)
            return self

        return super().__new__(cls)

    def __init__(self, *args, run_init=False):
        if not run_init:
            print("init skipped")
            return
        
        # your __init__ code goes here
        print("__init__ parameters are: ", args)
        self.args = args

在我的python 3.7.3上试了一下:

>>> b = B('1', '2', '3', '4')
__init__ parameters are:  (5, 7, 4, 2, 3)
init skipped
>>> b.args
(5, 7, 4, 2, 3)

相关问题