Python方法与函数如何实现原地修改传入对象本身的值
Python 原地修改传入对象的函数/方法实现原理
核心底层逻辑
Python的参数传递规则是传对象引用,既不是把整个对象拷贝一份传值,也不是其他语言里的传变量指针:
- 你把列表这类可变对象传给函数/方法时,传进去的是指向原对象的内存地址引用,没有任何对象拷贝的开销。
- 如果函数内部是直接修改对象本身的内容(比如修改列表某个索引的值、append/删除元素、给切片赋值),所有改动会直接作用在原对象上,外部访问原对象就能看到变化。
- 如果函数内部是给参数名重新赋值(比如
lst = [1,2,3]),这只是让函数内部的局部参数名指向了新对象,和外部的原对象完全无关,不会改动外部变量。
你之前写的kyub函数不生效,就是因为列表推导式会生成一个全新的列表,你直接return新列表,从头到尾没碰过传进来的原列表,原对象自然不会变。
实现原地修改的cube函数
不要创建新列表,直接遍历索引修改原列表的每个位置即可,不需要写返回值,默认返回None:
def cube(lst): for idx in range(len(lst)): lst[idx] = lst[idx] ** 3
测试效果完全符合你的预期:
>>> b = [-2,-1,0,3,4] >>> cube(b) >>> b [-8, -1, 0, 27, 64]
还有个更简洁的写法是用切片赋值,效果完全一致,也是原地修改:
def cube(lst): lst[:] = [i**3 for i in lst]
这里lst[:] = xxx的作用是把原列表里的所有元素逐个替换成右边可迭代对象的内容,不是让lst变量重新指向新列表,属于对原对象的修改操作。
.sort()和random.shuffle()的实现逻辑
这两个的本质和上面写的cube完全一样:
list.sort()是列表类型的内置方法,调用时拿到当前列表实例self的引用,直接在原内存空间里做排序、交换元素位置,全程不生成新列表,所以返回None,调用后原列表直接变成排序后的结果。- 你翻
random.py的源码就能看到,shuffle函数内部就是循环生成随机索引,交换传入列表里的元素位置,全程没有创建新列表,自然会直接改动传入的原列表。
这两个操作特意设计成返回
None,就是为了明确告知使用者这是原地操作,和sorted()这类返回新列表的非原地操作做区分,避免使用者混淆。
关于你要的a.square()方法
Python内置的list类型是C层面实现的,不允许直接给它追加自定义方法,要实现这个效果有两种方案:
- 方案1(推荐):和
cube一样写独立的square(lst)函数,这是Python更推荐的写法,没必要硬套面向对象的方法调用风格。 - 方案2:自定义一个继承自
list的子类,在子类里定义square方法:
class MyList(list): def square(self): for idx in range(len(self)): self[idx] = self[idx] ** 2
使用时实例化自定义的列表类即可:
>>> a = MyList([1,3,5,7,9]) >>> a.square() >>> a [1, 9, 25, 49, 81]
注意事项
- 只有可变对象支持原地修改:列表、字典、自定义类实例属于可变对象,可以通过函数修改内容;整数、字符串、元组是不可变对象,本身不允许修改内容,不管怎么写都没法在函数里原地改动原对象。
- 一定要区分「修改对象内容」和「重绑定局部变量」:
lst.append(1)、lst[0] = 2、lst[:] = [3,4]是修改原对象,会影响外部;lst = [1,2,3]是重绑定局部变量,和外部原对象无关。
内容的提问来源于stack exchange,提问作者Nikola Markovic
相关产品推荐
相关产品推荐

