美文网首页
Python 函数参数是可变对象为什么不好?

Python 函数参数是可变对象为什么不好?

作者: NoneLand | 来源:发表于2017-12-23 16:50 被阅读17次

Source

  • 《Fluent Python》chapter 8

总结

先上代码

class Bus1:
    def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = [ ]
        else:
            self.passengers = list(passengers)

    def pick(self, name):
        self.passengers.append(name)

    def drop(self, name):
        self.passengers.remove(name)


class Bus2:
    def __init__(self, passengers=[]):
            self.passengers = passengers

    def pick(self, name):
        self.passengers.append(name)

    def drop(self, name):
        self.passengers.remove(name)


class Bus3:
    def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = [ ]
        else:
            self.passengers = passengers

    def pick(self, name):
        self.passengers.append(name)

    def drop(self, name):
        self.passengers.remove(name)


class Bus4:
    def __init__(self, passengers=[]):
        self.passengers = list(passengers)

    def pick(self, name):
        self.passengers.append(name)

    def drop(self, name):
        self.passengers.remove(name)

上面的代码实现了几个校车类别, 但是只有Bus1是最优实现,Bus2, Bus3都有各自的问题。
Bus2的代码比Bus1更简练,看起来是更聪明的代码,但是其问题如下:

Bus2 Error
tBus2并没有搭载任何乘客,但是其乘客列表里却出现了tBus1上的乘客Bob。这是为什么呢?这是因为在模块加载的时候,函数定义的默认值就已经创建对象保存在__defaults__中了,如下图所示

那么,任何空乘客列表实例化的Bus2self.passengers都将成为之前函数定义时创建的默认列表的别名,也就是说共同指向同一个对象,就会出现之前图像中出现的问题。
Bus3相比Bus1只在__init__函数中的self.passengers=passengers一句,而就是这么一句,造成了如下的错误,
Bus3 Error
放到现实中,这是什么错误呢,校篮球队的Alice乘坐了一趟校车,从校车上下来之后就被篮球队除名了,Alice很无奈。这个就是Python按引用传递引起的问题,传入的basketball_team里面的内容被修改了,因为self.passengers参数被指定为传入的passengers参数的别名,所以对self.passengers也会引起basketball_team的内容发生改变。
Bus1的实现中,使用list()(浅)复制了passengers对象,这样就可以避免出现这种问题了。此外,使用list()还可以使得传入的参数多样化,可以是元组、集合或者其他的可迭代对象,并且保证了pick()drop()方法中列表相关函数使用的合法性,可谓是非常完美。嗯,对了,Bus1这种编程方式叫做防御式编程(defensive programming)
提一句Bus4,是综合Bus2Bus1给出的一个实现,小小测试了一下,并不会出现上述问题,会不会是一种好的实现呢?

One More Thing

Fluent Python在豆瓣上评分很高,果然奇书。另外,推荐一下它推荐的神奇网站Python Tutor,对于理解Python的内部机制很有用,而且这个网站是开源的,对应的源代码在github上。

感想

感觉高级语言的很多特性的理解还是是基于编译原理(还没有学)的,比如说引用和别名,是不是其实在CPU执行的时候,全部已经换成了对应对象的内存地址,对于CPU来说根本就不存在这些东西。

相关文章

  • Python 函数参数是可变对象为什么不好?

    Source 《Fluent Python》chapter 8 总结 先上代码 上面的代码实现了几个校车类别, 但...

  • Python 那些不为人知的冷知识

    1、默认参数最好不为可变对象 函数的参数分三种 可变参数 默认参数 关键字参数 Python 中的 def 语句在...

  • python notes 1

    python 笔记 列表的一些问题 列表是可变对象,字符串为不可变对象。 当列表作为函数的参数时,函数定义中的形参...

  • 可变参数

    定义默认参数要牢记一点:默认参数必须指向不变对象! 在Python函数中,还可以定义可变参数。顾名思义,可变参数就...

  • Python参数 * 与 ** ,及装包拆包

    (*args)可变参数在Python函数中,还可以定义可变参数。顾名思义,可变参数就是传入的参数个数是可变的,可以...

  • Python函数的参数(详细版)

    函数的参数有:必选参数、默认参数、可变参数、关键字参数。 在Python中定义函数,可以用必选参数、默认参数、可变...

  • Python实战:函数

    Python中函数的声明格式: 例如: 函数的调用: 关于位置参数、关键字参数、可变参数和不可变参数 位置参数:参...

  • python 传递参数

    返回结果 显然python传参数的方式是传引用。 不可变对象 对于不可变对象,你在函数内做什么都不会改变外面的结果...

  • python函数

    python函数 什么是函数 函数语法 调用函数 函数参数形参与实参传参指定参数传参参数默认值可变参数参数解包参数...

  • 函数相关

    函数定义 def 定义函数,函数返回值用return; python参数定义顺序必须是:必选参数、默认参数、可变参...

网友评论

      本文标题:Python 函数参数是可变对象为什么不好?

      本文链接:https://www.haomeiwen.com/subject/ubrqgxtx.html