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

1152-张同学

发表文章数:21

首页 » Python » 正文

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函数

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

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

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),则报错

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

  • 输出局部变量和全局变量
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

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

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

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中参数的传递都是“引用传递”,不是“值传递”。具体操作时分为两类:

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

  • 对“可变对象”进行“写操作”,直接作用于原对象本身
# 传递可变对象
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

传递不可变对象的引用

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

浅拷贝和深拷贝

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

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

    # 浅拷贝
    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]
    
  • 深拷贝:会连子对象的内存也全部拷贝一份,对子对象的修改不会影响源对象。

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

    # 深拷贝
    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)
    

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

参数的几种类型

位置参数

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

默认值参数

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

命名参数

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

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表达式和匿名函数

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

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()函数

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

# 用途:可以直接使用从外部(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

递归函数

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

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

    print("func01***", n)


func1(4)

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

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


res = factorial(5)
print(res)

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

未经允许不得转载:作者:1152-张同学, 转载或复制请以 超链接形式 并注明出处 拜师资源博客
原文地址:《Day6:函数的作用域、参数、eval、递归》 发布于2020-06-29

分享到:
赞(0) 打赏

评论 抢沙发

评论前必须登录!

  注册



长按图片转发给朋友

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏

Vieu3.3主题
专业打造轻量级个人企业风格博客主题!专注于前端开发,全站响应式布局自适应模板。

登录

忘记密码 ?

您也可以使用第三方帐号快捷登录

Q Q 登 录
微 博 登 录