Day6:函数的作用域、参数、eval、递归

本文深入探讨Python函数的特性,包括函数对象的概念、作用域规则、局部与全局变量的区别及效率对比,参数传递机制,以及浅拷贝和深拷贝的使用。同时,介绍了不同类型的参数,lambda表达式,eval()函数的应用,以及递归函数的设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Day6

函数也是对象,内存底层分析

Python中,“一切都是对象”。实际上,执行def定义函数后,系统就创建了相应的函数对象。

def func1():
    print("new pants")


func1()
c = func1  # 把func1的内存地址赋值给了c
print(func1, c)
# <function func1 at 0x0000028D9FCFC1E0> <function func1 at 0x0000028D9FCFC1E0>
c()  # c()也可以调用fucn1函数

变量的作用域(全局变量和局部变量)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zaay1Zlp-1593438074443)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200629093140759.png)]

def func1():
    b = 4  # 局部变量
    print(b * 10)
    print(a)  # local variable 'a' referenced before assignment,此处a被当作局部变量
    a = 300 # 在print(a)之前,还未定义a=300,所以出现如上报错
    print(a)


func1()
# 当执行函数fucn1()时,在栈内创建栈帧(stack frame),执行结束,栈帧就不存在了
func1()
# 不能在函数外调用局部变量,如print(b),则报错

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OnMjLDSs-1593438074446)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200629100451053.png)]

  • 输出局部变量和全局变量
a = 3  # 全局变量


def func1():
    b = 4  # 局部变量
    print(b * 10)
    global a  # 如果要在函数内改变全局变量的值,增加global关键字声明
    a = 300  # 在print(a)之前,还未定义a=300,所以出现如上报错
    print("局部", locals())  # 打印输出的局部变量
    #局部 {'b': 4}
    print("全局", globals())  # 打印输出的全局变量
'''
全局 {'__name__': '__main__', '__doc__': '\n@author: z&y\n@file: 函数变量的作用域.py\n@time: 2020/06/29\n@desc:\n', '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000020B8EB21CC0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/Tools/PythonProject/MyTest/Day_6/函数变量的作用域.py', '__cached__': None, 'a': 300, 'func1': <function func1 at 0x0000020B8EE95378>}
'''

func1()
print(a)  # 此时全局变量a的值变为300

局部变量和全局变量效率测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UxORK1Z3-1593438074448)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200629104851044.png)]

import math
import time


# 局部变量的查询和访问速度比全局变量快
def func1():
    start = time.time()
    for i in range(10000000):
        math.sqrt(30)  # 调用了全局的import math
    end = time.time()
    print("耗时{0}".format((end - start)))
    # 耗时1.1883010864257812


func1()


def func2():
    start = time.time()
    b = math.sqrt
    for i in range(10000000):
        b(30)  # 从局部去引用math.sqrt
    end = time.time()
    print("耗时{0}".format((end - start)))
    # 耗时0.8516945838928223


func2()

参数的传递

函数的参数传递本质上就是:从实参到形参的赋值操作。Python中“一切皆对象”,所有的赋值操作都是“引用的赋值”。所以,Python中参数的传递都是“引用传递”,不是“值传递”。具体操作时分为两类:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zc5iontk-1593438074450)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200629112303027.png)]

  • 对“可变对象”进行“写操作”,直接作用于原对象本身
# 传递可变对象
a = [10, 20]
print(id(a))
print(a)
print("********")


def func1(m):
    print(id(m))
    m.append(30)
    print(id(m))


func1(a)
print(id(a))
print(a)
# 三次打印id(a),内存地址都是2093959504456

传递不可变对象的引用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e8m8qZyz-1593438074451)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200629114642666.png)]

