什么是函数? 为什么要用函数? 函数的分类:内置函数与自定义函数 如何自定义函数 语法 定义有参数函数,及有参函数的应用场景 定义无参数函数,及无参函数的应用场景 定义空函数,及空函数的应用场景 调用函数 如何调用函数 函数的返回值 函数参数的应用:形参和实参,位置参数,关键字参数,默认参数,*args,**kwargs 高阶函数(函数对象) 函数嵌套 作用域与名称空间 装饰器 迭代器与生成器及协程函数 三元运算,列表解析、生成器表达式 函数的递归调用 内置函数 面向过程编程与函数式编程
本节课程重点
一:为何用函数之不使用函数的问题 #组织结构不清晰 #代码冗余 #无法统一管理且维护难度大 二:函数分类: 1. 内置函数 2. 自定义函数 三:为何要定义函数 函数即变量,变量必须先定义后使用,未定义而直接引用函数,就相当于在引用一个不存在的变量名 代码演示? 四:定义函数都干了哪些事? 只检测语法,不执行代码 五:如何定义函数(函数名要能反映其意义) def ... 六:定义函数的三种形式 无参:应用场景仅仅只是执行一些操作,比如与用户交互,打印 有参:需要根据外部传进来的参数,才能执行相应的逻辑,比如统计长度,求最大值最小值 空函数:设计代码结构 七 :函数的调用 1 先找到名字 2 根据名字调用代码 函数的返回值? 0->None 1->返回1个值 多个->元组 什么时候该有? 调用函数,经过一系列的操作,最后要拿到一个明确的结果,则必须要有返回值 通常有参函数需要有返回值,输入参数,经过计算,得到一个最终的结果 什么时候不需要有? 调用函数,仅仅只是执行一系列的操作,最后不需要得到什么结果,则无需有返回值 通常无参函数不需要有返回值 八:函数调用的三种形式 1 语句形式:foo() 2 表达式形式:3*len(‘hello‘) 4 当中另外一个函数的参数:range(len(‘hello‘)) 九:函数的参数: 1 形参和实参定义 2 形参即变量名,实参即变量值,函数调用则将值绑定到名字上,函数调用结束,解除绑定 3 具体应用 位置参数:按照从左到右的顺序定义的参数 位置形参:必选参数 位置实参:按照位置给形参传值 关键字参数:按照key=value的形式定义实参 无需按照位置为形参传值 注意的问题: 1. 关键字实参必须在位置实参右面 2. 对同一个形参不能重复传值 默认参数:形参在定义时就已经为其赋值 可以传值也可以不传值,经常需要变得参数定义成位置形参,变化较小的参数定义成默认参数(形参) 注意的问题: 1. 只在定义时赋值一次 2. 默认参数的定义应该在位置形参右面 3. 默认参数通常应该定义成不可变类型 可变长参数: 针对实参在定义时长度不固定的情况,应该从形参的角度找到可以接收可变长实参的方案,这就是可变长参数(形参) 而实参有按位置和按关键字两种形式定义,针对这两种形式的可变长,形参也应该有两种解决方案,分别是*args,**kwargs
===========*args=========== def foo(x,y,*args): print(x,y) print(args) foo(1,2,3,4,5) def foo(x,y,*args): print(x,y) print(args) foo(1,2,*[3,4,5]) def foo(x,y,z): print(x,y,z) foo(*[1,2,3]) ===========**kwargs=========== def foo(x,y,**kwargs): print(x,y) print(kwargs) foo(1,y=2,a=1,b=2,c=3) def foo(x,y,**kwargs): print(x,y) print(kwargs) foo(1,y=2,**{‘a‘:1,‘b‘:2,‘c‘:3}) def foo(x,y,z): print(x,y,z) foo(**{‘z‘:1,‘x‘:2,‘y‘:3}) ===========*args+**kwargs=========== def foo(x,y): print(x,y) def wrapper(*args,**kwargs): print(‘====>‘) foo(*args,**kwargs)
命名关键字参数:*后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递 可以保证,传入的参数中一定包含某些关键字
def foo(x,y,*args,a=1,b,**kwargs): print(x,y) print(args) print(a) print(b) print(kwargs) foo(1,2,3,4,5,b=3,c=4,d=5) 结果: 1 2 (3, 4, 5) 1 3 {‘c‘: 4, ‘d‘: 5}
十 阶段性练习
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‘)}))
题目答案
=======================本节课新内容========================== 一:函数对象:函数是第一类对象,即函数可以当作数据传递 1 可以被引用 2 可以当作参数传递 3 返回值可以是函数 3 可以当作容器类型的元素 #利用该特性,优雅的取代多分支的if def foo(): print(‘foo‘) def bar(): print(‘bar‘) dic={ ‘foo‘:foo, ‘bar‘:bar, } while True: choice=input(‘>>: ‘).strip() if choice in dic: dic[choice]() 二:函数的嵌套 1 函数的嵌套调用 def max(x,y): return x if x > y else y def max4(a,b,c,d): res1=max(a,b) res2=max(res1,c) res3=max(res2,d) return res3 print(max4(1,2,3,4)) 2 函数的嵌套定义 def f1(): def f2(): def f3(): print(‘from f3‘) f3() f2() f1() f3() #报错 三 名称空间和作用域: 名称空间:存放名字的地方,三种名称空间,(之前遗留的问题x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方) 加载顺序是? 名字的查找顺序?(在全局无法查看局部的,在局部可以查看全局的)
# max=1 def f1(): # max=2 def f2(): # max=3 print(max) f2() f1() print(max)
作用域即范围 - 全局范围:全局存活,全局有效 - 局部范围:临时存活,局部有效 - 作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,如下
x=1 def f1(): def f2(): print(x) return f2 def f3(func): x=2 func() f3(f1())
查看作用域:globals(),locals() global nonlocal LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__ locals 是函数内的名字空间,包括局部变量和形参 enclosing 外部嵌套函数的名字空间(闭包中常见) globals 全局变量,函数定义所在模块的名字空间 builtins 内置模块的名字空间 四:闭包:内部函数包含对外部作用域而非全局作用域的引用 提示:之前我们都是通过参数将外部的值传给函数,闭包提供了另外一种思路,包起来喽,包起呦,包起来哇 def counter(): n=0 def incr(): nonlocal n x=n n+=1 return x return incr c=counter() print(c()) print(c()) print(c()) print(c.__closure__[0].cell_contents) #查看闭包的元素 闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域 应用领域:延迟计算(原来我们是传参,现在我们是包起来) from urllib.request import urlopen def index(url): def get(): return urlopen(url).read() return get baidu=index(‘http://www.baidu.com‘) print(baidu().decode(‘utf-8‘)) 五: 装饰器(闭包函数的一种应用场景) 1 为何要用装饰器: 开放封闭原则:对修改封闭,对扩展开放 2 什么是装饰器 装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。 强调装饰器的原则:1 不修改被装饰对象的源代码 2 不修改被装饰对象的调用方式 装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能 3. 先看简单示范 import time def timmer(func): def wrapper(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) stop_time=time.time() print(‘run time is %s‘ %(stop_time-start_time)) return res return wrapper @timmer def foo(): time.sleep(3) print(‘from foo‘) foo() 4 def auth(driver=‘file‘): def auth2(func): def wrapper(*args,**kwargs): name=input("user: ") pwd=input("pwd: ") if driver == ‘file‘: if name == ‘egon‘ and pwd == ‘123‘: print(‘login successful‘) res=func(*args,**kwargs) return res elif driver == ‘ldap‘: print(‘ldap‘) return wrapper return auth2 @auth(driver=‘file‘) def foo(name): print(name) foo(‘egon‘) 5 装饰器语法: 被装饰函数的正上方,单独一行 @deco1 @deco2 @deco3 def foo(): pass foo=deco1(deco2(deco3(foo))) 6 装饰器补充:wraps
from functools import wraps def deco(func): @wraps(func) #加在最内层函数正上方 def wrapper(*args,**kwargs): return func(*args,**kwargs) return wrapper @deco def index(): ‘‘‘哈哈哈哈‘‘‘ print(‘from index‘) print(index.__doc__)
7 装饰器练习
一:编写函数,(函数执行的时间是随机的)
二:编写装饰器,为函数加上统计时间的功能
三:编写装饰器,为函数加上认证的功能
四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
注意:从文件中读出字符串形式的字典,可以用eval(‘{"name":"egon","password":"123"}‘)转成字典格式
五:编写装饰器,为多个函数加上认证功能,要求登录成功一次,在超时时间内无需重复登录,超过了超时时间,则必须重新登录
六:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果
七:为题目五编写装饰器,实现缓存网页内容的功能:
具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
扩展功能:用户可以选择缓存介质/缓存引擎,针对不同的url,缓存到不同的文件中
八:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作
九 编写日志装饰器,实现功能如:一旦函数f1执行,则将消息2017-07-21 11:12:11 f1 run写入到日志文件中,日志文件路径可以指定
注意:时间格式的获取
import time
time.strftime(‘%Y-%m-%d %X‘)
#题目一:略 #题目二:略 #题目三:略 #题目四: db=‘db.txt‘ login_status={‘user‘:None,‘status‘:False} def auth(auth_type=‘file‘): def auth2(func): def wrapper(*args,**kwargs): if login_status[‘user‘] and login_status[‘status‘]: return func(*args,**kwargs) if auth_type == ‘file‘: with open(db,encoding=‘utf-8‘) as f: dic=eval(f.read()) name=input(‘username: ‘).strip() password=input(‘password: ‘).strip() if name in dic and password == dic[name]: login_status[‘user‘]=name login_status[‘status‘]=True res=func(*args,**kwargs) return res else: print(‘username or password error‘) elif auth_type == ‘sql‘: pass else: pass return wrapper return auth2 @auth() def index(): print(‘index‘) @auth(auth_type=‘file‘) def home(name): print(‘welcome %s to home‘ %name) # index() # home(‘egon‘) #题目五 import time,random user={‘user‘:None,‘login_time‘:None,‘timeout‘:0.000003,} def timmer(func): def wrapper(*args,**kwargs): s1=time.time() res=func(*args,**kwargs) s2=time.time() print(‘%s‘ %(s2-s1)) return res return wrapper def auth(func): def wrapper(*args,**kwargs): if user[‘user‘]: timeout=time.time()-user[‘login_time‘] if timeout < user[‘timeout‘]: return func(*args,**kwargs) name=input(‘name>>: ‘).strip() password=input(‘password>>: ‘).strip() if name == ‘egon‘ and password == ‘123‘: user[‘user‘]=name user[‘login_time‘]=time.time() res=func(*args,**kwargs) return res return wrapper @auth def index(): time.sleep(random.randrange(3)) print(‘welcome to index‘) @auth def home(name): time.sleep(random.randrange(3)) print(‘welcome %s to home ‘ %name) index() home(‘egon‘) #题目六:略 #题目七:简单版本 import requests import os cache_file=‘cache.txt‘ def make_cache(func): def wrapper(*args,**kwargs): if not os.path.exists(cache_file): with open(cache_file,‘w‘):pass if os.path.getsize(cache_file): with open(cache_file,‘r‘,encoding=‘utf-8‘) as f: res=f.read() else: res=func(*args,**kwargs) with open(cache_file,‘w‘,encoding=‘utf-8‘) as f: f.write(res) return res return wrapper @make_cache def get(url): return requests.get(url).text # res=get(‘https://www.python.org‘) # print(res) #题目七:扩展版本 import requests,os,hashlib engine_settings={ ‘file‘:{‘dirname‘:‘./db‘}, ‘mysql‘:{ ‘host‘:‘127.0.0.1‘, ‘port‘:3306, ‘user‘:‘root‘, ‘password‘:‘123‘}, ‘redis‘:{ ‘host‘:‘127.0.0.1‘, ‘port‘:6379, ‘user‘:‘root‘, ‘password‘:‘123‘}, } def make_cache(engine=‘file‘): if engine not in engine_settings: raise TypeError(‘egine not valid‘) def deco(func): def wrapper(url): if engine == ‘file‘: m=hashlib.md5(url.encode(‘utf-8‘)) cache_filename=m.hexdigest() cache_filepath=r‘%s/%s‘ %(engine_settings[‘file‘][‘dirname‘],cache_filename) if os.path.exists(cache_filepath) and os.path.getsize(cache_filepath): return open(cache_filepath,encoding=‘utf-8‘).read() res=func(url) with open(cache_filepath,‘w‘,encoding=‘utf-8‘) as f: f.write(res) return res elif engine == ‘mysql‘: pass elif engine == ‘redis‘: pass else: pass return wrapper return deco @make_cache(engine=‘file‘) def get(url): return requests.get(url).text # print(get(‘https://www.python.org‘)) print(get(‘https://www.baidu.com‘)) #题目八 route_dic={} def make_route(name): def deco(func): route_dic[name]=func return deco @make_route(‘select‘) def func1(): print(‘select‘) @make_route(‘insert‘) def func2(): print(‘insert‘) @make_route(‘update‘) def func3(): print(‘update‘) @make_route(‘delete‘) def func4(): print(‘delete‘) print(route_dic) #题目九 import time import os def logger(logfile): def deco(func): if not os.path.exists(logfile): with open(logfile,‘w‘):pass def wrapper(*args,**kwargs): res=func(*args,**kwargs) with open(logfile,‘a‘,encoding=‘utf-8‘) as f: f.write(‘%s %s run\n‘ %(time.strftime(‘%Y-%m-%d %X‘),func.__name__)) return res return wrapper return deco @logger(logfile=‘aaaaaaaaaaaaaaaaaaaaa.log‘) def index(): print(‘index‘) index()
六:迭代器 迭代的概念:重复的过程称为迭代,每次重复即一次迭代,并且每次迭代的结果是下一次迭代的初始值 # while True: #只满足重复,因而不是迭代 # print(‘====>‘) #迭代 l=[1,2,3] count=0 while count < len(l): #只满足重复,因而不是迭代 print(‘====>‘,l[count]) count+=1 为何要有迭代器? 可迭代的对象? 哪些是可迭代对象? 迭代器? 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)) StopIteration? for循环 迭代器的优缺点: 优点: 提供统一的且不依赖于索引的迭代方式 惰性计算,节省内存 缺点: 无法获取长度 一次性的,只能往后走,不能往前退 迭代器协议 练习:判断以下对象哪个是可迭代对象,哪个是迭代器对象
s=‘hello‘
l=[1,2,3,4]
t=(1,2,3)
d={‘a‘:1}
set={1,2,3}
f=open(‘a.txt‘)
七 生成器 yield: 把函数做成迭代器 对比return,可以返回多次值,挂起函数的运行状态
# def foo(): # return 1 # return 2 # return 3 # # res=foo() # print(res) def foo(): yield 1 yield 2 yield 3 res=foo() print(res) from collections import Iterable,Iterator print(isinstance(res,Iterator)) print(next(res)) print(next(res)) print(next(res)) 应用一: def counter(n): print(‘start‘) i=0 while i < n: yield i i+=1 print(‘end‘) c=counter(5) # print(next(c)) #0 # print(next(c)) #1 # print(next(c)) #2 # print(next(c)) #3 # print(next(c)) #4 # print(next(c)) #5 --->没有yield,抛出StopIteration for i in counter(5): print(i) 应用二:管道tail -f a.txt |grep ‘python‘ import time def tail(filepath): with open(filepath,encoding=‘utf-8‘) as f: f.seek(0,2) while True: line=f.readline() if line: yield line else: time.sleep(0.5) def grep(pattern,lines): for line in lines: if pattern in line: yield line for i in grep(‘python‘,tail(‘a.txt‘)): print(i) #协程函数 def eater(name): print(‘%s说:我开动啦‘ %name) food_list=[] while True: food=yield food_list food_list.append(food) print(‘%s 吃了 %s‘ %(name,food)) e=eater(‘egon‘) e.send(None) #next(e) #初始化装饰器, e.close() #关闭 面向过程编程: import os def init(func): def wrapper(*args,**kwargs): g=func(*args,**kwargs) next(g) return g return wrapper def search(file_dir,target): for par_dir,_,files in os.walk(file_dir): for file in files: filepath=‘%s\%s‘ %(par_dir,file) target.send(filepath) @init def opener(target): while True: filepath=yield with open(filepath) as f: target.send((f,filepath)) @init def cat(target): while True: res=False f,filepath=yield res for line in f: print(line,end=‘‘) res=target.send((line,filepath)) if res: break @init def grep(pattern,target): res = False while True: line,filepath=yield res res=False if pattern in line: res=True target.send(filepath) @init def printer(): while True: filepath=yield print(filepath) search(r‘C:\Users\Administrator\PycharmProjects\test\字符编码\a‘, opener(cat(grep(‘python‘,printer()))))
#注意:target.send(...)在拿到target的返回值后才算执行结束 import os def init(func): def wrapper(*args,**kwargs): g=func(*args,**kwargs) next(g) return g return wrapper @init def search(target): while True: search_dir=yield for par_dir,_,files in os.walk(search_dir): for file in files: file_abs_path=r‘%s\%s‘ %(par_dir,file) # print(file_abs_path) target.send(file_abs_path) @init def opener(target): while True: file_abs_path=yield with open(file_abs_path,encoding=‘utf-8‘) as f: target.send((file_abs_path,f)) @init def cat(target): while True: file_abs_path,f=yield print(‘检索文件‘,file_abs_path) for line in f: tag=target.send((file_abs_path,line)) print(‘检索文件的行: %s‘ %line) if tag: break @init def grep(pattern,target): tag=False while True: file_abs_path,line=yield tag tag=False if pattern in line: tag=True target.send(file_abs_path) @init def printer(): while True: file_abs_path=yield print(‘过滤出的结果=========>‘,file_abs_path) search_dir=r‘C:\Users\Administrator\PycharmProjects\test\函数备课\a‘ e=search(opener(cat(grep(‘python‘,printer())))) e.send(search_dir)
备注
八:三元表达式,列表推导式,生成器表达式
==============================#三元表达式
name=‘alex‘
name=‘linhaifeng‘
res=‘SB‘ if name == ‘alex‘ else ‘shuai‘
print(res)
==============================列表推导式
------------------1:引子
生一筐鸡蛋
egg_list=[]
for i in range(10):
egg_list.append(‘鸡蛋%s‘ %i)
egg_list=[‘鸡蛋%s‘ %i for i in range(10)] #列表解析
------------------2:语法
[expression for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
...
for itemN in iterableN if conditionN
]
类似于
res=[]
for item1 in iterable1:
if condition1:
for item2 in iterable2:
if condition2
...
for itemN in iterableN:
if conditionN:
res.append(expression)
------------------3:优点
方便,改变了编程习惯,声明式编程
------------------4:应用
l1=[3,-4,-1,5,7,9]
[i**i for i in l1]
[i for i in l1 if i >0]
s=‘egon‘
[(i,j) for i in l1 if i>0 for j in s] #元组合必须加括号[i,j ...]非法
==============================生成器表达式
------------------1:引子
生一筐鸡蛋变成给你一只老母鸡,用的时候就下蛋,这也是生成器的特性
egg_list=[]
for i in range(10):
egg_list.append(‘鸡蛋%s‘ %i)
chicken=(‘鸡蛋%s‘ %i for i in range(10))
>>> chicken
<generator object <genexpr> at 0x10143f200>
>>> next(chicken)
‘鸡蛋5‘
------------------2:语法
语法与列表推导式类似,只是[]->()
(expression for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
...
for itemN in iterableN if conditionN
)
------------------3:优点
省内存,一次只产生一个值在内存中
------------------4:应用
读取一个大文件的所有内容,并且处理行
f=open(‘a.txt‘)
g=(line.strip() for line in f)
list(g) #因g可迭代,因而可以转成列表
------------------5:示例
#一
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)) #为何报错?
==============================声明式编程
文件a.txt内容
apple 10 3
tesla 100000 1
mac 3000 2
lenovo 30000 3
chicken 10 3
f=open(‘a.py‘)
#求花了多少钱
g=(line.split() for line in f)
sum(float(price)*float(count) for _,price,count in g)
模拟数据库查询
>>> f=open(‘a.txt‘)
>>> g=(line.split() for line in f)
>>> goods_l=[{‘name‘:n,‘price‘:p,‘count‘:c} for n,p,c in g]
过滤查询
>>> goods_l=[{‘name‘:n,‘price‘:p,‘count‘:c} for n,p,c in g if float(p) > 10000]
九:匿名函数lambda
匿名就是没有名字
def func(x,y,z=1):
return x+y+z
匿名
lambda x,y,z=1:x+y+z #与函数有相同的作用域,但是匿名意味着引用计数为0,使用一次就释放,除非让其有名字
func=lambda x,y,z=1:x+y+z
func(1,2,3)
#让其有名字就没有意义
有名函数:循环使用,保存了名字,通过名字就可以重复引用函数功能
匿名函数:一次性使用,随时随时定义
应用:max,min,sorted,map,reduce,filter
十 内建函数
注意:内置函数id()可以返回一个对象的身份,返回值为整数。这个整数通常对应与该对象在内存中的位置,但这与python的具体实现有关,不应该作为对身份的定义,即不够精准,最精准的还是以内存地址为准。is运算符用于比较两个对象的身份,等号比较两个对象的值,内置函数type()则返回一个对象的类型
字典的运算:最小值,最大值,排序 salaries={ ‘egon‘:3000, ‘alex‘:100000000, ‘wupeiqi‘:10000, ‘yuanhao‘:2000 } 迭代字典,取得是key,因而比较的是key的最大和最小值 >>> max(salaries) ‘yuanhao‘ >>> min(salaries) ‘alex‘ 可以取values,来比较 >>> max(salaries.values()) >>> min(salaries.values()) 但通常我们都是想取出,工资最高的那个人名,即比较的是salaries的值,得到的是键 >>> max(salaries,key=lambda k:salary[k]) ‘alex‘ >>> min(salaries,key=lambda k:salary[k]) ‘yuanhao‘ 也可以通过zip的方式实现 salaries_and_names=zip(salaries.values(),salaries.keys()) 先比较值,值相同则比较键 >>> max(salaries_and_names) (100000000, ‘alex‘) salaries_and_names是迭代器,因而只能访问一次 >>> min(salaries_and_names) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: min() arg is an empty sequence sorted(iterable,key=None,reverse=False)
#字符串可以提供的参数 ‘s‘ None >>> format(‘some string‘,‘s‘) ‘some string‘ >>> format(‘some string‘) ‘some string‘ #整形数值可以提供的参数有 ‘b‘ ‘c‘ ‘d‘ ‘o‘ ‘x‘ ‘X‘ ‘n‘ None >>> format(3,‘b‘) #转换成二进制 ‘11‘ >>> format(97,‘c‘) #转换unicode成字符 ‘a‘ >>> format(11,‘d‘) #转换成10进制 ‘11‘ >>> format(11,‘o‘) #转换成8进制 ‘13‘ >>> format(11,‘x‘) #转换成16进制 小写字母表示 ‘b‘ >>> format(11,‘X‘) #转换成16进制 大写字母表示 ‘B‘ >>> format(11,‘n‘) #和d一样 ‘11‘ >>> format(11) #默认和d一样 ‘11‘ #浮点数可以提供的参数有 ‘e‘ ‘E‘ ‘f‘ ‘F‘ ‘g‘ ‘G‘ ‘n‘ ‘%‘ None >>> format(314159267,‘e‘) #科学计数法,默认保留6位小数 ‘3.141593e+08‘ >>> format(314159267,‘0.2e‘) #科学计数法,指定保留2位小数 ‘3.14e+08‘ >>> format(314159267,‘0.2E‘) #科学计数法,指定保留2位小数,采用大写E表示 ‘3.14E+08‘ >>> format(314159267,‘f‘) #小数点计数法,默认保留6位小数 ‘314159267.000000‘ >>> format(3.14159267000,‘f‘) #小数点计数法,默认保留6位小数 ‘3.141593‘ >>> format(3.14159267000,‘0.8f‘) #小数点计数法,指定保留8位小数 ‘3.14159267‘ >>> format(3.14159267000,‘0.10f‘) #小数点计数法,指定保留10位小数 ‘3.1415926700‘ >>> format(3.14e+1000000,‘F‘) #小数点计数法,无穷大转换成大小字母 ‘INF‘ #g的格式化比较特殊,假设p为格式中指定的保留小数位数,先尝试采用科学计数法格式化,得到幂指数exp,如果-4<=exp<p,则采用小数计数法,并保留p-1-exp位小数,否则按小数计数法计数,并按p-1保留小数位数 >>> format(0.00003141566,‘.1g‘) #p=1,exp=-5 ==》 -4<=exp<p不成立,按科学计数法计数,保留0位小数点 ‘3e-05‘ >>> format(0.00003141566,‘.2g‘) #p=1,exp=-5 ==》 -4<=exp<p不成立,按科学计数法计数,保留1位小数点 ‘3.1e-05‘ >>> format(0.00003141566,‘.3g‘) #p=1,exp=-5 ==》 -4<=exp<p不成立,按科学计数法计数,保留2位小数点 ‘3.14e-05‘ >>> format(0.00003141566,‘.3G‘) #p=1,exp=-5 ==》 -4<=exp<p不成立,按科学计数法计数,保留0位小数点,E使用大写 ‘3.14E-05‘ >>> format(3.1415926777,‘.1g‘) #p=1,exp=0 ==》 -4<=exp<p成立,按小数计数法计数,保留0位小数点 ‘3‘ >>> format(3.1415926777,‘.2g‘) #p=1,exp=0 ==》 -4<=exp<p成立,按小数计数法计数,保留1位小数点 ‘3.1‘ >>> format(3.1415926777,‘.3g‘) #p=1,exp=0 ==》 -4<=exp<p成立,按小数计数法计数,保留2位小数点 ‘3.14‘ >>> format(0.00003141566,‘.1n‘) #和g相同 ‘3e-05‘ >>> format(0.00003141566,‘.3n‘) #和g相同 ‘3.14e-05‘ >>> format(0.00003141566) #和g相同 ‘3.141566e-05‘
format(了解即可)
十一:内建函数补充(结合lambda)
字典的运算:最小值,最大值,排序
salaries={
‘egon‘:3000,
‘alex‘:100000000,
‘wupeiqi‘:10000,
‘yuanhao‘:2000
}
迭代字典,取得是key,因而比较的是key的最大和最小值
>>> max(salaries)
‘yuanhao‘
>>> min(salaries)
‘alex‘
可以取values,来比较
>>> max(salaries.values())
100000000
>>> min(salaries.values())
2000
但通常我们都是想取出,工资最高的那个人名,即比较的是salaries的值,得到的是键
>>> max(salaries,key=lambda k:salary[k])
‘alex‘
>>> min(salaries,key=lambda k:salary[k])
‘yuanhao‘
也可以通过zip的方式实现
salaries_and_names=zip(salaries.values(),salaries.keys())
先比较值,值相同则比较键
>>> max(salaries_and_names)
(100000000, ‘alex‘)
salaries_and_names是迭代器,因而只能访问一次
>>> min(salaries_and_names)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: min() arg is an empty sequence
sorted(iterable,key=None,reverse=False)
#eval与compile
eval(str,[,globasl[,locals]])
eval(‘1+2+max(3,9,100)+1.3‘)
my_globals={‘x‘:1}
my_locals={‘x‘:2}
eval(‘1+x‘,my_globals,my_locals)
exec(‘for i in range(10):print("i")‘)
同样可以指定自己的名称空间
compile(str,filename,kind)
filename:用于追踪str来自于哪个文件,如果不想追踪就可以不定义
kind可以是:single代表一条语句,exec代表一组语句,eval代表一个表达式
s=‘for i in range(10):print(i)‘ code=compile(s,‘‘,‘exec‘) exec(code) s=‘1+2+3‘ code=compile(s,‘‘,‘eval‘) eval(code)
十二:函数的递归调用 图解:递推和回溯 # salary(5)=salary(4)+300 # salary(4)=salary(3)+300 # salary(3)=salary(2)+300 # salary(2)=salary(1)+300 # salary(1)=100 # # salary(n)=salary(n-1)+300 n>1 # salary(1) =100 n=1 def salary(n): if n == 1: return 100 return salary(n-1)+300 print(salary(5)) 函数在调用时,直接或间接调用了自身,就是递归调用 def fac(n):#阶乘运算 if n == 1:return 1 else:return n*fib(n-1) 递归效率低,需要在进入下一次递归时保留当前的状态,见51cto博客 解决方法是尾递归,即在函数的最后一步(而非最后一行)调用自己 但是python又没有尾递归,且对递归层级做了限制 1. 必须有一个明确的结束条件 2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少 3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出) 尾递归优化:http://egon09.blog.51cto.com/9161406/1842475 >>> sys.getrecursionlimit() 1000 >>> n=1 >>> def test(): ... global n ... n+=1 ... print(n) ... test() ... >>> test() >>> sys.setrecursionlimit(10000) >>> test() #可以递归10000层了 虽然可以设置,但是因为不是尾递归,仍然要保存栈,内存大小一定,不可能无限递归 十三 阶段性练习: 1 文件内容如下,标题为:姓名,性别,年纪,薪资 egon male 18 3000 alex male 38 30000 wupeiqi female 28 20000 yuanhao female 28 10000 要求: 从文件中取出每一条记录放入列表中, 列表的每个元素都是{‘name‘:‘egon‘,‘sex‘:‘male‘,‘age‘:18,‘salary‘:3000}的形式 2 根据1得到的列表,取出薪资最高的人的信息 3 根据1得到的列表,取出最年轻的人的信息 4 根据1得到的列表,将每个人的信息中的名字映射成首字母大写的形式 5 根据1得到的列表,过滤掉名字以a开头的人的信息 6 使用递归打印斐波那契数列(前两个数的和得到第三个数) 0 1 1 2 3 4 7... 7 l=[1,2,[3,[4,5,6,[7,8,[9,10,[11,12,13,[14,15]]]]]]] 一个列表嵌套很多层,用递归取出所有的值
#1 with open(‘db.txt‘) as f: items=(line.split() for line in f) info=[{‘name‘:name,‘sex‘:sex,‘age‘:age,‘salary‘:salary} for name,sex,age,salary in items] print(info) #2 print(max(info,key=lambda dic:dic[‘salary‘])) #3 print(min(info,key=lambda dic:dic[‘age‘])) # 4 info_new=map(lambda item:{‘name‘:item[‘name‘].capitalize(), ‘sex‘:item[‘sex‘], ‘age‘:item[‘age‘], ‘salary‘:item[‘salary‘]},info) print(list(info_new)) #5 g=filter(lambda item:item[‘name‘].startswith(‘a‘),info) print(list(g)) #6 #非递归 def fib(n): a,b=0,1 while a < n: print(a,end=‘ ‘) a,b=b,a+b print() fib(10) #递归 def fib(a,b,stop): if a > stop: return print(a,end=‘ ‘) fib(b,a+b,stop) fib(0,1,10) #7 l=[1,2,[3,[4,5,6,[7,8,[9,10,[11,12,13,[14,15]]]]]]] def get(seq): for item in seq: if type(item) is list: get(item) else: print(item) get(l)
十四:二分法 l=[1,2,10,2,30,40,33,22,99,31] def search(num,l): print(l) if len(l) > 1: mid=len(l)//2 if num > l[mid]: #in the right l=l[mid:] search(num,l) elif num < l[mid]: #in the left l=l[:mid] search(num,l) else: print(‘find it‘) else: if num == l[0]: print(‘find it‘) else: print(‘not exists‘) search(100,l)
def search(seq,num): print(seq) if len(seq) == 1: if num == seq[0]: print(‘you find it‘) else: print(‘not exist‘) return mid=len(seq)//2 if num > seq[mid]: #in the right seq=seq[mid:] search(seq,num) elif num < seq[mid]: #in the left seq=seq[:mid] search(seq,num) else: print(‘find it‘) search(l,3)
十五:面向过程编程,函数式编程
峰哥原创面向过程解释:
函数的参数传入,是函数吃进去的食物,而函数return的返回值,是函数拉出来的结果,面向过程的思路就是,把程序的执行当做一串首尾相连的函数,一个函数吃,拉出的东西给另外一个函数吃,另外一个函数吃了再继续拉给下一个函数吃。。。
面向过程:机械式思维,流水线式编程
例如:
用户登录流程:前端接收处理用户请求-》将用户信息传给逻辑层,逻辑词处理用户信息-》将用户信息写入数据库
验证用户登录流程:数据库查询/处理用户信息-》交给逻辑层,逻辑层处理用户信息-》用户信息交给前端,前端显示用户信息。
array=[1,3,4,71,2] ret=[] for i in array: ret.append(i**2) print(ret) #如果我们有一万个列表,那么你只能把上面的逻辑定义成函数 def map_test(array): ret=[] for i in array: ret.append(i**2) return ret print(map_test(array)) #如果我们的需求变了,不是把列表中每个元素都平方,还有加1,减一,那么可以这样 def add_num(x): return x+1 def map_test(func,array): ret=[] for i in array: ret.append(func(i)) return ret print(map_test(add_num,array)) #可以使用匿名函数 print(map_test(lambda x:x-1,array)) #上面就是map函数的功能,map得到的结果是可迭代对象 print(map(lambda x:x-1,range(5)))
map
from functools import reduce #合并,得一个合并的结果 array_test=[1,2,3,4,5,6,7] array=range(100) #报错啊,res没有指定初始值 def reduce_test(func,array): l=list(array) for i in l: res=func(res,i) return res # print(reduce_test(lambda x,y:x+y,array)) #可以从列表左边弹出第一个值 def reduce_test(func,array): l=list(array) res=l.pop(0) for i in l: res=func(res,i) return res print(reduce_test(lambda x,y:x+y,array)) #我们应该支持用户自己传入初始值 def reduce_test(func,array,init=None): l=list(array) if init is None: res=l.pop(0) else: res=init for i in l: res=func(res,i) return res print(reduce_test(lambda x,y:x+y,array)) print(reduce_test(lambda x,y:x+y,array,50))
reduce
movie_people=[‘alex‘,‘wupeiqi‘,‘yuanhao‘,‘sb_alex‘,‘sb_wupeiqi‘,‘sb_yuanhao‘] def tell_sb(x): return x.startswith(‘sb‘) def filter_test(func,array): ret=[] for i in array: if func(i): ret.append(i) return ret print(filter_test(tell_sb,movie_people)) #函数filter,返回可迭代对象 print(filter(lambda x:x.startswith(‘sb‘),movie_people))
filter
#当然了,map,filter,reduce,可以处理所有数据类型 name_dic=[ {‘name‘:‘alex‘,‘age‘:1000}, {‘name‘:‘wupeiqi‘,‘age‘:10000}, {‘name‘:‘yuanhao‘,‘age‘:9000}, {‘name‘:‘linhaifeng‘,‘age‘:18}, ] #利用filter过滤掉千年王八,万年龟,还有一个九千岁 def func(x): age_list=[1000,10000,9000] return x[‘age‘] not in age_list res=filter(func,name_dic) for i in res: print(i) res=filter(lambda x:x[‘age‘] == 18,name_dic) for i in res: print(i) #reduce用来计算1到100的和 from functools import reduce print(reduce(lambda x,y:x+y,range(100),100)) print(reduce(lambda x,y:x+y,range(1,101))) #用map来处理字符串列表啊,把列表中所有人都变成sb,比方alex_sb name=[‘alex‘,‘wupeiqi‘,‘yuanhao‘] res=map(lambda x:x+‘_sb‘,name) for i in res: print(i)
总结
一 数据结构和算法
‘‘‘ 不可变:数字,字符串,元组 可变:列表,字典 原子:数字,字符串 容器:列表,元组,字典 直接访问:数字 顺序:字符串,列表,元组 映射访问:字典 ‘‘‘ #一一对应 a,b,c,d,e=‘hello‘ print(e,d) #少一个报错 # a,b,c=‘hello‘ #*号的使用 a,*_,e=‘hello‘ print(a,e) #列表中的元素解压也是一一对应的关系 data=[‘mac‘,10000,[2016,10,12]] name,price,date=data print(name,price,date) #截取子元素 name,price,[year,month,day]=data print(name,price,year,month,day) #无用的元素用_代替 data=[‘mac‘,10000,[2016,10,12]] _,price,_=data print(price) #头截取,尾截取 record=[‘lhf‘,‘male‘,18,‘[email protected]‘,‘1861131211‘] *_,phone=record name,*_=record print(phone) print(name) #解压可迭代对象赋值给多个值 #一个八个月的业绩与前七个月的平均值比较 sales_record=[11,12,3,7,9,6,3,5] *head,tail=sales_record print(head,tail) print(sum(head)/len(head)) if tail > sum(head)/len(head): print(‘第八个月业绩高于前七个月平均值‘) elif tail == sum(head)/len(head): print(‘第八个月业绩等于前七个月平均值‘) else: print(‘第八个月业绩小于前七个月平均值‘) #解压元素在函数中的应用 records=[ (‘say_hi‘,‘hello‘), (‘calculate‘,10,20,30,40), (‘dic_handle‘,‘name‘,‘lhf‘) ] def say_hi(msg): print(msg) def calculate(l): res=0 for i in l: res+=i print(res) def dic_handle(l): key,val=l dic={key:val} print(dic) for func,*args in records: if func == ‘say_hi‘: say_hi(args) elif func == ‘calculate‘: calculate(args) elif func == ‘dic_handle‘: dic_handle(args) #linux系统中用户记录 record=‘root:x:0:0:super user:/root:/bin/bash‘ *head,home_dir,_=record.split(‘:‘) print(‘家目录‘,home_dir)
解压序列赋值给多个变量
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ from collections import deque # #实现队列,不指定大小则无限添加 # d=deque(maxlen=3) # d.append(1) # d.append(2) # d.append(3) # print(d) # d.append(4) # print(d) # d.appendleft(5) # print(d) # print(d.pop()) # print(d.popleft()) def search(file,pattern,max_len=5): pre_lines=deque(maxlen=max_len) for line in file: if pattern in line: yield pre_lines,line pre_lines.append(line) if __name__ == ‘__main__‘: with open(‘测试文件‘) as file: for pre_l,line in search(file,‘Exchange‘): print(‘-‘*60) for i in pre_l: print(i) print(‘匹配行----->‘,line)
保留最后n个元素
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ import heapq nums=[1,2,3,-10,100,30,200,21,9,7] print(heapq.nlargest(3,nums)) print(heapq.nsmallest(3,nums)) portfolio=[ {‘name‘:‘IBM‘,‘shares‘:100,‘price‘:91.1}, {‘name‘:‘AAPL‘,‘shares‘:50,‘price‘:532.1}, {‘name‘:‘FB‘,‘shares‘:200,‘price‘:21.01}, {‘name‘:‘HPQ‘,‘shares‘:35,‘price‘:32.75}, {‘name‘:‘YHOO‘,‘shares‘:45,‘price‘:16.35}, {‘name‘:‘ACME‘,‘shares‘:75,‘price‘:115.65} ] cheap=heapq.nsmallest(3,portfolio,key=lambda x:x[‘price‘]) print(cheap) ‘‘‘ 如果你想在一个集合中查找最小或最大的N个元素, 并且N小于集合元素数量, 那么这些函数提供了很好的性能。 因为在底层实现里面,首先会先将集合数据进行堆 排序后放入一个列表中 ‘‘‘ heapq.heapify(nums) print(nums) ‘‘‘ 堆数据结构最重要的特征是heap[0]永远是最小的元素。 并且剩余的元素可以很 容易的通过调用heap.heappop()方法得到, 该方法会先将第一个元素弹出来,然后 用下一个最小的元素来取代被弹出元素 (这种操作时间复杂度仅仅是O(log N),N是堆大小。)比如,如果想要查找最小的3个元素, 你可以这样做: ‘‘‘ print(heapq.heappop(nums)) print(heapq.heappop(nums)) print(heapq.heappop(nums)) ‘‘‘ nlarges(),nsmallest():当要查找的元素个数相对较小时使用 min(),max():就是要找唯一一个最大的或者最小的值时使用 sort[items][n:m]:当要查找的元素个数相接近items长度时使用 ‘‘‘
查找最大或最小的n个元素
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ import heapq class PriotryQueue: def __init__(self): self._queue=[] self._index=0 def push(self,item,priotry): heapq.heappush(self._queue,(-priotry,self._index,item)) self._index+=1 def pop(self): return heapq.heappop(self._queue)[-1] class Item: def __init__(self,name): self.name=name def __str__(self): return self.name def __repr__(self): # return self.name return ‘Item({!r})‘.format(self.name) q=PriotryQueue() q.push(Item(‘镇长‘),1) q.push(Item(‘省长‘),4) q.push(Item(‘主席‘),5) q.push(Item(‘市长‘),3) q.push(Item(‘县长‘),2) print(q._queue) print(q.pop()) print(q.pop()) print(q.pop()) print(q.pop()) print(q.pop())
基于heapq实现优先级队列
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ ‘‘‘ 一个字典就是一个键对应一个单值的映射。 如果你想要一个键映射多个值,那么你就需 要将这多个值放到另外的容器中,比如列表 或者集合里面。比如,你可以像下面 这样构造这样的字典: ‘‘‘ people={ ‘name‘:[‘alex‘,‘李杰‘], ‘hobby‘:[‘play‘,‘coding‘] } project={ ‘company‘:{‘IBM‘:‘CTO‘,‘Lenovo‘:‘CEO‘,‘baidu‘:‘COO‘,‘Alibaba‘:‘UFO‘}, ‘applicant‘:[‘小白‘,‘lhf‘,‘武藤兰‘] } ‘‘‘ 选择使用列表还是集合取决于你的实际需求。 如果你想保持元素的插入顺序就应该使用列表, 如果想去掉重复元素就使用集合(并且不关心元素的顺序问题)。 你可以很方便的使用collections模块中的defaultdict来构造这样的字典。 的一个特征是它会自动初始化每个 刚开始对应的值,所以你只需要 关注添加元素操作了。比如: ‘‘‘ from collections import defaultdict d=defaultdict(list) d[‘teacher‘].append(‘alex‘) d[‘teacher‘].append(‘wupeiqi‘) d[‘teacher‘].append(‘wuqiqi‘) d[‘boss‘].append(‘oldboy‘) d[‘boss‘].append(‘alex‘) d_set=defaultdict(set) d_set[‘a‘].add(1) d_set[‘a‘].add(2) d_set[‘a‘].add(3) print(d,d_set) #setdefault d={} d.setdefault(‘a‘,[]).append(1) d.setdefault(‘a‘,[]).append(2) d.setdefault(‘a‘,[]).append(2) print(d) #自己实现一个一键多值字典 l=[ (‘teacher‘,‘alex‘), (‘teacher‘,‘lhf‘), (‘teacher‘,‘papa‘), (‘boss‘,‘alex‘), (‘boss‘,‘wupeiqi‘), ] d={} for k,v in l: if k not in d: d[k]=[] d[k].append(v) print(d) #用defaultdict实现,更优雅 d=defaultdict(list) for k,v in l: d[k].append(v) print(d)
实现一个multidict
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ from collections import OrderedDict d=OrderedDict() d[‘dream1‘]=‘先挣他妈一个亿‘ d[‘dream2‘]=‘然后周游全世界‘ d[‘dream3‘]=‘再娶他妈七八个媳妇‘ d[‘dream4‘]=‘洗洗脸,然后梦就醒了‘ for key in d: print(key,d[key]) # import json # print(json.dumps(d)) ‘‘‘ OrderedDict内部维护着一个根据键插入顺序排序的双向链表每次当一个新的元素 插入进来的时候,它会被放到链表的尾部对于一个已经存在的键的重复赋值不会改变 键的顺序。需要注意的是,一个OrderedDict的大小是一个普通字典的两倍,因为它 内部维 护着另外一个链表。所以如果你要构建一个需要大量OrderedDict实例的数 据结构的时候,那么你就得仔细 权衡一下是否使用OrderedDict带来的好处要大过 额外内存消耗的影响。 ‘‘‘
创建有序字典
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ prices={ ‘ACME‘:45.23, ‘AAPL‘:612.78, ‘IBM‘:205.55, ‘HPQ‘:37.20, ‘FB‘:10.75 } #zip()创建的是只能访问一次的迭代器,下面的max()会报错 # prices_and_names=zip(prices.values(),prices.keys()) # min_price=min(prices_and_names) # max_price=max(prices_and_names) #单纯的min(prices)是按照key来取值 min_price=min(zip(prices.values(),prices.keys())) max_price=max(zip(prices.values(),prices.keys())) print(min_price) print(max_price) print(min(prices,key=lambda k:prices[k])) print(max(prices,key=lambda k:prices[k])) ‘‘‘ 需要注意的是在计算操作中使用到了(值,键)对。当多个实体 拥有相同的值的时 候,键会决定返回结果。比如,在执行min() 和max()操作的时候,如果恰巧最小或 最大值有重复的,那么拥 有最小或最大键的实体会返回 ‘‘‘ prices={‘A‘:45.23,‘Z‘:45.23} print(min(zip(prices.values(),prices.keys())))
字典的运算
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ a={ ‘x‘:1, ‘y‘:2, ‘z‘:3, } b={ ‘w‘:1, ‘x‘:2, ‘y‘:3, } print(a.keys() & b.keys()) print(a.keys() - b.keys()) print(a.items() - b.keys()) #生成一个新的字典,去掉某些key c={key:a[key] for key in a.keys() - {‘z‘,‘w‘}} print(c)
查找俩字典的相同点
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ a=[1,5,2,1,9,1,5,10] #如果想简单去重,可以使用set,但是set是无序的 # print(set(a)) #如果序列的值都是hashable类型,那么可以简单利用集合或者生成器来解决这个问题 def dedupe(items): seen=set() for i in items: if i not in seen: yield i seen.add(i) print(list(dedupe(a))) #如果序列元素是不可hashable类型 a=[ {‘name‘:‘alex‘,‘age‘:18}, {‘name‘:‘alex‘,‘age‘:100}, {‘name‘:‘alex‘,‘age‘:100}, {‘name‘:‘lhf‘,‘age‘:18}, ] def dedupe(items,key=None): seen=set() for i in items: k=i if not key else key(i) if k not in seen: yield i seen.add(k) print(list(dedupe(a,key=lambda k:(k[‘name‘],k[‘age‘])))) #去除文件中相同的内容用第一种方法即可
去除序列中重复的值且保持顺序
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ #普通切片,一堆硬编码 record=‘苍井空‘ record1=‘abcde‘ print(record[1:3]) print(record1[1:3]) #命名切片,减少硬编码 record=‘苍井空 18621452550 沙河汇德商厦‘ phone=slice(4,15) addr=slice(16,21) print(record[phone]) print(record[addr]) ‘‘‘ 一般来讲,代码中如果出现大量的硬编码下标值会使 得可读性和可维护性大大降 低。比如,如果你回过来 看看一年前你写的代码,你会摸着脑袋想那时候自己到 底想 干嘛啊。这里的解决方案是一个很简单的方法让 你更加清晰的表达代码到底要做什么。内置的slice() 函数创建了一个切片对象,可以被用在任何切片允许使用 的地方。 ‘‘‘ s=slice(5,50,2) print(s.start) print(s.stop) print(s.step)
命名切片
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ from collections import Counter words=[‘if‘, ‘you‘, ‘were‘, ‘a‘, ‘you,I‘, ‘would‘, ‘never‘,‘if‘, ‘would‘, ‘if‘] # d={} # for word in words: # if word not in d: # d[word]=1 # else: # d[word]+=1 # print(d) word_counts=Counter(words) # print(word_counts) #统计出现频率最高的3个单词 print(word_counts.most_common(2)) #可以像字典一样取值 print(word_counts[‘if‘]) #新增单词 more_words=[‘if‘,‘if‘] for word in more_words: word_counts[word]+=1 print(word_counts) #或者直接使用 word_counts.update(more_words) print(word_counts) #counter实例可以进行数学运算 a=Counter(words) b=Counter(more_words) print(a) print(b) print(a-b) print(b-a) print(a+b)
Counter统计出现次数最多的元素
文件conf.txt内容 global log 127.0.0.1 local2 daemon maxconn 256 log 127.0.0.1 local2 info defaults log global mode http timeout connect 5000ms timeout client 50000ms timeout server 50000ms option dontlognull listen stats :8888 stats enable stats uri /admin stats auth admin:1234 frontend oldboy.org bind 0.0.0.0:80 option httplog option httpclose option forwardfor log global acl www hdr_reg(host) -i www.oldboy.org use_backend www.oldboy.org if www backend www.oldboy.org server 100.1.7.9 100.1.7.9 weight 20 maxconn 3000 原配置文件 deque_test.py内容 from collections import deque import re def conf_dic(f): dic={} for line in f: if re.match(‘[a-zA-Z]‘,line): key=line elif re.match(‘^ ‘,line): dic.setdefault(key,[]).append(line) return dic if __name__ == ‘__main__‘: with open(‘conf.txt‘,encoding=‘utf-8‘) as f: dic=conf_dic(f) for i in dic: print(‘%s‘ %i,end=‘‘) for line in dic[i]: print(line,end=‘‘) print(‘-‘*20)
读取配置文件组成字典
#_*_coding:utf-8_*_ __author__ = ‘Alex Li‘ from operator import itemgetter rows=[ {‘fname‘:‘Brian1‘,‘lname‘:‘Jones1‘,‘uid‘:1003}, {‘fname‘:‘Brian2‘,‘lname‘:‘Jones2‘,‘uid‘:1002}, {‘fname‘:‘Brian3‘,‘lname‘:‘Jones3‘,‘uid‘:1001}, {‘fname‘:‘Brian4‘,‘lname‘:‘Jones4‘,‘uid‘:1004}, ] # rows_by_uid=sorted(rows,key=lambda rows:rows[‘uid‘]) # for i in rows_by_uid: # print(i) # # # # rows_by_uid=sorted(rows,key=itemgetter(‘uid‘)) # for i in rows_by_uid: # print(i) rows_by_lfname=sorted(rows,key=itemgetter(‘lname‘,‘fname‘)) print(rows_by_lfname) for i in rows_by_lfname: print(i)
字典通过关键字排序
#_*_coding:utf-8_*_ __author__ = ‘Alex Li‘ #要生成列表 l=[] for i in range(6): i*=2 l.append(i) print(l) y=[i*2 for i in range(6)] print(y) def func(n): return n+10 z=[func(i) for i in range(6)] print(z) l=[1,2,3,-1,-10,4,5] #过滤掉负数 l_new=[i for i in l if i >= 0] print(l_new)
列表解析
#_*_coding:utf-8_*_ __author__ = ‘Alex Li‘ #生成器,取一次生成一个值,只能next不能回退,因为只有一个 l=[i for i in range(10000000)] #机器卡死 g=(i for i in range(1000000)) #一秒生成 g.__next__() #next到最后报异常 def fib(n1,n2,count=0): if count > 10:return if count == 0: print(‘‘,n1,end=‘‘) x=n2 n2=(n1+n2) n1=x print(‘ ‘,n2,end=‘‘) count+=1 fib(n1,n2,count) # # 0 1 1 2 3 5 8 13 21 # fib(0,1) # def fib2(max=10): n,a,b=0,0,1 while n < max: # x=b # b=b+a # a=x yield b a,b=b,a+b # print(‘ ‘,b,end=‘‘) n+=1 return ‘done‘ x=fib2() x.__next__() x.__next__() x.__next__() x.__next__() x.__next__() x.__next__() x.__next__() x.__next__() x.__next__() x.__next__() x.__next__() x.__next__() x.__next__() x.__next__() x.__next__() # while True: # try: # print(x.__next__()) # except Exception as e: # print(e) # break
生成器
#_*_coding:utf-8_*_ __author__ = ‘Alex Li‘ import time def consumer(name): print(‘[%s]准备吃包子啦‘ %name) while True: baozi=yield print(‘包子[%s]来了,被[%s]吃了‘ %(baozi,name)) # c=consumer(‘alex‘) # c.__next__() # c.send(‘韭菜馅的‘) def producter(): c1=consumer(‘alex‘) c2=consumer(‘wupeiqi‘) c1.__next__() c2.__next__() print(‘开始做包子啦‘) for i in range(10): time.sleep(1) c1.send(i) c2.send(i) producter() f=open(‘a.txt‘) f.__next__()
生成器并行运算
#_*_coding:utf-8_*_ __author__ = ‘Alex Li‘ name=‘lhf‘ passwd=‘123‘ def auth(auth_type): def inner_auth(func): def _wrapper(*args,**kwargs): username=input(‘username: ‘) password=input(‘passwd: ‘) if auth_type == ‘local‘: if username == name and password ==passwd: print(‘user login successfull‘) res=func(*args,**kwargs) else: exit(‘log err‘) elif auth_type == ‘ldap‘: print(‘搞毛线ldap,谁特么会‘) return _wrapper return inner_auth def index(): print(‘welcome to index page‘) @auth(auth_type=‘local‘) def home(): print("welcome to home page") @auth(auth_type=‘ldap‘) def bbs(): print(‘welcome to bbs page‘) index() home() bbs()
带参数装饰器
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ prices={ ‘ACME‘:45.23, ‘AAPL‘:612.78, ‘IBM‘:205.55, ‘HPQ‘:37.20, ‘FB‘:10.75 } prices_new={key:val for key,val in prices.items() if val > 200} print(prices_new)
从字典中提取子集
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ from collections import namedtuple #商品,购买个数,单价 records=[ (‘mac‘,2,20000), (‘lenovo‘,1,3000), (‘apple‘,0,10), (‘tesla‘,10,1000000) ] ‘‘‘ 命名元组的一个主要用途是将你的代码从下标操作中解脱出来。 因此,如果你从数 据库调用中返回了一个很大的元组列表,通 过下标去操作其中的元素,当你在表中添 加了新的列的时候你 的代码可能就会出错了。但是如果你使用了命名元组,那么就不 会有这样的顾虑。 为了说明清楚,下面是使用普通元组的代码: ‘‘‘ cost=0.0 for rec in records: cost+=rec[1]*rec[2] print(‘商品:%s 购买个数:%s,总价格为:%s‘ %(rec[0],rec[1],cost)) #使用命名元祖后 sk=namedtuple(‘Stock‘,[‘name‘,‘count‘,‘price‘]) for rec in records: s=sk(*rec) print(s.count*s.price) p=namedtuple(‘People‘,[‘name‘,‘gender‘,‘age‘]) l=[‘alex‘,‘femal‘,18] p1=p(*l) print(p1) print(p1.name) print(p1.age) print(p1.gender) ‘‘‘ 命名元组另一个用途就是作为字典的替代, 因为字典存储需要更多的内存空间。如果 你需要构建一个非常大的包含字典的数据结构, 那么使用命名元组会更加高效。但 是需要注意的是, 不像字典那样,一个命名元组是不可更改的。比如: ‘‘‘ p=namedtuple(‘People‘,[‘name‘,‘gender‘,‘age‘]) l=[‘alex‘,‘femal‘,18] p1=p(*l) print(p1.name) # p1.name=‘sb‘#报错,不可修改 p1=p1._replace(name=‘sb‘)#需要重新赋值给p1 print(p1.name) #可以新建一个函数,弥补必须使用_replace才能修改元素的缺点 p=namedtuple(‘People‘,[‘name‘,‘gender‘,‘age‘]) p1=p(‘‘,‘‘,None) def dict_to_stock(s): return p1._replace(**s) print(dict_to_stock({‘name‘:‘alex‘,‘gender‘:‘f‘,‘age‘:18})) print(dict_to_stock({‘name‘:‘sb‘,‘gender‘:‘f‘,‘age‘:18}))
nametuple命名元组