学习python day 7

本文详细介绍了Python中的函数,包括形式参数、实际参数、作用域、全局和局部变量、闭包、装饰器、lambda表达式、生成器、递归、`open()`函数、路径处理、函数文档和类型注释等内容。特别强调了LEGB规则和装饰器的使用,以及如何在函数中处理参数和返回值。此外,还讨论了如何通过`nonlocal`和`global`语句来管理作用域。
摘要由CSDN通过智能技术生成

python学习系列文章目录

day 7 Python 学习



一、函数

>>> def myfunc():
	pass

>>> myfunc()
>>> def myfunc():
	for i in range(3):
		print("I love")

		
>>> myfunc()
I love
I love
I love

带入参数:f能识别字符串中的参数变量

>>> def myfunc(name):
	for i in range(3):
		print(f"I love {name}")
>>> myfunc("you")
I love you
I love you
I love you

>>> def myfunc(name,times):
	for i in range(times):
		print(f"I love {name}")

1、形式参数和实际参数

1.1 形参:函数定义时写的参数的名字;
1.2 实参:调用函数时传递进去的值。
1.3 函数的返回值:return()
若无返回值,python会自动返回一个none。
1.4 位置参数:

>>> def myfunc(s, vt, o):
	return"".join((o, vt, s))
//join是指定分隔符拼接的函数,""相当于没有分隔符。
>>> myfunc("I", "love", "you")
'youloveI'

1.5 关键字参数:

>>> myfunc(o="I", vt="love", s="you")
'Iloveyou'

同时使用位置参数与关键字参数:位置参数必须在关键字参数之前。
默认参数:

>>> def myfunc(s, vt, o="you"):
	return"".join((o, vt, s))

>>> myfunc("I", "love")
'youloveI'
>>> myfunc("I", "love", "hanyu")
'hanyuloveI'

使用默认参数时,要将默认参数放在最后。
1.6 定义函数时若添加了一个/,则其左侧不能使用关键字参数,只能使用位置参数。右侧随意。若使用*号右侧则只能使用关键字参数

>>> def myfunc(s, /, o="you"):
	return"".join((o, s))

>>> myfunc(s = "1") 报错

>>> def abd(a, *, b, c):
	print(a, b, c)

>>> abc(1,2,3) 报错

1.7 收集参数
只需在形参前加一个*

>>> def myfunc(*args):
	print("有{}个参数。".format(len(args)))
	print("第二个参数是:{}".format(args[1]))

	
>>> myfunc(1,2)2个参数。
第二个参数是:2

返回多个值,python使用了元组进行打包:

>>> def myfunc(*args):
	print(args)

>>> myfunc(1, 2, 3, 4, 5)
(1, 2, 3, 4, 5)

若除了收集参数外还有其他参数,则需使用关键字参数

>>> def myfunc(*args, a, b):
	print(args, a, b)
>>> myfunc(1, 2, 3, a=4, b=5)
(1, 2, 3) 4 5
>>> 

收集参数除了可以把参数打包为元组,还可以将参数打包为字典,方法是使用两个** :

>>> def myfunc(**k):
	print(k)
>>> myfunc(a=1, b=2, c=3)
{'a': 1, 'b': 2, 'c': 3}
>>> def myfunc(a, *b, **c):
	print(a, b, c)
>>> myfunc(1, 2, 3, 4, x=5, y=6)
1 (2, 3, 4) {'x': 5, 'y': 6}

1.8 解包参数
一个*,解包元组,* *解包字典

>>> args = (1, 2, 3, 4)
>>> def myfunc(a, b, c, d):
	print(a, b, c, d)
>>> myfunc(args)  报错
>>> myfunc(*args)
1 2 3 4

2、作用域

指一个变量可以被访问的范围,通常一个作用域的范围总是由它在代码中被赋值的位置来决定的。
变量定义的位置是在一个函数的里面,其作用的范围也仅在函数中,将其称为局部变量。具有全局作用域的变量在函数的内部也能被访问。在函数中,出现局部变量与全局变量名相同时,局部变量会覆盖全局变量。全局变量能在函数中使用,但无法在函数中去修改它的值。

代码如下(示例):

>>> def myfunc():
	x = 520
	print(x)
>>> myfunc()
520

2.1 global 语句

>>> def myfunc():
	global x
	x = 520
	print(x)
>>> myfunc()
520
>>> print(x)
520

函数的嵌套,内层定义的函数只能在定义时调用,无法在外部调用。内部函数可以访问外部函数的变量,但无法修改它。

>>> def funA():
	x = 520
	def funB():
		x = 880
		print("In funB, x=", x)
	funB()
	print("In funA, x=", x)
>>> funA()
In funB, x= 880
In funA, x= 520

2.2 nonlocal语句

可以在内部函数中修改外部函数的变量,

>>> def funA():
	x = 520
	def funB():
		nonlocal x
		x = 880
		print("In funB, x=", x)
	funB()
	print("In funA, x=", x)

	
>>> funA()
In funB, x= 880
In funA, x= 880

2.3 LEGB规则

LEGB含义解释:
L-Local(function);函数内的名字空间
E-Enclosing function locals;外部嵌套函数的名字空间(例如closure)
G-Global(module);函数定义所在模块(文件)的名字空间
B-Builtin(Python);Python内置模块的名字空间


Python的命名空间是一个字典,字典内保存了变量名称与对象之间的映射关系,因此,查找变量名就是在命名空间字典中查找键-值对。
Python有多个命名空间,因此,需要有规则来规定,按照怎样的顺序来查找命名空间,LEGB就是用来规定命名空间查找顺序的规则。

LEGB规定了查找一个名称的顺序为:local–>enclosing function locals–>global–>builtin

2.4 闭包-函数

1、可以使用return方法将funB()在外层调用,只需函数名即可。

>>> def funA():
	x = 880
	def funB():
		print(x)
	return funB
//对于嵌套函数来说,外层函数的作用域是会通过某种形式给保存下来的,尽管函数调用以及结束,但外层作用域里的变量是会保存下来的,,并不会像
>>> funA()
<function funA.<locals>.funB at 0x000001CBCE0E7D30>
>>> funA()()
880
>>> funny = funA()
>>> funny()
880

2、闭包

两个**表示幂运算

例一

>>>def power(exp):
	def exp_of(base):
		return base ** exp
	return exp_of
>>> square = power(2) 
//由于外层作用域里的变量是会保存下来的,故square所指向的exp参数为2
>>> cube = power(3)
>>> cube(5)
125
>>> square(2)
4

例二


>>> def outer():
	x = 0
	y = 0
	def inner(x1, y1):
		nonlocal x, y
		x += x1
		y += y1
		print(f"现在, x = {x}, y = {y}")
	return inner

>>> move = outer()
>>> move(1, 2)
现在, x = 1, y = 2
>>> move(-2, 3)
现在, x = -1, y = 5

闭包在游戏中的实际应用:将游戏中的角色移动位置给保护起来,不希望被其他函数轻易修改。
请添加图片描述
请添加图片描述

2.5 装饰器

多个装饰器能同时用在同一个函数上
装饰器指的定义一个函数,该函数是用来为其他函数添加额外的功能,就是拓展原来函数功能的一种函数
概括地讲,装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能。装饰器经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景,装饰器是解决这类问题的绝佳设计,有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

https://zhuanlan.zhihu.com/p/109078881

例一

import time

def time_master(func):
    def call_func():
        print("开始运行程序。。。")
        start = time.time()
        func()
        stop = time.time()
        print("结束程序运行...")
        print(f"一共花费了 {(stop-start):.2f}秒")
    return call_func

@time_master    //装饰器
def myfunc():
    time.sleep(2)
    print("I love hanyu.")
//myfunc = time_master(myfunc)   函数本来的样子
myfunc()

例二

同时使用多个装饰器,调用顺序为从下至上。

def add(func):
    def inner():
        x = func()
        return x + 1
    return inner

def cube(func):
    def inner():
        x = func()
        return x * x * x
    return inner

def square(func):
    def inner():
        x = func()
        return x * x
    return inner

@add
@cube
@square
def test():
    return 2

print(test())

例三 给装饰器传入参数

多一层嵌套

import time

def logger(msg):
    def time_master(func):
        def call_func():
            start = time.time()
            func()
            stop = time.time()
            print(f"[{msg}]一共耗费了 {(stop-start):.2f}")
        return call_func
    return time_master

@logger(msg="A")
def funA():
    time.sleep(1)
    print("funA")

@logger(msg="B")
def funB():
    time.sleep(1)
    print("funB")

funA()
funB()

2.6 lambda表达式(匿名表达式)-一行流

lambda表达式是一个表达式而非语句,能够出现在python语法不允许def语句出现的地方。
有时在使用函数时不需要给函数分配一个名称,该函数就是“匿名函数”。在python中使用lambda表达式表示匿名函数

lambda是关键字声明,在lambda表达式中,参数列表与函数中的参数列表一样,但不需要用小括号括起来,冒号后面是lambda体,lambda表达式的主要代码在lambda体处编写,类似于函数体。

提示:lambda体不能是一个代码块,不能包含多条语句,只能包含一条语句,该语句会计算一个结果返回给lambda表达式,但与函数不同的是,不需要使用return语句返回,而且当使用函数作为参数的时候。lambda表达式非常有用,可以让代码简洁,简单。

lambda函数的优势:
1.对于单行函数,使用lambda表达式可以省去定义函数的过程,让代码更加简洁
2.对于不需要多次复用的函数,用lambda表达式可以在用完后立即释放,提高程序执行的性能。

add_lambda = lambda a, b: a + b
get_odd_even1 = lambda x: "偶数" if x % 2 == 0 else "奇数"

2.7 生成器 yield表达式

获得生成器的两种方法:
1、使用yield表达式代替return0
2、生成器表达式

>>> def counter():
	i = 0
	while i <=5:
		yield i
		i += 1
		
>>> counter()
<generator object counter at 0x000002389E196890>

斐波那契数列

>>> def fib():
	backq, back2 = 0, 1
	while True:
		yield back1
		back1, back2 = back2, back1 + back2

生成表达式

t = (i ** 2 for i in range(10))

2.8 递归

要让递归正常工作,必须要有一个结束条件,并且每次调用都会向着这个结束条件区推进。

若不加控制,程序将一直运行,使用ctrl+C强制停止

>>> def func():
	print("III")
	func()

正确的应用:

>>> def funC(i):
	if i > 0:
		print("III")
		i -= 1
		funC(i)

求一个数的阶乘:迭代和递归两种方法。

>>> def factIter(n):
	result = n
	for i in range(1, n):
		result 8= i
		
SyntaxError: invalid syntax
>>> def factIter(n):
	result = n
	for i in range(1, n):
		result *= i
	return result

>>> factIter(5)
120
>>> def facRecur(n):
	if n == 1:
		return 1
	else:
		return n * facRecur(n-1)
>>> facRecur(5)
120

斐波拉契数列

>>> def fibIter(n):
	a = 1
	b = 1
	c = 1
	while n > 2:
		c = a + b
		a = b
		b = c
		n -= 1
	return c

>>> fibIter(12)
144
>>> def fibRecur(n):
	if n == 1 or n == 2:
		return 1
	else:
		return fibRecur(n-1) + fibRecur(n-2)

	
>>> fibRecur(12)
144

递归的应用

2.8.1 汉诺塔函数

注意tabs和spaces不要混用。

def hanoi(n, x, y, z):
    if n == 1:
        print(x, '-->', z)  #如果只有1层,直接将金片从x移动到z
    else:
            hanoi(n-1, x, z, y)   #将x上的n-1个金片移动到y
            print(x, '-->', z)    #将最底下的金片从x移动到 z
            hanoi(n-1, y, x, z)   #将y上的n-1个金片移动到z

n = int(input('请输入汉诺塔的层数: '))
hanoi(n, 'A', 'B', 'C')

2.9 open()函数

f.close()关闭文件后数据才能导入文件。
https://blog.csdn.net/a379749/article/details/123994571

2.10 路径处理

对于一个程序来说,函数就是一个结构组件,在函数的外部是不需要关心函数内部的执行细节的,更需要关注的是函数的接口以及执行后的结果。

https://zhuanlan.zhihu.com/p/475661402

2.10.1 函数文档

help()
创建一个函数文档,使用字符串:

>>> def exchange(dollar, rate=6.32):
	"""
	功能:汇率转换
	"""
	return dollar * rate

>>> exchange(20)
126.4
>>> help(exchange)
Help on function exchange in module __main__:

exchange(dollar, rate=6.32)
    功能:汇率转换

2.10.2 类型注释

希望调用者传入到s参数中的类型是字符串类型,n是指数类型,返回值是字符串,类型注释不是强制的,是给人看的

def times(s:str, n:int) -> str:
	return s * n
>>> def times(s:str, n:int) -> str:
	return s * n

>>> times("FidhV", 5)
'FidhVFidhVFidhVFidhVFidhV'
>>> def times(s:list[int], n:int = 3) -> list:
	return s * n

模块Mypy: 检测是否类型对应

https://blog.csdn.net/cainiao_python/article/details/98818428

2.10.3 内省

>>>times._name_
name
>>>times._annotations_          //返回函数的类型注释

>>>exchange._doc_             //查看函数文档
>>>print(exchange)

https://blog.csdn.net/bai666ai/article/details/123973442

2.11 高阶函数

funtools 模块
funtools.reduce():将可迭代对象中的元素依次传递到第一个参数指定的函数中。最终返回累计的结果。

>>> def add(x, y):
	return x + y

>>> import functools
>>> functools.reduce(add, [1, 2, 3, 4, 5])
15

偏函数:将一个函数拆分,多次进行传递
functools.partial()

>>> functools.reduce(lambda x,y:x*y, range(1, 11))
3628800
>>> square = functools.partial(pow, exp=2)
>>> square(2)
4
>>> square(3)
9
>>> cube = functools.partial(pow, exp=3)
>>> cube(2)
8

@wraps装饰器:@functools.wrap()

import time
import functools

def time_master(func):
    #@functools.wraps(func)
    def call_func():
        print("start:")
        start = time.time()
        func()
        stop = time.time()
        print("over")
        print(f"一共花费了{(stop-start):.2f}秒。")
    return call_func

@time_master
def myfunc():
    time.sleep(2)
    print("I love hanyu.")

#myfunc = time_master(myfunc)
myfunc()

如果不使用装饰器,调用myfunc._name_得到的是call_func
使用后则是myfunc()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值