数据的在内存中的地址就是数据的引用。
   如果两个变量为同一个引用,那么这两个变量对应的数据一定相同;
   如果两个变量对应的数据相同,引用不一定相同。
   通过id(数据)可以查看数据对应的地址,修改变量的值,其实是在修改变量的引用。
   数据可以分为:可变类型与不变类型
     可变类型:
       如果修改了数据的内容,数据的地址没有发生改变.
       有列表,字典,set集合
     不可变类型:
       如果修改了数据的内容,数据的地址发生改变.
       有字符串,元组,数字
   当python解释器首次启动时,会把小数字(-5~256)和短字符串(长度小于21)缓存到缓冲区中,当在程序中使用这些数字和字符串时,就直接从缓存区中取。
     m = 300
     n = 300
     print(id(m)) # 1811121856
     print(id(n))
     不在小数字或者小字符串范围内的数据,会在第一次使用时缓存起来
     m = 300
     n = 300
     print(id(m)) # 2345027009360
     print(id(n)) # 2345027009360

 演示:
 1.
 变量a实际存储的是1的引用(地址)(在程序执行过程中a被编译为一条指令)
 a = 1
 b = a
 print(id(a)) # 1820749280
 print(id(b)) # 1820749280
 a =2 # 修改不可变类型(的引用)
 print(id(a)) # 1820749312 a的id已经改变
 print(id(b)) # 1820749280
 print(a,b) a = [1, 2,[3,5]]
 b = a
 print(id(a)) # 1758314288776
 print(id(b)) # 1758314288776
 # a.append(3)
 a[0] = 6
 a[2][0] = 1
 b[2][1] = 6
 print(id(a)) # 1758314288776 注意a与b始终指向同一个地址 
 print(id(b)) # 1758314288776 
 print(a) # [6, 2, [1, 6]]
 print(b) # [6, 2, [1, 6]]

 2. list = []
 dict = {"name":"wangjie","age":23}
 print(id(dict))
 a = 10
 list.append(dict) # 把dict的引用加入到list中 0x111 的内容是(指向){"name":"wangjie","age":23}
 list.append(a) # 把a的引用加入到list中 0x222 的内容是(指向)10
 print(list) # list[0] 为0x111 ,内容是(指向)数据{"name":"wangjie","age":23},list[1]的内容0x222,内容是(指向)数据10
 a = 20 # 修改了a的值 a的引用发生的变化 0x333 
    # 但不影响list中的引用指向的值 还是指向0x111 指向{"name":"wangjie","age":23}
    # 和0x222 指向 10
 print(list)
 dict["name"] = "lili" #修改了dict的值 dict为可变数据类型,dict的引用不变,但0x111的内容已经变为{'name': 'lili', 'age': 23}
 print(list) # list[0]的内容是 0x111 , 指向数据{'name': 'lili', 'age': 23},list[1]的内容为0x222指向数据 10

 3.
 list = []
 list2 = []
 m = 10
 def func():
   global m
   m =20
   list2 = [1,2] # 不属于修改,修改需要通过方法,这种是覆盖全局变量list2
   list.append(1) # 通过append 方法修改,list的引用不变
   print(list2) # [1, 2]
 print(list) # []
 print(m) # 10
 print(id(m)) # 1811115776

 func() # [1, 2] print(list) # [1]
 print(list2) # []
 print(m) # 20
 print(id(m)) # 1811116096 4.

 def log2():
   #info_dict = {} # 存储学生信息的字典若放在for循环外,则旧数据会被新数据覆盖
   info_list = []
   num = input("请输入要录入信息的数量")
   num = int(num)
   for i in range(num):
     info_dict = {} # 重新直接对info_dict 赋值,info_dict的引用发生改变 存储学生信息的字典要放在for循环# 内
     print("录入第%s 位信息" % (i + 1))
     name = input("输入姓名:")
     id = input("输入学号")
     info_dict["姓名"] = name
     info_dict["学号"] = id
     info_list.append(info_dict)
   print(info_list)
   for info in info_list:
     for k ,v in info.items():
       print('%s = %s' % (k,v))
 log2()

 5.

 a = [1, 2]
 b = [3, 4]
 a.append(b) # [1,2,[3,4]]
 b.append(a)
 print(a) # [1, 2, [3, 4, [...]]] 
 print(b) # [3, 4, [1, 2, [...]]] 6.传递数据,传递的是数据对应的地址.

 a = [[]] * 5 
 print(a) [[],[],[],[],[]]
 print(id(a)) # 2132205131400
 print(id(a[0])) # 2132205131592
 print(id(a[1])) # 2132205131592
 a.append(1) 
 print(id(a)) # 2132205131400
 print(a) # [[],[],[],[],[],1]
 a[0].append(2) 
 print(id(a[0])) # 2132205131592
 print(a) # [[2],[2],[2],[2],[2],1]
 a[1].append(3) 
 print(id(a[1])) # 2132205131592
 print(a) # [[2,3],[2,3],[2,3],[2,3],[2,3],1]7.
>>> def selfAdd(a):
     """自增"""
     a += a 
 >>> a_int = 1
 >>> selfAdd(a_int)
 >>> a_int
 1
 >>> a_list = [1,2]
 >>> selfAdd(a_list)
 >>> a_list
 [1, 2, 1, 2]
 总结:python中函数参数是引用传递(注意不是值传递)。对于不可变类型,因变量的值不能修改,所以运算不会影响到变量自身;而对于可变类型来说,函数体中的运算有可能
 会更改传入参数的值8.
  += 和append()方法等对数据引用的修改一样
9.
  a = [1,2]
  b = a * 2
  print(b)  # [1,2,1,2]
  print(id(b))  # 2745079031688
  b[0] = 55
  print(b)  # [55,2,1,2]
  print(id(b))  # 2745079031688
  print(a)  # [1,2]
  修改了b[0] ,则b[0] 的引用发生改变,但b的引用没发生变化 
9.
  第一种情况:
  def func(x,l = []):
    for i in range(x):
      l.append(i+1)
    print(l)
  func(3)  #  [1,2,3]  func(2)  # [1,2,3,1,2]
  第二种情况:
  def func(x,l = []):
    for i in range(x):
      l.append(i+1)
    print(l)
  list = [1,2]
  func(3,list)  # [1,2,1,2,3]
  func(2)  #  [1,2]

  函数有默认参数,定义时即为默认参数分配地址了,也只分配这一个地址,所以第一种情况,用的全是l这个列表;第二种调用func(3,list),将list变为了[1,2,1,2,3],然后调用func(2),l又指向了原先的已经定义好的空列表,所以结果为[1,2]。