Py学习  »  Python

Python函数通过引用、值或对象调用

Albert G Lieu • 6 年前 • 1782 次点击  

python是通过引用、值还是对象传递的?即使在阅读了一些帖子之后,它也相当令人困惑。我还是不明白我是怎么得到这个奇怪的结果的。有谁能帮忙解释一下这个奇怪的结果是如何产生的,以及如何达到正确的结果?

我试着用python旋转一个2D列表。当没有调用如案例1所示的函数时,我得到了正确的结果。但是,当我把代码放在函数中时,它会返回一个奇怪的结果,这不是我所期望的。为什么案例2中的输出是[9,6,3],[8,5,2],[7,4,1]?它是从哪里来的?

case 1: 
matrix = [[1,2,3],[4,5,6],[7,8,9]]
matrix = matrix[::-1]
for i in range(len(matrix)):
    for j in range (len(matrix[0])):
        if i<=j:
            matrix[j][i],matrix[i][j] =matrix[i][j], matrix[j][i]

print(matrix)

case 2: 
def rotate(matrix) -> None:
    print('1',matrix)
    matrix = matrix[::-1]
    print('2',matrix)
    for i in range(len(matrix)):
        for j in range (len(matrix[0])):
            if i<=j:
                matrix[j][i],matrix[i][j] =matrix[i][j], matrix[j][i]
    print('3',matrix)

matrix = [[1,2,3],[4,5,6],[7,8,9]]
rotate(matrix)
print(matrix)


case 1 output:
[[7, 4, 1], [8, 5, 2], [9, 6, 3]]

case 2 output:
1 [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
2 [[7, 8, 9], [4, 5, 6], [1, 2, 3]]
3 [[7, 4, 1], [8, 5, 2], [9, 6, 3]]
[[9, 6, 3], [8, 5, 2], [7, 4, 1]]
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/54587
文章 [ 2 ]  |  最新文章 6 年前
Jmonsky
Reply   •   1 楼
Jmonsky    6 年前

可以通过返回矩阵的旋转副本而不是对其进行变异来解决此问题。

def rotate(matrix):
    print('1',matrix)
    matrix = matrix[::-1]
    print('2',matrix)
    for i in range(len(matrix)):
        for j in range (len(matrix[0])):
            if i<=j:
                matrix[j][i],matrix[i][j] =matrix[i][j], matrix[j][i]
    print('3',matrix)
    return matrix

mat = [[1,2,3],[4,5,6],[7,8,9]]
mat = rotate(mat)
print(mat)

输出

1 [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
2 [[7, 8, 9], [4, 5, 6], [1, 2, 3]]
3 [[7, 4, 1], [8, 5, 2], [9, 6, 3]]
[[7, 4, 1], [8, 5, 2], [9, 6, 3]]
Green Cloak Guy
Reply   •   2 楼
Green Cloak Guy    6 年前

Python通过引用传递。如果将对象传递给函数,然后修改该对象,则当函数结束时,该对象将保持修改状态。例如:

def f(a):
    a[1] = 'b'

x = [1, 2, 3]
f(x)
print(x)
# [1, 'b', 3]

不要 修改对象。例如,字符串几乎是完全不可变的-可以对字符串执行的大多数操作,以及可以执行的大多数函数 在字符串上,返回一个新的(修改的)字符串而不更改原始字符串。这包括连接、反转和切片。

my_list[1] = 'b' )或者用类似的方法 .append() .extend() . 但是,作为一个整体,iterables的许多共同点并不会改变列表本身。这包括连接、反转和切片。

赋值运算符 = 不会修改左侧的对象;相反,它 取代 它。

所以你不能得到相同输出的原因是这一行:

matrix = matrix[::-1]

matrix -相反,它通过切片创建一个新列表 矩阵 矩阵 . 当你在函数之外运行代码时,只有一个 命名空间中的引用,该引用将被替换,因此您永远看不到差异。然而 矩阵

# global scope
# matrix --> some object with address 0x001
def rotate(matrix):
    # local scope
    # matrix --> 0x001  (the reference that was passed in)
    matrix = matrix[::-1]  # the slice creates a new object 0x002
    # matrix --> 0x002
    ...

然后继续修改 矩阵 0x002 . 一切都很好,但这些变化并没有影响 0x001

有一些方法可以解决这个问题-例如,就地反转,而不是使用切片来获取原始列表的反转版本:

def reverse_in_place(lst):
    for i in range(len(lst) // 2):
        lst[i], lst[-i-1] = lst[-i-1], lst[i]

但正如@Jmonsky在他们的回答中指出的,更传统的做法是 返回修改后的值 比去 ,但有几个特殊的例外。