Python3:如何控制用户定义的行为 class?
Python3: How can I control behavior of user defined class?
排序函数是这样的:
def iterative_insertion_sort(A):
'''Sort A iteratively.'''
for i, key in enumerate(A[1:]):
while i > -1 and A[i] > key:
print(id(key), id(A[i+1]))
A[i + 1] = A[i]
print(id(key), id(A[i+1]))
i = i - 1
A[i + 1] = key
排序功能可以很好地处理浮点数。
示例输出:./insertion_sort.py .23 .21 .26
140566861513304 140566861513304
140566861513304 140566861513256
[0.21, 0.23, 0.26]
但是我有一个名为 lList 的自定义 class,它具有 link(另一种自定义 class)类型的元素。当我输入 lList 的实例时,排序不正确。
样本输出:0.23 0.21 0.26 /
139732300992808 139732300992808
139732300992808 139732300992808
0.23 0.23 0.26 /
我们可以看到,在float的情况下,key和数组元素的id在赋值语句之后是不同的。
但是在自定义 class 的情况下,即使在赋值操作之后,键和数组元素的 id 也是相同的。这就是麻烦的根源。
我怀疑问题是因为浮点数是不可变的,而我的自定义 class 不是。我的问题是解决这种情况的最佳方法是什么?
注意:我希望对排序过程进行零到最小的更改。但是,我愿意自定义我的 lList 或 link class.
P.S。我刚刚只发布了相关的代码片段。如果还需要class的定义,你提一下,我也补上。
非常感谢!
更新:
link定义:
class link:
'''Implement link.'''
def __init__(self, key=None):
self.key = key
self.nxt = None
self.prev = None
def __str__(self):
'''Print link.'''
if self:
return str(self.key)
else:
return '/'
def __gt__(self, other):
return self.key > other.key
这是 lList 定义:
class lList:
'''Implement list of links.'''
def __init__(self):
self._head = None
self._numberOfLinks = 0
def list_insert(self, x):
'''Insert link x at the beginning of list.'''
x.nxt = self._head
if self._head:
self._head.prev = x
self._head = x
self._numberOfLinks += 1
def __str__(self):
'''Print list of links.'''
listFormat = ''
temp = self._head
while temp:
listFormat += str(temp) + ' '
temp = temp.nxt
else:
listFormat += '/ '
return listFormat
def get_data(self, position):
'''Return link from list at position position from head.'''
i = 0
temp = self._head
while i < position:
temp = temp.nxt
i += 1
return temp
def set_data(self, position, newLink):
'''Overwrite key of link at position distance from head of list with key of newLink.'''
i = 0
temp = self._head
while i < position:
temp = temp.nxt
i += 1
temp.key = newLink.key
def __getitem__(self, position):
if type(position) is slice:
return [self[i] for i in range(*position.indices(len(self)))]
elif type(position) is int:
if position < 0:
position += len(self)
if position >= len(self):
raise IndexError("The index (%d) is out of range."%position)
return self.get_data(position)
else:
raise TypeError("Invalid argument type.")
def __setitem__(self, position, value):
self.set_data(position, value)
def __len__(self):
return self._numberOfLinks
这是创建相同场景的最小代码:
test = lList()
l = link(.26)
test.list_insert(l)
l = link(.21)
test.list_insert(l)
l = link(.23)
test.list_insert(l)
print(test)
iterative_insertion_sort(test)
print(test)
是的,问题不在于不变性,而在于共享引用。
对浮点值进行排序时,您将对存储在 A[1]
中的浮点数的引用存储在 key
中。然后,您使用 A[0]
中的值更改 A[1]
中的引用。这意味着 A[1]
现在指向不同的对象,稍后将 A[0]
设置为 key
就可以了。
但是在对链表进行排序时,您永远不会更改 A[1]
。您更改 A[1]
的一个属性。 key
和 A[1]
继续指向同一个 link
实例,设置 A[1].key
在 key.key
中也可见。
您可以通过将整个 link
对象替换为新对象来解决此问题:
def set_data(self, position, newLink):
'''Overwrite key of link at position distance from head of list with key of newLink.'''
i = 0
temp = self._head
while i < position:
temp = temp.nxt
i += 1
newlink = link(newLink.key)
if temp.prev:
temp.prev.nxt = newlink
else:
self._head = newlink
newlink.nxt = temp.nxt
这相当于在 Python list
对象中设置一个新值:
(4302695168, 4302695168)
(4302695168, 4303149248)
0.21 0.23 0.26 /
排序函数是这样的:
def iterative_insertion_sort(A):
'''Sort A iteratively.'''
for i, key in enumerate(A[1:]):
while i > -1 and A[i] > key:
print(id(key), id(A[i+1]))
A[i + 1] = A[i]
print(id(key), id(A[i+1]))
i = i - 1
A[i + 1] = key
排序功能可以很好地处理浮点数。
示例输出:./insertion_sort.py .23 .21 .26
140566861513304 140566861513304
140566861513304 140566861513256
[0.21, 0.23, 0.26]
但是我有一个名为 lList 的自定义 class,它具有 link(另一种自定义 class)类型的元素。当我输入 lList 的实例时,排序不正确。
样本输出:0.23 0.21 0.26 /
139732300992808 139732300992808
139732300992808 139732300992808
0.23 0.23 0.26 /
我们可以看到,在float的情况下,key和数组元素的id在赋值语句之后是不同的。 但是在自定义 class 的情况下,即使在赋值操作之后,键和数组元素的 id 也是相同的。这就是麻烦的根源。
我怀疑问题是因为浮点数是不可变的,而我的自定义 class 不是。我的问题是解决这种情况的最佳方法是什么?
注意:我希望对排序过程进行零到最小的更改。但是,我愿意自定义我的 lList 或 link class.
P.S。我刚刚只发布了相关的代码片段。如果还需要class的定义,你提一下,我也补上。
非常感谢!
更新:
link定义:
class link:
'''Implement link.'''
def __init__(self, key=None):
self.key = key
self.nxt = None
self.prev = None
def __str__(self):
'''Print link.'''
if self:
return str(self.key)
else:
return '/'
def __gt__(self, other):
return self.key > other.key
这是 lList 定义:
class lList:
'''Implement list of links.'''
def __init__(self):
self._head = None
self._numberOfLinks = 0
def list_insert(self, x):
'''Insert link x at the beginning of list.'''
x.nxt = self._head
if self._head:
self._head.prev = x
self._head = x
self._numberOfLinks += 1
def __str__(self):
'''Print list of links.'''
listFormat = ''
temp = self._head
while temp:
listFormat += str(temp) + ' '
temp = temp.nxt
else:
listFormat += '/ '
return listFormat
def get_data(self, position):
'''Return link from list at position position from head.'''
i = 0
temp = self._head
while i < position:
temp = temp.nxt
i += 1
return temp
def set_data(self, position, newLink):
'''Overwrite key of link at position distance from head of list with key of newLink.'''
i = 0
temp = self._head
while i < position:
temp = temp.nxt
i += 1
temp.key = newLink.key
def __getitem__(self, position):
if type(position) is slice:
return [self[i] for i in range(*position.indices(len(self)))]
elif type(position) is int:
if position < 0:
position += len(self)
if position >= len(self):
raise IndexError("The index (%d) is out of range."%position)
return self.get_data(position)
else:
raise TypeError("Invalid argument type.")
def __setitem__(self, position, value):
self.set_data(position, value)
def __len__(self):
return self._numberOfLinks
这是创建相同场景的最小代码:
test = lList()
l = link(.26)
test.list_insert(l)
l = link(.21)
test.list_insert(l)
l = link(.23)
test.list_insert(l)
print(test)
iterative_insertion_sort(test)
print(test)
是的,问题不在于不变性,而在于共享引用。
对浮点值进行排序时,您将对存储在 A[1]
中的浮点数的引用存储在 key
中。然后,您使用 A[0]
中的值更改 A[1]
中的引用。这意味着 A[1]
现在指向不同的对象,稍后将 A[0]
设置为 key
就可以了。
但是在对链表进行排序时,您永远不会更改 A[1]
。您更改 A[1]
的一个属性。 key
和 A[1]
继续指向同一个 link
实例,设置 A[1].key
在 key.key
中也可见。
您可以通过将整个 link
对象替换为新对象来解决此问题:
def set_data(self, position, newLink):
'''Overwrite key of link at position distance from head of list with key of newLink.'''
i = 0
temp = self._head
while i < position:
temp = temp.nxt
i += 1
newlink = link(newLink.key)
if temp.prev:
temp.prev.nxt = newlink
else:
self._head = newlink
newlink.nxt = temp.nxt
这相当于在 Python list
对象中设置一个新值:
(4302695168, 4302695168)
(4302695168, 4303149248)
0.21 0.23 0.26 /