python-函数基础
一.初识函数
1. 什么是函数
2. 函数定义, 函数名, 函数体以及函数的调用
3. 函数的返回值
4. 函数的参数
1. 什么是函数:
函数: 对代码块和功能的封装和定义,在需要时调用.
2. 函数定义, 函数名, 函数体以及函数的调用
2.1 函数定义:
我们使用def关键字来定义函数,函数的定义语法,这里的函数名的命名规则和变量的命名规则一样.
函数体:就是函数被执行之后要执行的代码.
def 函数名():
函数体
2.2 函数的调用
使用函数名加小括号就可以调用了,若不写括号,代表的函数的内存地址.
写法:函数名() 这个时候函数的函数体会被执行.
2.3 函数的执行流程
1 定义函数
2 开辟内存
3 调用函数
4 执行函数体
5 销毁函数开辟的内存
3. 函数的返回值
执行完函数之后,我们可以使用return来返回结果,return只在函数中使用,当返回值只用一个的时候,就是返回值本身,多个的话返回的是元组类型的数据.
函数中return的使用(),
1. 函数中遇到return, 此函数结束, 不再继续执⾏.
2. 给函数的调⽤者⼀个访问结果,不写返回的内容或没有return返回的是None.
4. 函数的参数
参数, 函数在调⽤的时候指定具体的⼀个变量的值. 就是参数. 语法:
def 函数名(参数): 函数体
def msg(name,sex,age):
print('姓名:%s'%name)
print('性别:%s'%sex)
print('年龄:%s'%age)
msg('sc','man',31)
执行结果:
姓名:sc 性别:man 年龄:31
关于参数:
1. 形参 写在函数声明的位置的变量叫形参. 形式上的⼀个完整. 表⽰这个函数需要xxx
形参有分为位置参数和默认值参数,
注意: 在使⽤混合参数的时候,位置参数必须在默认值参数前面.
2. 实参 在函数调⽤的时候给函数传递的值. 叫实参, 实际执⾏的时候给函数传递的信息. 表⽰给函数 xxx
实参又分为位置实参和关键中实参
注意: 在使⽤混合参数的时候, 关键字参数必须在位置参数后⾯
3. 传参 给函数传递信息的时候将实际参数交给形式参数的过程被称为传参.
二 函数的进阶
1. 函数参数--动态传参
2. 名称空间, 局部名称空间, 全局名称空间, 作⽤域, 加载顺序.
3. 函数的嵌套
4. gloabal, nonlocal关键字
1. 函数参数--动态传参
之前我们说过了传参, 如果我们需要给⼀个函数传参, ⽽参数⼜是不确定的. 或者我给⼀个 函数传很多参数, 我的形参就要写很多, 很⿇烦, 怎么办呢.
我们可以考虑使⽤动态参数. 形参的第三种: 动态参数 动态参数分成两种:
1 动态接收位置参数 *args
2 动态接收关键字参数 **kwargs
动态接收参数的时候要注意: 动态参数必须在位置参数后⾯
def func(a, b, c, *args, x=11, y=22, z=33, **kwargs): print(a, b, c) print(args) print(x, y, z) print(kwargs) func(1, 2, 3, 4, 5, g=1, j=2, k=36) '''
执行结果 1 2 3 (4, 5) 11 22 33 {'g': 1, 'j': 2, 'k': 36}
'''
总结动态传参:顺序: 位置参数, *args, 默认值参数,**kwargs
args得到的元组数据类型
kwargs得到的字典数据类型
args和kwargs是可以更换的,但是一般约定俗成都不改
def fuc(a,*args): #*在形参位置叫聚合 print(a) print(args)
li = [1,2,3,4] fuc(*li) # 在实参位置叫打散
''' 执行结果 1 (2, 3, 4) '''
2. 名称空间, 局部名称空间, 全局名称空间, 作用域, 加载顺序.
我们给存放名字和值的关系的空间起⼀个名字叫: 命名空间. 我们的变量在存储的时候就 是存储在这片空间中的.
命名空间分类:
1. 全局命名空间--> 我们直接在py⽂件中, 函数外声明的变量都属于全局命名空间
2. 局部命名空间--> 在函数中声明的变量会放在局部命名空间
3. 内置命名空间--> 存放python解释器为我们提供的名字, list, tuple, str, int这些都是内置命名空间
加载顺序:
1. 内置命名空间
2. 全局命名空间
3. 局部命名空间(函数被执⾏的时候)
取值顺序:
1. 局部命名空间
2. 全局命名空间
3. 内置命名空间
作⽤域:
作⽤域就是作⽤范围, 按照⽣效范围来看分为 全局作⽤域和局部作⽤域 全局作⽤域: 包含内置命名空间和全局命名空间.
在整个⽂件的任何位置都可以使⽤(遵循 从上到下逐⾏执⾏). 局部作⽤域: 在函数内部可以使⽤.
作⽤域命名空间:
1. 全局作⽤域: 全局命名空间 + 内置命名空间
2. 局部作⽤域: 局部命名空间 我们可以通过globals()函数来查看全局作⽤域中的内容, 也可以通过locals()来查看局部作 ⽤域中的变量和函数信息
3. 函数的嵌套
嵌套函数的执⾏顺序,从上往下执行,只要遇见了()就是函数的调⽤. 如果没有()就不是函数的调⽤
4. gloabal, nonlocal关键字
global表示直接使用全局变量中的值
def func():
global a #加了个global表示不再局部创建这个变量了. ⽽是直接使⽤全局的a
a = 20 #global表⽰. 不再使⽤局部作⽤域中的内容了.
print(a)
func()
print(a)
'''
执行结果
20
20
'''
lst = [1, 2, 3, 4]
def func():
lst.append(5)
print(lst)
func()
print(lst)
''' ## 对于可变数据类型可以直接进⾏访问. 但是不能改地址. 说⽩了. 不能赋值
执行结果
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
'''
nonlocal 表⽰在局部作⽤域中, 调⽤⽗级命名空间中的变量.
a = 10
def func():
a = 20
def inner():
nonlocal a #结果: 加了nonlocal结果为 30 30 10
a = 30
print(a)
inner()
print(a)
func()
print(a)
'''
30
30
10
'''
三 闭包和迭代器
1. 函数名的应用
2. 闭包
3. 迭代器
1. 函数名的应用
1. 函数名代表内存地址
2. 函数名可以赋值给其他变量
3. 函数名可以当做容器类的元素
4. 函数名可以当做函数的参数
5. 函数名可以作为函数的返回值
2. 闭包
闭包就是内层函数, 对外层函数(非全局)的变量的引⽤. 叫闭包
def outer():
a = 10
def inner():
print(a)
return inner
inner = outer()
inner() #输出结果 10
闭包的作⽤就是让⼀个变量能够常驻内存. 供后⾯的程序使⽤.
我们可以使⽤__closure__来检测函数是否是闭包. 使⽤函数名.__closure__返回cell就是 闭包. 返回None就不是闭包
3. 迭代器
li = [1,2,3]
a = li.__iter__() #获取迭代器
print(a.__next__()) #获取一个元素 1
print(a.__next__()) #获取一个元素 2
print(a.__next__()) # 获取一个元素3
print(a.__next__()) #Stopiteration
''' 结果: 1 2 3 '''
使用while模拟for循环:
li = [1, 23, 4, 5, 67, 8]
em = li.__iter__()
while 1:
try: #监听try一下的代码块 一旦出现except的判断语句就运行except
print(em.__next__())
except StopIteration:
break
总结:
Iterable: 可迭代对象. 内部包含__iter__()函数 Iterator: 迭代器. 内部包含__iter__() 同时包含__next__().
迭代器的特点: 1. 节省内存. 2. 惰性机制 3. 不能反复, 只能向下执⾏.
四 生成器和推导式
1.生成器
2.推导式
1.生成器
什么是⽣成器.
⽣成器实质就是迭代器.
在python中有三种⽅式来获取⽣成器:
1. 通过⽣成器函数
2. 通过各种推导式来实现⽣成器
3. 通过数据的转换也可以获取⽣成器
1.1生成器函数
def func():
print(1)
yield 2
print(3)
yield 4
a= func()
print(a.__next__())
print(a.__next__())
'''
执行结果:
1
2
3
4
'''
将函数中return换成yield就是生成器函数
这时候执行函数名+()就不是执行函数了,而是获取生成器,要想执行生成器函数就需要用迭代器的方法(生成器的本质就是迭代器)
使用__iter__()执行生成器函数,当程序运⾏完最后⼀个yield. 那么后⾯继续进⾏__next__()程序会报错.
yield和return的效果是⼀样的. 有什么区别呢?
yield是分段来执⾏⼀个 函数. return是直接停⽌执⾏函数.
send⽅法
send和__next__()⼀样都可以让⽣成器执⾏到下⼀个yield.
send和__next__()区别:
1. send和next()都是让⽣成器向下走⼀次
2. send可以给上⼀个yield的位置传递值, 不能给最后⼀个yield发送值.
3.在第⼀次执⾏⽣成器代码的时候使⽤send()时,只能使用则send(None)
def func():
print(11)
a = yield 22
print(a)
b = yield 44
print(b)
yield 55
s = func()
print(s.__next__()) #或者s.send(None)
print(s.send('一次传值'))
print(s.send('二次传值'))
'''
执行结果
11
22
一次传值
44
二次传值
55
'''
⽣成器可以使⽤for循环来循环获取内部的元素:
def func():
print(111)
yield 222
print(333)
yield 444
g = func()
for i in g:
print(i)
'''
111
222
333
444
'''
yield from:
在python3中提供一种可以直接把可迭代对象中的每一个数据作为生成器的结果进行返回
yield from 是将列表中的每一个元素返回,所以 如果写两个yield from 并不会产生交替的效果
li = [1,2,3]
def func(li):
yield from li
yield from li
g= func(li)
for i in g:
print(i)
'''
1
2
3
1
2
3
'''
2. 推导式
2.1列表推导式
列表推导式的常用写法: [ 结果 for 变量 in 可迭代对象 if 条件]
li = [i for i in range(10) if i % 2 == 0]
print(li)
'''
[0,2,4,6,8]
'''
2.2 集合推导式
集合推导式可以帮我们直接生成一个集合,集合的特点;无序,不重复 所以集合推导式自带去重功能
li = {i for i in range(10) if i % 2 == 0}
print(li)
'''
{0, 2, 4, 6, 8}
'''
2.3 字典推导式
lst1 = ['jay','jj']
lst2 = ['周杰伦','林俊杰']
dic = {lst1[i]:lst2[i] for i in range(len(lst1))}
print(dic)
'''
{'jay': '周杰伦', 'jj': '林俊杰'}
'''
2.4生成器推导式
⽣成器表达式和列表推导式的语法基本上是⼀样的. 只是把[]替换成()
li = (i for i in range(3)) print(li.__next__()) print(li.__next__()) print(li.__next__()) ''' 0 1 2 '''
⽣成器表达式和列表推导式的区别:
1. 列表推导式比较耗内存. ⼀次性加载. ⽣成器表达式⼏乎不占⽤内存. 使⽤的时候才分 配和使⽤内存
2. 得到的值不⼀样. 列表推导式得到的是⼀个列表. ⽣成器表达式获取的是⼀个⽣成器.