函数
一、目录
1、函数嵌套 2、名称空间和作用域 3、迭代器 4、生成器 5、三元表达式,列表解析,生成器表达式
6、声明,此博文不是为小白而生,所以特别基础的就不入博客里面
二、函数嵌套
函数对象:函数是第一类对象,即函数可以当作数据传递
1 可以被引用
2 可以当作参数传递
3 返回值可以是函数
3 可以当作容器类型的元素
def bar(): print("from nbar") def foo(): print("from foo") bar() foo()
函数的嵌套调用:在调用一个函数的过程中,又调用了其他函数
函数的嵌套定义:在一个函数的内部,又定义了另外一个函数
函数调用的三种形式 1 语句形式:foo() 2 表达式形式:3*len('hello') 3 当中另外一个函数的参数:range(len('hello'))
函数参数省略,*args和××kwargs不解释
××× 阶段性练习
1、写函数,,用户传入修改的文件名,与要修改的内容,执行函数,完成批了修改操作
2、写函数,计算传入字符串中【数字】、【字母】、【空格] 以及 【其他】的个数
3、写函数,判断用户传入的对象(字符串、列表、元组)长度是否大于5。
4、写函数,检查传入列表的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。
5、写函数,检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新列表返回给调用者。
6、写函数,检查字典的每一个value的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。
dic = {"k1": "v1v1", "k2": [11,22,33,44]}
PS:字典中的value只能是字符串或列表
#题目一 def modify_file(filename,old,new): import os with open(filename,'r',encoding='utf-8') as read_f,\ open('.bak.swap','w',encoding='utf-8') as write_f: for line in read_f: if old in line: line=line.replace(old,new) write_f.write(line) os.remove(filename) os.rename('.bak.swap',filename) modify_file('/Users/jieli/PycharmProjects/爬虫/a.txt','alex','SB') #题目二 def check_str(msg): res={ 'num':0, 'string':0, 'space':0, 'other':0, } for s in msg: if s.isdigit(): res['num']+=1 elif s.isalpha(): res['string']+=1 elif s.isspace(): res['space']+=1 else: res['other']+=1 return res res=check_str('hello name:aSB passowrd:alex3714') print(res) #题目三:略 #题目四 def func1(seq): if len(seq) > 2: seq=seq[0:2] return seq print(func1([1,2,3,4])) #题目五 def func2(seq): return seq[::2] print(func2([1,2,3,4,5,6,7])) #题目六 def func3(dic): d={} for k,v in dic.items(): if len(v) > 2: d[k]=v[0:2] return d print(func3({'k1':'abcdef','k2':[1,2,3,4],'k3':('a','b','c')}))
三、名称空间和作用域
名称空间:存放名字的地方,准确的说名称空间是存放名字和变量绑定关系,分为以下三种
内置名称空间:在python解释器启动的时候产生,存放一些python内置的名字
全局名称空间:在执行文件的时候产生,存放文件级别定义的名字
局部名称空间:在执行文件的过程中,如果调用了函数,则会产生该函数的局部名称空间,用来存放该函数内定义的名字,该函数在调用的时候生效,在函数调用结束后失效(或者调用了del)
注意:三个名称空间的加载顺序:首先内置--》全局--》局部
查找名字的顺序 首先局部--》然后全局--》然后内置
如:
# max=1 def f1(): # max=2 def f2(): # max=3 print(max) f2() f1() print(max)
补充:
locals() 是函数内的名字空间,包括局部变量和形参
globals() 全局变量,函数定义所在模块的名字空间
dir() 查看内置方法
nonlocal 所声明的变量必须已经存在,否则会报错,修改外层函数的变量
global global---将变量定义为全局变量。可以通过定义为全局变量,实现在函数内部改变变量值
eval() 将字符串str当成有效的表达式来求值并返回计算结果
enclosing 外部嵌套函数的名字空间(闭包中常见)
builtins 内置模块的名字空间
作用域关系: 在函数定义的时候都已经固定好了,和调用位置没有关系
作用域即范围
--全局范围:全局存活,全局有效
---局部范围:临时存活,局部有效
x=1 def f1(): def f2(): print(x) return f2 def f3(func): x=2 func() f3(f1()) #x=1 查看作用域:globals(),locals() global nonlocal LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
如果还不懂? 点击我看关于作用域详细
四、迭代器
迭代器:重复的过程称之为迭代器,每次重复即一次迭代,每次迭代的结果是下一次迭代的初始值
可迭代对象iterator:凡是对象有.__iter__方法,就是迭代器对象
什么是迭代器对象?
1、有__iter__方法(执行得到的仍然是迭代本身)
2、有__next__方法 (迭代器对象中还有__iter__方法,这是为for循环做准备)
l={'a':1,'b':2,'c':3,'d':4,'e':5}
i=l.__iter__() #等于i=iter(l)
print(next(i))
print(next(i))
print(next(i))
如果继续执行next方法,超过最大限制会爆出:
StopIteration 异常
迭代器对象的优点:
1、提供一种统一的(不依赖索引)的迭代方式
2、迭代器本身,比其他数据类型更加节省内存
迭代器的缺点
1、一次性的,只能我那个后奏,不能回退,不如索引取值灵活
2、无法预知什么时候取值结束,无法预知长度
其中for循环原理:遵守迭代器协议
for循环开始时,会通过迭代协议传递给iter()内置函数,从而能够从可迭代对象中获得一个迭代器,返回的对象含有需要的next()方法
迭代器对象有
(1)序列类型,如 str,list,tuple,set
(2)非序列类型,如 dict, file
(3)用户自定义的一些包含了__iter__()或__getitem__()方法的类
五、生成器
生成器:
在函数内部包含yeild关键字,那么该函数执行的结果就是生成器
生成器就是迭代器(可以用isinstance判断)
yield功能:
1、把函数的结果做成迭代器(以一种优雅的方式封装好__iter__ __next__方法)
2、函数暂停与再继续运行的状态是由yield完成
def func(): print("first") yield 1111 print("second") yield 222 print("third") yield 333 g=func() print(next(g)) next(g) print(next(g)) # first # 1111 # second # third # 333 # next(g) #StopIteration
可以把上面的做成for循环遍历
for i in g: print(i) first 1111 second 222 third 333
还没明白?好吧继续往下看
如何产生一个无穷无尽的序列?
是否会想到如下?
def func(n): l=[] while True: l.append(n) n+=1 func(1000000000000)
想到这里确实不错,可是我可不敢运行啊.。。。
优化:
def func(n): print("===") while True: yield n n+=1 g=func(0) for i in g: print(i)
这样就好了,优化后的代码,在电脑中跑一天一夜也没有问题,撑爆不了内存的,因为同一时间内存中只有一个值
range功能:
首先看代码:
for i in range(100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000): print(i)
你敢拿这个在服务器上跑吗?为什么不敢!
这里首先for循换执行__iter__方法变成迭代器,所以内存中根本就没有那么大。
注意:
在python2中就不能这样用,因为python2中range是转换变成了列表
再看生成器:
def my_range(start,stop): while True: if start == stop: raise StopIteration yield start start+=1 g=my_range(1,3) for i in my_range(1,3): print(i)
是否看懂?
其实就是创建一个开始和结束的值,如果开始等于结束,那么就结束,这里用了抛出异常来终止运行
模拟tail -f 文件路径|grep "error"
import time def tail(fileppath): with open(fileppath,"r") as f: f.seek(0,2) while True: line=f.readline() if line: yield line else: time.sleep(0.2) def grep(pattern,lines): for line in lines: if pattern in line: print(line,end="") grep("error",tail("文件路径"))
六、三元表达式,列表解析,生成器表达式
1、三元表达式
表达式:
def foo(x): if x>3: return "ok" else: return "no" g=foo(4) print(g)
使用三元表达式
x=10 res="ok" if x>3 else "no" print(res)
从我工作中发现性能上来说,三元表达式要好点
2、列表解析
往列表中添加元素
l=[] for i in range(10): l.append(i) print(l)
还可以用列表解析
l=[i for i in range(10)] print(l)
小思考下面的运行方式是什么样子的:
l=[i for i in range(10) if i>=5] print(l)
3、生成器表达式
生成器:应用于数据量很大的场景
l=(i for i in range(10)) print(l.__next__()) print(l.__next__())
语法:
(expr for iter_var in iterable)
(expr for iter_var in iterable if condition_expr)
生成器表达式优点:
生内存,一次只产生一个值在内存中
示例:
#一
with open('a.txt') as f:
print(max(len(line) for line in f))
print(sum(len(line) for line in f)) #求包换换行符在内的文件所有的字节数,为何得到的值为0?
#二
print(max(len(line) for line in open('a.txt')))
print(sum(len(line) for line in open('a.txt')))
#三
with open('a.txt') as f:
g=(len(line) for line in f)
print(sum(g)) #为何报错?

浙公网安备 33010602011771号