为什么给自己赋值不起作用,如何解决这个问题?

4

我有一个类(字典列表),我希望它能够自行排序:

class Table(list):
…
  def sort (self, in_col_name):
    self = Table(sorted(self, key=lambda x: x[in_col_name]))

但是它根本不起作用。为什么?如何避免这种情况?除了外部排序,例如:
new_table = Table(sorted(old_table, key=lambda x: x['col_name'])

“难道不能直接操作对象本身吗?下面这种方式更有意义:”
class Table(list):
  pass

比:

class Table(object):
  l = []
  …
  def sort (self, in_col_name):
    self.l = sorted(self.l, key=lambda x: x[in_col_name])

我认为这个代码可以运行。 而且,总的来说,在Python中难道没有任何一种方法让一个对象改变自己(不仅仅是实例变量)吗?


1
根据您在问题文本和Thomas Orozco答案评论中使用的术语,我认为您可能对“更改”的真正含义感到困惑。在Python中,有两种类型的更改:突变和*(重新)绑定*。简单来说,如果一个对象改变了状态,那就是突变;如果一个名称改变了它所引用的内容,那就是重新绑定。您可能会发现这篇博客文章有助于解释这些概念。 - John Y
这个回答解决了你的问题吗?为什么Python对象中的'self'是不可变的? - BeRT2me
3个回答

17

你不能在方法内重新为 self 赋值并期望它会改变对该对象的外部引用。

self 只是作为参数传递给函数的一个名称,它指向调用该方法的实例。"给 self 赋值" 等价于:

def fn(a):
   a = 2
a = 1
fn(a)
# a is still equal to 1

self赋值会改变self这个名称所指向的对象,从一个Table实例转为另一个Table实例。但这只是改变了名称(在您的方法范围内),并不影响底层对象,也不影响指向它的其他名称(引用)。


使用list.sort进行原地排序:

def sort(self, in_col_name):
    super(Table, self).sort(key=lambda x: x[in_col_name])

好主意,但看起来list.sort()不支持key参数。(至少它会抛出TypeError,在Python文档中也没有找到key参数)谢谢。 - Gyula Sámuel Karli
@nneonneo 这可能不是很好=)(但可能会更容易调试!我一直觉得list.sort的文档很难找到!;)) - Thomas Orozco
@ThomasOrozco:我已经在原问题中添加了一些额外的考虑。 - Gyula Sámuel Karli
@ThomasOrozco:我明白这一点,但在Python中难道没有_任何_方法可以让一个对象改变自身(不仅仅是实例变量)吗? - Gyula Sámuel Karli
1
@GyulaSámuelKarli 你可以修改一个对象,但是你不能替换它。这是因为你的方法无法控制指向该对象的引用,其他代码片段可以改变它们。 - Thomas Orozco
显示剩余6条评论

1

Python是按值传递的,始终如此。这意味着对参数进行赋值永远不会影响函数外部。self只是您为其中一个参数选择的名称。


1
@MarkRansom:您错了。这是按值传递。您只是不知道“按值传递”是什么意思。 - newacct
1
@MarkRansom:它确实是按值传递。改变变量所指向的对象并不等同于对变量进行赋值。对切片进行赋值也不是赋值——它是在调用变量所指向的对象上的方法(.__setitem__())。 - newacct
@MarkRansom:那么,你熟悉Java、C等编程语言吗?你认为这些语言是按值传递的吗? - newacct
1
我经常听到的语义是,Python被认为是“按对象调用”的:http://effbot.org/zone/call-by-object.htm - qwwqwwq
@qwwqwwq:无论听到什么,重要的是实际上它等价于什么。为某物编造一个新名称并不能解释它是什么。"按对象调用"、"按共享调用"或其他人发明的术语都是按值调用的情况。 - newacct
显示剩余10条评论

0
我对这个问题很感兴趣,因为我从来没有想过这个。我查找了list.sort代码,看看它是如何实现的,但显然它是用C编写的。我想我知道你的意思; 如果没有要调用的super方法怎么办?那么你可以像这样做:
class Table(list):
    def pop_n(self, n):
        for _ in range(n):
            self.pop()

>>> a = Table(range(10))
>>> a.pop_n(3)
>>> print a
[0, 1, 2, 3, 4, 5, 6]

你可以调用self的方法,对self进行索引赋值以及实现在其类中(或者你自己实现)的其他操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接