函数的参数作为引用

文章目录

  • 1. num,list ,tuple
  • 2. list 作为默认值导致共享同一列表
  • 3. 防御可变参数
  • 4. 结论

1. num,list ,tuple

结论:num ,tuple 作为参数,自身不会因为函数的原因而改变,list 为可变量,会因为函数变而变。

  • 测试
def f(a, b):
    a += b
    return a


if __name__ == "__main__":
    x = 1
    y = 2
    result = f(x, y)
    print("*"*40)
    print("num 数字作为参数")
    print(f"x= {x}")
    print(f"y= {y}")
    print(f"result={result}")
    print("*"*40,'\n')

    x1 = [1,2]
    y1 = [3,4]
    result1 = f(x1, y1)
    print("*"*40)
    print("list 数字作为参数")
    print(f"x1= {x1}")
    print(f"y1= {y1}")
    print(f"result1={result1}")
    print("*"*40,'\n')

    x2 = (1,2)
    y2 = (3,4)
    result2 = f(x2, y2)
    print("*"*40)
    print("tuple 数字作为参数")
    print(f"x2= {x2}")
    print(f"y2= {y2}")
    print(f"result2={result2}")
    print("*"*40)
  • 结论:
****************************************
num 数字作为参数
x= 1
y= 2
result=3
**************************************** 

****************************************
list 数字作为参数
x1= [1, 2, 3, 4]
y1= [3, 4]
result1=[1, 2, 3, 4]
**************************************** 

****************************************
tuple 数字作为参数
x2= (1, 2)
y2= (3, 4)
result2=(1, 2, 3, 4)

2. list 作为默认值导致共享同一列表

没有指定初始乘客的HauntedBus实例会共享同一乘客列表

class HauntedBus:
    """备受幽灵乘客折磨的校车"""
	# 默认值为list ,会导致新实例化的bus2 & bus3共享同一列表
    def __init__(self, passengers=[]):
        # 设置可变类型作为参数默认值
        self.passengers = passengers

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

    def drop(self, name):
        try:
            self.passengers.remove(name)
        except ValueError as e:
            print("error for ", e)


if __name__ == "__main__":
    bus1 = HauntedBus(['Alice', 'Bill'])
    print(f"bus1.passengers={bus1.passengers}")
    bus1.pick('Charlie')
    bus1.drop('Alice')
    print(f"bus1.passengers={bus1.passengers}")

    bus2 = HauntedBus()
    bus2.pick('Carrie')
    print(f"bus2.passengers={bus2.passengers}")

    bus3 = HauntedBus()
    print(f"bus3.passengers={bus3.passengers}")
  • 结果
bus1.passengers=['Alice', 'Bill']
bus1.passengers=['Bill', 'Charlie']
bus2.passengers=['Carrie']  # bus2 和bus3 没有默认值的情况下共享同一列表
bus3.passengers=['Carrie']

3. 防御可变参数

  • 缺点: 用list 作为形参,会改变实参的值
class TwilightBus:
    """让乘客销声匿迹的校车"""

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

            # 用 passengers 作为参数传入,会导致self.passengers,
            #                                  passengers ,basketball_team,
            # 都为列表['sue', 'tina', 'maya', 'diana', 'pat'] 的别名,所以任一改变,
            # 均可改变['sue', 'tina', 'maya', 'diana', 'pat']的值
            self.passengers = passengers

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

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

        except ValueError as e:
            print("Error for ", e)


if __name__ == "__main__":
    basketball_team = ['sue', 'tina', 'maya', 'diana', 'pat']
    bus = TwilightBus(basketball_team)
    print(f"before : basketball_team={basketball_team}")
    bus.drop('tina')
    bus.drop('jason')
    bus.drop('pat')
    print(f"after : basketball_team={basketball_team}")
  • 结果:
before : basketball_team=['sue', 'tina', 'maya', 'diana', 'pat']
Error for  list.remove(x): x not in list
# 列表在经过函数后居然发生变化
after : basketball_team=['sue', 'maya', 'diana']  
  • 方案:
    应该把参数值的副本赋值给self.passengers,
    错误:self.passengers = passengers
    正确:self.passengers = list(passengers)
class TwilightBus:
    """让乘客销声匿迹的校车"""

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

            # 用 passengers 作为参数传入,会导致self.passengers,passengers ,basketball_team,
            # 都为列表['sue', 'tina', 'maya', 'diana', 'pat'] 的别名,所以任一改变,均可改变['sue', 'tina', 'maya', 'diana', 'pat']的值
            self.passengers = passengers

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

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

        except ValueError as e:
            print("Error for ", e)


class OkBus:
    def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            # 创建 passengers 的副本,并且赋值给 self.passengers
            # 重点 list(passengers)!!!!!!!
            self.passengers = list(passengers)

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

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

        except ValueError as e:
            print("Error for ", e)

if __name__ == "__main__":
    basketball_team = ['sue', 'tina', 'maya', 'diana', 'pat']
    bus = TwilightBus(basketball_team)
    print("*"*40)
    print(f"before : basketball_team={basketball_team}")
    bus.drop('tina')
    bus.drop('jason')
    bus.drop('pat')
    print(f"after : basketball_team={basketball_team}")

    print("*"*40)
    basketball_teamok = ['sue', 'tina', 'maya', 'diana', 'pat']
    busok = OkBus(basketball_teamok)
    print(f"before : basketball_teamok={basketball_teamok}")
    busok.drop('tina')
    busok.drop('pat')
    print(f"after : basketball_teamok={basketball_teamok}")
    print("*"*40)
  • 结果
****************************************
# self.passengers = passengers 的结果,会影响原来的参数列表 basketball_team
before : basketball_team=['sue', 'tina', 'maya', 'diana', 'pat']
Error for  list.remove(x): x not in list
after : basketball_team=['sue', 'maya', 'diana']
****************************************
# self.passengers = list(passengers) 的结果,不影响 原来的参数列表 basketball_team
before : basketball_teamok=['sue', 'tina', 'maya', 'diana', 'pat']
after : basketball_teamok=['sue', 'tina', 'maya', 'diana', 'pat']
****************************************

4. 结论

当函数用参数时,尽量用不可变作为形参,比如数字,元组,如果用可变类型作为变量,会导致传入的值变化,为了解决这个问题,判断参数传入的是不是None,并且重新创建新的list

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

你可能感兴趣的:(python,windows,开发语言)