[英]Python: override __init__ args in __new__
我有一个__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
可以选择在处理 args 时创建,这就是为什么它们在__new__
而不是在__init__
处理的原因
将new_args
传递给__init__
的最佳方法是什么?
否则,我将不得不在__init__
重复 args 的处理(没有 some_check)
我最终采用的解决方案是修改__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
编辑:想出了一个更好的解决方案 - 以下行为不够一致。
我偶然发现了一些出乎意料的简单行为,从而解决了我自己的问题:
return cls(*new_args)
代替
return super(MyClass, cls).__new__(cls, *new_args)
它不会像我预期的那样进入无限递归,只要new_args
与提供给__new__
的原始 args __new__
。
既然您甚至不必创建 MyClass 对象,为什么不将 new 放入一个单独的函数new(*args)
,该函数根据需要返回MyClass
或SomeOtherClass
对象?
这会更简洁一些,因为您知道在任何地方放置 MyClass() 都会返回一个 MyClass 对象,而不是潜在的 SomeOtherClass,这可能会有点混乱。
嗯……这个对我有用
>>> 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)
编辑 :
或者这里有一个更好更通用的答案:
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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.