浅拷贝和深拷贝

  • 浅拷贝:不拷贝子对象的内容,只是拷贝对象的引用。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M9U3HoVB-1593438074452)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200629120302710.png)]

    # 浅拷贝
    import copy
    
    a = [10, 20, [5, 6]]
    b = copy.copy(a)
    print(id(a), id(b))  # 列表a与b的内存空间地址不同,但指向的对象相同
    
    b.append(30) # 仅对b增加了30,因此a中未增加30
    b[2].append(7) # a与b同时修改
    print("浅拷贝......")
    print("a", a)  # a [10, 20, [5, 6, 7]]
    print("b", b)  # b [10, 20, [5, 6, 7], 30]
    
  • 深拷贝:会连子对象的内存也全部拷贝一份,对子对象的修改不会影响源对象。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jdYsj9pS-1593438074452)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200629122128272.png)]

    # 深拷贝
    a = [10, 20, [5, 6]]
    b = copy.deepcopy(a)
    print(id(a), id(b))  # 列表a与b的内存空间地址不同,但指向的对象也不相同
    
    b.append(30)
    b[2].append(7)
    print("深拷贝......")
    print("a", a)  # a [10, 20, [5, 6]]
    print("b", b)  # b [10, 20, [5, 6, 7], 30]
    
  • 传递不可变对象时,不可对象中包含的子对象是可变的,则方法内修改了这个可变对象,源对象也发生了改变

    # 传递不可变对象时,不可对象中包含的子对象是可变的,则方法内修改了这个可变对象,源对象也发生了改变
    a = (10, 20, [5, 6])
    print("a:", id(a))
    
    
    def func1(m):
        print("m:", id(m))
        # m[0] = 15 #报错:'tuple' object does not support item assignment,不可变对象不能修改
        m[2][0] = 888
        print(m)
        print("m:", id(m))
    
    
    func1(a)
    print(a)
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fVDnxt9k-1593438074453)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200629141939359.png)]

参数的几种类型

位置参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LYYT8Wl6-1593438074453)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200629142148432.png)]

默认值参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E2yqq1GH-1593438074453)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200629142325998.png)]

命名参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7cUMyMzv-1593438074454)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200629142445714.png)]

def func1(a, b, c, d):
    print("{0}--{1}--{2}--{3}".format(a, b, c, d))


def func2(a, b, c=10, d=15):  # 默认值参数(c,d),必须位于其他参数后面!
    print("{0}--{1}--{2}--{3}".format(a, b, c, d))


func1(10, 20, 30, 40)  # 位置参数
# func1(10,20) #参数个数不匹配,报错

func1(d=20, b=40, a=10, c=100)  # 命名参数,通过形参名称来匹配,不用考虑参数循序
func2(2, 3)  # 2--3--10--15
func2(2, 3, 4)  # 2--3--4--15

可变参数

可变参数指的是”可变数量的参数“。分两种情况:

  • *args,将多个参数收集到一个”元组“对象中;
  • **kwargs,将多个参数收集到一个”字典”对象中;
def func1(a, b, *args):
    print(a, b, args)  # 1 2 (3, 4, 5, 6)
    print(a, b, *args)  # 1 2 3, 4, 5, 6


func1(1, 2, 3, 4, 5, 6)


def func2(a, b, **kwargs):
    print(a, b, kwargs)  # 1 2 {'name': 'gaoqi', 'age': 18}
    # print(a, b, **kwargs) #报错


func2(1, 2, name="gaoqi", age=18)


def func3(a, b, *args, **kwargs):
    print(a, b, args, kwargs)  # 1 2 (3, 4, 5) {'name': 'gaoqi', 'age': 18}


func3(1, 2, 3, 4, 5, name="gaoqi", age=18)

lambda表达式和匿名函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hRUB9KtO-1593438074454)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200629151923806.png)]

f = lambda a, b, c, d: a * b * c * d
print(f(2, 3, 4, 5))


# func1等同于lambda
def func1(a, b, c, d):
    return a * b * c * d


# 把函数对象作为列表的元素,放到列表中
g = [lambda a: a * 2, lambda b: b * 3, ]

print(g[0](6))  # g[0]指的是函数对象lambda a: a * 2,g[0]()则对lambda进行调用

h = [func1, func1]
print(h[0](3, 4, 5, 6))

eval()函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JbYR3Kga-1593438074455)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200629153439024.png)]

# 用途:可以直接使用从外部(server,文件等)接收的字符串
s = "print('abcde')"
eval(s)

a = 10
b = 20
c = eval("a+b")
print(c)

dic1 = dict(a=100, b=200)
d = eval("a+b")  # 此处的a,b为之前定义的变量,即a=10,b=20
print(d)
d = eval("a+b", dic1)
print(d)  # 此处的a,b为字典dic1中的a,b,因此a=100,b=200

递归函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WdQGiwhp-1593438074455)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200629160447147.png)]

def func1(n):
    print("func01:", n)
    if n == 0:
        print("over")
    else:
        func1(n - 1)

    print("func01***", n)


func1(4)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RjfyBKUq-1593438074456)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200629170609219.png)]

  • 使用递归函数,计算阶乘
# 使用递归函数计算阶乘
def factorial(n):
    if n == 1:
        return 1
    else:
        return n * factorial(n - 1)


res = factorial(5)
print(res)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xVp4Y92K-1593438074456)(C:\Users\z&y\AppData\Roaming\Typora\typora-user-images\image-20200629173838230.png)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值