Python:在__new__中覆盖__init__参数

6 投票
4 回答
5221 浏览
提问于 2025-04-15 20:33

我有一个 __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

SomeOtherClass 可以在处理参数时选择性地创建,这就是为什么这些参数是在 __new__ 中处理,而不是在 __init__ 中处理的原因。

那么,最好的方法是什么来把 new_args 传递给 __init__ 呢?

否则,我就得在 __init__ 中重复处理参数(没有 some_check 的情况下)。

4 个回答

2

编辑: 我找到了一种更好的解决方案 - 之前的方法表现得不够稳定。


我通过偶然发现了一些意想不到的简单行为,解决了我自己的问题:

return cls(*new_args)

而不是

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

只要 new_args 和传给 __new__ 的原始参数不一样,就不会出现我预期中的无限递归。

2

既然你不一定需要创建一个 MyClass 的对象,那为什么不把创建对象的过程放到一个单独的函数里呢,比如叫 new(*args),这个函数可以根据需要返回一个 MyClass 或者 SomeOtherClass 的对象呢?

这样做会更整洁,因为你可以确定每次调用 MyClass() 时,都会得到一个 MyClass 的对象,而不是可能得到 SomeOtherClass,这样就不会让人感到困惑了。

10

我最后选择的解决方案是修改新创建的对象,在__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

撰写回答