likes
comments
collection
share

【语法篇】函数的参数调用文章一览 变量的别名 变量的作用域 不可变对象与可变对象 函数会对实参造成影响 避免用可变对象作

作者站长头像
站长
· 阅读数 50

文章一览

Python的参数传递采用的是共享传参,也就是形参获取实参引用的副本,换句话说,形参是实参的一个别名。

变量的别名

别名:是指多个变量指向同一个对象,其他变量就是这个变量的别名

a = [1, 2]
b = a
print(id(a), id(b))
print(a is b)
2595122111744 2595122111744
True

此时,变量a和变量b都是指向[1, 2]这个列表对象的,只是变量名称不一样,我们可以说变量b是变量a的别名,也可以说变量a是变量b的别名

变量的作用域

在参数传递过程中,我们需要先了解一下作用域的问题,Python关于作用域分为两个部分,全局作用域局部作用域

x = 10  # 全局作用域
def run():
    y = 20
    print(y)  # 局部作用域
    print(x)
run()
print(x)
print(y)
20
10
10
NameError: name 'y' is not defined

变量x的作用域为全局,也就是在任何地方都可以使用,变量y的作用域是局部的,也就是只能在run函数内部去使用,当函数执行完毕之后,该变量也就被自动清理掉。

x = 10
def run():
    x = 20
    print(x)
run()
print(x)
20
10

【语法篇】函数的参数调用文章一览 变量的别名 变量的作用域 不可变对象与可变对象 函数会对实参造成影响 避免用可变对象作 通过这张图片,我们可以看到,虽然外部和函数内部的变量名称相同,但是实际上绑定的是两个不同的对象,而内部的变量只有在进入到run函数内部时才可以使用,其他时候是没有这个变量的。

不可变对象与可变对象

不可变对象

什么是不可变对象?不可变对象时对象中不提供增加删除功能的对象,最为常见的就是元组了。

t = (1, 2, 3)
t += (4, 5, 6)
print(t)
(1, 2, 3, 4, 5, 6)

从这段代码的运行上看,元组明显是被改变了,为什么说元组不可变的呢?

所谓的不可变,是指变量名绑定的对象不可变,(1, 2, 3)这个对象是不可变的

t += (4, 5, 6)这个过程中进行了三步操作。

  1. 通过t找到对象(1, 2, 3)
  2. 将对象(1, 2, 3)和对象(4, 5, 6)合并生成一个新的对象(1, 2, 3, 4, 5, 6)
  3. 将变量t重新指向新的对象(1, 2, 3, 4, 5, 6)
t = (1, 2, 3)
print("原始对象t:", id(t))
t += (4, 5, 6)
print("改变后的对象t:", id(t))
原始对象t: 2595145602048
改变后的对象t: 2595124188000

可变对象

常见的可变对象就是列表了,它提供了元素的添加删除

t = [1, 2, 3]
print("原始对象t:", id(t))
t += [4, 5, 6]
print("改变后的对象t:", id(t))
原始对象t: 2595145719296
改变后的对象t: 2595145719296

可以看到,当可变对象添加元素时,对象并不会发生改变(变量指向的仍然是原始对象,没有新的对象产生)。

可变与不可变?

可变是指对象内的元素是可以被改变的,元素的改变并不会造成对象的改变。不可变是指对象一旦创建,里面的元素是不能被改变的,一旦改变了元素,原始对象也就不存在了,取而代之的是一个新的对象

函数会对实参造成影响

函数可能会修改作为参数传入的可变对象,但是无法修改那些对象的标识(也就是说,不能把一个对象彻底替换成另一个对象)。 所以,当我们的实参是一个不可变对象时,我们是完全可以放心的。

def add(a, b):
    a += b
    return a
A = (1, 2, 3)
B = (4, 5, 6)
C = add(A, B)
print("A:", A)
print("B:", B)
print("C:", C)
print("对象A:", id(A))
print("对象B:", id(B))
print("对象C:", id(C))
A: (1, 2, 3)
B: (4, 5, 6)
C: (1, 2, 3, 4, 5, 6)
对象A: 2595145525632
对象B: 2595145602624
对象C: 2595124188000

通过这段代码我们可以看到,当传入的对象是不可变对象时,在函数内部无论做了什么操作,都无法影响到外部的对象的。 但是,我们要提防实参是可变对象时的情况

def add(a, b):
    a += b
    return a
A = [1, 2, 3]
B = [4, 5, 6]
C = add(A, B)
print("A:", A)
print("B:", B)
print("C:", C)
print("对象A:", id(A))
print("对象B:", id(B))
print("对象C:", id(C))
A: [1, 2, 3, 4, 5, 6]
B: [4, 5, 6]
C: [1, 2, 3, 4, 5, 6]
对象A: 2595145682240
对象B: 2595145292928
对象C: 2595145682240

出现这种问题的原因就在于,形参是实参的别名,也就是说形参与实参实际上指向的是同一个对象,当实参是不可变对象时,我们可以放心的,因为不可变对象是无法被改变的,而当实参是可变对象时,我们需要提防对这个形参的一些改变,因为会影响到外部。

避免用可变对象作为参数默认值

class Bus:
    def __init__(self, students=[]):
        self.students = students
    def drive(self, student):
        self.students.append(student)
    def __str__(self):
        return f"Student{self.students}"
B = Bus()
B.drive("张三")
B1 = Bus()
print(B1)
B1.drive("李四")
print(B)
Student['张三']
Student['张三', '李四']

可以看到,这段代码运行的逻辑是混乱的,明明B校车里面只有张三这个学生,B1校车里面只有李四这个学生,但是结果却和预期大相径庭。 接下来我们来分析一下这个问题产生的原因

class Bus:
    def __init__(self, students=[]):
        print(id(students))
        self.students = students
    def drive(self, student):
        self.students.append(student)
    def __str__(self):
        return f"Student{self.students}"
B = Bus()
B1 = Bus()

【语法篇】函数的参数调用文章一览 变量的别名 变量的作用域 不可变对象与可变对象 函数会对实参造成影响 避免用可变对象作 通过这张图,我们可以看到,BB1students属性指向的是同一个对象,至于为什么发生这样的情况,跟我们的默认值设计有关系,默认值会自动在内存中创建一个对象,这个对象是固定的,所以,只要用到默认值的内容,那么用到的一定是同一个对象,由于又是可变类型,所以就会导致,当某个实例改变这个对象时,其他实例也会受到影响。

class Bus:
    def __init__(self, students=()):
        print(id(students))
        self.students = students
    def drive(self, student):
        self.students.append(student)
    def __str__(self):
        return f"Student{self.students}"
B = Bus(["张三"])
B1 = Bus(["李四"])
B.drive("王五")
print(B)
print(B1)
2595145508928
2595123433280
Student['张三', '王五']
Student['李四']

当我们不使用默认值的时候,情况是没有问题的,但是这样我们需要对每一个使用者,亲自告诉他们,一定要传入参数,否则就无法保障实例的正常。所以这样的做法并不是最优解,当我们在需求中一定要用到可变类型时,最好的方法如下:

class Bus:
    def __init__(self, students=None):
        if students is None:
            self.students = []
        else:
            self.students = students
    def drive(self, student):
        self.students.append(student)
    def __str__(self):
        return f"Student{self.students}"
B = Bus()
B1 = Bus()
B.drive("王五")
print(B)
print(B1)
Student['王五']
Student[]
转载自:https://juejin.cn/post/7409148560777969715
评论
请登录