简体   繁体   English

Python:覆盖 __new__ 中的 __init__ 参数

[英]Python: override __init__ args in __new__

I have a __new__ method as follows:我有一个__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):
       ...

However, this fails with a deprecation warning:但是,这会失败并显示弃用警告:

DeprecationWarning: object.__new__() takes no parameters

SomeOtherClass can optionally get created as the args are processed, that's why they are being processed in __new__ and not in __init__ SomeOtherClass可以选择在处理 args 时创建,这就是为什么它们在__new__而不是在__init__处理的原因

What is the best way to pass new_args to __init__ ?new_args传递给__init__的最佳方法是什么?

Otherwise, I'll have to duplicate the processing of args in __init__ (without some_check)否则,我将不得不在__init__重复 args 的处理(没有 some_check)

The solution I went with in the end was to modify the newly created object in the __new__ and remove the __init__ method altogether:我最终采用的解决方案是修改__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

Edit : Came up with a better solution - the following wasn't behaving consistently enough.编辑:想出了一个更好的解决方案 - 以下行为不够一致。


I've solved my own question by stumbling on some unexpectedly simple behaviour:我偶然发现了一些出乎意料的简单行为,从而解决了我自己的问题:

return cls(*new_args)

instead of代替

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

It doesn't go into an infinite recursion, as I expected, so long as new_args is not the same as the original args given to __new__ .它不会像我预期的那样进入无限递归,只要new_args与提供给__new__的原始 args __new__

Since you don't even necessarily create a MyClass object, why not just put new into a separate function new(*args) that returns a MyClass or SomeOtherClass object as necessary?既然您甚至不必创建 MyClass 对象,为什么不将 new 放入一个单独的函数new(*args) ,该函数根据需要返回MyClassSomeOtherClass对象?

This will be a bit neater since you know that everywhere you put MyClass() you get a MyClass object back, and not potentially SomeOtherClass, which could be a bit confusing.这会更简洁一些,因为您知道在任何地方放置 MyClass() 都会返回一个 MyClass 对象,而不是潜在的 SomeOtherClass,这可能会有点混乱。

Well... this one works for me嗯……这个对我有用

>>> class B():
    def __new__(cls, *args):
        def is_str(x): return type(x) is 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 hasattr(self, '_skip_init') and self._skip_init:
            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)


EDIT :编辑 :

or here is a better and more general answer:或者这里有一个更好更通用的答案:

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


tried on my python 3.7.3 :在我的 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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM