一、函数的特殊用法:
1.变量可以指向函数:
def my_abs(n):
if n < 0:
return -n
return n
print(my_abs(-1)) # 1
f = my_abs # 函数名就是变量,可以赋值
print(f(-2)) # 2
结论:函数本身也可以直接赋值给一个变量,也就是说:变量可以指向一个函数 num = 10
如果一个变量指向了一个函数,则可以通过这个变量去调用这个函数
2.函数名是一个变量:
num = 10
num = "hello"
#让abs指向一个整型
print(abs)
abs = 10
print(abs)
结论:函数名其实就是指向函数的变量
abs():可以将abs看做一个变量,指向了一个可以计算绝对值的函数
abs更改指向【变量的重新赋值】
3.函数可以作为参数:
# 定义一个函数fn设定传入函数fn的一个形参为函数
def fn(x, f):
x = 10 + x
return f(x, 20)
# 定义一个函数
def f(x, y):
return x + y
# 将函数f作为一个参数传入fn函数当中
s = fn(1, f)
print(s)
# 运行结果:
31
结论:变量可以指向函数,函数名是一个变量,而函数的形参本身就是一个变量,可以接收实参,那么一个函数就可以接收另一个函数作为参数
高阶函数【一个函数就可以接收另一个函数作为参数】
二、函数的作用域:
1.变量的作用域:
出现原因:
-
变量的作用域:变量可以被使用【被访问】的范围
-
程序中的变量并不是在任意的语句中都可以被访问,访问权限取决于这个变量被定义在哪个位置
2.作用划分:
局部作用域:L【Local】
函数作用域:E【Enclosing】 将变量定义在闭包外的函数中
全局作用域:G【Global】
內建作用域:B【Built-in】
num4 = int(2.9) #B;內建作用域
num3 = 3 #G;全局作用域
def outer():
num1 = 1 #E:函数作用域
def inner():
num2 = 2 #L:局部作用域
#注意:当所有的变量不同名的时候,在闭包中,可以任意访问四种不同作用域对应的变量
print(num4,num3,num2,num1)
return inner
f = outer()
f()
3.全局变量与局部变量:
局部变量: 函数内部的变量
特点:
- 内存会被自动释放(调用后会自动回收内存,退出函数就不可以使用)
- 不会被污染
全局变量: 整个文件都可以使用的变量
特点:
- 内存不会释放(消耗内存,但是可以一直使用),
- 容易被污染(可能被其他地方修改)
b = 10 # 全局变量
def fn():
a = 10 # 局部变量
print('a =', a)
print('b =', b)
fn()
4.global与nonlocal关键字的使用:
- 使用原因:当内部作用域【局部作用域,函数作用域】想要修改全局变量的作用域的时候
- global 关键字:
#全局变量
num = 1
def fun1():
#此时要使用全局变量中的num,需要给编译器做一个声明,声明此处使用num就是使用的全局变量中的num
global num
print(num)
print(id(num)) #1355785312
#局部变量
num = 123
print(num)
print(id(num)) #1355789216
fun1()
- nonlocal 关键字:
#前提:nonlocal关键字是定义在闭包中
x = 0
def outer():
x = 1
def inner():
#使用nonlocal关键字进行声明,相当于将局部作用域范围扩大了
nonlocal x
x = 2
print("inner:",x)
#return inner
inner()
print("outer:",x) #2
outer()
print("global:",x)
"""
原本:
inner: 2
outer: 1
global: 0
"""
"""
修改:
inner: 2
outer: 2
global: 0
"""
结论:global是在函数中声明,所选变量为全局变量。nonlocal则是将全局变量的作用域扩大了,同时nonlocal只能在闭包函数之中使用。
三、函数嵌套与闭包:
1.函数嵌套:
既在一个函数之中再声明一个函数:
def f1():
print('f1')
def f2():
print('f2')
f2()
f1()
# 运行结果:
f1
f2
注:在嵌套函数中如果需要使用内部函数需要在外部函数声明,否则调用外部函数时内部函数无法使用
2.闭包:
如果在一个外部函数中定义一个内部函数,并且外部函数的返回值是内部函数,就构成了一个闭包,则这个内部函数就被称为闭包【closure】
# 声明一个函数,参数为可变参数
def f1(*arges):
# 声明内部函数,参数为n
def f2(n):
# 内部函数返回值为当外部函数传入值为一个的时候将传入外部函数参数
# 与传入内部函数的参数相加
return n + arges[0]
# 当传入可变参数的个数为二的时候将两个参数进行相加并输出
if len(arges) == 2:
return arges[0] + arges[1]
# 当传入参数为一的时候返回f2形成闭包
return f2
print(f1(2, 3)) # 5
print(f1(2)(3)) # 5
四、偏函数:
默认参数:可以降低函数调用的难度
偏函数:对函数参数做一些控制的函数
注意:偏函数不需要自定义,直接使用【系统函数】
import functools
# 偏函数的使用
#int(x) 可以将字符串或者浮点型转换为整型
#默认:将其中的字符串按照十进制输出
print(int("123"))
#int()还提供了一个额外的参数:base
#print(int("abc123"))
#base;指明前面数据的进制,int()执行完成之后,最终还是以十进制输出
print(int("123",base = 10)) #123
print(int("123",base = 8)) #83
print(int("110",base = 2))
print(int("11010",base = 2))
print(int("110001",base = 2))
#转换大量的二进制,每次传入base = 2麻烦,可以将这个功能提取出来
def customInt(x,base=2):
return int(x,base)
print(customInt("110")) #6
print(customInt("11010")) #26
#上面通过默认参数模仿了偏函数的使用,但是系统提供了功能:functools.partial可以创建一个偏函数【前提;需要导入import functools】
#参数:需要创建偏函数的原函数名 需要设定的参数
五、列表生成式与生成器:
1.列表生成式:
list comprehension
系统内置的用于创建list的方式
range(start,end,step)缺点:生成的列表一般情况下都是等差数列
# 生成列表
l = [1,2,3,4,5]
l = list(range(1,6))
l = []
for i in range(1,6):
l.append(i)
# 列表生成式/列表推导式
l = [i for i in range(1,6)] # [1, 2, 3, 4, 5]
l = [i*i for i in range(1,6)] # [1, 4, 9, 16, 25]
l = [i for i in range(1,6) if i%2] # [1, 3, 5]
l = [i for i in range(1,6) if i%2 and i>2] # [3, 5]
l = [i for i in range(1,6) if i%2 if i<5] # [1, 3]
l = [i+j for i in "123" for j in "abc"]
# => ['1a', '1b', '1c', '2a', '2b', '2c', '3a', '3b', '3c']
# for i in "123":
# for j in "abc":
# i+j
print(l)
l1 = [1,2,3,4,5]
l2 = [i*10 for i in l1]
print(l2) # [10, 20, 30, 40, 50]
# 字典生成式: 了解
d = {i:i*i for i in range(5)}
print(d) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 集合生成式: 了解
s = {i*i for i in range(5)}
print(s) # {0, 1, 4, 9, 16}
2.生成器:
generator
next()
方式一:(),将列表生成式中的[]改成()
#列表生成式的类型是list,生成器的类型是generator【当做一种新的数据类型】
r1 = (x ** 2 for x in range(1,6))
print(r1) #(1,4,9,16,25)
print(type(r1))
"""
for i in r1:
print(i)
"""
#生成器区别于列表生成式:可以使用next遍历,每调用一次则获取一个元素
#next()
print(next(r1))
print(next(r1))
print(next(r1))
print(next(r1))
print(next(r1))
#注意:当生成器中的元素全部获取完成之后,接着调用next函数的,则会出现StopIteration
#print(next(r1)) #StopIteration异常
方式二:yield---->让步
#(x for x in range(1,6))----->1,2,3,4,5
def test(n):
for i in range(1, n + 1):
#执行到yield的时候,则函数会停止,将yiled后面的变量返回
yield i ** 2
#yield后面的代码的执行时机:当调用next函数的时候
print(i)
t = test(5)
print(t) #<generator object test at 0x0000019CC432A1A8>
print(next(t))
print(next(t))
print(next(t))
print(next(t))
print(next(t))
六、迭代器与迭代对象:
1.可迭代对象:
可迭代对象【实体】:可以直接作用于for循环的实体【Iterable】
可以直接作用于for循环的数据类型:
a.list,tuple,dict,set,string
b.generator【() 和yield】
isinstance:判断一个实体是否是可迭代的对象
# 可迭代对象
# 可以使用for-in遍历的就是可迭代对象
# 有: list,tuple,dict,set,str
# type() : 获取数据类型
print(type(10)) # <class 'int'>
# isinstance(): 判断某个对象是否属于某个(或多个)类
print(isinstance(10, int)) # True
print(isinstance('abc', (int, float, str))) # True
print(isinstance([], Iterable)) # True
print(isinstance((), Iterable)) # True
print(isinstance({}, Iterable)) # True
print(isinstance({1}, Iterable)) # True
print(isinstance("abc", Iterable)) # True
print(isinstance((i for i in range(2)), Iterable)) # True
print(isinstance(10, Iterable)) # False
2.迭代器:
不但可以作用于for循环,还可以被next函数遍历【不断调用并返回一个元素,直到最后一个元素被遍历完成,则出现StopIteration】
目前为止,只有生成器才是迭代器【Iterator】
结论:迭代器肯定是可迭代对象,但是,可迭代对象不一定是迭代器
isinstance:判断一个实体是否是迭代器
from collections import Iterator
print(isinstance([],Iterator))
print(isinstance((),Iterator))
print(isinstance({},Iterator))
print(isinstance("hello",Iterator))
print(isinstance((x for x in range(10)),Iterator)) #True
print("****88")
3.迭代对象与迭代器的相互转换:
可以将可迭代对象转换为迭代器:iter()
#虽然list、tuple、dict、set、string都不是迭代器
#iter():将list、tuple、dict、set、string的 Iterable转换为Iterator
print(isinstance(iter([]),Iterator))
print(isinstance(iter(()),Iterator))
print(isinstance(iter({}),Iterator))
print(isinstance(iter("hello"),Iterator))
4.总结:
a.凡是可以作用于for循环的对象都是Iterable类型
b.凡是可以作用于next函数的对象都是Iterator类型
c.list/tuple/dict/set/string都不是Iterator,可以通过iter()获得一个Iterator对象
七、习题:
- 写一个函数,识别字符串是否符合python语法的变量名
数字字母下划线,且不能以数字开头,不能使用关键字
提示: 关键字获取使用
import keyword
keyword.kwlist - 封装函数,传入不定个数的数字,返回所有数字的和, 提示: *args
- 年月日分别为自定义函数的参数,判断某一个日期是否为合法的日期;
如: 2018年12月33日不是合法的日期
2018年11月13日是合法的日期 - 封装函数,判断一个年份是不是闰年
八、上期习题答案:
- 封装函数,判断某个数是否是素数,返回结果(True或False)
def judgeprime(n):
if n == 1:
return False
for i in range(2, n):
if n % i == 0:
return False
else:
return True
- 封装函数,实现如下要求
例如:输入2,5,则求2+22+222+2222+22222的和
def doth(n, m):
for i in range(1, m):
n += n*(10**i)
return n
- 已知邮箱的用户名只能由数字字母下划线组成,域名为@phone.com,
封装函数is_legal_email,判断一个字符串是否是千锋邮箱,是返回True,不是返回False。
mail@phone.com 是
$mail@phone.com 不是
mail@phone.comp 不是
# 方法一:
def is_legal_email(s):
import re
s1 = s.split('@')
l1 = s[0].replace('_', '')
if len(s1) != 2 or s1[1] != '1000phone.com' or not l1.isalnum():
return False
else:
return True
# 方法二:
def is_legal_emaill(s):
import re
s1 = s.split('@')
print(s1)
t = re.search("\W", s1[0])
if len(s1) != 2 or s1[1] != '1000phone.com' or t is not None:
return False
else:
return True