了不起的魔术师问题

发布时间 2023-07-14 18:03:41作者: yukina~

了不起的魔术师问题

前言

此问题来自于 <<Python 编程: 从入门到实践>>第一版中习题 8-10.

问题描述

了不起的魔术师: 创建一个包含魔术师名字的列表, 并将其传递给一个名为 show_magicians()的函数,这个函数打印列表中每个魔术师的名字.再编写一个名为 make_great()的函数,对魔术师列表进行修改,在每个魔术师的名字中都加入字样"the Great",调用函数 show_magicians(),确认魔术师列表确实改变了.

解决方案

起初我的想法非常简单,就是通过将魔术师的名单传递给make_great()函数,然后在函数内对列表元素进行遍历,并在每个元素前加上'the Great'字符串,代码如下:

def make_great(magicians):
    for magician in magicians:
        magician = "the Great " + magician


def show_magicians(magicians):
    make_great(magicians)
    for magician in magicians:
        print(magician.title())


magicians_name = ['john', 'kitty', 'peter']
show_magicians(magicians_name)

输出结果为:

John
Kitty
Peter

说明实际上列表元素并没有被修改,而且在 IDE 中还会有这样一条 warning:

Local variable 'magician' value is not used:3

能够很明显地看出在第三行,左值的 migician 变量和右值的 migician 变量实际上是两个变量,右值为 for 循环中列表内的元素,而左值是一个本地变量,打印其地址,代码如下:

def make_great(magicians):
    for magician in magicians:
        print(str(id(magician)) + " for magician")
        magician = "the Great " + magician
        print(str(id(magician)) + " local magician\n")


def show_magicians(magicians):
    print(str(id(magicians)) + " list address\n")
    print(str(id(magicians[0])) + " 0 address")
    print(str(id(magicians[1])) + " 1 address")
    print(str(id(magicians[2])) + " 2 address\n")
    make_great(magicians)
    for magician in magicians:
        print(str(id(magician)) + " show magician\n")
        print(magician.title())


magicians_name = ['john', 'kitty', 'peter']
show_magicians(magicians_name)

输出结果为:

4354652288 list address

4355416432 0 address
4355416112 1 address
4355416176 2 address

4355416432 for magician
4355416752 local magician

4355416112 for magician
4355416752 local magician

4355416176 for magician
4355416752 local magician

4355416432 show magician

John
4355416112 show magician

Kitty
4355416176 show magician

Peter

Process finished with exit code 0

从这个输出结果可以看出以下几点:

  • Python 中列表名并不代表首地址,它是一个指针,指向列表的首地址
  • 列表作为实参传递给函数时是地址传递而非值传递
  • 用 for 循环来遍历列表时无法对列表元素的值做出修改,即使变量名相同,作为左值的变量始终是一个 local variable 且是一个标量.且此规则再 main 和其它函数中都是一样的.说明应该是 for 的特性导致的,而非传参的问题.

目前还暂时没有想出为什么用这种方式无法修改列表中元素的值.因此只能尝试用相对比较繁琐的下标索引的方式来进行修改,代码如下:

def make_great(magicians):
    num = 0
    while num < len(magicians):
        magicians[num] = 'the Great ' + magicians[num]
        num += 1


def show_magicians(magicians):
    make_great(magicians)
    for magician in magicians:
        print(magician.title())


magicians_name = ['john', 'kitty', 'peter']
show_magicians(magicians_name)

输出结果为:

The Great John
The Great Kitty
The Great Peter

说明用索引的方式是可以修改列表中特定元素的值的.

另外还在网上看到了一种解决方法,用函数enumerate获取索引来解决,本质上和上一段代码是一样的,具体代码如下:

def show_magicians(magician_list):
    for magician in magician_list:
        print(magician.title())


def make_great(magician_list):
    for index, magician in enumerate(magician_list):
        magician_list[index] = "the Great " + magician


def main():
    magician_list = ['jack', 'peter', 'kitty']

    if not magician_list:
        print("magician_list is None")
        return

    print("magician:\n")
    show_magicians(magician_list)
    print("---")
    print("the Great magician:\n")
    make_great(magician_list)
    show_magicians(magician_list)


if __name__ == "__main__":
    main()

输出结果为:

magician:

Jack
Peter
Kitty
---
the Great magician:

The Great Jack
The Great Peter
The Great Kitty

参考

https://blog.csdn.net/u013272574/article/details/84319661

https://www.zhihu.com/question/61218362