我正在创建一个类sequence
,该类继承自内置的list
,并将保存第二个类的有序集合:d0
,该类继承自int
。 d0
除其int
值外,还必须包含一个辅助值i
,该值指示其在类中的位置以及对类本身的引用。
我的理解是因为int
是不可变的类型,我必须使用__new__
方法,并且由于它具有其他属性,因此我需要使用__init__
。
我一直在努力使它起作用,我已经探索了一些选择。
尝试1:
class sequence(list):
def __init__(self, data):
for i, elem in enumerate(data): self.append( d0(elem, i, self) )
class d0(int):
def __new__(self, val, i, parent):
self.i = i
self.parent = parent
return int.__new__(d0, val)
x = sequence([1,2,3])
print([val.i for val in x])
这对我来说是最直观的,但是每次分配self.i
时,它都会覆盖i
中d0
的所有其他实例的sequence
属性。尽管我不清楚为什么会发生这种情况,但我知道__new__
并不是实例化对象的地方。
尝试2:
class sequence(list):
def __init__(self, data):
for i, val in enumerate(data): self.append( d0(val, i, self) )
class d0(int):
def __new__(cls, *args):
return super().__new__(cls, *args)
def __init__(self, *args):
self = args[0]
self.i = args[1]
self.parent = args[2]
x = sequence([1,2,3])
print([val.i for val in x])
这引发了TypeError: int() takes at most 2 arguments (3 given)
,尽管我不确定为什么。
尝试3:
class sequence(list):
def __init__(self, data):
for i, val in enumerate(data):
temp = d0.__new__(d0, val)
temp.__init__(i, self)
self.append(temp)
class d0(int):
def __new__(cls, val):
return int.__new__(d0, val)
def __init__(self, i, parent):
self.i = i
self.parent = parent
x = sequence([1,2,3])
print([val.i for val in x])
这可以完成任务,但是很麻烦,否则必须显式调用__new__
和__init__
实例化一个对象就感到很奇怪。
完成此操作的正确方法是什么?我也希望对尝试1和2中的不良行为做出任何解释。
答案 0 :(得分:1)
首先,您的sequence
到目前为止还不是很多类型:对其调用append
不会保留其索引性质(更不用说sort
或切片分配!)。如果您只想制作如下所示的列表,则只需编写一个函数,该函数返回一个list
。请注意,list
本身的行为就像这样的函数(在Python 1天里它就是!),因此您仍然经常可以像类型一样使用它。
因此,让我们谈谈d0
。抛开从int
衍生是否是一个好主意的问题(至少比正确地从list
衍生的工作少!),您有一个正确的基本想法:您需要 __new__
表示不可变(基本)类型,因为在__init__
时,选择其值为时已晚。这样做:
class d0(int):
def __new__(cls,val,i,parent):
return super().__new__(cls,val)
请注意,这是一个 class 方法:尚无实例,但是我们确实需要知道我们要实例化哪个类(如果有人继承自d0
,该怎么办?)。这是尝试#1 错误的原因:它认为第一个参数是为其分配属性的实例。
请注意,我们仅传递一个(其他)参数:int
无法使用我们的辅助数据。 (也不能忽略它:考虑int('f',16)
。)因此失败了#2 :它把所有参数都发送了。
我们现在可以安装其他属性,但是正确的做法是使用__init__
将制造对象与初始化分开:
# d0 continued
def __init__(self,val,i,parent):
# super().__init__(val)
self.i=i; self.parent=parent
请注意,所有参数都将再次出现,甚至val
也被我们忽略。这是因为调用一个类仅涉及一个参数列表( cf。 d0(elem,i,self)
),因此__new__
和__init__
必须共享它。 (因此,将val
传递给int.__init__
在形式上是正确的,但是它会做什么呢?因为我们知道int
已经完全设置好了,所以调用它根本没有用。 。)使用#3 很痛苦,因为它没有遵循此规则。