变量进阶

变量的引用

变量数据 是分开存储的,数据保存在内存中的一个地址,变量中保存着数据在内存中的地址,即 引用

使用 id() 函数可以查看变量中保存数据所在的内存地址。

给变量赋值本质上就是修改数据的引用。

函数的 实参 / 返回值 就是靠 引用 来传递数据的。

def test(num):

    print("-" * 50)
    print("%d 在函数内的内存地址是 %x" % (num, id(num)))

    result = 100

    print("返回值 %d 在内存中的地址是 %x" % (result, id(result)))
    print("-" * 50)

    return  result

a = 10
print("调用函数前 a 的内存地址是 %x" % id(a))

r = test(a)

print("调用函数后 a 的内存地址是 %x" % id(a))
print("调用函数后 返回值内存地址是 %x" % id(r))

可变类型 和 不可变类型

可变类型:列表、字典
不可变类型

  • 数字类型 intboolfloatcomplexlong
  • 字符串 str
  • 元组 tuple

字典的 key 只能使用不可变类型的数据。

哈希
哈希 是一种 算法,其作用就是提取数据的 特征码(指纹),相同的内容得到相同的结果,不同的内容得到不同的结果。

Python 中内置有一个名字叫做 hash(o) 的函数。它接收一个 不可变类型 的数据作为 参数,返回一个整数。

设置字典的 键值对 时,会首先对 key 进行 hash,所以字典的 key 必须是不可变类型。

哈希决定了如何在内存中保存字典的数据,以方便后续对字典的增、删、改、查。

局部变量 和 全局变量

局部变量:定义在函数内部的变量,只能在函数内部使用。
全局变量:在函数外部定义的变量,所有位置都可以使用这个变量。

  • 局部变量的生命周期:函数执行期间。
  • 不同函数内部可以定义同名的局部变量。
  • 函数内部无法直接修改全局变量,只会修改局部变量。想在函数内部修改全局变量,需要用 global 关键字。
  • 全局变量命名建议:前面增加 g_ 或者 gl_ 的前缀。
  • 代码结构
    1. shebang
    2. import
    3. 全局变量
    4. 函数
    5. 代码

函数进阶

函数参数和返回值

  • 想同时返回多个参数时,可以返回元组。返回元组时,小括号应省略,如
    def measure():
    
        temp = 39
        wetness = 10
    
        return temp, wetness
    但是通过元组传递结果时,需要找到准确的下标,才能得到想要的结果:
    result = measure()
    print(result[0])
    print(result[1])
    因此可以定义全局变量来接收元组结果:
    gl_temp, gl_wetness = measure()
    
    print(gl_temp)
    print(gl_wetness)
    这种形式需要 变量数量 与 函数返回数量 一致。

交换两个变量 —— Python 专用,利用元组
a, b = (b, a)
小括号可以省略。

可变参数与不可变参数

在函数内部,针对 参数 使用赋值语句,不会影响调用函数时传递的 实参变量
针对参数使用赋值语句,会在函数内部修改局部变量的引用,不会影响到外部变量的引用。

def demo(num):

    num = 200

gl_num = 99
demo(gl_num)
print(gl_num)

输出结果:99

如果传递的参数是 可变类型,在函数内部使用 方法 修改了数据的内容,会影响到外部的数据:

def mutable(num_list):

    num_list.extend([1, 2, 3])
    print(num_list)

gl_list = [6, 7, 8]
mutable(gl_list)
print(gl_list)

输出结果:

[6, 7, 8, 1, 2, 3]
[6, 7, 8, 1, 2, 3]

另外,对于列表,+= 相当于 extend

def demo(num_list):

    num_list += num_list
    print(num_list)

gl_list = [1, 2, 3]
demo(gl_list)
print(gl_list)

输出结果:

[1, 2, 3, 1, 2, 3]
[1, 2, 3, 1, 2, 3]

缺省参数

定义函数时,可以给某个参数指定一个默认值,即 缺省参数。调用函数时,如果没有传入缺省参数的值,则在函数内部使用定义函数时指定的 参数默认值,否则使用传入的值。

gl_num_list = [6, 3, 9]
gl_num_list.sort()  # 缺省参数 reverse = False
gl_num_list.sort(reverse=True)
def print_info(name, gender=True):

    gender_text = "男生"
    if not gender:
        gender_text = "女生"

    print("%s 是 %s" % (name, gender_text))

必须保证带有默认值的缺省参数在 参数列表末尾
错误示例:

def print_info(name, gender=True, title):

多值参数

有时一个函数需要处理的参数个数可能是不确定的,这时就可以用多值参数。

  • 参数名前增加 1个 * 可以接收 元组
  • 参数名前增加 2个 * 可以接收 字典

在给多值参数命名时,习惯使用以下两个名字

  • *args —— 存放 元组 参数,前面有一个 *
    • argsarguments 的缩写,有变量的含义
  • **kwargs —— 存放 字典 参数,前面有两个 *
    • kwkeyword 的缩写,kwargs 意即键值对参数
      def demo(num, *nums, **person):
      
          print(num)
          print(nums)
          print(person)
      
      
      demo(1)
      demo(1, 2, 3, 4, 5, name="Anne")

多值参数案例 —— 计算任意多个数字的和:

def sum_numbers(*args):

    num = 0

    for n in args:
        num += n

    return num


result = sum_numbers(1, 2, 3, 4, 5)

元组和字典的拆包
在调用带有多值参数的函数时,如果希望:

  • 将一个 元组 变量,直接传递给 args
  • 将一个 字典 变量,直接传递给 kwargs

就可以使用 拆包,简化参数的传递,方法:

  • 元组 变量前,增加 一个 *
  • 字典 变量前,增加 两个 *
    def demo(*args, **kwargs):
    
        print(args)
        print(kwargs)
    
    
    gl_nums = (1, 2, 3)
    gl_dict = {"name": "小明", "age": 18}
    
    demo(gl_nums, gl_dict)
    print('-' * 50)
    demo(*gl_nums, **gl_dict)

函数的递归

def sum_numbers(num):

    if num == 1:
        return 1
    
    return num + sum_numbers(num - 1)


print(sum_numbers(100))

输出:5050