
暂无个人介绍
python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU资源,在python中大部分情况需要使用多进程。python提供了非常好用的多进程包Multiprocessing,只需要定义一个函数,python会完成其它所有事情。借助这个包,可以轻松完成从单进程到并发执行的转换。multiprocessing支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、LocK等组件 一、Process 语法:Process([group[,target[,name[,args[,kwargs]]]]]) 参数含义:target表示调用对象;args表示调用对象的位置参数元祖;kwargs表示调用对象的字典。name为别名,groups实际上不会调用。 方法:is_alive(): join(timeout): run(): start(): terminate(): 属性:authkey、daemon(要通过start()设置)、exitcode(进程在运行时为None、如果为-N,表示被信号N结束)、name、pid。其中daemon是父进程终止后自动终止,且自己不能产生新的进程,必须在start()之前设置。 1.创建函数,并将其作为单个进程 from multiprocessing import Process def func(name): print("%s曾经是好人"%name) if __name__ == "__main__": p = Process(target=func,args=('kebi',)) p.start() #start()通知系统开启这个进程 2.创建函数并将其作为多个进程 from multiprocessing import Process import random,time def hobby_motion(name): print('%s喜欢运动'% name) time.sleep(random.randint(1,3)) #Python学习交流QQ群:579817333 def hobby_game(name): print('%s喜欢游戏'% name) time.sleep(random.randint(1,3)) if __name__ == "__main__": p1 = Process(target=hobby_motion,args=('付婷婷',)) p2 = Process(target=hobby_game,args=('科比',)) p1.start() p2.start() 执行结果: 付婷婷喜欢运动 科比喜欢游戏 3.将进程定义为类(开启进程的另一种方法,并不是很常用) from multiprocessing import Process class MyProcess(Process): def __init__(self,name): super().__init__() self.name = name def run(self): #start()时,run自动调用,而且此处只能定义为run。 print("%s曾经是好人"%self.name) if __name__ == "__main__": p = MyProcess('kebi') p.start() #将Process当作父类,并且自定义一个函数。 4.daemon程序对比效果 不加daemon属性 import time def func(name): print("work start:%s"% time.ctime()) time.sleep(2) print("work end:%s"% time.ctime()) if __name__ == "__main__": p = Process(target=func,args=('kebi',)) p.start() print("this is over") #Python学习交流QQ群:579817333 #执行结果 this is over work start:Thu Nov 30 16:12:00 2017 work end:Thu Nov 30 16:12:02 2017 加上daemon属性 from multiprocessing import Process import time def func(name): print("work start:%s"% time.ctime()) time.sleep(2) print("work end:%s"% time.ctime()) if __name__ == "__main__": p = Process(target=func,args=('kebi',)) p.daemon = True #父进程终止后自动终止,不能产生新进程,必须在start()之前设置 p.start() print("this is over") #执行结果 this is over 设置了daemon属性又想执行完的方法: import time def func(name): print("work start:%s"% time.ctime()) time.sleep(2) print("work end:%s"% time.ctime()) if __name__ == "__main__": p = Process(target=func,args=('kebi',)) p.daemon = True p.start() p.join() #执行完前面的代码再执行后面的 print("this is over") #执行结果 work start:Thu Nov 30 16:18:39 2017 work end:Thu Nov 30 16:18:41 2017 this is over 5.join():上面的代码执行完毕之后,才会执行后i面的代码。 先看一个例子: from multiprocessing import Process import time,os,random def func(name,hour): print("A lifelong friend:%s,%s"% (name,os.getpid())) time.sleep(hour) print("Good bother:%s"%name) if __name__ == "__main__": p = Process(target=func,args=('kebi',2)) p1 = Process(target=func,args=('maoxian',1)) p2 = Process(target=func,args=('xiaoniao',3)) p.start() p1.start() p2.start() print("this is over") 执行结果: this is over #最后执行,最先打印,说明start()只是开启进程,并不是说一定要执行完 A lifelong friend:kebi,12048 A lifelong friend:maoxian,8252 A lifelong friend:xiaoniao,6068 Good bother:maoxian #最先打印,第二位执行 Good bother:kebi Good bother:xiaoniao 添加join() from multiprocessing import Process import time,os,random def func(name,hour): print("A lifelong friend:%s,%s"% (name,os.getpid())) time.sleep(hour) print("Good bother:%s"%name) start = time.time() if __name__ == "__main__": p = Process(target=func,args=('kebi',2)) p1 = Process(target=func,args=('maoxian',1)) p2 = Process(target=func,args=('xiaoniao',3)) p.start() p.join() #上面的代码执行完毕之后,再执行后面的 p1.start() p1.join() p2.start() p2.join() print("this is over") print(time.time() - start) #执行结果 A lifelong friend:kebi,14804 Good bother:kebi A lifelong friend:maoxian,11120 Good bother:maoxian A lifelong friend:xiaoniao,10252 #每个进程执行完了,才会执行下一个 Good bother:xiaoniao this is over 6.497815370559692 #2+1+3+主程序执行时间 改变一下位置 from multiprocessing import Process import time,os,random def func(name,hour): print("A lifelong friend:%s,%s"% (name,os.getpid())) time.sleep(hour) print("Good bother:%s"%name) start = time.time() if __name__ == "__main__": p = Process(target=func,args=('kebi',2)) p1 = Process(target=func,args=('maoxian',1)) p2 = Process(target=func,args=('xiaoniao',3)) p.start() p1.start() p2.start() p.join() #需要2秒 p1.join() #到这时已经执行完 p2.join() #已经执行了2秒,还要1秒 print("this is over") print(time.time() - start) #执行结果 A lifelong friend:kebi,13520 A lifelong friend:maoxian,11612 A lifelong friend:xiaoniao,17064 #几乎是同时开启执行 Good bother:maoxian Good bother:kebi Good bother:xiaoniao this is over 3.273620367050171 #以最长时间的为主 6.其它属性和方法 from multiprocessing import Process import time def func(name): print("work start:%s"% time.ctime()) time.sleep(2) print("work end:%s"% time.ctime()) if __name__ == "__main__": p = Process(target=func,args=('kebi',)) p.start() p.terminate() #将进程杀死,而且必须放在start()后面,与daemon的功能类似 #执行结果 this is over from multiprocessing import Process import time def func(name): print("work start:%s"% time.ctime()) time.sleep(2) print("work end:%s"% time.ctime()) if __name__ == "__main__": p = Process(target=func,args=('kebi',)) # p.daemon = True print(p.is_alive()) p.start() print(p.name) #获取进程的名字 print(p.pid) #获取进程的pid print(p.is_alive()) #判断进程是否存在 print("this is over")
一、数字类型内置方法 1.1 整型的内置方法 作用 描述年龄、号码、id号 定义方式 x = 10 x = int('10') x = int(10.1) x = int('10.1') # 报错 内置方法 没有内置方法,只有算术运算和比较运算 存在一个值还是多个值 存一个值 有序or无序 有序:有索引;无序:无索引 压根就没有这一说 可变or不可变(重点) **数字类型不可变** 可变(变量值而言) #Python学习交流QQ群:579817333 lis = [1,2,3] print(id(lis)) lis[0] = 2 #lis-->[2,2,3] print(id(lis)) 不可变(变量值而言),值变id也变 x = 10 print(id(x)) x = 20 print(id(x)) 1.2 浮点型内置方法 同整型 二、字符串类型的内置方法 2.1 作用 姓名/性别/地址 2.2 定义方式 s = b‘sdkfljl’ # 打印出来的bytes类型,二进制类型,010011001011001011 print(s) print(‘中文’,encode(‘utf8’)) 2.3 字符串内置方法(只有字符串类型才能使用) s = ‘forever handsome’ 优先掌握(今天必须得掌握) 索引取值 print(s[1]) 索引切片 print(s[4:0:1]) # 1 表示从左到右 print(s[-4:0:-1]) # -1 表示从右到左 for循环 for i in s: print(i) strip() 去除两端指定字符 s1 = ‘ nick handsome ’ print(s1.strip()) # 去除两端的空白 s2 = '****!!!nick handsome-----***' print(s2.strip('-*!')) # 指定多个字符一起去掉,只能strip里面有的字符就全部干掉 split() 切割 #Python学习交流QQ群:579817333 print(s.split()) # 默认以空格为切割条件 print(s.split('/')) # 以/切割 print(s.split('!')) # 以!切割 in 或 not in print('forever' in s ) #True print('!' not in s) # True 长度len s = '123' print(len(s)) # 3 # 求字符串的长度 2.4 存一个值还是多个值 一个值 2.5 有序or无序 有序 2.6 可变or不可变(重点) 可变:值变id不变,不可哈希 不可变:值变id也变,可哈希 不可变 s2 = 'abc' print(id(s2)) s2 += 'abc' print(id(s2))
1.26个字母大小写成对打印,例如:Aa,Bb...... for i in range(26): print(chr(65+i)+chr(97+i)) 2.一个list包含10个数字,然后生成一个新的list,要求新的list里面的数都比之前的数多1 list=[2,3,6,4,7,5,1,8,9,0] list1=[] for i in list: list1.append(i+1) print(list1) 3.倒序取出每个单词的第一个字母,例如:I am a good boy!方法1 tre='I am a good boy!' t=tre.split() #print(t) t.reverse() list=[] #print(t) for i in t: list.append(i[0]) print(list) 方法2 a = "I AM A BOY" result = [] for i in a.split()[::-1]: result.append(i[0]) print(result) 4.输入一个自己的生日月份,用if和else判断一下当月是不是你的生日月第一种方法,datetime模块获取时间 import datetime date=datetime.datetime.now() #获取当前时间 # print(date.strftime('%Y-%m-%d')) #把当前时间格式化为可读懂的年月日 r=date.strftime('%m') #把当前时间格式化为可读懂的年月日,只取月份 print(r) #Python学习交流QQ群:579817333 t=input('请输入自己的生日月份:') if t==r: print('true') else: print('不是') 第二种方法,time模块获取时间 import time # date=time.time() #获取当前时间 # print(date) # print(time.localtime(time.time()))#按固定格式显示当前时间 # print(time.strftime('%Y-%m-%d')) #把当前时间格式化为可读懂的年月日 # print(time.strftime('%Y-%m-%d',time.localtime(time.time()))) #把时间格式化为可读懂的年月日,后一个参数可省略 # print(time.strftime('%m',time.localtime(time.time()))) #只取月儿份 #t=time.strftime('%m',time.localtime(time.time()))#只取月儿份 t=time.strftime('%m')#只取月儿份 print(t)#Python学习交流QQ群:579817333 r=input('请输入自己的生日月份:') if t==r: print('true') else: print('不是') 5.输入3个字母:e、a、r,如果输入e,那么推出循环,如果输入a,执行continue,如果输入r,那么再读取一次字母,并打印,用死循环实现。 while True: str = input('请输入三个字母:') if str=='r': print(str) if str=='a': continue if str=='e': break else: print('输入有误') 6.输入3个字母:e、a、r,如果输入e,那么退出循环,如果输入a,执行continue,如果输入r,那么再读取一次字母,并打印,只允许输入三次字母,重复输入的字母不算在内。 count = 0 for i in range(3): letter = input("send a letter%d:"%i) if letter == 'e': break elif letter == 'a': continue elif letter == 'r': count += 1 if count == 2: input("send a letter dddd:") 7.把一个字符串"abcdefg"插入到一个list中,每个字母占一个list中的元素位置,例如: ["a","b","c","d","e","f","g"] ls=["a","b","c","d","e","f","g"] s="abcdefg" lt=[] #插入元素到后边 for i in s : ls.extend(i) print(ls) for i in s: ls.append(i) print(ls) #每个元素都插在第一个,或者说倒序插入列表前边 for i in s: ls.insert(0,i) print(ls) 8.['a','b','c','d','e','f','g']操作这个list,拼出一个字符串"adg" lis=['a','b','c','d','e','f','g'] print(len(lis)) t=lis[0]+lis[int(len(lis)/2)]+lis[-1] print(t) 或 print("".join(lis[::3]))
在Python语言中,json数据与dict字典以及对象之间的转化,是必不可少的操作。 在Python中自带json库。通过import json导入。 在json模块有2个方法, loads():将json数据转化成dict数据 dumps():将dict数据转化成json数据 load():读取json文件数据,转成dict数据 dump():将dict数据转化成json数据后写入json文件 下面是具体的示例: dict字典转json数据 #Python学习交流QQ群:579817333 import json def dict_to_json(): dict = {} dict['name'] = 'many' dict['age'] = 10 dict['sex'] = 'male' print(dict) # 输出:{'name': 'many', 'age': 10, 'sex': 'male'} j = json.dumps(dict) print(j) # 输出:{"name": "many", "age": 10, "sex": "male"} if __name__ == '__main__': dict_to_json() 对象转json数据 import json def obj_to_json(): stu = Student('007', '007', 28, 'male', '13000000000', '123@qq.com') print(type(stu)) # <class 'json_test.student.Student'> stu = stu.__dict__ # 将对象转成dict字典 print(type(stu)) # <class 'dict'> print(stu) # {'id': '007', 'name': '007', 'age': 28, 'sex': 'male', 'phone': '13000000000', 'email': '123@qq.com'} j = json.dumps(obj=stu)#Python学习交流QQ群:579817333 print(j) # {"id": "007", "name": "007", "age": 28, "sex": "male", "phone": "13000000000", "email": "123@qq.com"} if __name__ == '__main__': obj_to_json() json数据转成dict字典 import json #Python学习交流QQ群:579817333 def json_to_dict(): j = '{"id": "007", "name": "007", "age": 28, "sex": "male", "phone": "13000000000", "email": "123@qq.com"}' dict = json.loads(s=j) print(dict) # {'id': '007', 'name': '007', 'age': 28, 'sex': 'male', 'phone': '13000000000', 'email': '123@qq.com'} if __name__ == '__main__': json_to_dict() json数据转成对象 import json def json_to_obj(): j = '{"id": "007", "name": "007", "age": 28, "sex": "male", "phone": "13000000000", "email": "123@qq.com"}' dict = json.loads(s=j) stu = Student() stu.__dict__ = dict print('id: ' + stu.id + ' name: ' + stu.name + ' age: ' + str(stu.age) + ' sex: ' + str( stu.sex) + ' phone: ' + stu.phone + ' email: ' + stu.email) # id: 007 name: 007 age: 28 sex: male phone: 13000000000 email: 123@qq.com if __name__ == '__main__': json_to_obj() json的load()与dump()方法的使用 dump()方法的使用 import json #Python学习交流QQ群:579817333 def dict_to_json_write_file(): dict = {} dict['name'] = 'many' dict['age'] = 10 dict['sex'] = 'male' print(dict) # {'name': 'many', 'age': 10, 'sex': 'male'} with open('1.json', 'w') as f: json.dump(dict, f) # 会在目录下生成一个1.json的文件,文件内容是dict数据转成的json数据 if __name__ == '__main__': dict_to_json_write_file() load()的使用 import json def json_file_to_dict(): with open('1.json', 'r') as f: dict = json.load(fp=f) print(dict) # {'name': 'many', 'age': 10, 'sex': 'male'} if __name__ == '__main__': json_file_to_dict()
首先我是辣鸡,然后这个问题的确有点意思 首先,类是一个集合,包含了数据,操作描述的一个抽象集合 你可以首先只把类当做一个容器来使用 class Cycle: def __init__(self,r): self.pi=3.14 self.r=r a=Cycle(4) b=Cycle(7) 你看,我们定义了一个 Cycle 类,我们现在只是将它当做一个数据集合来用,我们利用其实例之间彼此数据隔离的特性来保证具体的实例数据彼此不污染。好了你现在想问,为什么我们要用数据集合来放数据 好了,我们来看看没有类之前我们会怎么样,假设我们现在要计算圆的面积 #Python学习交流QQ群:579817333 def square(r,pi): return pi * (r**2) PI=3.14 a_r=4 a_square=square(a_r,PI) b_r=7 b_square=square(b_r,PI) 看起来没有问题,好了,现在问题来了,假如,你现在要计算很多圆的面积,那么你是不是发现,不断的用变量命来隔离数据方式变得越来越脏了。而且你发现是不是有很多冗余的代码 好了我们这么改一改 class Cycle: def __init__(self,r): self.pi=3.14 self.r=r def square(value): if not isinstance(value,Cycle): raise ValueError("value muse be Cycle instace") value.square=value.pi * (value.r**2) #Python学习交流QQ群:579817333 a=Cycle(4) b=Cycle(7) square(a) square(b) 好了,你有没有觉得现在清晰了一点。 好了,现在我们现在还可以改一下 class Cycle: def __init__(self,r): self.pi=3.14 self.r=r def square(self,value): return self.pi * (self.r**2) 好了,现在你可能迷惑了,我们为啥要把 square 函数放在类中? 好了,我现在要计算长方形,原型,梯形各种各样二维几何图形的面积,这样该怎么写??? 你想了想我们之前说的将类作为数据容器,你想了想写了如下的代码 class Rectangle: def __init__(self,length,height): self.length=length self.height=height class Cycle: def __init__(self,r): self.pi=3.14 self.r=r #Python学习交流QQ群:579817333 def rec_square(value): if not isinstance(value,Rectangle): raise ValueError("value muse be Rectangle instace") value.square=value.length * value.height def cycle_square(value): if not isinstance(value,Cycle): raise ValueError("value muse be Cycle instace") value.square=value.pi * (value.r**2) 你想一想,这样是不是感觉如果计算需求越来越多,代码是不是还是会越来越脏? 如果我们将函数放在类里,并且用继承的特性,我们可以写出这样的代码 class Geometry: def get_square(self): raise NotImplementedError class Rectangle(Geometry): def __init__(self,length,height): self.length=length self.height=height def get_square(self): return self.length*self.height #Python学习交流QQ群:579817333 class Cycle(Geometry): def __init__(self,r): self.pi=3.14 self.r=r def get_square(self): return self.pi * (self.r**2) def square(value): if not isinstance(value,Geometry): raise ValueError("value muse be Geometry instace") value.square=value.get_square() 你看,我们现在只需要给用户暴露一个统一的接口,用户(用户也以是我们自己)不需要关心怎么样选择正确的函数,他只需要调用统一的 square 函数,就可以获取到具体的面积,是不是轻松很多了?? 所以,类,它是对数据,操作的一种封装,这个封装的意义在于我们可以去更好的优化代码结构。 好了再举一个例子,我们可以用类来控制访问权限 class People: def __init__(self,website): self.__favorite_website="1024.com" def bad_or_not(self): return self.__favorite_website=="1024.com" 你看,我们用 private 变量,来确保外部没法直接访问一些敏感数据(实际上 Python 里 private 并不严格,hook 一下还是可以访问的) 好,在举一个例子 class People: def __init__(self,website): self.__favorite_website="1024.com" def bad_or_not(self): return self.__favorite_website=="1024.com" @property #Python学习交流QQ群:579817333 def favorite_website(self): return self.__favorite_website @favorite_website.setter def favorite_website(self,value): if value=="1024.com": raise ValueError("你不能去草榴,兄弟,你营养不足") self.__favorite_website=value 你看,我们现在很方便的实现在设置数据值的时候,对其过滤。 撤了这么多,回到你的问题 首先A君说的没毛病,但我想知道仅仅使用函数锤子,螺丝刀来完成一个项目比使用Class工厂+函数锤子来完成一个项目的效率会更低么?理由是什么?大神在什么时候会考虑使用Class来提高代码的“执行效率”和代码的“可读性”。回归实际情况,我很多时候都是调用同一个函数/方法去输出某个结果。 至今还想不出为什么调用Class会更方便?(PS:本人大菜鸟,写了上千行代码了,但仍然搞不懂什么情况下需要用到Class类。也曾尝试在自己的代码中强行加入Class输出结果但感觉不灵活,而且要写的代码明显多了也不便于理解。求大神举例,碾压我的无知!)。C君说大型项目不使用Class调用对象会累死,到底这个“累死”体现在哪里? 首先一个问题,我整个答案里所写的这些代码,不用面向对象这一套能不能实现? 很明显,能。 但是实现的干净么?个人觉得不干净。 项目规格上去后,我们如果按照传统的方式进行开发,务必要多重检查,确保自己不会手抖调用了错误的东西。而 OOP 这一套思想,其实就是通过利用合适的代码结构和封装,某种程度上来讲是减少我们犯错的可能。 同时,现在开发基本都不是一个人的单打独斗,你写的代码可能会被其余人使用与维护。我们有个前提,要假设使用维护你代码的人都是傻逼。我们要适当的封装代码,优化结构,让使用者尽可能的少犯错、 所以最后,无论是各个语言的变量命名规则也好,还是 OOP 这一套范式也好。其本质是在自由度与可读性可维护性之间的一种相对较优的妥协,这种妥协根本的目的就在于通过规范化的操作与封装,减少团队开发维护的成本,优化开发体验。 另外,关于开发这一套还有个老生常谈的问题过度封装。我个人的观点是在你知道什么是 过度封装 之前,你没必要考虑这个问题,按照教科书和开源代码里的结构,去不断封装优化你的代码。 面向对象 更容易使用 ,缺点就是 换一个开发人,难维护,很难理解前面人的思维,出了错不好找位置
一.赋值即定义 1.运行以下代码会出现报错 #!/usr/bin/env python #_*_conding:utf-8_*_ x = 100 def outer(): def inner(): x += 100 #其实这里等效于"x = x + 100",我们直到这是一个赋值语句,会优先计算右边的等式,即"x + 100".而在此时由于x变量赋值即定义,即此时的x和全局作用域的x并非同一个对象。 print(x) return inner foo = outer() foo() 2.使用global关键字解决以上报错 #!/usr/bin/env python #_*_conding:utf-8_*_ x = 100 def outer(): def inner(): global x #Python学习交流QQ群:579817333 #注意,我们先要在inner作用域中使用全局作用域的同名x变量就得优先使用"global"关键字进行声明。 x += 100 print(x) return inner foo = outer() foo() #以上代码输出结果如下: 200 3.不推荐使用global global总结 x+=1这种是特殊形式产生的错误的原因?先引用后赋值,而python动态语言是赋值才算定义,才能被引用。解决办法,在这条语句前增加x=0之类的赋值语句,或者使用global告诉内部作用域,去全局作用域查找变量定义内部作用域使用x = 5之类的赋值语句会重新定义局部作用域使用的变量x,但是,一旦这个作用域中使用global声明x为全局的,那么x=5相当于在为全局作用域的变量x赋值 global使用原则 外部作用域变量会内部作用域可见,但也不要在这个内部的局部作用域中直接使用,因为函数的目的就是为了封装,尽量与外界隔离如果函数需要使用外部全局变量,请使用函数的形参传参解决一句话:不用global。学习它就是为了深入理解变量作用域. 二.列表中的"+"与"+="的区别 1.观察以下代码 #!/usr/bin/env python #_*_conding:utf-8_*_ def demo(x=[]): x += [1] #就地修改前一个列表,在其后追加后一个列表。就是extend方法。 print(x) print(demo.__defaults__) #我们可以查看默认参数列表 demo() demo() print(demo.__defaults__) #发现demo函数被调用2次后,默认参数的值也在跟随着变化!其原因是demo()执行完后弹栈会消亡,但解释器始终保留了一份"def demo(x=[])"的函数签名,这里面的x变量会随着解释器的消亡而消亡,除非我们使用"del"关键字去现实的删除该函数!如果我们这样干的话,后续就无法访问到该函数啦! #以上代码输出结果如下: ([],) [1] [1, 1] ([1, 1],)#!/usr/bin/env python #_*_conding:utf-8_*_ def demo(x=[]): x += [1] #就地修改前一个列表,在其后追加后一个列表。就是extend方法。 print(x) #Python学习交流QQ群:579817333 print(demo.__defaults__) #我们可以查看默认参数列表 demo() demo() print(demo.__defaults__) #发现demo函数被调用2次后,默认参数的值也在跟随着变化!其原因是demo()执行完后弹栈会消亡,但解释器始终保留了一份"def demo(x=[])"的函数签名,这里面的x变量会随着解释器的消亡而消亡,除非我们使用"del"关键字去现实的删除该函数!如果我们这样干的话,后续就无法访问到该函数啦! #以上代码输出结果如下: ([],) [1] [1, 1] ([1, 1],) 2.列表防坑总结 列表的"+"和"+="的区别: "+"表示两个列表合并并返回一个全新的列表。 "+="表示,就地修改前一个列表,在其后追加一个列表。就是extend方法。 3.注意引用变量的是可以被就地修改的(以函数的默认值参数为例) #!/usr/bin/env python #_*_conding:utf-8_*_ def demo2(x=1,y="abc",z={},*args,m=100,n,**kwargs): print(x,y,z) #打印位置参数 print(m,n) #打印key-only关键词参数 print(z.setdefault("abc","mn")) #我们为"z"变量设置一组键值对 print(demo2.__defaults__,demo2.__kwdefaults__) #我们知道"__defaults__"保留的是位置参数相关信息,而"__kwdefaults__"保留的是关键字相关信息 demo2(n=200) demo2(z = {},n = 200) print(demo2.__defaults__,demo2.__kwdefaults__) #我们发现默认的"z"变量值是被咱们有意修改啦~ #以上代码输出结果如下: (1, 'abc', {}) {'m': 100} 1 abc {} 100 200 mn 1 abc {} 100 200 mn (1, 'abc', {'abc': 'mn'}) {'m': 100}
匿名函数lambda 除了def语句,python还提供了一种生成函数对象的表达式形式。由于它与LISP语言中的一个工具类似,所以称为lambda。 就像def一样,这个表达式创建了一个之后能够调用的函数,但是它返回一个函数而不是将这个函数赋值给一个变量。这些就是lambda叫做匿名函数的原因。实际上,他常常以一种行内进行函数定义的方式使用,或者用作推迟执行一些代码。 lambda的一般形式是关键字lambda之后跟着一个或多个参数(与一个def头部内用括号括起来的参数列表类似),紧跟着是一个冒号,之后是表达式 lambda arg1,arg2,argn:expression using arguments 由lambda表达式所返回的函数对象与由def创建并复制后的函数对象工作起来是完全一致的,但lambda有一些不同之处,让其扮演特定的角色时更有用: lambda是一个表达式,而不是一个语句 因为这一点,lambda可以出现在python语法不允许def出现的地方。此外,作为一个表达式,lambda返回一个值(一个新的函数),可以选择性的赋值给一个变量相反,def语句总是得在头部将一个新的函数赋值给一个变量,而不是将这个函数作为结果返回。 lambda的主题是单个表达式,而不是一个代码块 这个lambda的主题简单的就好像放在def主体return语句中的代码一样。简单的将结果写成一个顺畅的表达式,而不是明确的返回。但由于它仅限于表达式,故lambda通常要比def功能少…你仅能够在lambda主体中封装有限的逻辑进去,因为他是一个为编写简单函数而设计的。除了上述这些差别,def和lambda都能过做同样种类的工作 def与lambda的相同用法 x = lambda x, y, z: x + y + z x(2, 3, 4) >>> 9 # Python学习交流QQ群:857662006 y = (lambda a='hello', b='world': a + b) y(b='Python') >>> 'hellopython' 为什么使用lambda 看过上面的两个小例子,很多人会说这个和def没什么差别,我们又为什么要使用lambda呢? 通常来说,lambda起到一种函数的速写作用,允许在使用的代码内嵌一个函数的定义,他完全是可选的(是可以使用def代替他们),但是在你仅需要切入一段可执行代码的情况下,它会带来一个更简洁的书写效果。 lambda通常用来编写跳转表,也就是行为的列表或者字典,能够按照需求执行操作,比如: l = [lambda x: x ** 2, lambda x: x ** 3, lambda x: x ** 4] for f in l: print(f(2)) >>> 4 >>> 8 >>> 16 print(l[0](3)) >>> 9 当需要把小段的可执行代码编写进def语句从语法上不能实现的地方是,lambda表达式作为def的一种速写来说,是最为有用的,如果上面的代码用def编写,则变为: def f1(x): return x ** 2 # Python学习交流QQ群:857662006 def f2(x): return x ** 3 def f3(x): return x ** 4 l = [f1, f2, f3] for f in l: print(f(2)) print(l[0](3)) 实际上,我们可以用python中的字典或者其他的数据结构来构建更多种类的行为表,从而做同样的事情。 lambda中实现if-else Python中具备的单行表达式:if a:b else c语法在lambda中同样适用: lower = lambda x,y:x if x<y else y lower(4,5) >>> 4 看了半天,大家可能也并未觉得lambda在python中到底比def优越与便利在哪里,那么说到lambda,就必须要提及三个函数map、filter、reduce,当你接触了这三个函数,那么你才能感受到lambda真实的方便之处 map 函数 程序对列表或者其他序列常常要做的一件事就是对每个元素进行一个操作,并把其结果集合起来。python提供了一个工具map,它会对一个序列对象中的每一个元素应用该的函数,并返回一个包含了所有函数调用结果的列表。 举个栗子,我们有一个列表,需要将列表的每一个字段+10,我们该如何操作? list_show = [1, 2, 3, 4] # 方式1 # Python学习交流QQ群:857662006 new_list_show = [] for i in list_show: new_list_show.append(i + 10) print(new_list_show) # 方式2 def adds(x): return x + 10 print(list(map(adds, list_show))) # 更优雅的方式3: print(list(map(lambda x: x + 10, list_show))) 看看上面三个实现方式,你觉得那种更加Pythonic? eg:需要注意一点,map在python3中是一个可迭代对象,引入需要使用列表调用来使它生成所有的结果用于显示,python2不必如此。 当然map的阐述函数,不仅仅支持自己编写的,同样也支持python自带的多种函数,比如: list_show = [1, -2, 3, -4, 5, -6] print(list(map(abs, list_show))) >>> [1, 2, 3, 4, 5, 6] filter函数 filter通过字面意思,大家就知道它的用处了,用于数据的过滤操作,它也是lambda的一个好基友,举个栗子。我们需要过滤0-9中,能被2整除的数字组成一个列表,我们该如何操作?只需要一行代码: print(list(filter(lambda x: x % 2 == 0, range(10)))) >>> [0, 2, 4, 6, 8] 没错,filter就是这么的简单实用…. reduce的妙用 reduce在python2中是一个简单的函数,但在python3中它责备收录与functools中。它接收一个迭代器来处理并返回一个单个的结果。 list_show = [1, 2, 3, 4] print(reduce(lambda x, y: x + y, list_show)) >>> 10 print(reduce(lambda x, y: x * y, list_show)) >>> 24 lambda的实用与它的好基友就介绍到这里,希望对大家有所帮助。
在Python函数中,传递的参数如果默认有一个为 列表(list),那么就要注意了,此处有坑. 入坑 挖坑 def f(x,li=[]): for i in range(x): li.append(i*i) print(li) print('---1---') f(4) print('---2---') f(5) 预期结果 ---1--- [0, 1, 4, 9] ---2--- [0, 1, 4, 9, 16] 执行结果 ---1--- [0, 1, 4, 9] ---2--- [0, 1, 4, 9, 0, 1, 4, 9, 16] 出坑 当定义函数时,会保存函数中默认参数 list 的值,也就是列表 li=[]; 在每次调用的时候如果传递了新的列表,则使用传递的列表,没有传递,使用定义函数时保存的默认参数(li=[]); 上面两次调用中,都没有传递新的列表(使用默认列表 li=[] ),程序会调用定义函数时保存的默认参数((li=[])); 列表在append的时候会在 li=[] 原来的基础上append追加值,所以会产生以上结果. 通过打印列表的ID进行辨识 打印列表 li=[] 的ID: def f(x,li=[]): print(id(li)) # 添加打印id for i in range(x): li.append(i*i) print(li) # Python学习交流QQ群:857662006 print('---1---') f(4) print('---2---') f(5) 结果: ---1--- 140306123906248 [0, 1, 4, 9] ---2--- 140306123906248 [0, 1, 4, 9, 0, 1, 4, 9, 16] 会发现ID值是相同的; 说明两次执行时使用的都是定义函数时的默认参数 li=[ ] 执行时往里面传新的列表 打印列表 li=[] 的ID 和 传的新列表的ID: def f(x,li=[]): print(id(li)) for i in range(x): li.append(i*i) print(li) # Python学习交流QQ群:857662006 print('---1---') f(4) print('---2---') f(5,[]) print('---3---') f(6) 结果: ---1--- [0, 1, 4, 9] ---2--- [0, 1, 4, 9, 16] ---3--- [0, 1, 4, 9, 0, 1, 4, 9, 16, 25] 会发现执行传递空(新)列表的函数时打印的ID不一样,而没有传递的一样; 当传递空列表时,函数体当中会使用传递的空列表,没有传递时,使用函数默认值 li=[ ], 所以会产生以上结果. 优化 如果想要达到预期的结果,只需要在函数体里进行判断即可: def f(x, li=[]): if not li: # Python学习交流QQ群:857662006 # 如果li不为空的话,就往下走(清空列表); 为空就不走 li = [] for i in range(x): li.append(i * i) print(li) print('---1---') f(4) print('---2---') f(5) print('---3---') f(6) 结果: ---1--- [0, 1, 4, 9] ---2--- [0, 1, 4, 9, 16] ---3--- [0, 1, 4, 9, 16, 25]
一、默认参数 python为了简化函数的调用,提供了默认参数机制: 这样在调用pow函数时,就可以省略最后一个参数不写: 在定义有默认参数的函数时,需要注意以下: 必选参数必须在前面,默认参数在后; 设置何种参数为默认参数?一般来说,将参数值变化小的设置为默认参数。 python标准库实践 python内建函数: 函数签名可以看出,使用print('hello python')这样的简单调用的打印语句,实际上传入了许多默认值,默认参数使得函数的调用变得非常简单。 二、出错了的默认参数 引用一个官方的经典示例地址 def bad_append(new_item, a_list=[]): a_list.append(new_item) return a_list print(bad_append('1')) print(bad_append('2')) 这个示例并没有按照预期打印 ['1'] ['2'] 而是打印了: ['1'] ['1', '2'] 其实这个错误问题不在默认参数上,而是我们对于及默认参数的初始化的理解有误。 三、默认参数初始化 实际上,默认参数的值只在定义时计算一次,因此每次使用默认参数调用函数时,得到的默认参数值是相同的。 我们以一个直观的例子来说明: #Python学习交流QQ群:857662006 import datetime as dt from time import sleep def log_time(msg, time=dt.datetime.now()): sleep(1) # 线程暂停一秒 print("%s: %s" % (time.isoformat(), msg)) log_time('msg 1') log_time('msg 2') log_time('msg 3') 运行这个程序,得到的输出是: 即使使用了sleep(1)让线程暂停一秒,排除了程序执行很快的因素。输出中三次调用打印出的时间还是相同的,即三次调用中默认参数time的值是相同的。 上面的示例或许还不能完全说明问题,以下通过观察默认参数的内存地址的方式来说明。 首先需要了解内建函数id(object) : id(object) Return the “identity” of an object. This is an integerwhich is guaranteed to be unique and constant for this object duringits lifetime. Two objects with non-overlapping lifetimes may have thesame id() value. CPython implementation detail: This is the address of the object inmemory. 即id(object)函数返回一个对象的唯一标识。这个标识是一个在对象的生命周期期间保证唯一并且不变的整数。在重叠的生命周期中,两个对象可能有相同的id值。在CPython解释器实现中,id(object)的值为对象的内存地址。 如下示例使用id(object)函数清楚说明了问题: #Python学习交流QQ群:857662006 def bad_append(new_item, a_list=[]): print('address of a_list:', id(a_list)) a_list.append(new_item) return a_list print(bad_append('1')) print(bad_append('2')) output: address of a_list: 31128072 ['1'] address of a_list: 31128072 ['1', '2'] 两次调用bad_append,默认参数a_list的地址是相同的。 而且a_list是可变对象,使用append方法添加新元素并不会造成list对象的重新创建,地址的重新分配。这样,‘恰好'就在默认参数指向的地址处修改了对象,下一次调用再次使用这个地址时,就可以看到上一次的修改了。 那么,出现上述的输出就不奇怪了,因为它们本来就是指向同一内存地址。 四、可变与不可变默认参数 当默认参数指向可变类型对象和不可变类型对象时,会表现出不同的行为。 可变默认参数 的表现就像上诉示例一样。 不可变默认参数 首先看一个示例: #Python学习交流QQ群:857662006 def immutable_test(i = 1): print('before operation, address of i', id(i)) i += 1 print('after operation, address of i', id(i)) return i print(immutable_test()) print(immutable_test()) Output: before operation, address of i 1470514832 after operation, address of i 1470514848 2 before operation, address of i 1470514832 after operation, address of i 1470514848 2 很明显,第二次调用时默认参数i的值不会受第一次调用的影响。因为i指向的是不可变对象,对i的操作会造成内存重新分配,对象重新创建,那么函数中i += 1之后名字i指向了另外的地址;根据默认参数的规则,下次调用时,i指向的地址还是函数定义时赋予的地址,这个地址的值1并没有被改变。 其实,可变默认参数和不可变默认参数放在这里讨论并没太大的价值,就像其他语言中所谓的值传递还是引用传递一样,不只会对默认参数造成影响。 五、最佳实践 不可变的默认参数的多次调用不会造成任何影响,可变默认参数的多次调用的结果不符合预期。那么在使用可变默认参数时,就不能只在函数定义时初始化一次,而应该在每次调用时初始化。 最佳实践是定义函数时指定可变默认参数的值为None,在函数体内部重新绑定默认参数的值。以下是对上面的两个可变默认参数示例最佳实践的应用: def good_append(new_item, a_list = None): if a_list is None: a_list = [] a_list.append(new_item) return a_list print(good_append('1')) print(good_append('2')) print(good_append('c', ['a', 'b'])) import datetime as dt from time import sleep def log_time(msg, time = None): if time is None: time = dt.datetime.now() sleep(1) print("%s: %s" % (time.isoformat(), msg)) log_time('msg 1') log_time('msg 2') log_time('msg 3')
1.print 打印带有颜色的信息 大家知道 Python 中的信息打印函数 print,一般我们会使用它打印一些东西,作为一个简单调试。 但是你知道么,这个 Print 打印出来的字体颜色是可以设置的。 一个小例子 def esc(code=0): return f'\033[{code}m' print(esc('31;1;0') + 'Error:'+esc()+'important') 在控制台或者 Pycharm 运行这段代码之后你会得到结果。 Error:important 其中 Error 是红色加下划线的,important 为默认色 其设置格式为:033[显示方式;前景色;背景色 m 下面可以设置的参数: 说明:前景色 背景色 颜色 --------------------------------------- 30 40 黑色 31 41 红色 32 42 绿色 33 43 黃色 34 44 蓝色 35 45 紫红色 36 46 青蓝色 37 47 白色 显示方式 意义 ------------------------- 0 终端默认设置 1 高亮显示 4 使用下划线 5 闪烁 7 反白显示 8 不可见 例子: \033[1;31;40m <!--1-高亮显示 31-前景色红色 40-背景色黑色--> 2.在 Python 中使用定时器 今天看到一个比较人性化的定时模块 schedule,目前 star 数为 6432,还是非常的受欢迎,这个模块也是秉承这 For Humans 的原则,这里推荐给大家。地址 https://github.com/dbader/schedule 1.通过 pip 即可安装。 pip install schedule 2.使用案例 import schedule import time def job(): print("I'm working...") schedule.every(10).minutes.do(job) schedule.every().hour.do(job) schedule.every().day.at("10:30").do(job) schedule.every().monday.do(job) schedule.every().wednesday.at("13:15").do(job) schedule.every().minute.at(":17").do(job) while True: schedule.run_pending() time.sleep(1) 从单词的字面意思,你就知道这是做什么的。 举个例子: schedule.every().monday.do(job) 这句代码作用就是就是单词意思,定时器会每个周一运行函数 job,怎么样是不是很简单。 3.实现一个进度条 # Python学习交流QQ群:857662006 from time import sleep def progress(percent=0, width=30): left = width * percent // 100 right = width - left print('\r[', '#' * left, ' ' * right, ']', f' {percent:.0f}%', sep='', end='', flush=True) for i in range(101): progress(i) sleep(0.1) 展示效果上面的代码中的 print 有几个有用的参数,sep 的作用是已什么为分隔符,默认是空格,这里设置为空串是为了让每个字符之间更紧凑,end 参数作用是已什么结尾,默认是回车换行符,这里为了实现进度条的效果,同样设置为空串。 还有最后一个参数 flush,该参数的作用主要是刷新, 默认 flush = False,不刷新,print 到 f 中的内容先存到内存中;而当 flush = True 时它会立即把内容刷新并输出。 之前在Python 下载夏目友人帐中提到了的 tqdm 模块,更好的实现一个进度条. 4.优雅的打印嵌套类型的数据 大家应该都有印象,在打印 json 字符串或者字典的时候,打印出的一坨东西根本就没有一个层次关系,这里主要说的就是输出格式的问题。 import json my_mapping = {'a': 23, 'b': 42, 'c': 0xc0ffee} print(json.dumps(my_mapping, indent=4, sort_keys=True)) 大家可以自己试试只用 print 打印 my_mapping,和例子的这种打印方法。 如果我们打印字典组成的列表呢,这个时候使用 json 的 dumps 方法肯定不行的,不过没关系 用标准库的 pprint 方法同样可以实现上面的方法 # Python学习交流QQ群:857662006 import pprint my_mapping = [{'a': 23, 'b': 42, 'c': 0xc0ffee},{'a': 231, 'b': 42, 'c': 0xc0ffee}] pprint.pprint(my_mapping,width=4) 5.功能简单的类使用 namedtuple 和 dataclass 的方式定义 有时候我们想实现一个类似类的功能,但是没有那么复杂的方法需要操作的时候,这个时候就可以考虑下下面两种方法了。 第一个,namedtuple 又称具名元组,带有名字的元组。它作为 Python 标准库 collections 里的一个模块,可以实现一个类似类的一个功能。 from collections import namedtuple # Python学习交流QQ群:857662006 # 以前简单的类可以使用 namedtuple 实现。 Car = namedtuple('Car', 'color mileage') my_car = Car('red', 3812.4) print(my_car.color) print(my_car) 但是呢,所有属性需要提前定义好才能使用,比如想使用my_car.name,你就得把代码改成下面的样子。 from collections import namedtuple # Python学习交流QQ群:857662006 # 以前简单的类可以使用 namedtuple 实现。 Car = namedtuple('Car', 'color mileage name') my_car = Car('red', 3812.4,"Auto") print(my_car.color) print(my_car.name) 使用 namedtuple 的缺点很明显了。 所以现在更优的方案,那就是 Python3.7 加入到标准库的 dataclass。 其实在 3.6 也可以使用不过需要它被作为第三方的库使用了,使用 pip 安装即可。 使用示例如下: from dataclasses import dataclass @dataclass class Car: color: str mileage: float my_car = Car('red', 3812.4) print(my_car.color) print(my_car) 6.f-string 的 !r,!a,!s f-string出现在Python3.6,作为当前最佳的拼接字符串的形式,看下 f-string 的结构 f ' <text> { <expression> <optional !s, !r, or !a> <optional : format specifier> } <text> ... ' 其中'!s' 在表达式上调用str(),'!r' 调用表达式上的repr(),'!a' 调用表达式上的ascii() (1) 默认情况下,f-string将使用str(),但如果包含转换标志,则可以确保它们使用repr () ! class Comedian: def __init__(self, first_name, last_name, age): self.first_name = first_name self.last_name = last_name self.age = age def __str__(self): return f"{self.first_name} {self.last_name} is {self.age}." def __repr__(self): return f"{self.first_name} {self.last_name} is {self.age}. Surprise!" 调用 >>> new_comedian = Comedian("Eric", "Idle", "74") >>> f"{new_comedian}" 'Eric Idle is 74.' >>> f"{new_comedian}" 'Eric Idle is 74.' >>> f"{new_comedian!r}" 'Eric Idle is 74. Surprise!' (2) !a的例子 >>> a = 'some string' >>> f'{a!r}' "'some string'" 等价于 >>> f'{repr(a)}' "'some string'" (3) !d的例子 类似2 pycon2019有人提出的一个展望!d的功能实现:在 Python3.8 中已经实现上述功能,不过不再使用 !d 了改为了 f"{a=}" 的形式,看过这个视频的发现没有 !d 应该很懵逼 7.f-string 里"="的应用 在 Python3.8 里有这样一个功能 a = 5 print(f"{a=}") 打印之后的结果为 a=5 是不是很方便,不用你再使用 f"a={a}" 了。 8.海象运算符:=的使用 a =6 if (b:=a+1)>6: print(b) 赋值的时候同时可以进行运算,和 Go 语言的赋值类似了。 代码的运行顺序,首先计算 a+1 得到值为 7,然后把 7 赋值给 b,到这里代码相当于下面这样了 b =7 if b>6: print(b) 怎么样?是不是简单了不少,不过这个功能 3.8 开始才能用哦。
内置函数 内置函数就是python给你提供的, 拿来直接用的函数, 比如print., input等. 截止到python版本3.6.2 python一共提供了68个内置函数. #68个内置函数 # abs() dict() help() min() setattr() # all() dir() hex() next() slice() # any() divmod() id() object() sorted() # ascii() enumerate() input() oct() staticmethod() # bin() eval() int() open() str() # bool() exec() isinstance() ord() sum() # bytearray() filter() issubclass() pow() super() # bytes() float() iter() print() tuple() # callable() format() len() property() type() # chr() frozenset() list() range() vars() # classmethod() getattr() locals() repr() zip() # compile() globals() map() reversed() __import__() # complex() hasattr() max() round() # delattr() hash() memoryview() set() 1. 和数字相关 (1) 数据类型 1) bool : 布尔型(True,False) 2) int : 整型(整数) 3) float : 浮点型(小数) 4) complex : 复数 (2) 进制转换 1) bin() 将给的参数转换成二进制 2) otc() 将给的参数转换成八进制 3) hex() 将给的参数转换成十六进制 print(bin(10)) # 二进制:0b1010 print(hex(10)) # 十六进制:0xa print(oct(10)) # 八进制:0o12 (3) 数学运算 1) abs() 返回绝对值 2) divmode() 返回商和余数 3) round() 四舍五入 4) pow(a, b) 求a的b次幂, 如果有三个参数. 则求完次幂后对第三个数取余 5) sum() 求和 6) min() 求最小值 7) max() 求最大值 print(abs(-2)) # 绝对值:2 print(divmod(20,3)) # 求商和余数:(6,2) print(round(4.50)) # 五舍六入:4 print(round(4.51)) #5 print(pow(10,2,3)) # 如果给了第三个参数. 表示最后取余:1 print(sum([1,2,3,4,5,6,7,8,9,10])) # 求和:55 print(min(5,3,9,12,7,2)) #求最小值:2 print(max(7,3,15,9,4,13)) #求最大值:15 2. 和数据结构相关 (1) 序列 1) 列表和元组 list() 将一个可迭代对象转换成列表 tuple() 将一个可迭代对象转换成元组 print(list((1,2,3,4,5,6))) #[1, 2, 3, 4, 5, 6] print(tuple([1,2,3,4,5,6])) #(1, 2, 3, 4, 5, 6) 相关内置函数 reversed() 将一个序列翻转, 返回翻转序列的迭代器 slice() 列表的切片 lst = "你好啊" it = reversed(lst) # 不会改变原列表. 返回一个迭代器, 设计上的一个规则 print(list(it)) #['啊', '好', '你'] lst = [1, 2, 3, 4, 5, 6, 7] print(lst[1:3:1]) #[2,3] s = slice(1, 3, 1) # 切片用的 print(lst[s]) #[2,3] 3) 字符串 str() 将数据转化成字符串 print(str(123)+'456') #123456 format() 与具体数据相关, 用于计算各种小数, 精算等. s = "hello world!" print(format(s, "^20")) #剧中 print(format(s, "<20")) #左对齐 print(format(s, ">20")) #右对齐 # hello world! # hello world! # hello world! print(format(3, 'b' )) # 二进制:11 print(format(97, 'c' )) # 转换成unicode字符:a print(format(11, 'd' )) # ⼗进制:11 print(format(11, 'o' )) # 八进制:13 print(format(11, 'x' )) # 十六进制(⼩写字母):b print(format(11, 'X' )) # 十六进制(大写字母):B print(format(11, 'n' )) # 和d⼀样:11 print(format(11)) # 和d⼀样:11 print(format(123456789, 'e' )) # 科学计数法. 默认保留6位小数:1.234568e+08 print(format(123456789, '0.2e' )) # 科学计数法. 保留2位小数(小写):1.23e+08 print(format(123456789, '0.2E' )) # 科学计数法. 保留2位小数(大写):1.23E+08 print(format(1.23456789, 'f' )) # 小数点计数法. 保留6位小数:1.234568 print(format(1.23456789, '0.2f' )) # 小数点计数法. 保留2位小数:1.23 print(format(1.23456789, '0.10f')) # 小数点计数法. 保留10位小数:1.2345678900 print(format(1.23456789e+3, 'F')) # 小数点计数法. 很大的时候输出INF:1234.567890 bytes() 把字符串转化成bytes类型 bs = bytes("今天吃饭了吗", encoding="utf-8") print(bs) #b'\xe4\xbb\x8a\xe5\xa4\xa9\xe5\x90\x83\xe9\xa5\xad\xe4\xba\x86\xe5\x90\x97' bytearray() 返回一个新字节数组. 这个数字的元素是可变的, 并且每个元素的值得范围是[0,256) ret = bytearray("alex" ,encoding ='utf-8') print(ret[0]) #97 print(ret) #bytearray(b'alex') ret[0] = 65 #把65的位置A赋值给ret[0] print(str(ret)) #bytearray(b'Alex') ord() 输入字符找带字符编码的位置 chr() 输入位置数字找出对应的字符 ascii() 是ascii码中的返回该值 不是就返回u... print(ord('a')) # 字母a在编码表中的码位:97 print(ord('中')) # '中'字在编码表中的位置:20013 print(chr(65)) # 已知码位,求字符是什么:A print(chr(19999)) #丟 for i in range(65536): #打印出0到65535的字符 print(chr(i), end=" ") print(ascii("@")) #'@' repr() 返回一个对象的string形式 s = "今天\n吃了%s顿\t饭" % 3 print(s) #今天 # 吃了3顿 饭 print(repr(s)) # 原样输出,过滤掉转义字符 \n \t \r 不管百分号% #'今天\n吃了3顿\t饭' (2) 数据集合 1) 字典 dict 创建一个字典 2) 集合 set 创建一个集合 frozenset() 创建一个冻结的集合. 冻结的集合不能进行添加和删除操作 (3) 相关内置函数 len() 返回一个对象中的元素的个数 sorted() 对可迭代对象进行排序操作 (lamda) 语法: sorted(Iterable, key=函数(排序规则), reverse=False) Iterable: 可迭代对象 key: 排序规则(排序函数), 在sorted内部会将可迭代对象中的每一个元素传递给这个函数的参数. 根据函数运算的结果进行排序 reverse: 是否是倒叙. True: 倒叙, False: 正序 #Python学习交流QQ群:857662006 lst = [5,7,6,12,1,13,9,18,5] lst.sort() # sort是list里面的一个方法 print(lst) #[1, 5, 5, 6, 7, 9, 12, 13, 18] ll = sorted(lst) # 内置函数. 返回给你一个新列表 新列表是被排序的 print(ll) #[1, 5, 5, 6, 7, 9, 12, 13, 18] l2 = sorted(lst,reverse=True) #倒序 print(l2) #[18, 13, 12, 9, 7, 6, 5, 5, 1] #根据字符串长度给列表排序 lst = ['one', 'two', 'three', 'four', 'five', 'six'] def f(s): return len(s) l1 = sorted(lst, key=f, ) print(l1) #['one', 'two', 'six', 'four', 'five', 'three'] enumerate() 获取集合的枚举对象 lst = ['one','two','three','four','five'] for index, el in enumerate(lst,1): # 把索引和元素一起获取,索引默认从0开始. 可以更改 print(index) print(el) # 1 # one # 2 # two # 3 # three # 4 # four # 5 # five all() 可迭代对象中全部是True, 结果才是True any() 可迭代对象中有一个是True, 结果就是True print(all([1,'hello',True,9])) #True print(any([0,0,0,False,1,'good'])) #True zip() 函数用于将可迭代的对象作为参数, 将对象中对应的元素打包成一个元组, 然后返回由这些元组组成的列表. 如果各个迭代器的元素个数不一致, 则返回列表长度与最短的对象相同 #Python学习交流QQ群:857662006 lst1 = [1, 2, 3, 4, 5, 6] lst2 = ['醉乡民谣', '驴得水', '放牛班的春天', '美丽人生', '辩护人', '被嫌弃的松子的一生'] lst3 = ['美国', '中国', '法国', '意大利', '韩国', '日本'] print(zip(lst1, lst1, lst3)) #<zip object at 0x00000256CA6C7A88> for el in zip(lst1, lst2, lst3): print(el) # (1, '醉乡民谣', '美国') # (2, '驴得水', '中国') # (3, '放牛班的春天', '法国') # (4, '美丽人生', '意大利') # (5, '辩护人', '韩国') # (6, '被嫌弃的松子的一生', '日本') fiter() 过滤 (lamda) 语法: fiter(function. Iterable) function: 用来筛选的函数. 在filter中会自动的把iterable中的元素传递给function. 然后根据function返回的True或者False来判断是否保留留此项数据 , Iterable: 可迭代对象 def func(i): # 判断奇数 return i % 2 == 1 lst = [1,2,3,4,5,6,7,8,9] l1 = filter(func, lst) #l1是迭代器 print(l1) #<filter object at 0x000001CE3CA98AC8> print(list(l1)) #[1, 3, 5, 7, 9] map() 会根据提供的函数对指定序列列做映射(lamda) 语法 : map(function, iterable) 可以对可迭代对象中的每一个元素进行映射. 分别去执行 function def f(i): return i lst = [1,2,3,4,5,6,7,] it = map(f, lst) # 把可迭代对象中的每一个元素传递给前面的函数进行处理. 处理的结果会返回成迭代器 print(list(it)) #[1, 2, 3, 4, 5, 6, 7] 3. 和作用域相关 locals() 返回当前作用域中的名字 globals() 返回全局作用域中的名字 def func(): a = 10 print(locals()) # 当前作用域中的内容 print(globals()) # 全局作用域中的内容 print("今天内容很多") func() # {'a': 10} # {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': # <_frozen_importlib_external.SourceFileLoader object at 0x0000026F8D566080>, # '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' # (built-in)>, '__file__': 'D:/pycharm/练习/week03/new14.py', '__cached__': None, # 'func': <function func at 0x0000026F8D6B97B8>} # 今天内容很多 4. 和迭代器/生成器相关 range() 生成数据 next() 迭代器向下执行一次, 内部实际使⽤用了__ next__()⽅方法返回迭代器的下一个项目 iter() 获取迭代器, 内部实际使用的是__ iter__()⽅方法来获取迭代器 #Python学习交流QQ群:857662006 for i in range(15,-1,-5): print(i) # 15 # 10 # 5 # 0 lst = [1,2,3,4,5] it = iter(lst) # __iter__()获得迭代器 print(it.__next__()) #1 print(next(it)) #2 __next__() print(next(it)) #3 print(next(it)) #4 5. 字符串类型代码的执行 eval() 执行字符串类型的代码. 并返回最终结果 exec() 执行字符串类型的代码 compile() 将字符串类型的代码编码. 代码对象能够通过exec语句来执行或者eval()进行求值 s1 = input("请输入a+b:") #输入:8+9 print(eval(s1)) # 17 可以动态的执行代码. 代码必须有返回值 s2 = "for i in range(5): print(i)" a = exec(s2) # exec 执行代码不返回任何内容 # 0 # 1 # 2 # 3 # 4 print(a) #None # 动态执行代码 exec(""" def func(): print(" 我是周杰伦") """ ) func() #我是周杰伦 code1 = "for i in range(3): print(i)" com = compile(code1, "", mode="exec") # compile并不会执行你的代码.只是编译 exec(com) # 执行编译的结果 # 0 # 1 # 2 code2 = "5+6+7" com2 = compile(code2, "", mode="eval") print(eval(com2)) # 18 code3 = "name = input('请输入你的名字:')" #输入:hello com3 = compile(code3, "", mode="single") exec(com3) print(name) #hello 6. 输入输出 print() : 打印输出 input() : 获取用户输出的内容 print("hello", "world", sep="*", end="@") # sep:打印出的内容用什么连接,end:以什么为结尾 #hello*world@ 7. 内存相关 hash() : 获取到对象的哈希值(int, str, bool, tuple). hash算法:(1) 目的是唯一性 (2) dict 查找效率非常高, hash表.用空间换的时间 比较耗费内存 s = 'alex' print(hash(s)) #-168324845050430382 lst = [1, 2, 3, 4, 5] print(hash(lst)) #报错,列表是不可哈希的 id() : 获取到对象的内存地址 s = 'alex' print(id(s)) #2278345368944 8.文件操作相关 open() : 用于打开一个文件, 创建一个文件句柄 f = open('file',mode='r',encoding='utf-8') f.read() f.close() 9. 模块相关 __ import__() : 用于动态加载类和函数 # 让用户输入一个要导入的模块 import os name = input("请输入你要导入的模块:") __import__(name) # 可以动态导入模块 10. 帮助 help() : 函数用于查看函数或模块用途的详细说明 print(help(str)) #查看字符串的用途 11. 调用相关 callable() : 用于检查一个对象是否是可调用的. 如果返回True, object有可能调用失败, 但如果返回False. 那调用绝对不会成功 a = 10 print(callable(a)) #False 变量a不能被调用 # def f(): print("hello") print(callable(f)) # True 函数是可以被调用的 12. 查看内置属性 dir() : 查看对象的内置属性, 访问的是对象中的__dir__()方法 print(dir(tuple)) #查看元组的方法
数据结构作为计算机基础的必修内容,也是很多大型互联网企业面试的必考题。可想而知,它在计算机领域的重要性。 然而很多计算机专业的同学,都仅仅是了解数据结构的相关理论,却无法用代码实现各种数据结构。 栈 class Stack(object): def __init__(self, limit=10): self.stack = [] #存放元素 self.limit = limit #栈容量极限 def push(self, data): #判断栈是否溢出 if len(self.stack) >= self.limit: print('StackOverflowError') pass self.stack.append(data) def pop(self): if self.stack: return self.stack.pop() else: raise IndexError('pop from an empty stack') #空栈不能被弹出 def peek(self): #查看堆栈的最上面的元素 if self.stack: return self.stack[-1] def is_empty(self): #判断栈是否为空 return not bool(self.stack) def size(self): #返回栈的大小 return len(self.stack) 单链表 class Node: def __init__(self, data): self.data = data self.next = None class Linked_List: def __init__(self): self.head = None def initlist(self,data_list): #链表初始化函数 self.head=Node(data_list[0]) #创建头结点 temp=self.head for i in data_list[1:]: #逐个为 data 内的数据创建结点, 建立链表 node=Node(i) temp.next=node temp=temp.next # def is_empty(self): #判断链表是否为空 if self.head.next==None: print("Linked_list is empty") return True else: return False def get_length(self): #获取链表的长度 temp=self.head #临时变量指向队列头部 length=0 #计算链表的长度变量 while temp!=None: length=length+1 temp=temp.next return length #返回链表的长度 def insert(self,key,value): #链表插入数据函数 if key<0 or key>self.get_length()-1: print("insert error") temp=self.head i=0 while i<=key: #遍历找到索引值为 key 的结点后, 在其后面插入结点 pre=temp temp=temp.next i=i+1 node=Node(value) pre.next=node node.next=temp def print_list(self): #遍历链表,并将元素依次打印出来 print("linked_list:") temp=self.head new_list=[] while temp is not None: new_list.append(temp.data) temp=temp.next print(new_list) def remove(self,key): #链表删除数据函数 if key<0 or key>self.get_length()-1: print("insert error") i=0 temp=self.head while temp !=None: #遍历找到索引值为 key 的结点 pre=temp temp=temp.next i=i+1 if i==key: pre.next=temp.next temp=None return True pre.next=None def reverse(self): #将链表反转 prev = None current = self.head while current: next_node = current.next current.next = prev prev = current current = next_node self.head = prev 双链表 class Node(object): # 双向链表节点 def __init__(self, item): self.item = item self.next = None self.prev = None class DLinkList(object): # 双向链表 def __init__(self): self._head = None def is_empty(self): # 判断链表是否为空 return self._head == None def get_length(self): # 返回链表的长度 cur = self._head count = 0 while cur != None: count=count+1 cur = cur.next return count def travel(self): # 遍历链表 cur = self._head while cur != None: print(cur.item) cur = cur.next print("") def add(self, item): # 头部插入元素 node = Node(item) if self.is_empty(): # 如果是空链表,将_head指向node self._head = node else: # 将node的next指向_head的头节点 node.next = self._head # 将_head的头节点的prev指向node self._head.prev = node # 将_head 指向node self._head = node def append(self, item): # Python学习交流QQ群:857662006 # 尾部插入元素 node = Node(item) if self.is_empty(): # 如果是空链表,将_head指向node self._head = node else: # 移动到链表尾部 cur = self._head while cur.next != None: cur = cur.next # 将尾节点cur的next指向node cur.next = node # 将node的prev指向cur node.prev = cur def search(self, item): # 查找元素是否存在 cur = self._head while cur != None: if cur.item == item: return True cur = cur.next return False def insert(self, pos, item): # 在指定位置添加节点 if pos <= 0: self.add(item) elif pos > (self.length()-1): self.append(item) else: node = Node(item) cur = self._head count = 0 # 移动到指定位置的前一个位置 while count < (pos-1): count += 1 cur = cur.next # 将node的prev指向cur node.prev = cur # 将node的next指向cur的下一个节点 node.next = cur.next # 将cur的下一个节点的prev指向node cur.next.prev = node # 将cur的next指向node cur.next = node def remove(self, item): # 删除元素 if self.is_empty(): return else: cur = self._head if cur.item == item: # 如果首节点的元素即是要删除的元素 if cur.next == None: # 如果链表只有这一个节点 self._head = None else: # 将第二个节点的prev设置为None cur.next.prev = None # 将_head指向第二个节点 self._head = cur.next return while cur != None: if cur.item == item: # 将cur的前一个节点的next指向cur的后一个节点 cur.prev.next = cur.next # 将cur的后一个节点的prev指向cur的前一个节点 cur.next.prev = cur.prev break cur = cur.next 队列(链表形式实现) class Node(object): def __init__(self,elem,next=None): self.elem = elem #表示对应的元素值 self.next=next #表示下一个链接的链点 class Queue(object): def __init__(self): self.head = None #头部链点为 None self.rear = None #尾部链点为 None def is_empty(self): return self.head is None #判断队列是否为空 def enqueue(self, elem): p = Node(elem) #初始化一个新的点 if self.is_empty(): #Python学习交流QQ群:857662006 self.head = p #队列头部为新的链点 self.rear = p #队列尾部为新的链点 else: self.rear.next = p #队列尾部的后继是这个新的点 self.rear =p #然后让队列尾部指针指向这个新的点 def dequeue(self): if self.is_empty(): #判断队列是否为空 print('Queue_is_empty') #若队列为空,则退出 dequeue 操作 else: result = self.head.elem #result为队列头部元素 self.head = self.head.next #改变队列头部指针位置 return result #返回队列头部元素 def peek(self): if self.is_empty(): #判断队列是否为空 print('NOT_FOUND') #为空则返回 NOT_FOUND else: return self.head.elem #返回队列头部元素 def print_queue(self): print("queue:") temp=self.head myqueue=[] #暂时存放队列数据 while temp is not None: myqueue.append(temp.elem) temp=temp.next print(myqueue) 队列(数组形式实现) class Queue(): def __init__(self): self.entries = [] #表示队列内的参数 self.length = 0 #表示队列的长度 self.front=0 #表示队列头部位置 def enqueue(self, item): self.entries.append(item) #添加元素到队列里面 self.length = self.length + 1 #队列长度增加 1 def dequeue(self): self.length = self.length - 1 #队列的长度减少 1 dequeued = self.entries[self.front] #队首元素为dequeued self.front-=1 #队首的位置减少1 self.entries = self.entries[self.front:] #队列的元素更新为退队之后的队列 return dequeued def peek(self): return self.entries[0] #直接返回队列的队首元素 二叉树 class Node(object): def __init__(self,item): self.item=item #表示对应的元素 self.left=None #表示左节点 self.right=None #表示右节点 def __str__(self): return str(self.item) #print 一个 Node 类时会打印 __str__ 的返回值 class Tree(object): #Python学习交流QQ群:857662006 def __init__(self): self.root=Node('root') #根节点定义为 root 永不删除,作为哨兵使用。 def add(self,item): node = Node(item) if self.root is None: #如果二叉树为空,那么生成的二叉树最终为新插入树的点 self.root = node else: q = [self.root] # 将q列表,添加二叉树的根节点 while True: pop_node = q.pop(0) if pop_node.left is None: #左子树为空则将点添加到左子树 pop_node.left = node return elif pop_node.right is None: #右子树为空则将点添加到右子树 pop_node.right = node return else: q.append(pop_node.left) q.append(pop_node.right) def get_parent(self, item): if self.root.item == item: return None # 根节点没有父节点 tmp = [self.root] # 将tmp列表,添加二叉树的根节点 while tmp: pop_node = tmp.pop(0) if pop_node.left and pop_node.left.item == item: #某点的左子树为寻找的点 return pop_node #返回某点,即为寻找点的父节点 if pop_node.right and pop_node.right.item == item: #某点的右子树为寻找的点 return pop_node #返回某点,即为寻找点的父节点 if pop_node.left is not None: #添加tmp 元素 tmp.append(pop_node.left) if pop_node.right is not None: tmp.append(pop_node.right) return None def delete(self, item): if self.root is None: # 如果根为空,就什么也不做 return False parent = self.get_parent(item) if parent: del_node = parent.left if parent.left.item == item else parent.right # 待删除节点 if del_node.left is None: if parent.left.item == item: parent.left = del_node.right else: parent.right = del_node.right del del_node return True elif del_node.right is None: if parent.left.item == item: parent.left = del_node.left else: parent.right = del_node.left del del_node return True else: # 左右子树都不为空 tmp_pre = del_node tmp_next = del_node.right if tmp_next.left is None: # 替代 tmp_pre.right = tmp_next.right tmp_next.left = del_node.left tmp_next.right = del_node.right else: while tmp_next.left: # 让tmp指向右子树的最后一个叶子 tmp_pre = tmp_next tmp_next = tmp_next.left # 替代 tmp_pre.left = tmp_next.right tmp_next.left = del_node.left tmp_next.right = del_node.right if parent.left.item == item: parent.left = tmp_next else: parent.right = tmp_next del del_node return True else: return False 字典树 class TrieNode: def __init__(self): self.nodes = dict() # 构建字典 self.is_leaf = False def insert(self, word: str): curr = self for char in word: if char not in curr.nodes: curr.nodes[char] = TrieNode() curr = curr.nodes[char] curr.is_leaf = True def insert_many(self, words: [str]): for word in words: self.insert(word) def search(self, word: str): curr = self for char in word: if char not in curr.nodes: return False curr = curr.nodes[char] return curr.is_leaf 堆 class heap(object): def __init__(self): #初始化一个空堆,使用数组来在存放堆元素,节省存储 self.data_list = [] def get_parent_index(self,index): #返回父节点的下标 if index == 0 or index > len(self.data_list) -1: return None else: return (index -1) >> 1 def swap(self,index_a,index_b): #交换数组中的两个元素 #Python学习交流QQ群:857662006 self.data_list[index_a],self.data_list[index_b] = self.data_list[index_b],self.data_list[index_a] def insert(self,data): #先把元素放在最后,然后从后往前依次堆化 #这里以大顶堆为例,如果插入元素比父节点大,则交换,直到最后 self.data_list.append(data) index = len(self.data_list) -1 parent = self.get_parent_index(index) #循环,直到该元素成为堆顶,或小于父节点(对于大顶堆) while parent is not None and self.data_list[parent] < self.data_list[index]: #交换操作 self.swap(parent,index) index = parent parent = self.get_parent_index(parent) def removeMax(self): #删除堆顶元素,然后将最后一个元素放在堆顶,再从上往下依次堆化 remove_data = self.data_list[0] self.data_list[0] = self.data_list[-1] del self.data_list[-1] #堆化 self.heapify(0) return remove_data def heapify(self,index): #从上往下堆化,从index 开始堆化操作 (大顶堆) total_index = len(self.data_list) -1 while True: maxvalue_index = index if 2*index +1 <= total_index and self.data_list[2*index +1] > self.data_list[maxvalue_index]: maxvalue_index = 2*index +1 if 2*index +2 <= total_index and self.data_list[2*index +2] > self.data_list[maxvalue_index]: maxvalue_index = 2*index +2 if maxvalue_index == index: break self.swap(index,maxvalue_index) index = maxvalue_index
只要是有属性的数据对象(不一定是面向对象的对象实例,而是指具有数据类型的数据对象),都可以通过- ---- __dict__和dir()来显示数据对象的相关属性。 __ dict__可以看作是数据对象的名称空间,所以只包含自己的属性,且可以直接增、删、改、查__dict__。 dir()可以看作是显示属性的包含显示,除了显示自己的还显示继承来的属性。 对于模块参见:查看模块属性 对于类和对象 以下面的例子解释__ dict__和dir()在应用于类和对象上的不同之处。 class supcls: def hello(self): self.data1 = 'hello' class childcls(supcls): def world(self): self.data2 = "world" 在显示内容上 __dict__是个dict结构,仅仅只显示完全独属于自己的属性 dir()是一个list结构,除了显示自己的属性,还显示父类继承而来的属性,比如从祖先类object中继承的属性 下面是__dict__在类属性上显示的结果。注意,这里用keys()只显示数据对象的属性名称,实际上__dict__中既包含了名称,也包含了值。 #Python学习交流QQ群:857662006 >>> childcls.__dict__.keys() dict_keys(['__module__', 'world', '__doc__']) >>> supcls.__dict__.keys() dict_keys(['__module__', 'hello', '__dict__', '__weakref__', '__doc__']) 下面是dir()显示类属性时候的结果: >>> dir(childcls) ['__class__', '__delattr__', '__dict__', ...... 'hello', 'world'] >>> dir(supcls) ['__class__', '__delattr__', '__dict__', ...... 'hello'] 注意上面dir(childcls)的结果中含有hello属性,它是来自父类supcls的属性。dir()还显示了一大堆的下划线属性,它们基本上都是继承自祖先类object的属性。 再看看对类的实例对象,它们的显示结果。 #Python学习交流QQ群:857662006 >>> s = supcls() >>> c = childcls() >>> s.__dict__.keys() dict_keys([]) >>> c.__dict__.keys() dict_keys([]) >>> dir(s) ['__class__', '__delattr__', '__dict__', ...... 'hello'] >>> dir(c) ['__class__', '__delattr__', '__dict__', ...... 'hello', 'world'] 设置对象自己的属性,再查看: #Python学习交流QQ群:857662006 >>> s.hello() >>> s.__dict__.keys() dict_keys(['data1']) >>> dir(s) ['__class__', '__delattr__', '__dict__', ...... 'data1', 'hello'] >>> c.world() >>> c.__dict__.keys() dict_keys(['data2']) >>> dir(c) ['__class__', '__delattr__', '__dict__', ...... 'data2', 'hello', 'world'] >>> c.hello() >>> c.__dict__.keys() dict_keys(['data2', 'data1']) >>> dir(c) ['__class__', '__delattr__', '__dict__', ...... 'data1', 'data2', 'hello', 'world'] 在作用上 __ dict__是一个dict,它和数据对象的属性直接关联,可以直接通过__dict__访问、设置、修改、删除属性,比如类的对象实例可以通过self.x=3设置x属性,也可以通过__dict__['x']=3来设置属性x。而dir()函数仅仅只是展现一些属性。 例如: >>> c.__dict__['newkey']="NEWKEY" >>> c.__dict__.keys() dict_keys(['data2', 'data1', 'newkey']) 上面通过__dict__设置了一个新属性newkey,现在newkey已经是对象c的一个对象属性了。上面的设置方式和c.newkey="NEWKEY"是等价的。
见过很多获取服务器本地IP的代码,个人觉得都不是很好,例如以下这些 不推荐:靠猜测去获取本地IP方法 #!/usr/bin/env python # -*- coding: utf-8 -*- import socket import fcntl import struct def get_ip_address(ifname): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) return socket.inet_ntoa(fcntl.ioctl( s.fileno(), 0x8915, # SIOCGIFADDR struct.pack('256s', ifname[:15]) )[20:24]) print "br1 = "+ get_ip_address('br1') print "lo = " + get_ip_address('lo') print "virbr0 = " + get_ip_address('virbr0') 这类代码带有猜测的行为。 如果机器上只有eth0 或者 只有bond0上有IP,那么此类代码都有可能失败,而且还不容易移植到其他平台上。 不推荐:通过hostname来获取本机IP import socket print(socket.gethostbyname(socket.gethostname())) # Python学习交流QQ群:857662006 # 有可能出现这个情况 Traceback (most recent call last): File "<stdin>", line 1, in <module> socket.gaierror: [Errno -2] Name or service not known 这个方法是通过获取hostname,然后再通过hostname反查处机器的IP。这个方法也是不推荐的。因为很多的机器没有规范这个hostname的设置。 另外就是有些服务器会在 /etc/hosts 中添加本机的hostname的地址,这个做法也不是不可以,但是如果设置成了 127.0.0.1,那么获取出来的IP就都是这个地址了。 通过 UDP 获取本机 IP,目前见过最优雅的方法 这个方法是目前见过最优雅获取本机服务器的IP方法了。没有任何的依赖,也没有去猜测机器上的网络设备信息。 而且是利用 UDP 协议来实现的,生成一个UDP包,把自己的 IP 放如到 UDP 协议头中,然后从UDP包中获取本机的IP。 这个方法并不会真实的向外部发包,所以用抓包工具是看不到的。但是会申请一个 UDP 的端口,所以如果经常调用也会比较耗时的,这里如果需要可以将查询到的IP给缓存起来,性能可以获得很大提升。 # 在 shell 中可以一行调用,获取到本机IP python -c "import socket;print([(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1])" 10.12.189.16 # 可以封装成函数,方便 Python 的程序调用 import socket def get_host_ip(): try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(('8.8.8.8', 80)) ip = s.getsockname()[0] finally: s.close() return ip
1、内建函数reversed() li =[1, 2, 3, 4, 5, 6] a = list(reversed(li)) print (a) 注意:reversed()函数返回的是一个迭代器,而不是一个List,所以需要list函数转换一下 2、内建函数sorted() sorted()语法 sorted(iterable[, cmp[, key[, reverse]]]) 参数说明: iterable -- 可迭代对象。 cmp -- 比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。 key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。 reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)。 返回值返回重新排序的列表。 #Python学习交流QQ群:857662006 a=[1,2,3,4,5,6,7,8,9] c=sorted(a, reverse=True) print (c) 注意:sorted()按降序排列,对于反转内容不是顺序排列的无效果,此处待改善。 3: 使用分片 a=[1,2,3,4,5,6,7,8,9] d=a[::-1] print (d) 注意:其中[::-1]代表从后向前取值,每次步进值为1
1、Python数字求和 # -*- codingLuft-8 -*- #Filename: test.py #author by:Leq #用户输入数字 num1 = input("输入第一个数:") num2 = input("输入第二个数:") #求和 sum= float(num1)+float(num2) #要做运算,必须保证运算之前将字符格式转为整形init或浮点型float #第一种显示方式:格式化输出 print("两个数字相加的计算结果是:%d"%sum) #第二种显示方式:.format() print('数字 {0} 和 {1} 相加结果为: {2}'.format(num1, num2, sum)) 2、平方根√ ̄,例:√ ̄16=4 # -*- codingLuft-8 -*- #Filename: 平方根.py num = float(input('请输入一个数字: ')) num_sqrt = num ** 0.5 print(' %0.3f 的平方根为 %0.3f'%(num ,num_sqrt))#小数点后3位的浮点数 3、计算三角形面积;注 :三角形面积=(半周长(半周长-边长A)(半周长-边长B)(半周长-边长C))0.5* # -*- codingLuft-8 -*- #Filename: 计算三角形面积.py #Python学习交流QQ群:857662006 #运算过程:三角形面积=(半周长*(半周长-边长A)*(半周长-边长B)*(半周长-边长C))**0.5 a = float(input('输入三角形第一边长: ')) b = float(input('输入三角形第二边长: ')) c = float(input('输入三角形第三边长: ')) #计算半周长 s = (a+b+c)/2 #计算面积 area = (s*(s-a)*(s-b)*(s-c))**0.5 print('三角形的面积是%0.2f'%area) 4、生成随机数 # -*- codingLuft-8 -*- #Filename: 生成随机数.py #引入random模块 import random print(random.randint(0,9)) 5、判断奇偶数 # -*- codingLuft-8 -*- #Filename: 判断奇偶数.py num = int(input("请输入一个数,判断奇偶数:")) if num%2 ==0: print('%d是偶数'%num) else: print('%d不是偶数'%num) 6、判断闰年 # -*- codingLuft-8 -*- #Filename: 判断闰年.py #整百年能被400整除,非整百年能被4整除的年份是闰年 num = int(input("请输入一个年份,判断是不是闰年:")) if num%100 == 0: if num%400 == 0: print("%s 年是闰年"%num) else: print("%s年不是闰年"%num) else: if num%4 == 0: print("%s年是闰年"%num) else: print("%s年不是闰年"%num) 7、判断是不是质数 # -*- codingLuft-8 -*- #Filename: 质数判断.py #Python学习交流QQ群:857662006 #质数:一个大于1的自然数,除了1和它本身外,不能被其他自然数(质数)整除(2, 3, 5, 7等),换句话说就是该数除了1和它本身以外不再有其他的因数。 num= int(input("输入一个数,本系统可以判断是不是质数:")) if num >1: for i in range(2,num): if num%i==0: print('%s不是质数'%num) break else: print('%s是质数'%num) else: print("请输入大于1的数") 8、阶乘 # -*- codingLuft-8 -*- #Filename: 阶乘实例.py #阶乘:自然数、全部相乘 num =int(input("输入一个数,计算阶乘:")) f=1 if num <0: print("SORRY,负数没有阶乘") if num==0: print("0的阶乘是1") else: for i in range(1,num+1): f=f*i # f+=1 print("%s的阶乘是%s"%(num,f)) 9、九九乘法表 # -*- codingLuft-8 -*- #Filename: 九九乘法表.py #两个for循环、print()自带换行 #Python学习交流QQ群:857662006 for i in range(1,10): for j in range(1,i+1): print('%s*%s=%s'%(i,j,i*j),end=' ') #print() 函数自带换行‘\h\t’,这里去掉,让输出完这一段后再换行 print() #print() == print('\n\t') 10、判断是不是数字【这个得引入库】 # -*- coding: UTF-8 -*- # Filename : test.py def is_number(s): try: float(s) return True except ValueError: pass try: import unicodedata unicodedata.numeric(s) return True except (TypeError, ValueError): pass return False # 测试字符串和数字 print(is_number('foo')) # False print(is_number('1')) # True print(is_number('1.3')) # True print(is_number('-1.37')) # True print(is_number('1e3')) # True # 测试 Unicode # 阿拉伯语 5 print(is_number('٥')) # True # 泰语 2 print(is_number('๒')) # True # 中文数字 print(is_number('四')) # True # 版权号 print(is_number('©')) # False 11、Python 十进制转二进制(bin)、八进制(oct)、十六进制(hex) dec = int(input("输入数字:")) print("十进制数为:", dec) print("转换为二进制为:", bin(dec)) print("转换为八进制为:", oct(dec)) print("转换为十六进制为:", hex(dec)) 12、最大公约数 # Filename : test.py # Python学习交流QQ群:857662006 # 定义一个函数 def hcf(x, y): """该函数返回两个数的最大公约数""" # 获取最小值 if x > y: smaller = y else: smaller = x for i in range(1,smaller + 1): if((x % i == 0) and (y % i == 0)): hcf = i return hcf # 用户输入两个数字 num1 = int(input("输入第一个数字: ")) num2 = int(input("输入第二个数字: ")) print( num1,"和", num2,"的最大公约数为", hcf(num1, num2)) 13、生成日历【菜鸟教程runoob.com】 # Filename : test.py # 引入日历模块 import calendar # 输入指定年月 yy = int(input("输入年份: ")) mm = int(input("输入月份: ")) # 显示日历 print(calendar.month(yy,mm)) #注意这里格式
1. 判断指定目录是否存在: os.path.exists(input_folder) 2. 判断指定目录是不是文件夹 os.path.isdir(input_folder) 3. 判断指定目录是不是文件 os.path.isfile(input_folder) 4. 判断指定文件是不是图片(判断给定文件是何种图片类型) #Python学习交流QQ群:857662006 import imghdr img_list= {'jpg','bmp','png','jpeg','rgb','gif','pbm','ppm','tiff','xbm'} if imghdr.what(input_filename) not in img_list: print(not image) 5. 判断指定txt(文件)是否为空 import os if os.path.getsize('test.txt') is 0: print('test.txt is empty!') 6. 按行读取txt文件内容 f = open('test.txt', "r") lines = f.readlines() for line in lines: print line line = line.strip('\n') # 去掉换行符号 '\n' print line 7. 遍历指定目录文件夹下所有文件 for file in sorted(glob.glob(os.path.join(input_folder, '*.*'))): print(file) 8. 在python程序中兼容路径中的中文符号 for file in sorted(glob.glob(os.path.join(input_folder, '*.*'))): file = unicode(file,'utf-8') 9. 判断文件夹是否存在,不存在则创建,存在则删除后再创建: if not os.path.exists('folder1'): os.makedirs('folder1') else: shutil.rmtree('folder1') os.makedirs('folder1') 10. 创建一个txt文件并写入,如果存在则清空后写入: f = open('test.txt', "wt") f.writelines('test' + '\n') f.close() 11. 判断路径(字符串) path_str 中是否有中文字符: # coding:utf-8 #Python学习交流QQ群:857662006 for ch in path_str.decode('utf-8'): if u'\u4e00' <= ch <= u'\u9fff': print('chinese character founded!') 12. os.walk 遍历文件夹下所有文件(包括文件夹下的文件夹内文件) for root, dirs, files in os.walk(INPUT_FOLDER): for file in files: item = os.path.join(root,file) print(item) 13. 在python程序中获取文件或文件夹的绝对权限: if os.path.exists(input_pathof_fileOrdir): os.system("chmod 777 %s" % './{0}'.format(input_pathof_fileOrdir))
想从一个序列中随机抽取若干元素,或者想生成几个随机数。 random 模块有大量的函数用来产生随机数和随机选择元素。比如,要想从一个序列中随机的抽取一个元素,可以使用random.choice() : >>> import random >>> values = [1, 2, 3, 4, 5, 6] >>> random.choice(values) 2 >>> random.choice(values) 3 >>> random.choice(values) 1 >>> 为了提取出N 个不同元素的样本用来做进一步的操作,可以使用random.sample() #Python学习交流QQ群:857662006 >>> random.sample(values, 2) [6, 2] >>> random.sample(values, 2) [4, 3] >>> random.sample(values, 3) [4, 3, 1] 如果你仅仅只是想打乱序列中元素的顺序,可以使用random.shuffle() : >>> random.shuffle(values) >>> values [2, 4, 6, 5, 3, 1] >>> random.shuffle(values) >>> values [3, 5, 2, 1, 6, 4] >>> 生成随机整数,请使用random.randint() : >>> random.randint(0,10) 2 >>> random.randint(0,10) 5 为了生成0 到1 范围内均匀分布的浮点数,使用random.random() : #Python学习交流QQ群:857662006 >>> random.random() 0.9406677561675867 >>> random.random() 0.133129581343897 如果要获取N 位随机位(二进制) 的整数,使用random.getrandbits() : >>> random.getrandbits(200) 335837000776573622800628485064121869519521710558559406913275 了上述介绍的功能,random 模块还包含基于均匀分布、高斯分布和其他分布的随机数生成函数。比如, random.uniform() 计算均匀分布随机数, random.gauss()计算正态分布随机数。对于其他的分布情况请参考在线文档。在random 模块中的函数不应该用在和密码学相关的程序中。如果你确实需要类似的功能,可以使用ssl 模块中相应的函数。比如, ssl.RAND bytes() 可以用来生成一个安全的随机字节序列。
python作为越来越流行的一种编程语言,不仅仅是因为它语言简单,有许多现成的包可以直接调用。 python中还有大量的小工具,让你的python工作更有效率。 1. 快速共享 HTTP服务器 SimpleHTTPServer是python内置的web服务器,使用8000端口和HTTP协议共享。 能够在任意平台(Window,Linux,MacOS)快速搭建一个HTTP服务和共享服务,只需要搭建好python环境。 python2版本: python -m SimpleHTTPServer python3版本: python -m http.server FTP服务器 ftp共享需要第三方组件支持,安装命令: pip install pyftpdlib python -m pyftpdlib-p端口号 访问方式:ftp://IP:端口。 2. 解压缩 这里介绍利用python解压五种压缩文件:.gz .tar .zip .rar zip import zipfile # zipfile压缩 z = zipfile.ZipFile('x.zip', 'w', zipfile.ZIP_STORED) #打包,zipfile.ZIP_STORED是默认参数 # z = zipfile.ZipFile('ss.zip', 'w', zipfile.ZIP_DEFLATED) #压缩 z.write('x2') z.write('x1') z.close() #zipfile解压 z = zipfile.ZipFile('x.zip', 'r') z.extractall(path=r"C:UsersAdministratorDesktop") z.close() tar import tarfile # 压缩 tar = tarfile.open('your.tar', 'w') tar.add('/Users/wupeiqi/PycharmProjects/bbs2.log', arcname='bbs2.log') tar.add('/Users/wupeiqi/PycharmProjects/cmdb.log', arcname='cmdb.log') tar.close() # 解压 tar = tarfile.open('your.tar', 'r') tar.extractall() # 可设置解压地址 tar.close() gz gz一般仅仅压缩一个文件,全部常与其它打包工具一起工作。比方能够先用tar打包为X.tar,然后在压缩为X.tar.gz 解压gz,事实上就是读出当中的单一文件,Python方法例如以下: import gzip import os def un_gz(file_name): """ungz zip file""" f_name = file_name.replace(".gz", "") #获取文件的名称,去掉 g_file = gzip.GzipFile(file_name) #创建gzip对象 open(f_name, "w+").write(g_file.read()) #gzip对象用read()打开后,写入open()建立的文件里。 g_file.close() #关闭gzip对象 rar 由于rar通常为window下使用,须要额外的Python包rarfile。安装: Python setup.py install 解压缩: ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' import rarfile import os def un_rar(file_name): """unrar zip file""" rar = rarfile.RarFile(file_name) if os.path.isdir(file_name + "_files"): pass else: os.mkdir(file_name + "_files") os.chdir(file_name + "_files"): rar.extractall() rar.close() 3.pip常用操作 pip 是 Python 著名的包管理工具,在 Python 开发中必不可少。 安装 在线安装 pip install <包名> 或 pip install -r requirements.txt 本地安装: pip install <目录>/<文件名> 或 pip install --use-wheel --no-index --find-links=wheelhouse/ <包名> 查找包 pip search <包名> 删除包 pip uninstall <包名> 或 pip uninstall -r requirements.txt 查看包信息 pip show <包名> 检查包依赖是否完整 pip check <包名> 查看已安装包列表 pip list 导出所有已安装包 pip freeze requirements.txt 4. 字符串与Json转换 json转str import json str = '{"name": "zyl", "age": "two"}' p = json.loads(str) print(p) print(type(p)) json转str 使用json.dumps的方法,可以将json对象转化为字符串。 s = {'name':'zyl','age':'22'} s = json.dumps(s) 5. python读取excel 步骤 安装python官方Excel库-->xlrd 获取Excel文件位置并读取 读取sheet 读取指定rows和cols内容 示例 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' # -*- coding: utf-8 -*- import xlrd from datetime import date,datetime def read_excel(): #文件位置 ExcelFile=xlrd.open_workbook(r'C:UsersAdministratorDesktopTestData.xlsx') #获取目标EXCEL文件sheet名 print ExcelFile.sheet_names() #若有多个sheet,则需要指定读取目标sheet例如读取sheet2 #sheet2_name=ExcelFile.sheet_names()[1] #获取sheet内容【1.根据sheet索引2.根据sheet名称】 #sheet=ExcelFile.sheet_by_index(1) sheet=ExcelFile.sheet_by_name('TestCase002') #打印sheet的名称,行数,列数 print sheet.name,sheet.nrows,sheet.ncols #获取整行或者整列的值 rows=sheet.row_values(2)#第三行内容 cols=sheet.col_values(1)#第二列内容 print cols,rows #获取单元格内容 print sheet.cell(1,0).value.encode('utf-8') print sheet.cell_value(1,0).encode('utf-8') print sheet.row(1)[0].value.encode('utf-8') #打印单元格内容格式 print sheet.cell(1,0).ctype if__name__ =='__main__': read_excel() 6. python 截图 python实现截图功能,windows环境下,需要用到PIL库。 安装: pip install Pillow 示例: from PIL import ImageGrab bbox = (x1, y1, x2,y2 ) # x1: 开始截图的x坐标;x2:开始截图的y坐标;x3:结束截图的x坐标;x4:结束截图的y坐标 im = ImageGrab.grab(bbox) im.save('as.png')#保存截图文件的路径 7. ipython 最后介绍的示一个强大的python工具——IPython 。 IPython 支持变量自动补全,自动缩进,支持 bash shell 命令,内置了许多实用功能和函数; 它是一个 for Humans 的 Python 交互式 shell,用了它之后你就不想再用自带的 Python shell 了。
众所周知,我们可以通过索引值(或称下标)来查找序列类型(如字符串、列表、元组...)中的单个元素,那么,如果要获取一个索引区间的元素该怎么办呢? 切片(slice)就是一种截取索引片段的技术,借助切片技术,我们可以十分灵活地处理序列类型的对象。通常来说,切片的作用就是截取序列对象,然而,它还有一些使用误区与高级用法,都值得我们注意。所以,本文将主要跟大家一起来探讨这些内容,希望你能学有所获。 事先声明,切片并非列表的专属操作,但因为列表最具有代表性,所以,本文仅以列表为例作探讨。 1、切片的基础用法 列表是 Python 中极为基础且重要的一种数据结构,我曾写过一篇汇总文章(链接见文末)较全面地学习过它。文中详细地总结了切片的基础用法,现在回顾一下: 切片的书写形式:[i : i+n : m] ;其中,i 是切片的起始索引值,为列表首位时可省略;i+n 是切片的结束位置,为列表末位时可省略;m 可以不提供,默认值是1,不允许为0 ,当m为负数时,列表翻转。注意:这些值都可以大于列表长度,不会报越界。 切片的基本含义是:从序列的第i位索引起,向右取到后n位元素为止,按m间隔过滤 。 li = [1, 4, 5, 6, 7, 9, 11, 14, 16] # 以下写法都可以表示整个列表,其中 X >= len(li) li[0:X] == li[0:] == li[:X] == li[:] == li[::] == li[-X:X] == li[-X:] li[1:5] == [4,5,6,7] # 从1起,取5-1位元素 li[1:5:2] == [4,6] # 从1起,取5-1位元素,按2间隔过滤 li[-1:] == [16] # 取倒数第一个元素 li[-4:-2] == [9, 11] # 从倒数第四起,取-2-(-4)=2位元素 li[:-2] == li[-len(li):-2] == [1,4,5,6,7,9,11] # 从头开始,取-2-(-len(li))=7位元素 # Python学习交流QQ群:857662006 # 步长为负数时,列表先翻转,再截取 li[::-1] == [16,14,11,9,7,6,5,4,1] # 翻转整个列表 li[::-2] == [16,11,7,5,1] # 翻转整个列表,再按2间隔过滤 li[:-5:-1] == [16,14,11,9] # 翻转整个列表,取-5-(-len(li))=4位元素 li[:-5:-3] == [16,9] # 翻转整个列表,取-5-(-len(li))=4位元素,再按3间隔过滤 # 切片的步长不可以为0 li[::0] # 报错(ValueError: slice step cannot be zero) 上述的某些例子对于初学者(甚至很多老手)来说,可能还不好理解。我个人总结出两条经验:(1)牢牢记住公式[i : i+n : m] ,当出现缺省值时,通过想象把公式补全;(2)索引为负且步长为正时,按倒数计算索引位置;索引为负且步长为负时,先翻转列表,再按倒数计算索引位置。 2、切片是伪独立对象 切片操作的返回结果是一个新的独立的序列。以列表为例,列表切片后得到的还是一个列表,占用新的内存地址。 当取出切片的结果时,它是一个独立对象,因此,可以将其用于赋值操作,也可以用于其它传递值的场景。但是,切片只是浅拷贝,它拷贝的是原列表中元素的引用,所以,当存在变长对象的元素时,新列表将受制于原列表。 li = [1, 2, 3, 4] ls = li[::] li == ls # True id(li) == id(ls) # False li.append(li[2:4]) # [1, 2, 3, 4, [3, 4]] ls.extend(ls[2:4]) # [1, 2, 3, 4, 3, 4] # Python学习交流QQ群:857662006 # 下例等价于判断li长度是否大于8 if(li[8:]): print("not empty") else: print("empty") # 切片列表受制于原列表 lo = [1,[1,1],2,3] lp = lo[:2] # [1, [1, 1]] lo[1].append(1) # [1, [1, 1, 1], 2, 3] lp # [1, [1, 1, 1]] 由于可见,将切片结果取出,它可以作为独立对象使用,但是也要注意,是否取出了变长对象的元素。 3、切片可作为占位符 切片既可以作为独立对象被“取出”原序列,也可以留在原序列,作为一种占位符使用。 对于列表来说,使用切片作为占位符,同样能够实现拼接列表的效果。特别需要注意的是,给切片赋值的必须是可迭代对象。 li = [1, 2, 3, 4] # 在头部拼接 li[:0] = [0] # [0, 1, 2, 3, 4] # 在末尾拼接 li[len(li):] = [5,7] # [0, 1, 2, 3, 4, 5, 7] # 在中部拼接 li[6:6] = [6] # [0, 1, 2, 3, 4, 5, 6, 7] # Python学习交流QQ群:857662006 # 给切片赋值的必须是可迭代对象 li[-1:-1] = 6 # (报错,TypeError: can only assign an iterable) li[:0] = (9,) # [9, 0, 1, 2, 3, 4, 5, 6, 7] li[:0] = range(3) # [0, 1, 2, 9, 0, 1, 2, 3, 4, 5, 6, 7] 上述例子中,若将切片作为独立对象取出,那你会发现它们都是空列表,即 li[:0]== li[len(li):] == li [6 : 6]==[] ,我将这种占位符称为“纯占位符”,对纯占位符赋值,并不会破坏原有的元素,只会在特定的索引位置中拼接进新的元素。删除纯占位符时,也不会影响列表中的元素。 与“纯占位符”相对应,“非纯占位符”的切片是非空列表,对它进行操作(赋值与删除),将会影响原始列表。如果说纯占位符可以实现列表的拼接,那么,非纯占位符可以实现列表的替换。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' li = [1, 2, 3, 4] # 不同位置的替换 li[:3] = [7,8,9] # [7, 8, 9, 4] li[3:] = [5,6,7] # [7, 8, 9, 5, 6, 7] li[2:4] = ['a','b'] # [7, 8, 'a', 'b', 6, 7] # Python学习交流QQ群:857662006 # 非等长替换 li[2:4] = [1,2,3,4] # [7, 8, 1, 2, 3, 4, 6, 7] li[2:6] = ['a'] # [7, 8, 'a', 6, 7] # 删除元素 del li[2:3] # [7, 8, 6, 7] 切片占位符可以带步长,从而实现连续跨越性的替换或删除效果。需要注意的是,这种用法只支持等长替换。 li = [1, 2, 3, 4, 5, 6] li[::2] = ['a','b','c'] # ['a', 2, 'b', 4, 'c', 6] li[::2] = [0]*3 # [0, 2, 0, 4, 0, 6] li[::2] = ['w'] # 报错,attempt to assign sequence of size 1 to extended slice of size 3 del li[::2] # [2, 4, 6] 4、更多思考 其它编程语言是否有类似于 Python 的切片操作呢?有什么差异?
字符串拼接 实际场景:把列表中的数据拼接成一个字符串 解决方案:使用 str.join() 方法 >>> li = ['cxk', 'cxk', 'kk', 'caibi'] >>> ''.join([str(i) for i in li]) 'cxkcxkkkcaibi' 推荐使用生成器表达式,如果列表很大,可以节省很多内存空间 >>> ''.join(str(i) for i in li) '3cxkkkcaibi' 拆分含有多种分隔符的字符串 实际场景:把某个字符串依据分割符号拆分不同的字段,该字符串包含多种不同的分隔符 s = "ab;fd/ft|fs,f\tdf.fss*dfd;fs:uu}fsd" 1.使用 python 中的 split() 方法,由于 split 一次处理一个分隔符,例如: >>> res = s.split(';') >>> res ['ab', 'fd/ft|fs,f\tdf.fss*dfd', 'fs:uu}fsd'] 所以我们需要根据字符串中的分隔符,依次分割,可以是 map 函数! >>> list(map(lambda x: x.split("|"), res)) [['ab'], ['fd/ft', 'fs,f\tdf.fss*dfd'], ['fs:uu}fsd']] 结果变成了一个二维列表,而我们想要的结果是一维列表,怎么办? 创建一个临时列表保存结果。 # Python学习交流QQ群:857662006 >>> t = [] >>> list(map(lambda x: t.extend(x.split("|")), res)) [None, None, None] >>> t ['ab', 'fd/ft', 'fs,f\tdf.fss*dfd', 'fs:uu}fsd'] 结果符合我们的预期!接下来继续处理剩余的分隔符,重复动作,用 for 循环搞定! 最终代码如下: def my_split(s, ds): res = [s] for d in ds: t = [] list(map(lambda x: t.extend(x.split(d)), res)) res = t return res 将字符串和字符串中所有的分隔符传入,结果如下: s = "ab;fd/ft|fs,f\tdf.fss*dfd;fs:uu}fsd" print(my_split(s, ";/|,.}:*\t")) 运行结果:['ab', 'fd', 'ft', 'fs', 'f', 'df', 'fss', 'dfd', 'fs', 'uu', 'fsd'] 2.使用 re 模块的中 split() 方法 re() 也给我们提供了 split() 方法,可以一次性分隔字符串! import re s = "ab;fd/ft|fs,f\tdf.fss*dfd;fs:uu}fsd" print(re.split('[;/|,.}:*\t]', s)) 结果一致,是不是很简单粗暴! 判读字符串a是否以字符串b开头或结尾实际场景:比如某目录下有一系列文件:编写程序给其中所有 .txt 文件和 .py 文件加上用户可执行权限 解决方案: 使用字符串 str.startswith() 和 str.endswith() 找出以 .txt 和 .py 结尾的文件,其接受一个元组 >>> import os >>> os.listdir(".") ['app', 'config', 'requirements.txt', 'run.py', '__pycache__', 'gunicorn.conf.py', 'chromedriver', 'login_after2.png', 'readme.txt', 'slide.png', 'test.py', 'logs', 'chrome-linux.zip', 'gunicorn.pid', 'asgi.py', 'chrome-linux'] >>> [name for name in os.listdir(".") if name.endswith((".txt", ".py"))] ['requirements.txt', 'run.py', 'gunicorn.conf.py', 'readme.txt', 'test.py', 'asgi.py'] 调整字符串中文本的格式 实际案列:例如在日志文件中,其中日期格式为'yyyy-mm-dd': 我们想把其中的日期改为美国日期格式'mm/dd/yyyy'.比如 2019-06-12 改成 06/12/2019 格式 解决方案:使用 re 中的 sub() 方法做字符串替换 利用正则表达式中的捕获组,捕获每个部分的内容,然后在替换在替换的字符串中调整各个捕获组的顺序! 代码如下: import re # Python学习交流QQ群:857662006 with open("info.log", "r", encoding="utf-8") as f: file = f.read() print(re.sub('(\d{4})-(\d{2})-(\d{2})', r'\2/\3/\1', file)) 捕获组中每组需要用括号括起来,然后默认从左至右分为组1, 组2… 下一个参数是我们要替换的格式,用 1,2,3 分别表示组1, 组2… 运行结果如下: 对字符串进行左,右,居中对齐 解决方案: 1.使用字符串中的 str.ljust(), str.rjust(), str.center() 进行左右居中对齐!以上三种方法的基本用法: >>> s = 'abc' >>> s.ljust(20, '=') 'abc=================' >>> s.ljust(20) 'abc ' 三种方法都可以设置默认填充值 2.使用内置的 format() 方法 >>> format(s, ">20") ' abc' >>> format(s, "<20") 'abc ' >>> format(s, "^20") ' abc ' 删除字符串中不需要的字符 实际案例: 过滤掉用户输入中前后多余的空白字符:“ nick2008@gmail.com ” 过滤掉某 windows 下编辑文本中的 “r” :“hello world rn” 去掉文本中的 unicode 组合符号(音调):nǐ hǎo mā 解决方案: 使用 str.strip(), str.lstrip(), str.rstrip() 方法去掉字符串两端字符 使用 str.replace() 或者正则中的 re.sub() 使用字符串中 str.translate() 方法,可以同时删除多个不同的字符
对于每一个学习 Python 的同学,想必对 @ 符号一定不陌生了,正如你所知, @ 符号是装饰器的语法糖,@符号后面的函数就是我们本文的主角:装饰器。 装饰器放在一个函数开始定义的地方,它就像一顶帽子一样戴在这个函数的头上。和这个函数绑定在一起。在我们调用这个函数的时候,第一件事并不是执行这个函数,而是将这个函数做为参数传入它头顶上这顶帽子,这顶帽子我们称之为 装饰器 。 曾经我在刚转行做程序员时的一次的面试中,被面试官问过这样的两个问题: 1、你都用过装饰器实现过什么样的功能? 2、如何写一个可以传参的装饰器? 对于当时实战经验非常有限的我,第一个问题只能回答一些非常简单的用法,而第二个问题却没能回答上来。 当时带着这两个问题,我就开始系统的学习装饰器的所有内容。这些一直整理在自己的博客中,今天对其进行了大量的补充和勘误,发表在这里分享给大家。希望对刚入门以及进阶的朋友可以提供一些参考。01. Hello,装饰器 装饰器的使用方法很固定 先定义一个装饰器(帽子) 再定义你的业务函数或者类(人) 最后把这装饰器(帽子)扣在这个函数(人)头上 就像下面这样子 def decorator(func): def wrapper(*args, **kw): return func() return wrapper @decorator def function(): print("hello, decorator") 实际上,装饰器并不是编码必须性,意思就是说,你不使用装饰器完全可以,它的出现,应该是使我们的代码 更加优雅,代码结构更加清晰 将实现特定的功能代码封装成装饰器,提高代码复用率,增强代码可读性 接下来,我将以实例讲解,如何编写出各种简单及复杂的装饰器。 02. 入门:日志打印器 首先是日志打印器。 实现的功能: 在函数执行前,先打印一行日志告知一下主人,我要执行函数了。 在函数执行完,也不能拍拍屁股就走人了,咱可是有礼貌的代码,再打印一行日志告知下主人,我执行完啦。 # 这是装饰器函数,参数 func 是被装饰的函数 def logger(func): def wrapper(*args, **kw): print('主人,我准备开始执行:{} 函数了:'.format(func.__name__)) # 真正执行的是这行。 func(*args, **kw) print('主人,我执行完啦。') return wrapper 假如,我的业务函数是,计算两个数之和。写好后,直接给它带上帽子。 @logger def add(x, y): print('{} + {} = {}'.format(x, y, x+y)) 然后执行一下 add 函数。 add(200, 50) 来看看输出了什么? 主人,我准备开始执行:add 函数了: 200 + 50 = 250 主人,我执行完啦。 03. 入门:时间计时器 再来看看 时间计时器实现功能:顾名思义,就是计算一个函数的执行时长。 # Python学习交流QQ群:857662006 # 这是装饰函数 def timer(func): def wrapper(*args, **kw): t1=time.time() # 这是函数真正执行的地方 func(*args, **kw) t2=time.time() # 计算下时长 cost_time = t2-t1 print("花费时间:{}秒".format(cost_time)) return wrapper 假如,我们的函数是要睡眠10秒。这样也能更好的看出这个计算时长到底靠不靠谱。 import time @timer def want_sleep(sleep_time): time.sleep(sleep_time) want_sleep(10) 来看看输出,如预期一样,输出10秒。 花费时间:10.0073800086975098秒 04. 进阶:带参数的函数装饰器 通过上面两个简单的入门示例,你应该能体会到装饰器的工作原理了。 不过,装饰器的用法还远不止如此,深究下去,还大有文章。今天就一起来把这个知识点学透。 回过头去看看上面的例子,装饰器是不能接收参数的。其用法,只能适用于一些简单的场景。不传参的装饰器,只能对被装饰函数,执行固定逻辑。 装饰器本身是一个函数,做为一个函数,如果不能传参,那这个函数的功能就会很受限,只能执行固定的逻辑。这意味着,如果装饰器的逻辑代码的执行需要根据不同场景进行调整,若不能传参的话,我们就要写两个装饰器,这显然是不合理的。 比如我们要实现一个可以定时发送邮件的任务(一分钟发送一封),定时进行时间同步的任务(一天同步一次),就可以自己实现一个 periodic_task (定时任务)的装饰器,这个装饰器可以接收一个时间间隔的参数,间隔多长时间执行一次任务。 可以这样像下面这样写,由于这个功能代码比较复杂,不利于学习,这里就不贴了。 @periodic_task(spacing=60) def send_mail(): pass @periodic_task(spacing=86400) def ntp() pass 那我们来自己创造一个伪场景,可以在装饰器里传入一个参数,指明国籍,并在函数执行前,用自己国家的母语打一个招呼。 # 小明,中国人 @say_hello("china") def xiaoming(): pass # jack,美国人 @say_hello("america") def jack(): pass 那我们如果实现这个装饰器,让其可以实现 传参 呢? 会比较复杂,需要两层嵌套。 def say_hello(contry): def wrapper(func): def deco(*args, **kwargs): if contry == "china": print("你好!") elif contry == "america": print('hello.') else: return # 真正执行函数的地方 func(*args, **kwargs) return deco return wrapper 来执行一下 xiaoming() print("------------") jack() 看看输出结果。 你好! ------------ hello. 05. 高阶:不带参数的类装饰器 以上都是基于函数实现的装饰器,在阅读别人代码时,还可以时常发现还有基于类实现的装饰器。 基于类装饰器的实现,必须实现 call 和 __init__两个内置函数。 init :接收被装饰函数 call :实现装饰逻辑。 还是以日志打印这个简单的例子为例 class logger(object): def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print("[INFO]: the function {func}() is running..." .format(func=self.func.__name__)) return self.func(*args, **kwargs) @logger def say(something): print("say {}!".format(something)) say("hello") 执行一下,看看输出 [INFO]: the function say() is running... say hello! 06. 高阶:带参数的类装饰器上面不带参数的例子,你发现没有,只能打印INFO级别的日志,正常情况下,我们还需要打印DEBUG WARNING等级别的日志。这就需要给类装饰器传入参数,给这个函数指定级别了。 带参数和不带参数的类装饰器有很大的不同。 init :不再接收被装饰函数,而是接收传入参数。 call :接收被装饰函数,实现装饰逻辑。 class logger(object): def __init__(self, level='INFO'): self.level = level def __call__(self, func): # 接受函数 def wrapper(*args, **kwargs): print("[{level}]: the function {func}() is running..." .format(level=self.level, func=func.__name__)) func(*args, **kwargs) return wrapper #返回函数 @logger(level='WARNING') def say(something): print("say {}!".format(something)) say("hello") 我们指定WARNING级别,运行一下,来看看输出。 [WARNING]: the function say() is running... say hello! 07. 使用偏函数与类实现装饰器 绝大多数装饰器都是基于函数和闭包实现的,但这并非制造装饰器的唯一方式。 事实上,Python 对某个对象是否能通过装饰器( @decorator)形式使用只有一个要求:decorator 必须是一个“可被调用(callable)的对象。 对于这个 callable 对象,我们最熟悉的就是函数了。 除函数之外,类也可以是 callable 对象,只要实现了__ call__ 函数(上面几个例子已经接触过了)。 还有容易被人忽略的偏函数其实也是 callable 对象。 接下来就来说说,如何使用 类和偏函数结合实现一个与众不同的装饰器。 如下所示,DelayFunc 是一个实现了 call 的类,delay 返回一个偏函数,在这里 delay 就可以做为一个装饰器。(以下代码摘自 Python工匠:使用装饰器的小技巧) import time import functools class DelayFunc: def __init__(self, duration, func): self.duration = duration self.func = func def __call__(self, *args, **kwargs): print(f'Wait for {self.duration} seconds...') time.sleep(self.duration) return self.func(*args, **kwargs) def eager_call(self, *args, **kwargs): print('Call without delay') return self.func(*args, **kwargs) def delay(duration): """ 装饰器:推迟某个函数的执行。 同时提供 .eager_call 方法立即执行 """ # Python学习交流QQ群:857662006 # 此处为了避免定义额外函数, # 直接使用 functools.partial 帮助构造 DelayFunc 实例 return functools.partial(DelayFunc, duration) 我们的业务函数很简单,就是相加 @delay(duration=2) def add(a, b): return a+b 来看一下执行过程 >>> add # 可见 add 变成了 Delay 的实例 <__main__.DelayFunc object at 0x107bd0be0> >>> >>> add(3,5) # 直接调用实例,进入 __call__ Wait for 2 seconds... 8 >>> >>> add.func # 实现实例方法 <function add at 0x107bef1e0> 08. 如何写能装饰类的装饰器? 用 Python 写单例模式的时候,常用的有三种写法。其中一种,是用装饰器来实现的。 以下便是我自己写的装饰器版的单例写法。 instances = {} def singleton(cls): def get_instance(*args, **kw): cls_name = cls.__name__ print('===== 1 ====') if not cls_name in instances: print('===== 2 ====') instance = cls(*args, **kw) instances[cls_name] = instance return instances[cls_name] return get_instance @singleton class User: _instance = None def __init__(self, name): print('===== 3 ====') self.name = name 可以看到我们用singleton 这个装饰函数来装饰 User 这个类。装饰器用在类上,并不是很常见,但只要熟悉装饰器的实现过程,就不难以实现对类的装饰。在上面这个例子中,装饰器就只是实现对类实例的生成的控制而已。 其实例化的过程,你可以参考我这里的调试过程,加以理解。09. wraps 装饰器有啥用?在 functools 标准库中有提供一个 wraps 装饰器,你应该也经常见过,那他有啥用呢? 先来看一个例子 def wrapper(func): def inner_function(): pass return inner_function @wrapper def wrapped(): pass print(wrapped.__name__) #inner_function 为什么会这样子?不是应该返回 func 吗? 这也不难理解,因为上边执行func 和下边 decorator(func) 是等价的,所以上面 func.__ name__ 是等价于下面decorator(func).__ name__ 的,那当然名字是 inner_function def wrapper(func): def inner_function(): pass return inner_function def wrapped(): pass print(wrapper(wrapped).__name__) #inner_function 那如何避免这种情况的产生?方法是使用 functools .wraps 装饰器,它的作用就是将 被修饰的函数(wrapped) 的一些属性值赋值给 修饰器函数(wrapper) ,最终让属性的显示更符合我们的直觉。 from functools import wraps def wrapper(func): @wraps(func) def inner_function(): pass return inner_function @wrapper def wrapped(): pass print(wrapped.__name__) # wrapped 准确点说,wraps 其实是一个偏函数对象(partial),源码如下 def wraps(wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES): return partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated) 可以看到wraps其实就是调用了一个函数update_wrapper,知道原理后,我们改写上面的代码,在不使用 wraps的情况下,也可以让 wrapped.__ name__ 打印出 wrapped,代码如下: from functools import update_wrapper WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__', '__annotations__') def wrapper(func): def inner_function(): pass update_wrapper(inner_function, func, assigned=WRAPPER_ASSIGNMENTS) return inner_function @wrapper def wrapped(): pass print(wrapped.__name__) 10. 内置装饰器:property 以上,我们介绍的都是自定义的装饰器。 其实Python语言本身也有一些装饰器。比如property这个内建装饰器,我们再熟悉不过了。 它通常存在于类中,可以将一个函数定义成一个属性,属性的值就是该函数return的内容。 通常我们给实例绑定属性是这样的 class Student(object): def __init__(self, name, age=None): self.name = name self.age = age # 实例化 xiaoming = Student("小明") # 添加属性 xiaoming.age=25 # 查询属性 xiaoming.age # 删除属性 del xiaoming.age 但是稍有经验的开发人员,一下就可以看出,这样直接把属性暴露出去,虽然写起来很简单,但是并不能对属性的值做合法性限制。为了实现这个功能,我们可以这样写。 class Student(object): def __init__(self, name): self.name = name self.name = None def set_age(self, age): if not isinstance(age, int): raise ValueError('输入不合法:年龄必须为数值!') if not 0 < age < 100: raise ValueError('输入不合法:年龄范围必须0-100') self._age=age def get_age(self): return self._age def del_age(self): self._age = None xiaoming = Student("小明") # 添加属性 xiaoming.set_age(25) # 查询属性 xiaoming.get_age() # 删除属性 xiaoming.del_age() 上面的代码设计虽然可以变量的定义,但是可以发现不管是获取还是赋值(通过函数)都和我们平时见到的不一样。按照我们思维习惯应该是这样的。 # 赋值 xiaoming.age = 25 # 获取 xiaoming.age 那么这样的方式我们如何实现呢。请看下面的代码。 class Student(object): def __init__(self, name): self.name = name self.name = None @property def age(self): return self._age @age.setter def age(self, value): if not isinstance(value, int): raise ValueError('输入不合法:年龄必须为数值!') if not 0 < value < 100: raise ValueError('输入不合法:年龄范围必须0-100') self._age=value @age.deleter def age(self): del self._age xiaoming = Student("小明") # 设置属性 xiaoming.age = 25 # 查询属性 xiaoming.age # 删除属性 del xiaoming.age 用@property装饰过的函数,会将一个函数定义成一个属性,属性的值就是该函数return的内容。同时,会将这个函数变成另外一个装饰器。就像后面我们使用的@age.setter和@age.deleter。 @age.setter 使得我们可以使用XiaoMing.age = 25这样的方式直接赋值。 @age.deleter 使得我们可以使用del XiaoMing.age这样的方式来删除属性。 property 的底层实现机制是「描述符」,为此我还写过一篇文章。 这里也介绍一下吧,正好将这些看似零散的文章全部串起来。 如下,我写了一个类,里面使用了 property 将 math 变成了类实例的属性 class Student: def __init__(self, name): self.name = name @property def math(self): return self._math @math.setter def math(self, value): if 0 <= value <= 100: self._math = value else: raise ValueError("Valid value must be in [0, 100]") 为什么说 property 底层是基于描述符协议的呢?通过 PyCharm 点击进入 property 的源码,很可惜,只是一份类似文档一样的伪源码,并没有其具体的实现逻辑。 不过,从这份伪源码的魔法函数结构组成,可以大体知道其实现逻辑。 这里我自己通过模仿其函数结构,结合「描述符协议」来自己实现类 property 特性。 代码如下: class TestProperty(object): def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel self.__doc__ = doc def __get__(self, obj, objtype=None): print("in __get__") if obj is None: return self if self.fget is None: raise AttributeError return self.fget(obj) def __set__(self, obj, value): print("in __set__") if self.fset is None: raise AttributeError self.fset(obj, value) def __delete__(self, obj): print("in __delete__") if self.fdel is None: raise AttributeError self.fdel(obj) def getter(self, fget): print("in getter") return type(self)(fget, self.fset, self.fdel, self.__doc__) def setter(self, fset): print("in setter") return type(self)(self.fget, fset, self.fdel, self.__doc__) def deleter(self, fdel): print("in deleter") return type(self)(self.fget, self.fset, fdel, self.__doc__) 然后 Student 类,我们也相应改成如下 class Student: def __init__(self, name): self.name = name # Python学习交流QQ群:857662006 # 其实只有这里改变 @TestProperty def math(self): return self._math @math.setter def math(self, value): if 0 <= value <= 100: self._math = value else: raise ValueError("Valid value must be in [0, 100]") 为了尽量让你少产生一点疑惑,我这里做两点说明: 使用TestProperty装饰后,math 不再是一个函数,而是TestProperty类的一个实例。所以第二个math函数可以使用 math.setter 来装饰,本质是调用TestProperty.setter 来产生一个新的 TestProperty 实例赋值给第二个math。 第一个 math 和第二个 math 是两个不同 TestProperty 实例。但他们都属于同一个描述符类(TestProperty),当对 math 对于赋值时,就会进入 TestProperty.__ set__,当对math 进行取值里,就会进入 TestProperty.__ get__。仔细一看,其实最终访问的还是Student实例的 _math 属性。 说了这么多,还是运行一下,更加直观一点。 # 运行后,会直接打印这一行,这是在实例化 TestProperty 并赋值给第二个math in setter >>> >>> s1.math = 90 in __set__ >>> s1.math in __get__ 90 如对上面代码的运行原理,有疑问的同学,请务必结合上面两点说明加以理解,那两点相当关键。 11. 其他装饰器:装饰器实战 读完并理解了上面的内容,你可以说是Python高手了。别怀疑,自信点,因为很多人都不知道装饰器有这么多用法呢。 在我看来,使用装饰器,可以达到如下目的: 使代码可读性更高,逼格更高; 代码结构更加清晰,代码冗余度更低; 刚好我在最近也有一个场景,可以用装饰器很好的实现,暂且放上来看看。 这是一个实现控制函数运行超时的装饰器。如果超时,则会抛出超时异常。 有兴趣的可以看看。 import signal class TimeoutException(Exception): def __init__(self, error='Timeout waiting for response from Cloud'): Exception.__init__(self, error) def timeout_limit(timeout_time): def wraps(func): def handler(signum, frame): raise TimeoutException() def deco(*args, **kwargs): signal.signal(signal.SIGALRM, handler) signal.alarm(timeout_time) func(*args, **kwargs) signal.alarm(0) return deco return wraps 以上,便是我对装饰器的所有分享。
Python语言简单易用,但容易给新入门的朋友造成一些微妙的,难以捕捉的错误,稍不注意就入坑了。因此,今天给大家总结一些易犯的小错误,让你轻松进行不踩坑的Python学习。 1、缩进,符号和空格不正确 写代码时大家会使用缩进、对齐、空格等,其目的是为了提高代码的可读性。但在python语言中,许多功能都依赖于缩进。 比如在创建一个新类时,该类中的所有内容都在声明下缩进,决策、循环还有其它结构语句也会出现类似的情况, 如果你在代码执行时发现问题,可以查看一下是否使用了正确的缩进。 来看看下面的例子,在使用IF语句时,请确保使用正确且合适的冒号和缩进,因为它们会导致语法和缩进错误。 val = 500 if val > 100 print("value is grater then 100") File "<ipython-input-1-a271e37c300f>", line 2 if val > 100 ^ SyntaxError: invalid syntax 在上面的代码当中,出现了两处错误:if语句后面的:缺失;下一行没有进行正确的缩进,执行代码出错。 val = 500 if val > 100: print("value is grater then 100") value is grater then 100 当你更正上述代码中的两个问题后,你会发现整段代码能够很好的运行。 2、错误使用类变量 class A(object):x = 1class B(A):passclass C(A):passprint( A.x, B.x, C.x)1 1 1这里输出的值都是1,然后我们试着来改变一下A.x和B.x的值看看有什么变化。 B.x = 2 print (A.x, B.x, C.x) A.x = 3 print (A.x, B.x, C.x) 1 2 1 3 2 3 我们只改变了A.x,为什么C.x改变呢? 这里需要简单了解一下python的命名空间。 python中,命名空间是名字到对象映射的结合,不同命名空间中的名字是没有关联的,这种映射的实现有点类似于python中的字典。 当你名字访问一个对象的属性时,先从对象的命名空间寻找。如果找到了这个属性,就返回这个属性的值;如果没有找到的话,则从类的命名空间中寻找,找到了就返回这个属性的值,找不到则抛出异常。 在Python中,类变量在内部作为字典处理,并遵循通常称为方法解析顺序(MRO)的方法。 MRO:Method Resolution Order 方法解析顺序,Python支持多继承,该方法用于解决父类存在同名函数的时存在的二义性问题。 因此在上面的代码中,由于x在对象的命名空间中找不到该属性C,因此将在类中查找它。换句话说,C没有自己的x属性,独立于A。因此,引用C.x实际上是指A.x。 3、误解python范围规则 如果你不了解python的范围规则,那么你很容易犯错误,这是因为Python使用一种独有的范围规则来确定变量范围。python范围解析是基于LEGB规则,以下是Python范围规则的概述: ·L -代表Local。它包含在函数内指定的(标识符/变量)名称(使用def或lambda),而不是使用global关键字声明。 ·E -代表Enclosing function locals。它包含来自任何/所有封闭函数的本地范围的名称(例如,使用def或lambda)。 ·G -指全球实体。它包括在模块文件的顶层运行或使用global关键字定义的名称。 ·B -指内置插件。它跨越预先指定为内置名称的名称,如打印,输入,打开等。 LEGB规则指定名称空间的以下顺序,用于搜索名称: Local - > Enclosed - > Global - > Built-in 考虑以下的例子: x = 10 def foo(): x += 1 print(x) foo() #Python学习交流QQ群:857662006 UnboundLocalError Traceback (most recent call last): <ipython-input-26-234e54482865> in <module> <ipython-input-26-234e54482865> in foo() UnboundLocalError: local variable 'x' referenced before assignment 发生上述错误的原因是,对作用域中的变量进行赋值时,Python会自动将该变量视为该作用域的本地变量,并在外部作用域中隐藏任何类似命名的变量。因此,许多人在代码提示出错并显示需要在函数中添加赋值语句而感到不解。考虑一个在使用列表时遇到的例子: lst = [1, 2, 3] def foo1(): lst.append(5) foo1() lst [1, 2, 3, 5] lst = [1, 2, 3] def foo2(): lst += [5] foo2() UnboundLocalError Traceback (most recent call last): <ipython-input-30-579469eed71a> in <module> <ipython-input-30-579469eed71a> in foo2() UnboundLocalError: local variable 'lst' referenced before assignment 为什么foo2出错了但是foo1运行良好? 答案在前面就已经有所提示,在这个例子当中foo1()做一个分配到lst,而在foo2()当中lst += [5]其实只是lst = lst + [5]的简写,我们希望分配一个值给lst,但是分配的值lst是基于lst自身,但其尚未定义。 4、python闭包变量绑定 python的闭包变量问题也是新手们容易混淆的一个点,来看看下面的例子: def create_multipliers(): return [lambda x : i * x for i in range(5)] for multiplier in create_multipliers(): print (multiplier(2)) 8 8 8 8 8 为什么结果是88888,和我所想的02468不一样呢? 这是由于Python的迟绑定(late binding)机制,闭包中内部函数的值只有在被调用时才会进行查询。 因此create_multipliers函数返回的lambda函数被调用时,会在附近的作用域中查询变量i的值,而在create_multipliers生成返回数组之后,整数i的值是4,不会再改变,因此返回数组中每个匿名函数实际上都是:lambda x: 4*x。、 解决办法是将临时值也保存在匿名函数的作用域内,在声明匿名函数时就查询变量的值。了解原理之后,让我们来改一改代码,surprise! def create_multipliers(): return [lambda x, i=i : i * x for i in range(5)] for multiplier in create_multipliers(): print (multiplier(2)) 0 2 4 6 8 5、名称与Python标准库模块发生冲突 Python拥有大量的库模块,开箱即用。但是,如果您遇到一个模块的名称与Python附带的标准库中具有相同名称的模块之间的名称冲突,则可能会出现问题。 例如导入另一个库,而这个库又会尝试导入模块的Python标准库版本,但由于你有一个同名的模块,另一个包会错误地导入你的版本而不是Python标准库。 因此,应该注意避免使用与Python标准库模块中相同的名称,并且更改包中的模块名称比提交Python Enhancement Proposal(PEP)以请求名称更改更容易。 6、is和==/=和== Python中有很多运算符,例如is,=,==这三个,许多刚刚入门的新手会误解这三个运算符的意义和用法,以致于代码出错。 在 Python 中会用到对象之间比较,可以用 ==,也可以用 is,但对对象比较判断的内容并不相同,区别在哪里? ·is 比较两个对象的 id 值是否相等,是否指向同一个内存地址,== 比较的是两个对象的内容是否相等,值是否相等; a = ["Python"] b = a b is a True id(a) 2222222 id(b) 2222222 b == a True 可以发现上面的例子当中b和a的内存地址是相同的,它们指向同一块内存,因而 is 和 == 的结果都为True,这是因为直接赋值都是赋值的引用。如果新建对象之后,b 和 a 指向了不同的内存,那么 b is a 的结果为False,而 b==a的结果为True。 ·小整数对象[-5,256]在全局解释器范围内被放入缓存供重复使用,例如: a = 1 b = 1 a is b True a == b True a = 257 b = 257 a is b False Python仅仅对比较小的整数对象进行缓存(范围为范围[-5, 256])缓存起来,而并非是所有整数对象。需要注意的是,这仅仅是在命令行中执行,而在Pycharm或者保存为文件执行,结果是不一样的,这是因为解释器做了一部分优化。 =和==的含义不同: =代表的含义是赋值,将某一数值赋给某个变量,比如a=3,将3这个数值赋予给a。==是判断是否相等,返回True或False,比如1==1。他们是相等的,那么就返回true。1==2,他们是不相等的,那么就返回false。例子: a = [1,2] b = [1,2] c = a a is b False a is c true a == b true 7、滥用__init__ __init__方法在Python中用作构造函数,当Python将内存分配给新的类对象时,它会自动被调用。首先,__init__并不相当于C#中的构造函数,在执行它的时候,实例已经构造出来。 #小编创建了一个Python学习交流QQ群:857662006 class A(object): def __init__(self,name): self.name=name def getName(self): return 'A '+self.name 执行代码: a=A('hello') 可以理解为: a=object.__new__(A) A.__init__(a,'hello') 即__init__作用是初始化已实例化后的对象。 其次,子类可以不重写__init__,实例化子类时,会自动调用超类中已定义的__init__。 class B(A): def getName(self): return 'B '+self.name if __name__=='__main__': b=B('hello') print (b.getName()) 但如果重写了__init__,实例化子类时,则不会隐式的再去调用超类中已定义的__init__。 class C(A): def __init__(self): pass def getName(self): return 'C '+self.name if __name__=='__main__': c=C() print (c.getName()) 此时执行代码则会报"AttributeError: 'C' object has noattribute 'name'”错误,所以如果重写了__init__,为了能使用或扩展超类中的行为,最好显式的调用超类的__init__方法。 class C(A): def __init__(self,name): super(C,self).__init__(name) def getName(self): return 'C '+self.name if __name__=='__main__': c=C('hello') print (c.getName())
前言 什么是计算机语言 计算机就是一台用来计算的机器,人让计算机干什么计算机就得干什么! 需要通过计算机的语言来控制计算机(也就是编程语言)! 计算机语言其实和人类的语言没有本质的区别,不同点就是交流的主体不同!计算机语言发展经历了三个阶段: 1). 机器语言 机器语言通过二进制编码来编写程序 执行效率好,编写起来太麻烦 2). 符号语言(汇编) 使用符号来代替机器码 编写程序时,不需要使用二进制, 而是直接编写符号 编写完成后,需要将符号转换为机器码,然后再由计算机执行符号转换为机器码的过程称为汇编 将机器码转换为符号的过程,称为反汇编 汇编语言一般只适用于某些硬件,兼容性比较差 3). 高级语言 高级语言的语法基本和现在英语语法类似,并且和硬件的关系没有那么紧密了。也就是说我们通过高级语言开发程序可以在不同的硬件系统中执行 并且高级语言学习起来也更加的容易,现在我们知道的语言基本都是高级语言 如:C、 C++、C#、Java、JavaScript、Python 等.. 编译型语言和解释型语言 计算机只能识别二进制编码(机器码),所以任何的语言在交由计算机执行时必须要先转换为机器码,也就是像 print('hello') 必须要转换为类似 1010101 这样的机器码。 根据转换时机的不同,语言分成了两大类: 1). 编译型语言 如:C语言 编译型语言,会在代码执行前将代码编译为机器码,然后将机器码交由计算机执行 执行过程:a(源码) --编译--> b(编译后的机器码) 特点: 执行速度特别快 跨平台性比较差 2). 解释型语言 如:Python JS Java 解释型语言,不会在执行前对代码进行编译,而是在执行的同时一边执行一边编译 执行过程:a(源码)--解释器--> 解释执行 特点: 执行速度比较慢 跨平台性比较好 下面我们切入正题,谈谈Python。 Python的介绍 Python是解释型语言。 Python(英国发音:/ˈpaɪθən/ 美国发音:/ˈpaɪθɑːn/),是一种广泛使用的高级编程语言,属于通用型编程语言,由吉多·范罗苏姆创造,第一版发布于1991年。可以视之为一种改良(加入一些其他编程语言的优点,如面向对象)的LISP。作为一种解释型语言,Python的设计哲学强调代码的可读性和简洁的语法(尤其是使用空格缩进划分代码块,而非使用大括号或者关键词)。相比于C++或Java,Python让开发者能够用更少的代码表达想法。不管是小型还是大型程序,该语言都试图让程序的结构清晰明了。 Python的用途: WEB应用 Facebook 豆瓣 爬虫程序 科学计算 自动化运维 大数据(数据清洗) 云计算 桌面软件/游戏 人工智能 Python开发环境搭建 开发环境搭建就是安装Python的解释器 Python的解释器分类: CPython(官方)用c语言编写的Python解释器 PyPy用Python语言编写的Python解释器 IronPython用.net编写的Python解释器 JPython用Java编写的Python解释器 步骤: 1.下载安装包 python-3.6.exe2.安装(傻瓜式安装, 默认选项..) 安装完成可以看到以下几个程序: 3.打开命令行窗口,输入python 出现如下内容Python的交互界面 当我们通过Windows命令行来输入Python,所进入到的界面就是Python的交互界面 结构: 版本和版权声明: Python 3.6.3 (tags/v3.6.3:2c5fed8, Oct 3 2017, 17:26:49) [MSC v.1900 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. 命令提示符: >>> 在命令提示符后可以直接输入Python的指令!输入完的指令将会被Python的解释器立即执行! 安装Python的同时,会自动安装一个Python的开发工具IDLE,通过IDLE也可以进入到交互模式。但是不同的是,在IDLE中可以通过TAB键来查看语句的提示。 IDLE实际上就是一个交互界面,但是他可以有一些简单的提示,并且可以将代码保存。 注意:交互模式只能你输入一行代码,它就是执行一行,所以他并不适用于我们日常的开发! 仅可以用来做一些日常的简单的测试! 我们一般会将Python代码编写到一个py文件中,然后通过python指令来执行文件中的代码。 Python和Sublime的整合 1.在Sublime中执行Python代码,按 ctrl + b 可以自动在Sublime内置的控制台中执行 这种执行方式,在某些版本的Sublime中对中文支持不好,并且不能使用input()函数。 2.使用SublimeREPL来运行python代码 如下图:选择SublimeREPL-> Python来执行Python代码但是这样很麻烦。为方便我们可以设置快捷键,按f5则自动执行当前的Python代码。 如下设置快捷键来执行python代码呢? 找到Preferences-->Key Bindings ,然后复制下面的内容到右边的中括号内。然后保存。 这样我们就可以按F5快捷键来自动执行当前的Python代码了。 { "keys": ["f5"], "caption": "SublimeREPL:Python","command": "run_existing_window_command", "args":{"id": "repl_python_run","file": "config/Python/Main.sublime-menu"}}, Python的几个基本概念 1.表达式 表达式就是一个类似于数学公式的东西 比如:10 + 5 8 - 4 表达式一般仅仅用了计算一些结果,不会对程序产生实质性的影响 如果在交互模式中输入一个表达式,解释器会自动将表达式的结果输出 2.语句 在程序中语句一般需要完成某种功能,比如打印信息、获取信息、为变量赋值比如: #Python学习交流QQ群:857662006 print() input() a = 10 语句的执行一般会对程序产生一定的影响 在交互模式中不一定会输出语句的执行结果 3.程序(program) 程序就是由一条一条的语句和一条一条的表达式构成的。 4.函数(function) 函数就是一种语句,函数专门用来完成特定的功能函数长的形如:xxx() 函数的分类: 1). 内置函数 由Python解释器提供的函数,可以在Python中直接使用 2). 自定义函数 由程序员自主的创建的函数 当我们需要完成某个功能时,就可以去调用内置函数,或者自定义函数 函数的两个要素: 1). 参数 ()中的内容就是函数的参数 函数中可以没有参数,也可以有多个参数,多个参数之间使用,隔开 2). 返回值 返回值是函数的返回结果,不是所有的函数都有返回值 Python的基本语法 在Python中严格区分大小写 Python中的每一行就是一条语句,每条语句以换行结束 Python中每一行语句不要过长(规范中建议每行不要超过80个字符) 一条语句可以分多行编写,多行编写时语句后边以结尾 Python是缩进严格的语言,所以在Python中不要随便写缩进 在Python中使用#来表示注释,#后的内容都属于注释,注释的内容将会被解释器所忽略 我们可以通过注释来对程序进行解释说明,一定要养成良好的编写注释的习惯注释要求简单明了,一般习惯上#后边会跟着一个空格 字面量和变量 字面量就是一个一个的值,比如:1,2,3,4,5,6,‘HELLO’ 字面量所表示的意思就是它的字面的值,在程序中可以直接使用字面量 变量(variable)变量可以用来保存字面量,并且变量中保存的字面量是不定的 变量本身没有任何意思,它会根据不同的字面量表示不同的意思 一般我们在开发时,很少直接使用字面量,都是将字面量保存到变量中,通过变量来引用字面量 数据类型 数据类型指的就是变量的值得类型,也就是可以为变量赋哪些值 在Python中,能够直接处理的数据类型有以下几种:整数、浮点数、字符串、布尔值、列表、元组、字典、集合。 以后我们详细介绍这几个数据类型。 对象(object) Python是一门面向对象的语言 一切皆对象! 程序运行当中,所有的数据都是存储到内存当中然后再运行的! 对象就是内存中专门用来存储指定数据的一块区域 对象实际上就是一个容器,专门用来存储数据 像我们之前学习的数值、字符串、布尔值、None都是对象 对象的结构 每个对象中都要保存三种数据 1). id(标识) id用来标识对象的唯一性,每一个对象都有唯一的id 对象的id就相当于人的身份证号一样 可以通过id()函数来查看对象的id id是由解析器生成的,在CPython中,id就是对象的内存地址 对象一旦创建,则它的id永远不能再改变 2). type(类型) 类型用来标识当前对象所属的类型 比如:int str float bool 类型决定了对象有哪些功能 通过type()函数来查看对象的类型 Python是一门强类型的语言,对象一旦创建类型便不能修改 3). value(值) 值就是对象中存储的具体的数据 对于有些对象值是可以改变的 对象分成两大类,可变对象 不可变对象 可变对象的值可以改变 不可变对象的值不能改变 变量和对象 对象并没有直接存储到变量中,在Python中变量更像是给对象起了一个别名 变量中存储的不是对象的值,而是对象的id(内存地址), 当我们使用变量时,实际上就是在通过对象id在查找对象 变量中保存的对象,只有在为变量重新赋值时才会改变 变量和变量之间是相互独立的,修改一个变量不会影响另一个变量 类型转换 所谓的类型转换,将一个类型的对象转换为其他对象 类型转换不是改变对象本身的类型,而是根据当前对象的值创建一个新对象 运算符(操作符) 运算符可以对一个值或多个值进行运算或各种操作 比如 + 、-、= 都属于运算符 运算符的分类: 1.算术运算符2.赋值运算符3.比较运算符(关系运算符)4.逻辑运算符5.条件运算符(三元运算符)
为什么有这篇"杂项"文章实在是因为python中对象方面的内容太多、太乱、太杂,在写相关文章时比我所学过的几种语言都更让人"糟心",很多内容似独立内容、又似相关内容,放这也可、放那也可、放这也不好、放那也不好。 所以,用一篇单独的文章来收集那些在我其它文章中不好归类的知识点,而且会随时更新。 class、type、object的关系 在python 3.x中,类就是类型,类型就是类,它们变得完全等价。 要理解class、type、object的关系,只需几句话: object是所有类的祖先类,包括type类也继承自object 所有class自身也是对象,所有类/类型都是type的实例对象,包括object和type自身都是type的实例对象论证略,网上一大堆。 鸭子模型(duck typing) Duck typing的概念来源于的诗句"When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck."。 意思是:如果我看到一只鸟走路像一只鸭子,游泳像一只鸭子,叫起来像一只鸭子,那么我就认为这只鸟是一只鸭子。 在python中,鸭子模型非常容易理解。下面是典型的鸭子模型示例: class Duck(): def walk(self): print("duck walk") def swim(self): print("duck swim") def quacks(self): print("duck quacks") class Bird(): def walk(self): print("bird walk") def swim(self): print("bird swim") def quacks(self): print("bird quacks") 对于Python来说,鸭子模型的意思是:只要某个地方需要调用Duck的walk、swim、quacks方法,就可以让Bird也作为Duck,因为它也实现了这3个方法。 Python并不强制检查类型,只要对象实现了某个所需要的方法,就认为这是可以接受的对象。 它还传达一种思想,A类对象能放在一个地方,如果想让B类对象也可以放在这个地方,只需要让B实现这个地方所需要的方法就可以。 鸭子模型贯穿了python中的运算符重载行为,也贯穿了整个Python的类设计理念。例如print()执行的时候需要调用__ str__方法,所以只要实现了__str__方法的类,都可以被print()调用。 绑定方法和非绑定方法 绑定之意,在于方法是否与实例对象(或类名)进行了绑定。 当通过实例对象去调用方法时,或者说会自动传递self的方法是绑定方法,其它通过类名调用、手动传递self的方法调用是非绑定方法,在3.x中没有非绑定方法的概念,它直接被当作是普通函数。 例如: class cls(): def m1(self): print("m1: ", self) def m2(arg1): print("m2: ", arg1) 当通过cls类的实例对象去调用m1、m2的时候,是绑定方法: >>> c = cls() >>> c.m1 <bound method cls.m1 of <__main__.cls object at 0x000001EE2DA75860>> >>> c.m1() m1: <__main__.cls object at 0x000001EE2DA75860> >>> c.m2 <bound method cls.m2 of <__main__.cls object at 0x000001EE2DA75860>> >>> c.m2() m2: <__main__.cls object at 0x000001EE2DA75860> 也就是说,绑定方法中是绑定了实例对象的,无需手动去传递实例对象。例如: >>> cc = c.m1 >>> cc() m1: <__main__.cls object at 0x000001EE2DA75860> 当通过类名去访问的时候,是普通函数(非绑定方法): >>> cls.m1 <function cls.m1 at 0x000001EE2DA78620> >>> cls.m2 <function cls.m2 at 0x000001EE2DA786A8> >>> cls.m1(c) m1: <__main__.cls object at 0x000001EE2DA75860> >>> cls.m2(c) m2: <__main__.cls object at 0x000001EE2DA75860> 唯一需要在意的是,并非一定要通过实例对象去调用方法,通过类方法也能的调用,也能手动传递实例对象。此外,类中的方法并非一定要求有self参数。 静态方法和类方法 python的面向对象中有3种类型的方法:普通的实例方法、类方法、静态方法。 普通实例方法:通过self参数传递实例对象自身 类方法:传递的是类名而非对象 静态方法:不通过self传递. 从这些方法的简单定义上看,很容易知晓实例方法可以操作类属性、对象属性,而类方法和静态方法只能操作类属性,不能操作对象属性。 所以,要实现类方法、静态方法需要合理地定义、传递参数。例如: class cls(): def m1(self, arg1): print("m1: ", self, arg1) def m2(arg1, arg2): print("m2: ", arg1) 显然这里m2()是静态方法,m1根据调用方式可以是类方法,也可以是实例方法,甚至是静态方法。例如: # m1作为实例方法 >>> c.m1("hello") m1: <__main__.cls object at 0x000001EE2DA75BA8> hello # m1作为类方法,通过类名调用,并传递类名作为self参数 >>> cls.m1(cls,"hello") m1: <class '__main__.cls'> hello # m1作为静态方法,通过类名调用,随意处置self参数 >>> cls.m1("asdfas","hello") m1: asdfas hello 这样的调用方式并没有什么问题,python是允许这样做的,很自由,但很容易犯错。比如想要通过对象名去调用上面的m2,arg1就必须当作self一样解释成对象自身,换句话说只能传递一个参数c.m2("arg2"),这显然有悖静态方法的编码方式。 在python中,要定义严格的类方法、静态方法,需要使用内置的装饰器函数classmethod()、staticmethod()来装饰,装饰后无论使用对象名去调用还是使用类名去调用,都可以。 例如: class cls(): def m1(self,arg1): print("m1: ", self, arg1) @classmethod def m2(self,arg1): print("m2: ", self, arg1) @staticmethod def m3(arg1, arg2): print("m3: ", arg1, arg2) 上面定义了普通方法、类方法和静态方法。如果尚不了解装饰器的用法,暂时只需知道上面的@xxx将它下面的函数(方法)扩展成了类方法、静态方法即可。 调用实例方法: >>> c = cls() >>> c.m1("hello") m1: <__main__.cls object at 0x000001EE2DA840B8> hello 注意输出的self是"…object…",和下面的类方法调用注意区分比较。 调用类方法。因为@classmethod已经将m2包装成了类方法,所以m2的第一个self参数将总是代表类名,而无论是使用对象去调用m2还是使用类名去调用m2。 >>> c.m2("hello") m2: <class '__main__.cls'> hello >>> cls.m2("hello") m2: <class '__main__.cls'> hello 如果输出m2方法,会发现它已经是绑定方法,也就是说和类名进行了绑定(这里不是和对象名进行绑定)。 >>> c.m2 <bound method cls.m2 of <class '__main__.cls'>> >>> cls.m2 <bound method cls.m2 of <class '__main__.cls'>> 调用静态方法。 >>> c.m3("hello","world") m3: hello world >>> cls.m3("hello","world") m3: hello world 静态方法都是未绑定的函数: >>> c.m3 <function cls.m3 at 0x000001EE2DA789D8> >>> cls.m3 <function cls.m3 at 0x000001EE2DA789D8> 一般来说,类方法用于在类中操作/返回和类名有关的内容,静态方法用于在类中做和类或对象完全无关的操作。一个比较好理解的例子是,一个Employee类,要检查员工的年龄范围在16-35,如果年龄在这范围内,就返回一个员工对象,可以将这个逻辑定义为类方法。如果只是检查年龄范围来决定True或False这样和类/对象无关的操作,则定义为静态方法。 class Employee: @staticmethod def age_ok(age): if 16<age<35: return True else: return False @classmethod def age_check(cls, age): if 16<age<35: return cls(...) 私有属性 python没有private关键字来修饰属性使其变成私有属性,但是带上双下划线前缀的属性且没有后缀下划线的属性(__X)可以认为是私有属性。它仅仅只是约定性的私有属性,不代表外界真的不能访问。 实际上,使用__X这样的属性,在类的内部访问时会自动进行扩展为_clsname__X,也就是加个前缀下划线,再加个类名。因为扩展时加上了类名,使得这个属性在名称上是独属于这个类的。 例如: class cls(): __X = 12 def m1(self,y): self.__Y = y print(self.__X) print(self.__Y) >>> print(cls.__dict__.keys()) dict_keys([..., '_cls__X', 'm1', ....]) >>> c = cls() >>> c.m1(22) 12 22 >>> print(c.__dict__.keys()) dict_keys(['_cls__Y']) 因为已经扩展了属性的名称,所以无法在类的外界通过直接的名称__X去访问对应的属性。 >>> c.__Y Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'cls' object has no attribute '__Y' >>> c.__X Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'cls' object has no attribute '__X' >>> cls.__X Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: type object 'cls' has no attribute '__X' 前面说了,这种加双下划线前缀的属性仅仅只是一个约定形式,虽然在外界无法直接通过名称去访问,但是仍有不少方法去访问。例如通过扩展后的名称、通过字典__dict__: >>> cls._cls__X 12 >>> c._cls__Y 22 >>> c.__dict__['_cls__Y'] 22 要想严格地声明属性的私有性,可以编写装饰器类,在装饰器类中完成属性的判断。 方法的默认可变参数陷阱 如果一个方法的参数给了默认参数,且这个默认参数是一个可变类型,那么这里有一个陷阱:使用这个默认参数的时候各对象会共享这个可变默认值。 例如: class A: def __init__(self, arg=[]): self.data = arg def add(self, value): self.data.append(value) # 两个不同对象,且都使用参数arg的默认值 a1 = A() a2 = A() # 向两个对象中添加元素 a1.add("a1") a2.add("a2") print(a1.data) print(a2.data) 执行结果: ['a1', 'a2'] ['a1', 'a2'] 发现a1和a2这两个不同的对象中的data竟然是相同的数据,如果输出下它们的data属性,会发现是同一个对象: >>> a1.data is a2.data True 这是因为参数的默认值是在申请变量之前就先评估好的,也就是在赋值给参数变量arg之前,这个空列表就已经存在了。然后使用默认值来构造对象时,这些对象都使用同一个空列表,而这个空列表是可变的类型,所以无论谁修改这个列表都会影响其它对象。 如果不使用默认值,那么每个对象的列表就是独占的,不会被其它对象修改。 a3 = A([]) a3.add("a3") print(a3.data) 结果: ['a3'] MethodType:添加外部函数作为方法 python的types模块中提供了一个MethodType(funcName, instance)函数,它可以将类外部定义的函数funcName链接到实例对象或类上。 例如连接到实例对象上: # 注意外部函数上加了self参数 def func(self, age): print(age) class cls: pass >>> c = cls >>> import types >>> c.printage = types.MethodType(func, c) >>> c.printage(22) 22 type.MethodType()是将某个可调用对象(这里的func)动态地链接到实例对象或类上,使其临时作为对象或类的方法属性,只有在被调用的时候才会进行属性的添加。 需要注意的是,当外部函数链接到实例对象上时,这个链接只对这个实例对象有效,其它对象是不具备这个属性的。如果链接到类上,那么所有对象都可以访问这个链接的方法。 call 正常情况下定义了一个类,调用这个类表示创建一个对象。 class cls: pass c = cls() 但是,对象c不再是可调用的对象,也就是说,它不能再被执行。 >>> callable(c) False python对象的__call__可以让实例对象也变成可调用类型,就像函数一样。 class cls: def __call__(self, *args, **kwargs): print('__call__: ', args, kwargs) >>> c = cls() >>> c(1,2,3,x=4,y=5) __call__: (1, 2, 3) {'x': 4, 'y': 5} >>> callable(c) True 将类定义为一个可调用对象是非常有用的,它可以像函数一样去修饰、扩展其它内容的功能,特别是编写装饰器类的时候。 例如,正常情况下写装饰器总要返回一个新装饰器函数,但是想要直接使用类作为装饰器,就需要在这个类中定义__call__,将__call__作为函数装饰器中的装饰器函数wrapped()。下面是一个示例: import types from functools import wraps class DecoratorClass(): def __init__(self, func): wraps(func)(self) self.callcount = 0 def __call__(self, *args, **kwargs): self.callcount += 1 return self.__wrapped__(*args, **kwargs) def __get__(self, instance, cls): if instance is None: return self else: return types.MethodType(self, instance) 上面是装饰器类,可以像函数装饰器一样去装饰其它函数。 @DecoratorClass def add(x, y): return x + y >>> add(2,3) 5 >>> add(3,4) 7 >>> add.callcount 2 判断对象是否可调用的几种方式 根据前面的说明可知,判断一个对象是否是可调用的依据有2种方式: 使用内置函数callable(X),X可调用则返回True,否则False注:返回False一定表示不可调用,但返回True不代表一定可调用判断是否定义了call__方法。使用hasattr(obj,'__call')即可判断 >>> callable(c) True >>> hasattr(c,'__call__') True __slots__ python是一门动态语言,而且是极其开放的动态语言。在面向对象上,它允许我们随意地、任意时间地添加属性。例如: # 小编创建了一个Python学习交流QQ群:857662006 class cls(): attr1 = 111 # 在类中添加属性 def __init__(self): self.attr2 = 222 # 添加实例对象的属性 >>> c = cls() >>> c.attr3 = 333 # 在类的外部添加属性 >>> c.__dict__.keys() dict_keys(['attr2', 'attr3']) 如果想要限定对象只能拥有某些属性,可以使用__slots__来限定,__slots__可以指定为一个元组、列表、集合等。 例如: class cls(): __slots__ = ['a', 'b'] >>> c = cls() >>> c.a=13 >>> c.b=14 >>> c.cc=15 # 报错 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'cls' object has no attribute 'cc' 但注意: __slots__定义在类级别上,它仅仅只限定实例对象属性,不会限制类属性 __slots__不会被子类继承 slots__定义后,对象默认就没有了__dict属性 但可以将__dict__放进__slots__的范围内来允许__dict__ 还有几个注意点在下面的示例中解释 例如: class cls(): __slots__ = ['a', 'b'] x = 13 # 允许定义类属性 c = cls() c.a = 14 c.b = 15 cls.y = 16 # 允许定义类属性 print(c.x, c.a, c.b, c.y) print(cls.__dict__.keys()) # 类有__dict__属性 print(c.__dict__.keys()) # 报错,对象没有__dict__属性 可以将__dict__放进__slots__中,使得对象可以带有属性字典。但这会让__slots__的限定失效:实例对象可以继续添加任意属性,那些不在__slots__中的属性会加入到__dict__中。 class cls1(): __slots__ = ['a', 'b', '__dict__'] cc = cls1() cc.a = 14 cc.b = 15 cc.c = 16 cc.d = 17 print(cc.__slots__) print(cc.__dict__.keys()) --------------------------------- 输出结果: ['a', 'b', '__dict__'] dict_keys(['c', 'd']) 因为子类不会继承父类的__slots__,所以如果父类中没有定义__slots__的话,因为子类可以访问父类的__dict__,这会使得子类自身定义的__slots__的属性限定功能失效。 # 小编创建了一个Python学习交流QQ群:857662006 class cls1(): pass class cls2(cls1): __slots__ = ['a', 'b'] ccc = cls2() ccc.a=13 ccc.b=14 ccc.ddd=15 print(ccc.__slots__) print(ccc.__dict__.keys()) ---------------------------------- 结果: ['a', 'b'] dict_keys(['ddd']) 多重继承和__ mro__和super() python支持多重继承,只需将需要继承的父类放进子类定义的括号中即可。 class cls1(): ... class cls2(): ... class cls3(cls1,cls2): ... 上面cls3继承了cls1和cls2,它的名称空间将连接到两个父类名称空间,也就是说只要cls1或cls2拥有的属性,cls3构造的对象就拥有(注意,cls3类是不拥有的,只有cls3类的对象才拥有)。 但多重继承时,如果cls1和cls2都具有同一个属性,比如cls1.x和cls2.x,那么cls3的对象c3.x取哪一个?会取cls1中的属性x,因为规则是按照(括号中)从左向右的方式搜索父类。 再考虑一个问题,如果cls1中没有属性x,但它继承自cls0,而cls0有x属性,那么,c3.x取哪个属性。 这在python 3.x中是一个比较复杂的问题,它根据MRO(Method Resolution Order)算法来决定多重继承时的访问顺序,这个算法的规则可以总结为:先左后右,先深度再广度,但必须遵循共同的超类最后搜索。 每个类都有一个__mro__属性,这个属性是一个元组,从左向右的元素顺序代表的是属性搜索顺序。 class D(): pass class C(D): pass class B(D): pass class A(B, C): pass >>> A.__mro__ (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>) >>> D.__mro__ (<class '__main__.D'>, <class 'object'>) 不仅多重继承时是按照MRO顺序进行属性搜索的,super()引用的时候也一样是按照mro算法来引用属性的,所以super并不一定总是引用父类属性。 例如: class D(): def __init__(self): print("D") class C(D): def __init__(self): print("C") super().__init__() class B(D): def __init__(self): print("B") super().__init__() # 调用的不是父类D的构造方法 class A(B, C): def __init__(self): print("A") super().__init__() a = A() ----------------------------------------------- 输出结果为: A B C D 面向对象中,一般不推荐使用多重继承,因为很容易出现属性引用混乱的问题,而且有些面向对象的语言根本就不支持多重继承。但在Python中,使用多重继承的情况也非常多,如果真的要使用多重继承,一定要设计好类。一种更好的方式是使用Mixin类,见下文。 关于Mixin Mixin的wiki页:https://en.wikipedia.org/wiki/Mixin 对于那些想要从多个类中继承的方法,如果想要避免多重继承可能引起的属性混乱,可以将这些方法单独编写到一个类中,而这个功能/方法相对单一的类称为Mixin类。 Mixin类通过特殊的多重继承方法来扩展主类的功能,却又很安全,不会出现多重继承时属性混乱的问题。 例如: class Mixin1(): def test1(self): print("test1 method provided by Mixin1") class Mixin2(): def test2(self): print("test2 method provided by Mixin1") class Base(): def mymethod(self): print("mymethod is the base method") class Myclass(Mixin1, Mixin2, Base): pass 上面的Mixin1和Mixin2是Mixin类,它们都只有一个方法,功能非常单一,它们可以看作是Base类的功能扩充类,也可以认为Mixin类是主类Include的类。 例如wiki页中给的一个例子,class TCPServer中提供了UDP/TCP server的功能,这时每个连接都通过一个相同的进程进行处理。但是可以将class ThreadingMixIn通过Mixin的方法对TCPServer进行扩充: class ThreadingTCPServer(ThreadMixIn, TCPServer): pass 这相当于将ThreadingMixIn类中的方法添加到了TCPServer类中,使得每个新连接都会创建一个新的线程,这个功能是ThreadMixIn提供的,但看上去作用在TCPServer上。 关于Mixin类,有几个编码规范需要遵守: 类名使用Mixin结尾,例如ListMixin、AbcMixin 多重继承时Mixin类放在主类的前面,或者说主类放在最后面,避免主类有和Mixin类中重名函数而使得Mixin类失效 Mixin类中不规定只能定义一个方法,而是少定义一点,让功能尽量单一、独立 抽象类 抽象类是指:这个类的子类必须重写这个类中的方法,且这个类没法进行实例化产生对象。 先说明在Python中如何定义抽象类。Python中的abc模块(Abstract Base Classes)专门用来实现抽象类、接口。 例如,在设计某个程序的缓存接口时,想要让它未来既可以使用普通的cache,也可以使用redis缓存。那么只需要定义一个抽象的类Cache,里面实现两个抽象方法get()和set(),以后无论使用普通的cache还是redis缓存,都只需让这两种缓存类型实现且必须实现get()和set()即可。 import abc class Cache(metaclass=abc.ABCMeta): @abc.abstractmethod def get(self, key): pass @abc.abstractmethod def get(self, key, value): pass # 子类继承时,必须实现这两个方法 class CacheBase(Cache): def get(self, key): pass def set(self, key, value): pass class Redis(Cache): def get(self, key): pass def set(self, key, value): pass 如果子类没有实现或者少实现了抽象类中的方法,在构造子类实例化对象的时候就会立即报错。 在Python中大多数时候不建议直接定义抽象类,这可能会造成过度封装/过度抽象的问题。如果想要让子类必须实现父类的某个方法,可以在父类方法中加上raise来抛出异常NotImplementedError,这时如果子类对象没有实现该方法,就会查找到父类的这个方法,从而抛出异常。 class Cache(): def get(self, key): raise NotImplementedError("must define get method") def set(self, key): raise NotImplementedError("must define set method") 使用raise NotImplementedError的方式来模拟抽象类,它只有在调用到set/get的时候才会抛异常,在实例化对象的时候或者没有调用到这两个方法的时候不会报错。
初学Python常见错误 忘记写冒号 误用= 错误 缩紧 变量没有定义 中英文输入法导致的错误 不同数据类型的拼接 索引位置问题 使用字典中不存在的键 忘了括号 漏传参数 缺失依赖库 使用了python中对关键词 编码问题 1. 忘记写冒号 在 if、elif、else、for、while、def语句后面忘记添加 : age = 42 if age == 42 print('Hello!') File "<ipython-input-19-4303141d6f97>", line 2 if age == 42 ^ SyntaxError: invalid syntax 2. 误用 = =` 是赋值操作,而判断两个值是否相等是 `== gender = '男' if gender = '男': print('Man') File "<ipython-input-20-191d01f95984>", line 2 if gender = '男': ^ SyntaxError: invalid syntax 3. 错误的缩进 Python用缩进区分代码块,常见的错误用法: print('Hello!') print('Howdy!') File "<ipython-input-9-784bdb6e1df5>", line 2 print('Howdy!') ^ IndentationError: unexpected indent num = 25 if num == 25: print('Hello!') File "<ipython-input-21-8e4debcdf119>", line 3 print('Hello!') ^ IndentationError: expected an indented block 4. 变量没有定义 if city in ['New York', 'Bei Jing', 'Tokyo']: print('This is a mega city') --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-22-a81fd2e7a0fd> in <module> ----> 1 if city in ['New York', 'Bei Jing', 'Tokyo']: 2 print('This is a mega city') NameError: name 'city' is not defined 5. 中英文输入法导致的错误 英文冒号 英文括号 英文逗号 英文单双引号 if 5>3: print('5比3大') File "<ipython-input-46-47f8b985b82d>", line 1 if 5>3: ^ SyntaxError: invalid character in identifier if 5>3: print('5比3大') File "<ipython-input-47-4b1df4694a8d>", line 2 print('5比3大') ^ SyntaxError: invalid character in identifier spam = [1, 2,3] File "<ipython-input-45-47a5de07f212>", line 1 spam = [1, 2,3] ^ SyntaxError: invalid character in identifier if 5>3: print('5比3大‘) File "<ipython-input-48-ae599f12badb>", line 2 print('5比3大‘) ^ SyntaxError: EOL while scanning string literal 6. 不同数据类型的拼接 字符串/列表/元组 支持拼接 字典/集合不支持拼接 #小编创建了一个Python学习交流QQ群:857662006 'I have ' + 12 + ' eggs.' #'I have {} eggs.'.format(12) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-29-20c7c89a2ec6> in <module> ----> 1 'I have ' + 12 + ' eggs.' TypeError: can only concatenate str (not "int") to str ['a', 'b', 'c']+'def' --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-31-0e8919333d6b> in <module> ----> 1 ['a', 'b', 'c']+'def' TypeError: can only concatenate list (not "str") to list ('a', 'b', 'c')+['a', 'b', 'c'] --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-33-90742621216d> in <module> ----> 1 ('a', 'b', 'c')+['a', 'b', 'c'] TypeError: can only concatenate tuple (not "list") to tuple set(['a', 'b', 'c'])+set(['d', 'e']) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-35-ddf5fb1e6c8c> in <module> ----> 1 set(['a', 'b', 'c'])+set(['d', 'e']) TypeError: unsupported operand type(s) for +: 'set' and 'set' grades1 = {'Mary':99, 'Henry':77} grades2 = {'David':88, 'Unique':89} grades1+grades2 --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-36-1b1456844331> in <module> 2 grades2 = {'David':88, 'Unique':89} 3 ----> 4 grades1+grades2 TypeError: unsupported operand type(s) for +: 'dict' and 'dict' 7. 索引位置问题 spam = ['cat', 'dog', 'mouse'] print(spam[5]) --------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-38-e0a79346266d> in <module> 1 spam = ['cat', 'dog', 'mouse'] ----> 2 print(spam[5]) IndexError: list index out of range 8. 使用字典中不存在的键 在字典对象中访问 key 可以使用 [], 但是如果该 key 不存在,就会导致:KeyError: 'zebra' spam = {'cat': 'Zophie', 'dog': 'Basil', 'mouse': 'Whiskers'} print(spam['zebra']) --------------------------------------------------------------------------- KeyError Traceback (most recent call last) <ipython-input-39-92c9b44ff034> in <module> 3 'mouse': 'Whiskers'} 4 ----> 5 print(spam['zebra']) KeyError: 'zebra' 为了避免这种情况,可以使用 get 方法 spam = {'cat': 'Zophie', 'dog': 'Basil', 'mouse': 'Whiskers'} print(spam.get('zebra')) None key 不存在时,get 默认返回 None 9. 忘了括号 当函数中传入的是函数或者方法时,容易漏写括号 spam = {'cat': 'Zophie', 'dog': 'Basil', 'mouse': 'Whiskers'} print(spam.get('zebra') File "<ipython-input-43-100a51a7b630>", line 5 print(spam.get('zebra') ^ SyntaxError: unexpected EOF while parsing 10. 漏传参数 def diyadd(x, y, z): return x+y+z diyadd(1, 2) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-44-7184f3f906ca> in <module> 2 return x+y+z 3 ----> 4 diyadd(1, 2) TypeError: diyadd() missing 1 required positional argument: 'z' 11. 缺失依赖库 电脑中没有相关的库 12. 使用了python中的关键词 如try、except、def、class、object、None、True、False等 try = 5 print(try) File "<ipython-input-1-508e87fe2ff3>", line 1 try = 5 ^ SyntaxError: invalid syntax def = 6 print(6) File "<ipython-input-2-d04205303265>", line 1 def = 6 ^ SyntaxError: invalid syntax 13. 文件编码问题 import pandas as pd df = pd.read_csv('data/twitter情感分析数据集.csv') df.head() 尝试encoding编码参数传入utf-8、gbk df = pd.read_csv('data/twitter情感分析数据集.csv', encoding='utf-8') df.head() 都报错说明编码不是utf-8和gbk,而是不常见都编码,这里我们需要传入正确都encoding,才能让程序运行。 python有个chardet库,专门用来侦测编码。 import chardet binary_data = open('data/twitter情感分析数据集.csv', 'rb').read() chardet.detect(binary_data) {'encoding': 'Windows-1252', 'confidence': 0.7291192008535122, 'language': ''
由于总结了太多的东西,所以篇幅有点长,这也是我"缝缝补补"总结了好久的东西。 Py2 VS Py3 print成为了函数,python2是关键字 不再有unicode对象,默认str就是unicode python3除号返回浮点数 没有了long类型 xrange不存在,range替代了xrange 可以使用中文定义函数名变量名 高级解包 和*解包 限定关键字参数 *后的变量必须加入名字=值 raise from iteritems移除变成items() yield from 链接子生成器 asyncio,async/await原生协程支持异步编程 新增enum,mock,ipaddress,concurrent.futures,asyncio urllib,selector 不同枚举类间不能进行比较 同一枚举类间只能进行相等的比较 枚举类的使用(编号默认从1开始) 为了避免枚举类中相同枚举值的出现,可以使用@unique装饰枚举类 #枚举的注意事项 from enum import Enum class COLOR(Enum): YELLOW=1 #YELLOW=2#会报错 GREEN=1#不会报错,GREEN可以看作是YELLOW的别名 BLACK=3 RED=4 print(COLOR.GREEN)#COLOR.YELLOW,还是会打印出YELLOW for i in COLOR:#遍历一下COLOR并不会有GREEN print(i) #COLOR.YELLOW\nCOLOR.BLACK\nCOLOR.RED\n怎么把别名遍历出来 for i in COLOR.__members__.items(): print(i) # output:('YELLOW', <COLOR.YELLOW: 1>)\n('GREEN', <COLOR.YELLOW: 1>)\n('BLACK', <COLOR.BLACK: 3>)\n('RED', <COLOR.RED: 4>) for i in COLOR.__members__: print(i) # output:YELLOW\nGREEN\nBLACK\nRED #枚举转换 #最好在数据库存取使用枚举的数值而不是使用标签名字字符串 #在代码里面使用枚举类 a=1 print(COLOR(a))# output:COLOR.YELLOW py2/3转换工具 six模块:兼容pyton2和pyton3的模块 2to3工具:改变代码语法版本 __future__:使用下一版本的功能 常用的库 必须知道的collections https://segmentfault.com/a/1190000017385799 python排序操作及heapq模块 https://segmentfault.com/a/1190000017383322 itertools模块超实用方法 https://segmentfault.com/a/1190000017416590 不常用但很重要的库 dis(代码字节码分析) inspect(生成器状态) cProfile(性能分析) bisect(维护有序列表) fnmatch fnmatch(string,"*.txt") #win下不区分大小写 fnmatch根据系统决定 fnmatchcase完全区分大小写 timeit(代码执行时间) def isLen(strString): #还是应该使用三元表达式,更快 return True if len(strString)>6 else False def isLen1(strString): #这里注意false和true的位置 return [False,True][len(strString)>6] import timeit print(timeit.timeit('isLen1("5fsdfsdfsaf")',setup="from __main__ import isLen1")) print(timeit.timeit('isLen("5fsdfsdfsaf")',setup="from __main__ import isLen")) contextlib @contextlib.contextmanager使生成器函数变成一个上下文管理器 types(包含了标准解释器定义的所有类型的类型对象,可以将生成器函数修饰为异步模式) import types types.coroutine #相当于实现了__await__ html(实现对html的转义) import html html.escape("<h1>I'm Jim</h1>") # output:'&lt;h1&gt;I&#x27;m Jim&lt;/h1&gt;' html.unescape('&lt;h1&gt;I&#x27;m Jim&lt;/h1&gt;') # <h1>I'm Jim</h1> mock(解决测试依赖) concurrent(创建进程池河线程池) ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' from concurrent.futures import ThreadPoolExecutor pool = ThreadPoolExecutor() task = pool.submit(函数名,(参数)) #此方法不会阻塞,会立即返回 task.done()#查看任务执行是否完成 task.result()#阻塞的方法,查看任务返回值 task.cancel()#取消未执行的任务,返回True或False,取消成功返回True task.add_done_callback()#回调函数 task.running()#是否正在执行 task就是一个Future对象 for data in pool.map(函数,参数列表):#返回已经完成的任务结果列表,根据参数顺序执行 print(返回任务完成得执行结果data) from concurrent.futures import as_completed as_completed(任务列表)#返回已经完成的任务列表,完成一个执行一个 wait(任务列表,return_when=条件)#根据条件进行阻塞主线程,有四个条件 selector(封装select,用户多路复用io编程) asyncio ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' future=asyncio.ensure_future(协程) 等于后面的方式 future=loop.create_task(协程) future.add_done_callback()添加一个完成后的回调函数 loop.run_until_complete(future) future.result()查看写成返回结果 asyncio.wait()接受一个可迭代的协程对象 asynicio.gather(*可迭代对象,*可迭代对象) 两者结果相同,但gather可以批量取消,gather对象.cancel() 一个线程中只有一个loop 在loop.stop时一定要loop.run_forever()否则会报错 loop.run_forever()可以执行非协程 最后执行finally模块中 loop.close() asyncio.Task.all_tasks()拿到所有任务 然后依次迭代并使用任务.cancel()取消 偏函数partial(函数,参数)把函数包装成另一个函数名 其参数必须放在定义函数的前面 loop.call_soon(函数,参数) call_soon_threadsafe()线程安全 loop.call_later(时间,函数,参数) 在同一代码块中call_soon优先执行,然后多个later根据时间的升序进行执行 如果非要运行有阻塞的代码 使用loop.run_in_executor(executor,函数,参数)包装成一个多线程,然后放入到一个task列表中,通过wait(task列表)来运行 通过asyncio实现http reader,writer=await asyncio.open_connection(host,port) writer.writer()发送请求 async for data in reader: data=data.decode("utf-8") list.append(data) 然后list中存储的就是html as_completed(tasks)完成一个返回一个,返回的是一个可迭代对象 协程锁 async with Lock(): Python进阶 进程间通信: Manager(内置了好多数据结构,可以实现多进程间内存共享) from multiprocessing import Manager,Process def add_data(p_dict, key, value): p_dict[key] = value if __name__ == "__main__": progress_dict = Manager().dict() from queue import PriorityQueue first_progress = Process(target=add_data, args=(progress_dict, "bobby1", 22)) second_progress = Process(target=add_data, args=(progress_dict, "bobby2", 23)) first_progress.start() second_progress.start() first_progress.join() second_progress.join() print(progress_dict) Pipe(适用于两个进程) ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' from multiprocessing import Pipe,Process #pipe的性能高于queue def producer(pipe): pipe.send("bobby") def consumer(pipe): print(pipe.recv()) if __name__ == "__main__": recevie_pipe, send_pipe = Pipe() #pipe只能适用于两个进程 my_producer= Process(target=producer, args=(send_pipe, )) my_consumer = Process(target=consumer, args=(recevie_pipe,)) my_producer.start() my_consumer.start() my_producer.join() my_consumer.join() Queue(不能用于进程池,进程池间通信需要使用Manager().Queue()) from multiprocessing import Queue,Process def producer(queue): queue.put("a") time.sleep(2) def consumer(queue): time.sleep(2) data = queue.get() print(data) if __name__ == "__main__": queue = Queue(10) my_producer = Process(target=producer, args=(queue,)) my_consumer = Process(target=consumer, args=(queue,)) my_producer.start() my_consumer.start() my_producer.join() my_consumer.join() 进程池 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' def producer(queue): queue.put("a") time.sleep(2) def consumer(queue): time.sleep(2) data = queue.get() print(data) if __name__ == "__main__": queue = Manager().Queue(10) pool = Pool(2) pool.apply_async(producer, args=(queue,)) pool.apply_async(consumer, args=(queue,)) pool.close() pool.join() sys模块几个常用方法 argv 命令行参数list,第一个是程序本身的路径 path 返回模块的搜索路径 modules.keys() 返回已经导入的所有模块的列表 exit(0) 退出程序 a in s or b in s or c in s简写 采用any方式:all() 对于任何可迭代对象为空都会返回True # 方法一 True in [i in s for i in [a,b,c]] # 方法二 any(i in s for i in [a,b,c]) # 方法三 list(filter(lambda x:x in s,[a,b,c])) set集合运用 {1,2}.issubset({1,2,3})#判断是否是其子集 {1,2,3}.issuperset({1,2}) {}.isdisjoint({})#判断两个set交集是否为空,是空集则为True 代码中中文匹配 [u4E00-u9FA5]匹配中文文字区间[一到龥] 查看系统默认编码格式 import sys sys.getdefaultencoding() # setdefaultencodeing()设置系统编码方式 getattr VS getattribute class A(dict): def __getattr__(self,value):#当访问属性不存在的时候返回 return 2 def __getattribute__(self,item):#屏蔽所有的元素访问 return item 类变量是不会存入实例__dict__中的,只会存在于类的__dict__中 globals/locals(可以变相操作代码) globals中保存了当前模块中所有的变量属性与值 locals中保存了当前环境中的所有变量属性与值 python变量名的解析机制(LEGB) 本地作用域(Local) 当前作用域被嵌入的本地作用域(Enclosing locals) 全局/模块作用域(Global) 内置作用域(Built-in) 实现从1-100每三个为一组分组 print([[x for x in range(1,101)][i:i+3] for i in range(0,100,3)]) 什么是元类? 即创建类的类,创建类的时候只需要将metaclass=元类,元类需要继承type而不是object,因为type就是元类 type.__bases__ #(<class 'object'>,) object.__bases__ #() type(object) #<class 'type'> ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' class Yuan(type): def __new__(cls,name,base,attr,*args,**kwargs): return type(name,base,attr,*args,**kwargs) class MyClass(metaclass=Yuan): pass 什么是鸭子类型(即:多态)? Python在使用传入参数的过程中不会默认判断参数类型,只要参数具备执行条件就可以执行 深拷贝和浅拷贝 深拷贝拷贝内容,浅拷贝拷贝地址(增加引用计数) copy模块实现神拷贝 单元测试 一般测试类继承模块unittest下的TestCase pytest模块快捷测试(方法以test_开头/测试文件以test_开头/测试类以Test开头,并且不能带有 init 方法) coverage统计测试覆盖率 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' class MyTest(unittest.TestCase): def tearDown(self):# 每个测试用例执行前执行 print('本方法开始测试了') def setUp(self):# 每个测试用例执行之前做操作 print('本方法测试结束') @classmethod def tearDownClass(self):# 必须使用 @ classmethod装饰器, 所有test运行完后运行一次 print('开始测试') @classmethod def setUpClass(self):# 必须使用@classmethod 装饰器,所有test运行前运行一次 print('结束测试') def test_a_run(self): self.assertEqual(1, 1) # 测试用例 gil会根据执行的字节码行数以及时间片释放gil,gil在遇到io的操作时候主动释放 什么是monkey patch? 猴子补丁,在运行的时候替换掉会阻塞的语法修改为非阻塞的方法 什么是自省(Introspection)? 运行时判断一个对象的类型的能力,id,type,isinstance python是值传递还是引用传递? 都不是,python是共享传参,默认参数在执行时只会执行一次 try-except-else-finally中else和finally的区别 else在不发生异常的时候执行,finally无论是否发生异常都会执行 except一次可以捕获多个异常,但一般为了对不同异常进行不同处理,我们分次捕获处理 GIL全局解释器锁 同一时间只能有一个线程执行,CPython(IPython)的特点,其他解释器不存在 cpu密集型:多进程+进程池 io密集型:多线程/协程 什么是Cython 将python解释成C代码工具 生成器和迭代器 可迭代对象只需要实现__iter__方法 实现__next__和__iter__方法的对象就是迭代器 使用生成器表达式或者yield的生成器函数(生成器是一种特殊的迭代器) 什么是协程 yield async-awiat 比线程更轻量的多任务方式 实现方式 dict底层结构 为了支持快速查找使用了哈希表作为底层结构 哈希表平均查找时间复杂度为o(1) CPython解释器使用二次探查解决哈希冲突问题 Hash扩容和Hash冲突解决方案 链接法 二次探查(开放寻址法):python使用 循环复制到新空间实现扩容 冲突解决: for gevent import monkey monkey.patch_all() #将代码中所有的阻塞方法都进行修改,可以指定具体要修改的方法 判断是否为生成器或者协程 co_flags = func.__code__.co_flags # 检查是否是协程 if co_flags & 0x180: return func # 检查是否是生成器 if co_flags & 0x20: return func 斐波那契解决的问题及变形 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' #一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。 #请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? #方式一: fib = lambda n: n if n <= 2 else fib(n - 1) + fib(n - 2) #方式二: def fib(n): a, b = 0, 1 for _ in range(n): a, b = b, a + b return b #一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。 fib = lambda n: n if n < 2 else 2 * fib(n - 1) 获取电脑设置的环境变量 import os os.getenv(env_name,None)#获取环境变量如果不存在为None 垃圾回收机制 引用计数 标记清除 分代回收 #查看分代回收触发 import gc gc.get_threshold() #output:(700, 10, 10) True和False在代码中完全等价于1和0,可以直接和数字进行计算,inf表示无穷大 C10M/C10K C10M:8核心cpu,64G内存,在10gbps的网络上保持1000万并发连接 C10K:1GHz CPU,2G内存,1gbps网络环境下保持1万个客户端提供FTP服务 yield from与yield的区别: yield from跟的是一个可迭代对象,而yield后面没有限制 GeneratorExit生成器停止时触发 单下划线的几种使用 在定义变量时,表示为私有变量 在解包时,表示舍弃无用的数据 在交互模式中表示上一次代码执行结果 可以做数字的拼接(111_222_333) 使用break就不会执行else 10进制转2进制 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' def conver_bin(num): if num == 0: return num re = [] while num: num, rem = divmod(num,2) re.append(str(rem)) return "".join(reversed(re)) conver_bin(10) list1 = ['A', 'B', 'C', 'D'] 如何才能得到以list中元素命名的新列表 A=[],B=[],C=[],D=[]呢 list1 = ['A', 'B', 'C', 'D'] # 方法一 for i in list1: globals()[i] = [] # 可以用于实现python版反射 # 方法二 for i in list1: exec(f'{i} = []') # exec执行字符串语句 memoryview与bytearray$color{#000}(不常用,只是看到了记载一下)$ ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' # bytearray是可变的,bytes是不可变的,memoryview不会产生新切片和对象 a = 'aaaaaa' ma = memoryview(a) ma.readonly # 只读的memoryview mb = ma[:2] # 不会产生新的字符串 a = bytearray('aaaaaa') ma = memoryview(a) ma.readonly # 可写的memoryview mb = ma[:2] # 不会会产生新的bytearray mb[:2] = 'bb' # 对mb的改动就是对ma的改动 Ellipsis类型 # 代码中出现...省略号的现象就是一个Ellipsis对象 L = [1,2,3] L.append(L) print(L) # output:[1,2,3,[…]] lazy惰性计算 class lazy(object): def __init__(self, func): self.func = func def __get__(self, instance, cls): val = self.func(instance) #其相当于执行的area(c),c为下面的Circle对象 setattr(instance, self.func.__name__, val) return val` class Circle(object): def __init__(self, radius): self.radius = radius @lazy def area(self): print('evalute') return 3.14 * self.radius ** 2 遍历文件,传入一个文件夹,将里面所有文件的路径打印出来(递归) ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' all_files = [] def getAllFiles(directory_path): import os for sChild in os.listdir(directory_path): sChildPath = os.path.join(directory_path,sChild) if os.path.isdir(sChildPath): getAllFiles(sChildPath) else: all_files.append(sChildPath) return all_files 文件存储时,文件名的处理 #secure_filename将字符串转化为安全的文件名 from werkzeug import secure_filename secure_filename("My cool movie.mov") # output:My_cool_movie.mov secure_filename("../../../etc/passwd") # output:etc_passwd secure_filename(u'i contain cool \xfcml\xe4uts.txt') # output:i_contain_cool_umlauts.txt 日期格式化 from datetime import datetime datetime.now().strftime("%Y-%m-%d") import time #这里只有localtime可以被格式化,time是不能格式化的 time.strftime("%Y-%m-%d",time.localtime()) tuple使用+=奇怪的问题 # 会报错,但是tuple的值会改变,因为t[1]id没有发生变化 t=(1,[2,3]) t[1]+=[4,5] # t[1]使用append\extend方法并不会报错,并可以成功执行 __missing__你应该知道 class Mydict(dict): def __missing__(self,key): # 当Mydict使用切片访问属性不存在的时候返回的值 return key +与+= # +不能用来连接列表和元祖,而+=可以(通过iadd实现,内部实现方式为extends(),所以可以增加元组),+会创建新对象 #不可变对象没有__iadd__方法,所以直接使用的是__add__方法,因此元祖可以使用+=进行元祖之间的相加 如何将一个可迭代对象的每个元素变成一个字典的所有键? dict.fromkeys(['jim','han'],21) # output:{'jim': 21, 'han': 21} wireshark抓包软件 网络知识 什么是HTTPS? 安全的HTTP协议,https需要cs证书,数据加密,端口为443,安全,同一网站https seo排名会更高 常见响应状态码 204 No Content //请求成功处理,没有实体的主体返回,一般用来表示删除成功 206 Partial Content //Get范围请求已成功处理 303 See Other //临时重定向,期望使用get定向获取 304 Not Modified //求情缓存资源 307 Temporary Redirect //临时重定向,Post不会变成Get 401 Unauthorized //认证失败 403 Forbidden //资源请求被拒绝 400 //请求参数错误 201 //添加或更改成功 503 //服务器维护或者超负载 http请求方法的幂等性及安全性 WSGI # environ:一个包含所有HTTP请求信息的dict对象 # start_response:一个发送HTTP响应的函数 def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return '<h1>Hello, web!</h1>' RPC CDN SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。 SSH(安全外壳协议) 为 Secure Shell 的缩写,由 IETF 的网络小组(Network Working Group)所制定;SSH 为建立在应用层基础上的安全协议。SSH 是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议。利用 SSH 协议可以有效防止远程管理过程中的信息泄露问题。SSH最初是UNIX系统上的一个程序,后来又迅速扩展到其他操作平台。SSH在正确使用时可弥补网络中的漏洞。SSH客户端适用于多种平台。几乎所有UNIX平台—包括HP-UX、Linux、AIX、Solaris、Digital UNIX、Irix,以及其他平台,都可运行SSH。 TCP/IP TCP:面向连接/可靠/基于字节流 UDP:无连接/不可靠/面向报文 三次握手四次挥手 三次握手(SYN/SYN+ACK/ACK) 四次挥手(FIN/ACK/FIN/ACK) 为什么连接的时候是三次握手,关闭的时候却是四次握手? 因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。 为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态? 虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。 XSS/CSRF HttpOnly禁止js脚本访问和操作Cookie,可以有效防止XSS Mysql 索引改进过程 线性结构->二分查找->hash->二叉查找树->平衡二叉树->多路查找树->多路平衡查找树(B-Tree) Mysql面试总结基础篇 https://segmentfault.com/a/1190000018371218 Mysql面试总结进阶篇 https://segmentfault.com/a/1190000018380324 深入浅出Mysql http://ningning.today/2017/02/13/database/深入浅出mysql/ 清空整个表时,InnoDB是一行一行的删除,而MyISAM则会从新删除建表 text/blob数据类型不能有默认值,查询时不存在大小写转换 什么时候索引失效 以%开头的like模糊查询 出现隐士类型转换 没有满足最左前缀原则 对于多列索引,不是使用的第一部分,则不会使用索引 失效场景: 应尽量避免在 where 子句中使用 != 或 <> 操作符,否则引擎将放弃使用索引而进行全表扫描 尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,即使其中有条件带索引也不会使用,这也是为什么尽量少用 or 的原因 如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不会使用索引 应尽量避免在 where 子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描 例如: select id from t where substring(name,1,3) = 'abc' – name; 以abc开头的,应改成: select id from t where name like 'abc%' 例如: select id from t where datediff(day, createdate, '2005-11-30') = 0 – '2005-11-30'; 应改为: 不要在 where 子句中的 “=” 左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引 应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描 如: select id from t where num/2 = 100 应改为: select id from t where num = 100*2; 不适合键值较少的列(重复数据较多的列)比如:set enum列就不适合(枚举类型(enum)可以添加null,并且默认的值会自动过滤空格集合(set)和枚举类似,但只可以添加64个值) 如果MySQL估计使用全表扫描要比使用索引快,则不使用索引 什么是聚集索引 B+Tree叶子节点保存的是数据还是指针 MyISAM索引和数据分离,使用非聚集 InnoDB数据文件就是索引文件,主键索引就是聚集索引 Redis命令总结 为什么这么快? 基于内存,由C语言编写 使用多路I/O复用模型,非阻塞IO 使用单线程减少线程间切换 因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了(毕竟采用多线程会有很多麻烦!)。 数据结构简单 自己构建了VM机制,减少调用系统函数的时间 优势 性能高 – Redis能读的速度是110000次/s,写的速度是81000次/s 丰富的数据类型 原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行 丰富的特性 – Redis还支持 publish/subscribe(发布/订阅), 通知, key 过期等等特性 什么是redis事务? 将多个请求打包,一次性、按序执行多个命令的机制 通过multi,exec,watch等命令实现事务功能 Python redis-py pipeline=conn.pipeline(transaction=True) 持久化方式 RDB(快照) save(同步,可以保证数据一致性) bgsave(异步,shutdown时,无AOF则默认使用) AOF(追加日志) 怎么实现队列 push rpop 常用的数据类型(Bitmaps,Hyperloglogs,范围查询等不常用) String(字符串):计数器 整数或sds(Simple Dynamic String) List(列表):用户的关注,粉丝列表 ziplist(连续内存块,每个entry节点头部保存前后节点长度信息实现双向链表功能)或double linked list Hash(哈希): Set(集合):用户的关注者 intset或hashtable Zset(有序集合):实时信息排行榜 skiplist(跳跃表) 与Memcached区别 Memcached只能存储字符串键 Memcached用户只能通过APPEND的方式将数据添加到已有的字符串的末尾,并将这个字符串当做列表来使用。但是在删除这些元素的时候,Memcached采用的是通过黑名单的方式来隐藏列表里的元素,从而避免了对元素的读取、更新、删除等操作 Redis和Memcached都是将数据存放在内存中,都是内存数据库。不过Memcached还可用于缓存其他东西,例如图片、视频等等 虚拟内存–Redis当物理内存用完时,可以将一些很久没用到的Value 交换到磁盘 存储数据安全–Memcached挂掉后,数据没了;Redis可以定期保存到磁盘(持久化) 应用场景不一样:Redis出来作为NoSQL数据库使用外,还能用做消息队列、数据堆栈和数据缓存等;Memcached适合于缓存SQL语句、数据集、用户临时性数据、延迟查询数据和Session等 Redis实现分布式锁 使用setnx实现加锁,可以同时通过expire添加超时时间 锁的value值可以是一个随机的uuid或者特定的命名 释放锁的时候,通过uuid判断是否是该锁,是则执行delete释放锁 常见问题 缓存雪崩 短时间内缓存数据过期,大量请求访问数据库 缓存穿透 请求访问数据时,查询缓存中不存在,数据库中也不存在 缓存预热 初始化项目,将部分常用数据加入缓存 缓存更新 数据过期,进行更新缓存数据 缓存降级 当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级 一致性Hash算法 使用集群的时候保证数据的一致性 基于redis实现一个分布式锁,要求一个超时的参数 setnx 虚拟内存 内存抖动 Linux Unix五种i/o模型 阻塞io 非阻塞io 多路复用io(Python下使用selectot实现io多路复用) select 并发不高,连接数很活跃的情况下 poll 比select提高的并不多 epoll 适用于连接数量较多,但活动链接数少的情况 信号驱动io 异步io(Gevent/Asyncio实现异步) 比man更好使用的命令手册 tldr:一个有命令示例的手册 kill -9和-15的区别 -15:程序立刻停止/当程序释放相应资源后再停止/程序可能仍然继续运行 -9:由于-15的不确定性,所以直接使用-9立即杀死进程 分页机制(逻辑地址和物理地址分离的内存分配管理方案): 操作系统为了高效管理内存,减少碎片 程序的逻辑地址划分为固定大小的页 物理地址划分为同样大小的帧 通过页表对应逻辑地址和物理地址 分段机制 为了满足代码的一些逻辑需求 数据共享/数据保护/动态链接 每个段内部连续内存分配,段和段之间是离散分配的 查看cpu内存使用情况? top free 查看可用内存,排查内存泄漏问题 设计模式 单例模式 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' # 方式一 def Single(cls,*args,**kwargs): instances = {} def get_instance (*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return get_instance @Single class B: pass # 方式二 class Single: def __init__(self): print("单例模式实现方式二。。。") single = Single() del Single # 每次调用single就可以了 # 方式三(最常用的方式) class Single: def __new__(cls,*args,**kwargs): if not hasattr(cls,'_instance'): cls._instance = super().__new__(cls,*args,**kwargs) return cls._instance 工厂模式 class Dog: def __init__(self): print("Wang Wang Wang") class Cat: def __init__(self): print("Miao Miao Miao") def fac(animal): if animal.lower() == "dog": return Dog() if animal.lower() == "cat": return Cat() print("对不起,必须是:dog,cat") 构造模式 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' class Computer: def __init__(self,serial_number): self.serial_number = serial_number self.memory = None self.hadd = None self.gpu = None def __str__(self): info = (f'Memory:{self.memoryGB}', 'Hard Disk:{self.hadd}GB', 'Graphics Card:{self.gpu}') return ''.join(info) class ComputerBuilder: def __init__(self): self.computer = Computer('Jim1996') def configure_memory(self,amount): self.computer.memory = amount return self #为了方便链式调用 def configure_hdd(self,amount): pass def configure_gpu(self,gpu_model): pass class HardwareEngineer: def __init__(self): self.builder = None def construct_computer(self,memory,hdd,gpu) self.builder = ComputerBuilder() self.builder.configure_memory(memory).configure_hdd(hdd).configure_gpu(gpu) @property def computer(self): return self.builder.computer 数据结构和算法内置数据结构和算法 python实现各种数据结构 快速排序 def quick_sort(_list): if len(_list) < 2: return _list pivot_index = 0 pivot = _list(pivot_index) left_list = [i for i in _list[:pivot_index] if i < pivot] right_list = [i for i in _list[pivot_index:] if i > pivot] return quick_sort(left) + [pivot] + quick_sort(right) 选择排序 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' def select_sort(seq): n = len(seq) for i in range(n-1) min_idx = i for j in range(i+1,n): if seq[j] < seq[min_inx]: min_idx = j if min_idx != i: seq[i], seq[min_idx] = seq[min_idx],seq[i] 插入排序 def insertion_sort(_list): n = len(_list) for i in range(1,n): value = _list[i] pos = i while pos > 0 and value < _list[pos - 1] _list[pos] = _list[pos - 1] pos -= 1 _list[pos] = value print(sql) 归并排序 def merge_sorted_list(_list1,_list2): #合并有序列表 len_a, len_b = len(_list1),len(_list2) a = b = 0 sort = [] while len_a > a and len_b > b: if _list1[a] > _list2[b]: sort.append(_list2[b]) b += 1 else: sort.append(_list1[a]) a += 1 if len_a > a: sort.append(_list1[a:]) if len_b > b: sort.append(_list2[b:]) return sort def merge_sort(_list): if len(list1)<2: return list1 else: mid = int(len(list1)/2) left = mergesort(list1[:mid]) right = mergesort(list1[mid:]) return merge_sorted_list(left,right) 堆排序heapq模块 from heapq import nsmallest def heap_sort(_list): return nsmallest(len(_list),_list) 栈 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' from collections import deque class Stack: def __init__(self): self.s = deque() def peek(self): p = self.pop() self.push(p) return p def push(self, el): self.s.append(el) def pop(self): return self.pop() 队列 from collections import deque class Queue: def __init__(self): self.s = deque() def push(self, el): self.s.append(el) def pop(self): return self.popleft() 二分查找 def binary_search(_list,num): mid = len(_list)//2 if len(_list) < 1: return Flase if num > _list[mid]: BinarySearch(_list[mid:],num) elif num < _list[mid]: BinarySearch(_list[:mid],num) else: return _list.index(num) 面试加强题: 关于数据库优化及设计 如何使用两个栈实现一个队列 反转链表 合并两个有序链表 删除链表节点 反转二叉树 设计短网址服务?62进制实现 设计一个秒杀系统(feed流)? 为什么mysql数据库的主键使用自增的整数比较好?使用uuid可以吗?为什么? 如果InnoDB表的数据写入顺序能和B+树索引的叶子节点顺序一致的话,这时候存取效率是最高的。为了存储和查询性能应该使用自增长id做主键。 对于InnoDB的主索引,数据会按照主键进行排序,由于UUID的无序性,InnoDB会产生巨大的IO压力,此时不适合使用UUID做物理主键,可以把它作为逻辑主键,物理主键依然使用自增ID。为了全局的唯一性,应该用uuid做索引关联其他表或做外键 如果是分布式系统下我们怎么生成数据库的自增id呢? 使用redis 基于redis实现一个分布式锁,要求一个超时的参数 setnx setnx + expire 如果redis单个节点宕机了,如何处理?还有其他业界的方案实现分布式锁码? 使用hash一致算法 缓存算法 LRU(least-recently-used):替换最近最少使用的对象 LFU(Least frequently used):最不经常使用,如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小 服务端性能优化方向 使用数据结构和算法 数据库 索引优化 慢查询消除 slow_query_log_file开启并且查询慢查询日志 通过explain排查索引问题 调整数据修改索引 批量操作,从而减少io操作 使用NoSQL:比如Redis 网络io 批量操作 pipeline 缓存 Redis 异步 Asyncio实现异步操作 使用Celery减少io阻塞 并发 多线程 Gevent
多进程锁 lock = multiprocessing.Lock() 创建一个锁 lock.acquire() 获取锁 lock.release() 释放锁 with lock: 自动获取、释放锁 类似于 with open() as f: 特点: 谁先抢到锁谁先执行,等到该进程执行完成后,其它进程再抢锁执行 当程序不加锁时: import multiprocessing import time def add(num, value, lock): print('add{0}:num={1}'.format(value, num)) for i in xrange(0, 2): num += value print('add{0}:num={1}'.format(value, num)) time.sleep(1) if __name__ == '__main__': lock = multiprocessing.Lock() num = 0 p1 = multiprocessing.Process(target=add, args=(num, 1, lock)) p2 = multiprocessing.Process(target=add, args=(num, 3, lock)) p3 = multiprocessing.Process(target=add, args=(num, 5, lock)) p1.start() p2.start() p3.start() print('main end...') # 执行结果: add1:num=0 add1:num=1 main end... add3:num=0 add3:num=3 add5:num=0 add5:num=5 add3:num=6 add1:num=2 add5:num=10 运得没有顺序,三个进程交替运行 当程序加锁时 import multiprocessing import time def add(num, value, lock): try: lock.acquire() print('add{0}:num={1}'.format(value, num)) for i in xrange(0, 2): num += value print('add{0}:num={1}'.format(value, num)) time.sleep(1) except Exception as err: raise err finally: lock.release() if __name__ == '__main__': lock = multiprocessing.Lock() num = 0 p1 = multiprocessing.Process(target=add, args=(num, 1, lock)) p2 = multiprocessing.Process(target=add, args=(num, 3, lock)) p3 = multiprocessing.Process(target=add, args=(num, 5, lock)) p1.start() p2.start() p3.start() print('main end...') # 执行结果: add3:num=0 add3:num=3 main end... add3:num=6 add1:num=0 add1:num=1 add1:num=2 add5:num=0 add5:num=5 add5:num=10 只有当其中一个进程执行完成后,其它的进程才会去执行,且谁先抢到锁谁先执行 共享内存 agre = multiproessing.Value(type, value) 创建一个共享内存的变量agre def Value(typecode_or_type, *args, **kwds): ''' Returns a synchronized shared object ''' from multiprocessing.sharedctypes import Value return Value(typecode_or_type, *args, **kwds) type 声明共享变量agre的类型 value 共享变量agre的值 agre.value 获取共享变量agre的值 arr = muliproessing.Array(type, values) 创建一个共享内存的数组arr def Array(typecode_or_type, size_or_initializer, **kwds): ''' Returns a synchronized shared array ''' from multiprocessing.sharedctypes import Array return Array(typecode_or_type, size_or_initializer, **kwds) 例子: ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴, 互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' import multiprocessing import time def add(num, value, lock): try: lock.acquire() print('add{0}:num={1}'.format(value, num.value)) for i in xrange(0, 2): num.value += value print('add{0}:num={1}'.format(value, num.value)) print('-------add{} add end-------'.format(value)) time.sleep(1) except Exception as err: raise err finally: lock.release() def change(arr): for i in range(len(arr)): arr[i] = 1 if __name__ == '__main__': lock = multiprocessing.Lock() num = multiprocessing.Value('i', 0) arr = multiprocessing.Array('i', range(10)) print(arr[:]) p1 = multiprocessing.Process(target=add, args=(num, 1, lock)) p3 = multiprocessing.Process(target=add, args=(num, 3, lock)) p = multiprocessing.Process(target=change, args=(arr,)) p1.start() p3.start() p.start() p.join() print(arr[:]) print('main end...') 执行结果: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] add3:num=0 add3:num=3 -------add3 add end------- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] main end... add3:num=6 -------add3 add end------- add1:num=6 add1:num=7 -------add1 add end------- add1:num=8 -------add1 add end------- 先执行进程p3并加锁,p3执行过程中进程p执行,因为p没有调用锁且使用了join()方法,阻塞了其它进程,只有当p执行完成后 p3才会继续执行,p3执行完成后,p1抢到锁并执行 p1、p3 都对共享内存num 进行累加操作,所以num的值一直在增加p 对 arr 共享数组中的每个值进行了重新赋值的操作,所以当P进程执行完成后,arr数组中的值均发生了变化 由上例可以看出: 1、进程锁只对调用它的进程起锁的作用,未调用该锁的进程不受影响2、在未调用进程锁的进程中使用 join() 方法会阻塞已调用进程锁的进程
PEP 8 规范 PEP 是 Python Enhancement Proposal 的缩写,翻译过来叫“Python 增强规范”。 缩进规范 PEP 8 规范告诉我们,请选择四个空格的缩进,不要使用 Tab,更不要 Tab 和空格混着用。 第二个要注意的是,每行最大长度请限制在 79 个字符。 空行规范 PEP 8 规定,全局的类和函数的上方需要空两个空行,而类的函数之间需要空一个空行。 空格规范 函数的参数列表中,调用函数的参数列表中会出现逗号,请注意逗号后要跟一个空格,这是英语的使用习惯,也能让每个参数独立阅读,更清晰。 冒号后面也要跟一个空格。 在#后、注释前加一个空格。 操作符,例如+,-,*,/,&,|,=,==,!=,请在两边都保留空格。不过与此对应,括号内的两端并不需要空格。 换行规范 控制每行的最大长度不超过 79 个字符,但是有时候,函数调用逻辑过长而不得不超过这个数字时按以下规范: def solve1(this_is_the_first_parameter, this_is_the_second_parameter, this_is_the_third_parameter, this_is_the_forth_parameter, this_is_the_fifth_parameter, this_is_the_sixth_parameter): return (this_is_the_first_parameter + this_is_the_second_parameter + this_is_the_third_parameter + this_is_the_forth_parameter + this_is_the_fifth_parameter + this_is_the_sixth_parameter) def solve2(this_is_the_first_parameter, this_is_the_second_parameter, this_is_the_third_parameter, this_is_the_forth_parameter, this_is_the_fifth_parameter, this_is_the_sixth_parameter): return this_is_the_first_parameter + this_is_the_second_parameter + this_is_the_third_parameter + \ this_is_the_forth_parameter + this_is_the_fifth_parameter + this_is_the_sixth_parameter (top_secret_func(param1=12345678, param2=12345678, param3=12345678, param4=12345678, param5=12345678).check() .launch_nuclear_missile().wait()) top_secret_func(param1=12345678, param2=12345678, param3=12345678, param4=12345678, param5=12345678).check() \ .launch_nuclear_missile().wait() 1.通过括号来将过长的运算进行封装. 2.通过换行符来实现. 文档规范 import 尽量放在开头. 不要使用 import 一次导入多个模块. from module import func 这样的语句,请确保 func 在本文件中不会出现命名冲突。或者通过 from module import func as new_func 来进行重命名,从而避免冲突。 注释规范 行注释并不是很推荐的方式。 文档描述 docstring 的写法,它是用三个双引号开始、三个双引号结尾。我们首先用一句话简单说明这个函数做什么,然后跟一段话来详细解释;再往后是参数列表、参数格式、返回值格式。 class SpatialDropout2D(Dropout): """Spatial 2D version of Dropout. This version performs the same function as Dropout, however it drops entire 2D feature maps instead of individual elements. If adjacent pixels within feature maps are strongly correlated (as is normally the case in early convolution layers) then regular dropout will not regularize the activations and will otherwise just result in an effective learning rate decrease. In this case, SpatialDropout2D will help promote independence between feature maps and should be used instead. Arguments: rate: float between 0 and 1. Fraction of the input units to drop. data_format: 'channels_first' or 'channels_last'. In 'channels_first' mode, the channels dimension (the depth) is at index 1, in 'channels_last' mode is it at index 3. It defaults to the `image_data_format` value found in your Keras config file at `~/.keras/keras.json`. If you never set it, then it will be "channels_last". Input shape: 4D tensor with shape: `(samples, channels, rows, cols)` if data_format='channels_first' or 4D tensor with shape: `(samples, rows, cols, channels)` if data_format='channels_last'. Output shape: Same as input References: - [Efficient Object Localization Using Convolutional Networks](https://arxiv.org/abs/1411.4280) """ def __init__(self, rate, data_format=None, **kwargs): super(SpatialDropout2D, self).__init__(rate, **kwargs) if data_format is None: data_format = K.image_data_format() if data_format not in {'channels_last', 'channels_first'}: raise ValueError('data_format must be in ' '{"channels_last", "channels_first"}') self.data_format = data_format self.input_spec = InputSpec(ndim=4) 命名规范 变量使用小写,通过下划线串联起来,例如:data_format、input_spec、image_data_set。唯一可以使用单字符的地方是迭代,比如 for i in range(n) 这种,为了精简可以使用。如果是类的私有变量,请记得前面增加两个下划线。 常量,最好的做法是全部大写,并通过下划线连接,例如:WAIT_TIME、SERVER_ADDRESS、PORT_NUMBER。 函数名,同样也请使用小写的方式,通过下划线连接起来,例如:launch_nuclear_missile()、check_input_validation()。 类名,则应该首字母大写,然后合并起来,例如:class SpatialDropout2D()、class FeatureSet()。 代码分解技巧 不写重复代码。 如: if i_am_rich: money = 100 send(money) else: money = 10 send(money) 都有send函数,可改为: if i_am_rich: money = 100 else: money = 10 send(money) 代码嵌套过深: #Python学习交流QQ群:857662006 def send(money): if is_server_dead: LOG('server dead') return else: if is_server_timed_out: LOG('server timed out') return else: result = get_result_from_server() if result == MONEY_IS_NOT_ENOUGH: LOG('you do not have enough money') return else: if result == TRANSACTION_SUCCEED: LOG('OK') return else: LOG('something wrong') return 可改为: def send(money): if is_server_dead: LOG('server dead') return if is_server_timed_out: LOG('server timed out') return result = get_result_from_server() if result == MONET_IS_NOT_ENOUGH: LOG('you do not have enough money') return if result == TRANSACTION_SUCCEED: LOG('OK') return LOG('something wrong') 以一个简单的二分搜索来举例说明。给定一个非递减整数数组,和一个 target,要求找到数组中最小的一个数 x,可以满足 x*x > target。一旦不存在,则返回 -1。 代码实现如果如下所示,那么可以再以一个函数只干一件事情的原则再优化下。 def solve(arr, target): l, r = 0, len(arr) - 1 ret = -1 while l <= r: m = (l + r) // 2 if arr[m] * arr[m] > target: ret = m r = m - 1 else: l = m + 1 if ret == -1: return -1 else: return arr[ret] print(solve([1, 2, 3, 4, 5, 6], 8)) print(solve([1, 2, 3, 4, 5, 6], 9)) print(solve([1, 2, 3, 4, 5, 6], 0)) print(solve([1, 2, 3, 4, 5, 6], 40)) 优化如下: def comp(x, target): return x * x > target def binary_search(arr, target): l, r = 0, len(arr) - 1 ret = -1 while l <= r: m = (l + r) // 2 if comp(arr[m], target): ret = m r = m - 1 else: l = m + 1 return ret def solve(arr, target): id = binary_search(arr, target) if id != -1: return arr[id] return -1 print(solve([1, 2, 3, 4, 5, 6], 8)) print(solve([1, 2, 3, 4, 5, 6], 9)) print(solve([1, 2, 3, 4, 5, 6], 0)) print(solve([1, 2, 3, 4, 5, 6], 40)) 类中属性很多时可以抽出相同特性的单独作为类,如: class Person: def __init__(self, name, sex, age, job_title, job_description, company_name): self.name = name self.sex = sex self.age = age self.job_title = job_title self.job_description = description self.company_name = company_name job_title , job_description , company_name 都与工作有关,表达是同一个意义实体,就可以抽出单独作为类: class Person: def __init__(self, name, sex, age, job_title, job_description, company_name): self.name = name self.sex = sex self.age = age self.job = Job(job_title, job_description, company_name) class Job: def __init__(self, job_title, job_description, company_name): self.job_title = job_title self.job_description = description self.company_name = company_name
I. 跳出单循环不管是什么编程语言,都有可能会有跳出循环的需求,比如枚举时,找到一个满足条件的数就终止。跳出单循环是很简单的,比如 for i in range(10): if i > 5: print i break 然而,我们有时候会需要跳出多重循环,而break只能够跳出一层循环,比如 for i in range(10): for j in range(10): if i+j > 5: print i,j break 这样的代码并非说找到一组i+j > 5就停止,而是连续找到10组,因为break只跳出了for j in range(10)这一重循环。那么,怎么才能跳出多重呢?在此记录备忘一下。 II. 跳出多重循环 事实上,Python的标准语法是不支持跳出多重循环的,所以只能利用一些技巧,大概的思路有:写成函数、利用笛卡尔积、利用调试。 当然最常用的思路是使用变量标记法 def f(): flag = 0 for i in range(10): for j in range(i): if i+j>5: print i,j flag = 1 break if flag == 1: break if __name__ == "__main__": f() 写成函数 在Python中,函数运行到return这一句就会停止,因此可以利用这一特性,将功能写成函数,终止多重循环, 例如 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def work(): for i in range(10): for j in range(10): if i+j > 5: return i,j print work() 利用笛卡尔积 这种方法的思路就是,既然可以跳出单循环,我就将多重循环改写为单循环,这可以利用itertools中的笛卡尔积函数product,例如 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' from itertools import product for i,j in product(range(10), range(10)): if i+j > 5: print i,j break 利用调试模式 笛卡尔积的方式很巧妙,也很简洁,但它只能用于每次循环的集合都是独立的情形,假如每层循环都与前一层紧密相关,就不能用这种技巧了。这时候可以用第一种方法,将它写成函数,另外,还可以利用调试模式。这个利用了调试模式中,只要出现报错就退出的原理,它伪装了一个错误出来。 class Found(Exception): pass try: for i in range(10): for j in range(i): #第二重循环跟第一重有关 if i + j > 5: raise Found except Found: print i, j
学 Python 怎样才最快,当然是实战各种小项目,只有自己去想与写,才记得住规则。本文是 30 个极简任务,初学者可以尝试着自己实现;本文同样也是 30 段代码,Python 开发者也可以看看是不是有没想到的用法。 Python 是机器学习最广泛采用的编程语言,它最重要的优势在于编程的易用性。如果读者对基本的 Python 语法已经有一些了解,那么这篇文章可能会给你一些启发。作者简单概览了 30 段代码,它们都是平常非常实用的技巧,我们只要花几分钟就能从头到尾浏览一遍。 1. 重复元素判定 以下方法可以检查给定列表是不是存在重复元素,它会使用 set() 函数来移除所有重复元素。 def all_unique(lst): return len(lst) == len(set(lst)) x = [1,1,2,2,3,2,3,4,5,6] y = [1,2,3,4,5] all_unique(x) # False all_unique(y) # True 2. 字符元素组成判定 检查两个字符串的组成元素是不是一样的。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' from collections import Counter def anagram(first, second): return Counter(first) == Counter(second) anagram("abcd3", "3acdb") # True 3. 内存占用 下面的代码块可以检查变量 variable 所占用的内存。 import sys variable = 30 print(sys.getsizeof(variable)) # 24 4. 字节占用 下面的代码块可以检查字符串占用的字节数。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def byte_size(string): return(len(string.encode('utf-8'))) byte_size('') # 4 byte_size('Hello World') # 11 5. 打印 N 次字符串 该代码块不需要循环语句就能打印 N 次字符串。 n = 2; s ="Programming"; print(s * n); # ProgrammingProgramming 6. 大写第一个字母 以下代码块会使用 title() 方法,从而大写字符串中每一个单词的首字母。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' s = "programming is awesome" print(s.title()) # Programming Is Awesome 7. 分块 给定具体的大小,定义一个函数以按照这个大小切割列表。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' from math import ceil def chunk(lst, size): return list( map(lambda x: lst[x * size:x * size + size], list(range(0, ceil(len(lst) / size))))) chunk([1,2,3,4,5],2) # [[1,2],[3,4],5] 8. 压缩 这个方法可以将布尔型的值去掉,例如(False,None,0,“”),它使用 filter() 函数。 def compact(lst): return list(filter(bool, lst)) compact([0, 1, False, 2, '', 3, 'a', 's', 34]) # [ 1, 2, 3, 'a', 's', 34 ] 9. 解包 如下代码段可以将打包好的成对列表解开成两组不同的元组。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' array = [['a', 'b'], ['c', 'd'], ['e', 'f']] transposed = zip(*array) print(transposed) # [('a', 'c', 'e'), ('b', 'd', 'f')] 10. 链式对比 我们可以在一行代码中使用不同的运算符对比多个不同的元素。 a = 3 print( 2 < a < 8) # True print(1 == a < 2) # False 11. 逗号连接 下面的代码可以将列表连接成单个字符串,且每一个元素间的分隔方式设置为了逗号。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' hobbies = ["basketball", "football", "swimming"] print("My hobbies are: " + ", ".join(hobbies)) # My hobbies are: basketball, football, swimming 12. 元音统计 以下方法将统计字符串中的元音 (‘a’, ‘e’, ‘i’, ‘o’, ‘u’) 的个数,它是通过正则表达式做的。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' import re def count_vowels(str): return len(len(re.findall(r'[aeiou]', str, re.IGNORECASE))) count_vowels('foobar') # 3 count_vowels('gym') # 0 13. 首字母小写 如下方法将令给定字符串的第一个字符统一为小写。 def decapitalize(string): return str[:1].lower() + str[1:] decapitalize('FooBar') # 'fooBar' decapitalize('FooBar') # 'fooBar' 14. 展开列表 该方法将通过递归的方式将列表的嵌套展开为单个列表。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def spread(arg): ret = [] for i in arg: if isinstance(i, list): ret.extend(i) else: ret.append(i) return ret def deep_flatten(lst): result = [] result.extend( spread(list(map(lambda x: deep_flatten(x) if type(x) == list else x, lst)))) return result deep_flatten([1, [2], [[3], 4], 5]) # [1,2,3,4,5] 15. 列表的差 该方法将返回第一个列表的元素,其不在第二个列表内。如果同时要反馈第二个列表独有的元素,还需要加一句 set_b.difference(set_a)。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def difference(a, b): set_a = set(a) set_b = set(b) comparison = set_a.difference(set_b) return list(comparison) difference([1,2,3], [1,2,4]) # [3] 16. 通过函数取差 如下方法首先会应用一个给定的函数,然后再返回应用函数后结果有差别的列表元素。 def difference_by(a, b, fn): b = set(map(fn, b)) return [item for item in a if fn(item) not in b] from math import floor difference_by([2.1, 1.2], [2.3, 3.4],floor) # [1.2] difference_by([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], lambda v : v['x']) # [ { x: 2 } ] 17. 链式函数调用 你可以在一行代码内调用多个函数。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def add(a, b): return a + b def subtract(a, b): return a - b a, b = 4, 5 print((subtract if a > b else add)(a, b)) # 9 18. 检查重复项 如下代码将检查两个列表是不是有重复项。 def has_duplicates(lst): return len(lst) != len(set(lst)) x = [1,2,3,4,5,5] y = [1,2,3,4,5] has_duplicates(x) # True has_duplicates(y) # False 19. 合并两个字典 下面的方法将用于合并两个字典。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def merge_two_dicts(a, b): c = a.copy() # make a copy of a c.update(b) # modify keys and values of a with the ones from b return c a = { 'x': 1, 'y': 2} b = { 'y': 3, 'z': 4} print(merge_two_dicts(a, b)) # {'y': 3, 'x': 1, 'z': 4} 在 Python 3.5 或更高版本中,我们也可以用以下方式合并字典: ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def merge_dictionaries(a, b) return {**a, **b} a = { 'x': 1, 'y': 2} b = { 'y': 3, 'z': 4} print(merge_dictionaries(a, b)) # {'y': 3, 'x': 1, 'z': 4} 20. 将两个列表转化为字典 如下方法将会把两个列表转化为单个字典。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def to_dictionary(keys, values): return dict(zip(keys, values)) keys = ["a", "b", "c"] values = [2, 3, 4] print(to_dictionary(keys, values)) # {'a': 2, 'c': 4, 'b': 3} 21. 使用枚举 我们常用 For 循环来遍历某个列表,同样我们也能枚举列表的索引与值。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' list = ["a", "b", "c", "d"] for index, element in enumerate(list): print("Value", element, "Index ", index, ) # ('Value', 'a', 'Index ', 0) # ('Value', 'b', 'Index ', 1) #('Value', 'c', 'Index ', 2) # ('Value', 'd', 'Index ', 3) 22. 执行时间 如下代码块可以用来计算执行特定代码所花费的时间。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' import time start_time = time.time() a = 1 b = 2 c = a + b print(c) #3 end_time = time.time() total_time = end_time - start_time print("Time: ", total_time) # ('Time: ', 1.1205673217773438e-05) 23.Try else 我们在使用 try/except 语句的时候也可以加一个 else 子句,如果没有触发错误的话,这个子句就会被运行。 try: 2*3 except TypeError: print("An exception was raised") else: print("Thank God, no exceptions were raised.") #Thank God, no exceptions were raised. 24. 元素频率 下面的方法会根据元素频率取列表中最常见的元素。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def most_frequent(list): return max(set(list), key = list.count) list = [1,2,1,2,3,2,1,4,2] most_frequent(list) 25. 回文序列 以下方法会检查给定的字符串是不是回文序列,它首先会把所有字母转化为小写,并移除非英文字母符号。最后,它会对比字符串与反向字符串是否相等,相等则表示为回文序列。 def palindrome(string): from re import sub s = sub('[\W_]', '', string.lower()) return s == s[::-1] palindrome('taco cat') # True 26. 不使用 if-else 的计算子 这一段代码可以不使用条件语句就实现加减乘除、求幂操作,它通过字典这一数据结构实现: ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' import operator action = { "+": operator.add, "-": operator.sub, "/": operator.truediv, "*": operator.mul, "**": pow } print(action['-'](50, 25)) # 25 27.Shuffle 该算法会打乱列表元素的顺序,它主要会通过 Fisher-Yates 算法对新列表进行排序: ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' from copy import deepcopy from random import randint def shuffle(lst): temp_lst = deepcopy(lst) m = len(temp_lst) while (m): m -= 1 i = randint(0, m) temp_lst[m], temp_lst[i] = temp_lst[i], temp_lst[m] return temp_lst foo = [1,2,3] shuffle(foo) # [2,3,1] , foo = [1,2,3] 28. 展开列表 将列表内的所有元素,包括子列表,都展开成一个列表。 def spread(arg): ret = [] for i in arg: if isinstance(i, list): ret.extend(i) else: ret.append(i) return ret spread([1,2,3,[4,5,6],[7],8,9]) # [1,2,3,4,5,6,7,8,9] 29. 交换值 不需要额外的操作就能交换两个变量的值。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def swap(a, b): return b, a a, b = -1, 14 swap(a, b) # (14, -1) spread([1,2,3,[4,5,6],[7],8,9]) # [1,2,3,4,5,6,7,8,9] 30. 字典默认值 通过 Key 取对应的 Value 值,可以通过以下方式设置默认值。如果 get() 方法没有设置默认值,那么如果遇到不存在的 Key,则会返回 None。 d = {'a': 1, 'b': 2} print(d.get('c', 3)) # 3 参考链接:https://towardsdatascience.com/30-helpful-python-snippets-that-you-can-learn-in-30-seconds-or-less-69bb49204172
[ 导读 ]本文重点讲述for语句和while语句。for语句属于遍历循环,while语句属于当型循环。除了两个循环语句外,还介绍了break、continue与pass三个用于控制循环结构中的程序流向的语句。在此基础之上,也介绍了列表推导式,这是一种特殊的循环语句。 循环语句又称为重复结构,用于反复执行某一操作。面对大数量级的重复运算,即使借助计算机,重复编写代码也是费时的,这时就需要借助循环语句。使用循环语句一般要用到条件判断,根据判断式的返回值决定是否执行循环体。 循环分为两种模式,一种是条件满足时执行循环体,另一种则相反,在条件不满足时执行循环体。前者称为当型循环,后者称为直到型循环。 在图1中,虚线框内是一个当型循环结构,此结构包含判断条件和循环体,以及连接各部分的流向线。程序执行时,先判断条件的真假。判断为真时,则执行循环体;判断为假时,不再执行循环体,循环结束。当型循环先进行条件判断,如果满足循环条件,再执行循环体,因此又被称为前测试型循环。 在图2中,虚线框内是一个直到型循环结构,此结构包括判断条件和循环体,以及连接各部分的流向线。程序执行时,先执行一次循环体,再判断执行循环的结果是否满足判断条件。满足条件时,再次执行循环体;不满足条件时,不再执行循环体。直到型循环在执行判断前先进入循环体运行,因此又被称为后测试型循环。Python中主要有两种循环语句,即for语句和while语句。前者采用遍历的形式指定循环范围,后者视判断式返回值的情况而决定是否执行。要更灵活地操纵循环的流向,就要用到break、continue和pass等语句。 01 for for循环是迭代循环,在Python中相当于一个通用的序列迭代器,可以遍历任何有序序列,如str、list、tuple等,也可以遍历任何可迭代对象,如dict。不同于C语言,Python中的for语句将遍历系列中的所有成员,遍历顺序为成员在系列中的顺序。需要注意,在for循环中改变任何序列的内容都是危险的! for语句不属于当型循环或直到型循环,它遍历序列对象内的元素,对每个元素运行一次循环体,循环的步数在程序开始执行时已经指定,不属于条件判断。 在for语句中,for和in搭配组成for-in循环结构,for-in循环依次把list或tuple中的每个元素迭代出来。for语句的基本语法格式如下: for 变量 in 序列: 操作语句 for语句常用的语法格式及其参数说明如下所示: 序列:接收序列,表示遍历范围,无默认值 操作语句:接收操作语句,表示执行一段代码,无默认值 程序的执行从“for变量in序列”开始,该语句把序列中的每个元素代入变量,执行一遍操作语句1,重复的次数就是序列中元素的个数。为了展示for循环的遍历功能,依次打印list中的姓名,如代码清单1所示。 代码清单1:for语句遍历提取str ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' # 单纯遍历的for语句 names = ['Michael', 'Bob', 'Tracy'] # 遍历输出names中的元素 for name in names: print(name) 输出结果: Michael Bob Tracy for语句同样可以实现dict的遍历方法,如代码清单2所示。 代码清单2:for语句遍历查询dict ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' dic = {'a': 1, 'b': 2, 'c': 3, 'd': 4} # 遍历键值对 print('key_value:', end = '') for key, value in dic.items(): print(key, value, sep = ':', end = ' ') # 遍历键 print('keys:', end = '') for key in dic.keys(): print(key, end = ' ') # 遍历值 print('values:', end = '') for value in dic.values(): print(value, end = ' ') 输出结果: key_value:a:1 b:2 c:3 d:4 keys:a b c d values:1 2 3 4 从代码清单2可以看到,for语句中用于遍历的“变量”不仅可以是Python默认的指代词,也可以是常规的变量。 和条件语句一样,循环语句也可以使用嵌套,作用同样是丰富程序的功能性。设计一个成绩录入系统,就必然要录入姓名和课程这两类信息,仅靠一层循环是无法实现的,可使用两层循环结构,如代码清单3所示。 代码清单3:嵌套for语句 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' students = ['小明', '小红'] subjects = ['语文', '数学'] sum1 = [] avg = [] for i in students: print ('开始录入%s的考试成绩!'%i) sum = 0 for j in subjects: print('请输入%s成绩:'%j) score = int(input()) sum += score average = sum / 2 avg.append(average) sum1.append(sum) print(students, '的总分依次是', sum1, ',', '平均分依次是', avg) print('完成成绩录入!') 输出结果: 开始录入小明的考试成绩! 请输入语文成绩: 97 请输入数学成绩: 90 开始录入小红的考试成绩! 请输入语文成绩: 89 请输入数学成绩: 100 ['小明', '小红'] 的总分依次是 [187, 189] , 平均分依次是 [93.5, 94.5] 完成成绩录入! 理论上,for循环也可以无限嵌套,但并不推荐。 02 while while语句是Python中最常用的递归结构。区别于for循环,while循环结构包含条件判断式,是一种条件循环,属于当型循环。 while语句最基本的形式包括一个位于顶部的布尔表达式,一个或多个属于while代码块的缩进语句。也可以在结尾处包含一个else代码块,它与while代码块是同级的,组成while-else的形式。while语句的基本语法格式如下。 while 条件表达式: 操作语句 1 操作语句 2 while语句常用的参数及其说明如下所示: 条件表达式:接收布尔表达式,表示判断条件是否成立。无默认值 操作语句:接收操作语句,表示执行一段代码。无默认值 执行while语句时,只要顶部的条件表达式返回真值,就一直执行while部分嵌套的递归代码,当条件表达式返回假值时,不再执行操作语句,程序跳出while结构。 while语句的基础使用方法如代码清单4所示。 代码清单4:while语句 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' sum = 0 n = 99 while n > 0: sum += n n -= 2 print(sum) 输出结果:2500 如果布尔表达式不带有<、>、==、!=、in、not in等运算符,仅仅给出数值之类的条件,也是可以的。当while后写入一个非零整数时,视为真值,执行循环体;写入0时,视为假值,不执行循环体。也可以写入str、list或任何序列,长度非零则视为真值,执行循环体;否则视为假值,不执行循环体。 如果布尔表达式始终返回1,while语句就变成无限循环,如代码清单5所示。 代码清单5:while语句无限循环 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' # 布尔表达式为常数1,始终为真值 while 1: print('循环') 输出结果: 循环 循环 … # 布尔表达式每次递归运算都为2,始终为真值 x, y = 2, 1 while x / y: print('循环') x = x * 2 y = y * 2 输出结果: 循环 循环 … 运行代码清单5,将会不断打印出“循环”。代码清单5展示了制造无限循环的两种方式,既可以在while后写入一个固定的真值,也可以写入一个一直生成真值的表达式。要终止无限循环,可以使用快捷键Ctrl+C中断循环的执行,也可以用循环终止语句,这将在下文中介绍。 灵活地利用while语句中的布尔表达式及代入表达式的递归值,可以实现特别的功能,如代码清单6所示。 代码清单6:while语句实现str截取 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' string = 'abcd' while string: print(string) # 该语句的递归计算是,每次从str的第2个字符开始截取 string = string[1:] 输出结果: abcd bcd cd d 代码清单6包含一个自减迭代值,它并不通过明显的运算符实现自减,而是利用索引法则,x变量一直从str中第2个值截取至结尾,每次都将位于str最前面的字符截取掉,最终只剩下一个字符时,再次截取就只有空的结果,布尔表达式返回0,循环终止。 通过代码清单5和代码清单6可以看到,灵活地利用递归式,可以实现程序流向的控制。 while循环同样可以使用嵌套,嵌套的while循环实现成绩录入系统如代码清单7所示。 代码清单7:嵌套while语句 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' j = 1 while j <= 2: sum = 0 i = 1 name = input('请输入学生姓名:') while i <= 2: print ('请输入第%d门的考试成绩: '%i) sum += int(input()) i += 1 avg = sum / (i-1) print(name, '的平均成绩是%d'%avg) j += 1 print('学生成绩输入完成!') 输出结果: 请输入学生姓名:小明 请输入第1门的考试成绩: 98 请输入第2门的考试成绩: 88 小明 的平均成绩是93 请输入学生姓名:小红 请输入第1门的考试成绩: 65 请输入第2门的考试成绩: 100 小红 的平均成绩是82 学生成绩输入完成! 代码清单7的第1层while语句用于录入人名,第2层则在各人名下录入多门成绩,布尔表达式决定录入的人数和课程数。 03 break、continue与pass 在前两节中,已经介绍了Python中的两种循环语句。循环语句中还可以嵌入break、continue和pass语句,以灵活地改变流向,实现更多功能。 1. break 在Python中,break语句用于终止循环语句的执行。使用该语句时,即使循环条件判断为真,或序列未被完全递归,循环语句也会被立刻停止。 break语句一般配合条件判断使用,因为程序的终止必须是在某一条件被满足时执行。break语句在for循环和while循环中的使用如代码清单8所示。 代码清单8:break语句的使用 # break语句用于for循环 string = "Python" for i in string: # 遍历至string中的字符n时,不再执行else代码块 if i == 'n': break else: print("letter:{}". format(i)) 输出结果: letter:P letter:y letter:t letter:h letter:o ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' # break语句用于while循环 counts = 0 while True: print(counts) counts += 1 # 满足counts等于3时跳出循环,不再进入循环体 if counts == 3: break 输出结果: 0 1 2 从代码清单8可以看到,break语句用于for循环和while循环是有区别的。用于for循环时,只终止遍历中某一次的循环体执行;用于while循环时,整个循环被终止。 break只终止本层循环,如有多层嵌套的循环,在其中一层循环中写入break,只在这层循环中生效,程序将跳到上一层循环中继续运行,如代码清单9所示。 代码清单9:break语句用于嵌套循环结构 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' # 第1层循环,遍历次数为2 for i in range(2): print("-----%d-----" %i) # 第2层循环,遍历次数为10 for j in range(10): # 使用break语句,j>1时不执行循环体 if j > 1: break print(j) 输出结果: -----0----- 0 1 -----1----- 0 1 在代码清单9中,break语句在条件判断式“if j>1:”后被使用,因此尽管j的指定遍历次数为10,实际上遍历只运行两次。由于break语句只终止本层循环的运行,i依旧遍历执行了两次,而不是在第1次遍历过程末尾终止。 2. continue Python中的continue语句用于跳出当前循环,并执行下一次循环,而break跳出整层循环,两者的功能具有明显区别。 如果一段代码中包含continue语句,循环执行至continue处时,先忽略本次循环,在本层仍满足条件的剩余循环次数中继续执行,不会终止这一层循环。实际上,如果在某一层的每次循环中都使用continue语句,就相当于使用break语句。 打印一个数表,要不打印某些指定的数字,或只打印某类数,就可以使用continue语句跳过一些循环次数,该语句在for循环和while循环中都可以自由地使用,如代码清单10所示。 代码清单10:continue语句用于循环结构 # 当i等于1或3时,跳过continue后的print语句 for i in range(0,5): if i == 1 or i == 3: continue print(i) 输出结果: 0 2 4 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' # 如果遇到偶数,跳过continue后的print语句 counts = 0 while counts < 10: counts += 1 if counts % 2 == 0: continue print(counts) 输出结果: 1 3 5 7 9 break语句一旦用于嵌套循环中的第n层,该层循环会被终止,但在执行第n-1层循环时,仍会创造一个第n层循环并执行。continue语句同样如此,只是仍会执行某一层的剩余部分。因此,无论使用哪种循环终止语句,都只会影响使用终止语句的那一层循环,而不会干扰到其他层。continue语句用于循环的例子如代码清单11所示。 代码清单11:continue语句用于嵌套循环结构 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' # 第1层循环,遍历次数为2 for i in range(2): print("-----%d-----" %i) # 第2层循环,遍历次数为5 for j in range(5): # 当j等于2或4时,不执行循环体 if j == 2 or j == 4: continue print(j) 输出结果: -----0----- 0 1 3 -----1----- 0 1 3 3. pass pass是空语句,不做任何操作,只起到占位的作用,其作用是为了保持程序结构的完整性。尽管pass语句不做任何操作,但如果暂时不确定要在一个位置放上什么样的代码,可以先放置一个pass语句,让代码可以正常运行。pass语句并非循环或者条件语句的一部分,但与break、continue在代码形式上有些类似。 使用pass语句遍历输出str及数值计算,如代码清单12所示。 代码清单12:pass语句 for element in "Python": # element为y时,不做任何操作,不会被输出 if element == "y": pass else: print(element) 输出结果: P t h o n ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' counts = 0 while counts < 5: counts += 1 # i=3时,不执行循环体 if counts == 3: pass else: print(counts ** 2) 输出结果: 1 4 16 25 从代码清单12可以看到,Python在“P”和“t”之间占位,当循环遍历到“y”时不做任何操作;当i等于3时,幂运算不执行,但不影响其他数值。上述两个代码如果在pass的位置缺失,程序将无法执行,因为判断条件没有给出相应的执行语句,会导致逻辑出错。使用pass语句占位,一方面为了让程序正常执行,另一方面也是为了方便以后补充操作语句。 04 列表推导式 推导式是可以从一个数据序列构建另一个新的数据序列的结构体,能够非常简洁地构造新的变量。列表推导式是其中最常用的类型。 列表推导式又称为列表解析式,是Python迭代机制的一种应用,也是一种高效创建列list的方式,可以动态地创建list。由于列表推导式必须用到遍历循环,因此属于一种特殊的循环。 使用列表推导式时,需要将推导式写在[]中。list中的元素可以来源于其他类型序列、可迭代对象或自建的满足一定条件的序列。使用列表推导式的好处是代码更加简洁,实现效率更高。 列表推导式的基本语法格式如下: [操作语句 for变量 in 序列 if 条件表达式] 列表推导式常用的参数及其说明如下所示: 操作语句:接收操作语句,表示执行一段代码。无默认值 条件表达式:接收布尔表达式,表示判断条件是否成立。无默认值 序列:接收序列,表示遍历范围。无默认值 列表推导式可以不包含条件表达式,只做遍历,生成list,如代码清单13所示。 代码清单13:使用列表推导式生成list ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' vec = [-4, -2, 0, 2, 4] # 用vec中元素的倍数,创建一个数组 print([x * 2 for x in vec]) 输出结果: [-8, -4, 0, 4, 8] # 创建一个包含2元tuple的list print([(x, x ** 2) for x in range(6)]) 输出结果: [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)] 在代码清单13中,除了列表推导式的简单形式,还说明了列表推导式中可以使用多样的函数和变量类型。另外,列表推导式中也可以包含条件语句,如代码清单14所示。 代码清单14:包含条件语句的列表推导式 # 过滤list,删除list中的负数 print([x for x in vec if x >= 0]) 输出结果: [0, 2, 4] 列表推导式最大的优点还是简洁,这需要与常规的编程方式进行对比。如代码清单15所示,要创建一个平方数组成的list,这里的两种方式是等价的,但列表推导式的方式显然代码更加简洁。 代码清单15:常规方式与列表推导式对比 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' # 创建一个平方数list:常规方式 squares = [] for x in range(10): squares.append(x ** 2) print(squares) 输出结果: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] # 创建一个平方数list列表推导式 squares = [x ** 2 for x in range(10)] print(squares) 输出结果: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 列表推导式中同样可以实现嵌套循环,如代码清单16所示。 代码清单16:包含嵌套循环的列表推导式 # 打印由tuple组成的list,tuple中i由0至2,j由0至2 [(i, j) for i in range(0, 3) for j in range(0, 3)] 输出结果: [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)] # 打印由tuple组成的list,i在0至2之间且小于1,j在0至2之间且大于1 [(i, j) for i in range(0, 3) if i < 1 for j in range(0, 3) if j > 1] 输出结果: [(0, 2)] 代码清单16所示的列表推导式将两个不同list中的元素整合到了一起。列表推导式中包含一对括号,在括号中有一个表达式,表达式后面紧跟一条for语句,然后是零条或多条for语句或if语句。通过for语句和if语句计算出表达式,结果作为新list的元素。
Python是目前最流行的语言之一,它在数据科学、机器学习、web开发、脚本编写、自动化方面被许多人广泛使用。它的简单和易用性造就了它如此流行的原因。在本文中,我们将会介绍 30 个简短的代码片段,你可以在 30 秒或更短的时间里理解和学习这些代码片段。 1.检查重复元素 下面的方法可以检查给定列表中是否有重复的元素。它使用了 set() 属性,该属性将会从列表中删除重复的元素。 def all_unique(lst): return len(lst) == len(set(lst)) x = [1,1,2,2,3,2,3,4,5,6] y = [1,2,3,4,5] all_unique(x) # False all_unique(y) # True 2.变位词 检测两个字符串是否互为变位词(即互相颠倒字符顺序) ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' from collections import Counter def anagram(first, second): return Counter(first) == Counter(second) anagram("abcd3", "3acdb") # True 3.检查内存使用情况 以下代码段可用来检查对象的内存使用情况。 import sys variable = 30 print(sys.getsizeof(variable)) # 24 4.字节大小计算 以下方法将以字节为单位返回字符串长度。 def byte_size(string): return(len(string.encode( utf-8 ))) byte_size( ) # 4 byte_size( Hello World ) # 11 5.重复打印字符串 N 次 以下代码不需要使用循环即可打印某个字符串 n 次 n = 2; s ="Programming"; print(s * n); # ProgrammingProgramming 6.首字母大写 以下代码段使用 title() 方法将字符串内的每个词进行首字母大写。 s = "programming is awesome" print(s.title()) # Programming Is Awesome 7.分块 以下方法使用 range() 将列表分块为指定大小的较小列表。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' from math import ceil def chunk(lst, size): return list( map(lambda x: lst[x * size:x * size + size], list(range(0, ceil(len(lst) / size))))) chunk([1,2,3,4,5],2) # [[1,2],[3,4],5] 8.压缩 以下方法使用 fliter() 删除列表中的错误值(如:False, None, 0 和“”) def compact(lst): return list(filter(bool, lst)) compact([0, 1, False, 2, , 3, a , s , 34]) # [ 1, 2, 3, a , s , 34 ] 9.间隔数 以下代码段可以用来转换一个二维数组。 array = [[ a , b ], [ c , d ], [ e , f ]] transposed = zip(*array) print(transposed) # [( a , c , e ), ( b , d , f )] 10.链式比较 以下代码可以在一行中用各种操作符进行多次比较。 a = 3 print( 2 < a < 8) # True print(1 == a < 2) # False 11.逗号分隔 以下代码段可将字符串列表转换为单个字符串,列表中的每个元素用逗号分隔。 hobbies = ["basketball", "football", "swimming"] print("My hobbies are: " + ", ".join(hobbies)) # My hobbies are: basketball, football, swimming 12.计算元音字母数 以下方法可计算字符串中元音字母(‘a’, ‘e’, ‘i’, ‘o’, ‘u’)的数目。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' import re def count_vowels(str): return len(len(re.findall(r [aeiou] , str, re.IGNORECASE))) count_vowels( foobar ) # 3 count_vowels( gym ) # 0 13.首字母恢复小写 以下方法可用于将给定字符串的第一个字母转换为小写。 def decapitalize(string): return str[:1].lower() + str[1:] decapitalize( FooBar ) # fooBar decapitalize( FooBar ) # fooBar 14.平面化 以下方法使用递归来展开潜在的深度列表。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def spread(arg): ret = [] for i in arg: if isinstance(i, list): ret.extend(i) else: ret.append(i) return ret def deep_flatten(lst): result = [] result.extend( spread(list(map(lambda x: deep_flatten(x) if type(x) == list else x, lst)))) return result deep_flatten([1, [2], [[3], 4], 5]) # [1,2,3,4,5] 15.差异 该方法只保留第一个迭代器中的值,从而发现两个迭代器之间的差异。 def difference(a, b): set_a = set(a) set_b = set(b) comparison = set_a.difference(set_b) return list(comparison) difference([1,2,3], [1,2,4]) # [3] 16.寻找差异 下面的方法在将给定的函数应用于两个列表的每个元素后,返回两个列表之间的差值。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def difference_by(a, b, fn): b = set(map(fn, b)) return [item for item in a if fn(item) not in b] from math import floor difference_by([2.1, 1.2], [2.3, 3.4],floor) # [1.2] difference_by([{ x : 2 }, { x : 1 }], [{ x : 1 }], lambda v : v[ x ]) # [ { x: 2 } ] 17.链式函数调用 以下方法可在一行中调用多个函数。 def add(a, b): return a + b def subtract(a, b): return a - b a, b = 4, 5 print((subtract if a > b else add)(a, b)) # 9 18.检查重复值 以下方法使用 set() 方法仅包含唯一元素的事实来检查列表是否具有重复值。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def has_duplicates(lst): return len(lst) != len(set(lst)) x = [1,2,3,4,5,5] y = [1,2,3,4,5] has_duplicates(x) # True has_duplicates(y) # False 19.合并两个词典 以下方法可用于合并两个词典。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def merge_two_dicts(a, b): c = a.copy() # make a copy of a c.update(b) # modify keys and values of a with the ones from b return c a = { x : 1, y : 2} b = { y : 3, z : 4} print(merge_two_dicts(a, b)) # { y : 3, x : 1, z : 4} 在Python 3.5及更高版本中,你还可以执行以下操作: def merge_dictionaries(a, b) return {**a, **b} a = { x : 1, y : 2} b = { y : 3, z : 4} print(merge_dictionaries(a, b)) # { y : 3, x : 1, z : 4} 20.将两个列表转换成一个词典 以下方法可将两个列表转换成一个词典。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def to_dictionary(keys, values): return dict(zip(keys, values)) keys = ["a", "b", "c"] values = [2, 3, 4] print(to_dictionary(keys, values)) # { a : 2, c : 4, b : 3} 21.使用枚举 以下方法将字典作为输入,然后仅返回该字典中的键。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' list = ["a", "b", "c", "d"] for index, element in enumerate(list): print("Value", element, "Index ", index, ) # ( Value , a , Index , 0) # ( Value , b , Index , 1) #( Value , c , Index , 2) # ( Value , d , Index , 3) 22.计算所需时间 以下代码段可用于计算执行特定代码所需的时间。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' import time start_time = time.time() a = 1 b = 2 c = a + b print(c) #3 end_time = time.time() total_time = end_time - start_time print("Time: ", total_time) # ( Time: , 1.1205673217773438e-05) 23.Try else 指令 你可以将 else 子句作为 try/except 块的一部分,如果没有抛出异常,则执行该子句。 try: 2*3 except TypeError: print("An exception was raised") else: print("Thank God, no exceptions were raised.") #Thank God, no exceptions were raised. 24.查找最常见元素 以下方法返回列表中出现的最常见元素。 def most_frequent(list): return max(set(list), key = list.count) list = [1,2,1,2,3,2,1,4,2] most_frequent(list) 25.回文 以下方法可检查给定的字符串是否为回文结构。该方法首先将字符串转换为小写,然后从中删除非字母数字字符。最后,它会将新的字符串与反转版本进行比较。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def palindrome(string): from re import sub s = sub( [W_] , , string.lower()) return s == s[::-1] palindrome( taco cat ) # True 26.没有 if-else 语句的简单计算器 以下代码段将展示如何编写一个不使用 if-else 条件的简单计算器。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' import operator action = { "+": operator.add, "-": operator.sub, "/": operator.truediv, "*": operator.mul, "**": pow } print(action[ - ](50, 25)) # 25 27.元素顺序打乱 以下算法通过实现 Fisher-Yates算法 在新列表中进行排序来将列表中的元素顺序随机打乱。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' from copy import deepcopy from random import randint def shuffle(lst): temp_lst = deepcopy(lst) m = len(temp_lst) while (m): m -= 1 i = randint(0, m) temp_lst[m], temp_lst[i] = temp_lst[i], temp_lst[m] return temp_lst foo = [1,2,3] shuffle(foo) # [2,3,1] , foo = [1,2,3] 28.列表扁平化 以下方法可使列表扁平化,类似于JavaScript中的[].concat(…arr)。 ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,互帮互助, 群里还有不错的视频学习教程和PDF电子书! ''' def spread(arg): ret = [] for i in arg: if isinstance(i, list): ret.extend(i) else: ret.append(i) return ret spread([1,2,3,[4,5,6],[7],8,9]) # [1,2,3,4,5,6,7,8,9] 29.变量交换 以下是交换两个变量的快速方法,而且无需使用额外的变量。 def swap(a, b): return b, a a, b = -1, 14 swap(a, b) # (14, -1) 30.获取缺失键的默认值 以下代码段显示了如何在字典中没有包含要查找的键的情况下获得默认值。 d = { a : 1, b : 2} print(d.get( c , 3)) # 3 以上是你在日常工作中可能会发现的有用方法的简短列表。它主要基于这个GitHub项目(https://github.com/30-seconds/30_seconds_of_knowledge),你可以在其中找到许多其他有用的代码片段,包括Python及其他编程语言和技术。