Python基础知识
字符串 Str
-
字符串拼接
a = 'eagle ' b = 'welcome ' print(b + a,'*' * 3,a * 3)
运行结果
welcome eagle *** eagle eagle eagle
-
字符串索引与切片
a = 'ABCDEFGHIJK' print(a[0:]) #默认到最后 print(a[0:-1]) # -1 是列表中最后一个元素的索引,但是要满足顾头不顾腚的原则,所以取不到K元素
运行结果
ABCDEFGHIJK ABCDEFGHIJ
-
字符串常用方法
words = "beautiful is better than ugly" print(words.capitalize()) #首字母大写 print(words.swapcase()) #大小写翻转 print(words.title()) #每个单词的首字母大写 # 内容居中,总长度,空白处填充 a = "test" ret = a.center(20, "*") print(ret) # 统计字符串中的元素出现的个数 ret = words.count("e", 0, 30) print(ret) # startswith 判断是否以…开头 # endswith 判断是否以…结尾 a = "aisdjioadoiqwd12313assdj" print(a.startswith("a")) print(a.endswith("j")) print(a.startswith('sdj',2,5)) print(a.endswith('ado',7,10)) # 寻找字符串中的元素是否存在 print(a.find('sdj',1,10)) # 返回的找到的元素的索引,如果找不到返回-1 print(a.index('sdj',1,10)) # 返回的找到的元素的索引,找不到报错。 # split 以什么分割,最终形成一个列表此列表不含有这个分割的元素。 ret = words.split(' ') print(ret) ret = words.rsplit(' ',2) print(ret) # format的三种玩法 格式化输出 print('{} {} {}'.format('aaron',18,'teacher')) print('{1} {0} {1}'.format('aaron',18,'teacher')) print('{name} {age} {job}'.format(job='teacher', name='arron', age=18)) # strip a = '****asdasdasd********' print(a.strip('*')) print(a.lstrip('*')) print(a.rstrip('*')) print(a.strip()) # 去除空格 # replace print(words.replace('e','a',2)) # 字符串从左向右开始,把e替换成a,一共替换两次 print(words.isalnum()) #字符串由字母或数字组成 print(words.isalpha()) #字符串只由字母组成 print(words.isdigit()) #字符串只由数字组成
元组 tuple
元组被称为只读列表,即数据可以被查询,但不能被修改。
元组可变与不可变详解:
链接:https://blog.csdn.net/machi1/article/details/86601119
列表 List
-
增
li = [1,'a',2,'d',4] li.insert(0,22) # 按照索引去增加 print(li) li.append('ddd') # 增加到最后 print(li) li.extend(['q,a,w']) # 迭代的去增 print(li) li.extend(['q,a,w','das']) # 迭代的去增 print(li)
运行结果
[22, 1, 'a', 2, 'd', 4] [22, 1, 'a', 2, 'd', 4, 'ddd'] [22, 1, 'a', 2, 'd', 4, 'ddd', 'q,a,w'] [22, 1, 'a', 2, 'd', 4, 'ddd', 'q,a,w', 'q,a,w', 'das']
-
删
li = [1,'a',2,'d',4,5,'f'] a = li.pop(1) # 按照位置去删除,有返回值 print(a) del li[1:3] # 按照位置去删除,也可切片删除没有返回值。 print(li) li.remove('f') print(li) li.clear() print(li)
运行结果
a [1, 4, 5, 'f'] [1, 4, 5] []
-
改
li = [1,'a',2,'d',4,5,'f'] li[1] = 'aaa' print(li) li[2:3] = [3,'e'] print (li)
运行结果
[1, 'aaa', 2, 'd', 4, 5, 'f'] [1, 'aaa', 3, 'e', 'd', 4, 5, 'f']
-
查
切片去查,或者循环去查。
-
其他
li = [1,2,4,5,4,2,4] print (li.count(4)) # 统计某个元素在列表中出现的次数 print (li.index(2)) # 用于从列表中找出某个值第一个匹配项的索引位置 li.sort() # 用于在原位置对列表进行排序 print (li) li.reverse() # 将列表中的元素反向存放 print (li)
运行结果
3 1 [1, 2, 2, 4, 4, 4, 5] [5, 4, 4, 4, 2, 2, 1]
字典 dict
从Python3.6开始字典为有序
参考博客:https://www.cnblogs.com/xieqiankun/p/python_dict.html
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Zj50hau-1611720947146)(file:///D:\QQ文件\2652516133\Image\Group2@]Z@]ZKGM2PF2F1R]U]S~BTOC.png)]
-
增
dic = {"age":18, "name":"aaron"} dic['li'] = ["a", "b", "c"] print(dic) dic.setdefault('k', 'v') #在字典中添加键值对,如果只有键那对应的值是none,但是如果原字典中存在设置的键值对,则他不会更改或覆盖 print(dic) dic.setdefault('k', 'v1') print(dic)
-
删
dic = {"age":18, "name":"aaron"} dic_pop = dic.pop('age') # pop根据key删除键值对,并返回对应的值,如果没有key则返回默认返回值 print(dic_pop) dic['age'] = 18 print(dic) del dic['name'] print(dic) del dic['name'] print(dic) dic['name'] = 'demo' dic_pop = dic.popitem() # 随机删除字典中的某个键值对,将删除的键值对以元组的形式返回 print(dic_pop) dic_clear = dic.clear() # 清空字典 print(dic,dic_clear)
-
改
dic = {"age":18, "name":"aaron", 'sex':'male'} dic2 = {"age":30, "name":'demo'} dic2.update(dic) # 将dic所有的键值对覆盖添加(相同的覆盖,没有的添加)到dic2中 print(dic2) dic2['age'] = 30 print(dic2)
-
查
dic = {"age":18, "name":"aaron", 'sex':'male'} value = dic['name'] # 没有会报错 print(value) value = dic.get('abc','查无此项') print(value)
-
其他
dic = {"age":18, "name":"aaron", 'sex':'male'} for i in dic.items(): # 将键和值作为元组列出 print(i) for key,value in dic.items(): print(key,value) for i in dic: # 只是迭代键 print(i) keys = dic.keys() print(keys,type(keys)) value = dic.values() print(value,type(value))
集合 set
-
创建
set1 = set({1,2,'barry'}) set2 = {1,2,'barry'} print(set1,set2)
-
增
set1 = {'abc', 'def', 123, 'asdas'} set1.add('qwer') print(set1) set1.update('A') # update:迭代着增加 print(set1) set1.update('哈哈哈') print(set1) set1.update([1, 2, 3]) print(set1)
-
删
set1 = {'abc','def',123,'asdas'} set1.remove('abc') print(set1) set1.pop() # 随机删除一个数 print(set1) set1.clear() # 清空合集 print(set1) del set1 # 删除合集 print(set1)
-
其他
交集(&或者intersection)
set1 = {1,2,3,4,5} set2 = {3,4,5,6,7} print(set1 & set2) print(set1.intersection(set2)) # 列出两个集合中共同拥有的项
并集(|或者union)
set1 = {1,2,3,4,5} set2 = {3,4,5,6,7} print(set1 | set2) print(set2.union(set1)) # 列出两个集合中所有的项
差集(-或者difference)
set1 = {1,2,3,4,5} set2 = {3,4,5,6,7} print(set1 - set2) print(set1.difference(set2)) # 在set1中删除set2中有的项
反交集(^或者symmetric_difference)
set1 = {1,2,3,4,5} set2 = {3,4,5,6,7} print(set1 ^ set2) print(set1.symmetric_difference(set2)) # 显示set1和set2不共存的项
子集与超集
set1 = {1,2,3} set2 = {1,2,3,4,5,6} print(set1 < set2) print(set1.issubset(set2)) # 这两个相同,都是说明set1是set2子集。 print(set2 > set1) print(set2.issuperset(set1)) # 这两个相同,都是说明set2是set1超集
frozenset不可变集合,让集合变成不可变类型
set1 = {1,2,3,4,5,6} s = frozenset(set1) print(s, type(s)) s.add(7) # 不可以修改,会报错
流程控制(易忽视)
-
while…else…
while后面的else作用是指,当while循环正常执行完,中间没有被break中止的话,就会执行else后面的语句
-
enumerate
enumerate:枚举,对于一个可迭代的(iterable)/可遍历的对象(如列表、字符串),enumerate将其组成一个索引序列,利用它可以同时获得索引和值。
li = ['甲','乙','丙','丁'] for i in li: print(i) for i in enumerate(li): print(i) for index,value in enumerate(li): print(index,value) for index,value in enumerate(li,100): #从哪个数字开始索引 print(index,value)
运行结果
甲 乙 丙 丁 (0, '甲') (1, '乙') (2, '丙') (3, '丁') 0 甲 1 乙 2 丙 3 丁 100 甲 101 乙 102 丙 103 丁
文件操作
文本文件和二进制文件
- 文本文件:可以使用文本编辑器查看;
- 二进制文件:保存的内容不是直接给人查看的,而是使用专用软件查看的,例如图片文件、视频文件;
操作文件的流程
-
打开文件;
-
读写文件;
-
1.读文件:将文件内容读入内存;
2.写文件:将内存内容写入文件
-
关闭文件。
操作文件中的函数/方法
序号 | 函数/方法 | 说明 |
---|---|---|
01 | open | 打开文件,并且返回文件操作对象 |
02 | read | 将文件内容读取到内存 |
03 | write | 将指定内容写入文件 |
04 | close | 关闭文件 |
- open 函数负责打开文件,并且返回文件对象
- read / write / close 三个方法都需要通过 文件对象 来调用
-
open函数:
-
-
第一个参数是文件名(文件名区分大小写),第二个参数是打开方式;
-
如果文件存在返回文件操作对象;
-
如果文件不存在抛出异常
-
-
read()方法:可以一次性读入并返回文件的所有内容;
-
close()方法:负责关闭文件;
# 1. 打开 - 文件名需要注意大小写
file = open("README")
# 2. 读取
text = file.read()
print(text)
# 3. 关闭
file.close()
文件打开方式
f = open("文件名", "访问方式")
访问方式 | 说明 |
---|---|
r | 以只读方式打开文件。文件的指针将会放在文件的开头,这是默认模式。如果文件不存在,抛出异常 |
w | 以只写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件 |
a | 以追加方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入 |
r+ | 以读写方式打开文件。文件的指针将会放在文件的开头。如果文件不存在,抛出异常 |
w+ | 以读写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件 |
a+ | 以读写方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入 |
以bytes类型操作的读写,写读,写读模式
r+b | 读写【可读,可写】 |
---|---|
w+b | 写读【可写,可读】 |
a+b | 写读【可写,可读】 |
对于非文本文件,我们只能使用b模式,“b"表示以字节的方式操作(而所有文件也都是以字节的形式存储的,使用这种模式无需考虑文本文件的字符编码、图片文件的jpg格式、视频文件的avi格式)
rb
wb
ab
注:以b方式打开时,读取到的内容是字节类型,写入时也需要提供字节类型,不能指定编码
- 频繁的移动文件指针,会影响文件的读写效率,开发中更多的时候会以 只读、只写 的方式来操作 文件。
按行读取文件内容
- read方法默认会把文件的所有内容一次性读取到内存;
- readline方法可以一次读取一行内容;
- 方法执行后,文件指针移动到下一行,准备再次读取;
# 方式一、通过循环按行读取文件所有内容
file1 = open("Readme.txt")
i = 1
while True:
text1 = file1.readline().strip()
if text1:
print("这是第%s行内容" % i)
i += 1
print(text1)
else:
break
file1.close()
# 方式二、通过for遍历按行读取文件所有内容
file2 = open("Readme.txt")
for i in file2.readlines():
print(i.strip())
file2.close()
with 结构
with open("README.txt") as file1:
while True:
text1 = file1.readline().strip()
if text1:
print("这是第%s行内容" % i)
i += 1
print(text1)
else:
break
close()在操作完毕文件后,一定要记住f.close(),推荐操作方式:使用with关键字来帮我们管理上下文
with open('a.txt','r') as read_f,open('b.txt','w') as write_f:
data=read_f.read()
write_f.write(data)
文件编码
f = open(…)是由操作系统打开文件,那么如果我们没有为open指定编码,那么打开文件的默认编码很明显就是操作系统说的算,操作系统会用自己的默认编码去打开文件,在Windows下是gbk,在Linux下是utf-8。
f=open('a.txt','r',encoding='utf-8')
文件操作
read(3):
- 文件打开方式为文本模式时,代表读取3个字符
- 文件打开方式为b模式时,代表读取3个字节
其余的文件内光标移动都是以字节为单位的如:seek,tell,truncate
注意:
- seek有三种移动方式0,1,2,其中1和2必须在b模式下进行,但无论哪种模式,都是以bytes为单位移动的
- truncate是截断文件,所以文件的打开方式必须可写,但是不能用w或w+等方式打开,因为那样直 接清空文件了,所以truncate要在r+或a或a+等模式下测试效果。
其他操作方法
def close(self, *args, **kwargs): # real signature unknown
关闭文件
pass
def fileno(self, *args, **kwargs): # real signature unknown
文件描述符
pass
def flush(self, *args, **kwargs): # real signature unknown
刷新文件内部缓冲区
pass
def isatty(self, *args, **kwargs): # real signature unknown
判断文件是否是同意tty设备
pass
def read(self, *args, **kwargs): # real signature unknown
读取指定字节数据
pass
def readable(self, *args, **kwargs): # real signature unknown
是否可读
pass
def readline(self, *args, **kwargs): # real signature unknown
仅读取一行数据
pass
def seek(self, *args, **kwargs): # real signature unknown
指定文件中指针位置
pass
def seekable(self, *args, **kwargs): # real signature unknown
指针是否可操作
pass
def tell(self, *args, **kwargs): # real signature unknown
获取指针位置
pass
def truncate(self, *args, **kwargs): # real signature unknown
截断数据,仅保留指定之前数据
pass
def writable(self, *args, **kwargs): # real signature unknown
是否可写
pass
def write(self, *args, **kwargs): # real signature unknown
写内容
pass
def __getstate__(self, *args, **kwargs): # real signature unknown
pass
def __init__(self, *args, **kwargs): # real signature unknown
pass
@staticmethod # known case of __new__
def __new__(*args, **kwargs): # real signature unknown
""" Create and return a new object. See help(type) for accurate signature.
"""
pass
def __next__(self, *args, **kwargs): # real signature unknown
""" Implement next(self). """
pass
def __repr__(self, *args, **kwargs): # real signature unknown
""" Return repr(self). """
pass
案例
案例一、文件的修改
文件的数据是存放于硬盘上的,因而只存在覆盖、不存在修改这么一说,我们平时看到的修改文件,都是模拟出来的效果,具体的说有两种实现方式:
方式一:将硬盘存放的该文件的内容全部加载到内存,在内存中是可以修改的,修改完毕后,再由内存覆盖到硬盘(Word,vim,nopad++等编辑器)
import os
with open('a.txt') as read_f,open('a.txt.new','w') as write_f:
data = read_f.read()
data = data.replace('Hello','nihao')
write_f.write(data)
os.remove('a.txt')
os.rename('a.txt.new','a.txt')
方式二:将硬盘存放的该文件的内容一行一行地读入内存,修改完毕就写入新文件,最后用新文件覆盖源文件
import os
with open('a.txt') as read_f,open('a.txt.new','w') as write_f:
for line in read_f:
line = line.replace('nihao','Hello')
write_f.write(line)
os.remove('a.txt')
os.rename('a.txt.new','a.txt')
案例二、文件的复制
file = open("README")
while True:
text = file.readline()
print(text)
if not text:
break
file.close()
# -------------------------------------------------------------------------------
------
# 小文件复制
file1 = open("README", "r")
file2 = open("README[复件]", "w")
text = file1.read()
file2.write(text)
file1.close()
file2.close()
# -------------------------------------------------------------------------------
------
# 大文件复制
file3 = open("README", "r")
file4 = open("README[大文件复制]", "w")
while True:
text = file3.readline()
if not text:
break
file4.write(text)
file3.close()
file4.close()
案例三、计算总价
文件a.txt内容:每一行内容分别为商品名字,价钱,个数。
apple 10 3
tesla 100000 1
mac 3000 2
lenovo 30000 3
chicken 10 3
通过代码,将其构建成这种数据类型:[{‘name’:‘apple’,‘price’:10,‘amount’:3}, {‘name’:‘tesla’,‘price’:1000000,‘amount’:1}…] 并计算出总价钱。
list = []
with open('a.txt','r',encoding='utf-8') as file:
for line in file:
list2 = line.strip().split()
if list2:
dic = {'name':list2[0],'price':list2[1],'amount':list2[2]}
list.append(dic)
continue
print(list)
price = 0
for i in list:
price += int(i['price']) * int(i['amount'])
print(price)
函数
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数的定义与调用
def my_len(s):
length = 0
for i in s:
length = length + 1
print(length)
my_len("hello world")
定义:def 关键词开头,空格之后接函数名称和圆括号(),最后还有一个":"。
def 是固定的,不能变,他就是定义函数的关键字。
空格 为了将def关键字和函数名分开,必须空(四声),当然你可以空2格、3格或者你想空多少都行,但正常人还是空1格。
函数名:函数名只能包含字符串、下划线和数字且不能以数字开头。虽然函数名可以随便起,但我们给函数起名字还是要尽量简短,并能表达函数功能
括号:是必须加的,先别问为啥要有括号,总之加上括号就对了!
注释:每一个函数都应该对功能和参数进行相应的说明,应该写在函数下面第一行。以增强代码的可读性。
调用:就是 函数名() 要记得加上括号。
函数的返回值
def my_len():
s = 'hello world'
length = 0
for i in s:
length = length + 1
return length
str_len = my_len()
print(str_len)
return关键字的作用
-
return 是一个关键字,这个词翻译过来就是“返回”,所以我们管写在return后面的值叫“返回值”。
-
不写return的情况下,会默认返回一个None
-
一旦遇到return,结束整个函数。
-
返回多个值会被组织成元组被返回,也可以用多个值来接收
def ret_demo(): return 1,2,'a',['hello','world'] ret = ret_demo() print(ret) 运行结果: (1, 2, 'a', ['hello', 'world'])
函数的参数
带参数的函数
def my_len(s):
length = 0
for i in s:
length += 1
return length
ret = my_len('hello world!')
print(ret)
实际的要交给函数的内容,简称实参
在定义函数的时候它只是一个形式,表示这里有一个参数,简称形参。
-
按照位置传值:位置参数
def maxnumber(x,y): the_max = x if x > y else y return the_max ret = maxnumber(10,20) print(ret)
-
按照关键字传值:关键字参数
def maxnumber(x,y): the_max = x if x > y else y return the_max ret = maxnumber(y = 10,x = 20) print(ret)
-
位置、关键字形式混着用:混合传参。
def maxnumber(x,y): the_max = x if x > y else y return the_max ret = maxnumber(10,y = 20) print(ret)
位置参数必须在关键字参数的前面
对于一个形参只能赋值一次
-
默认参数
def stu_info(name,age = 18): print(name,age) stu_info('aaron') stu_info('song',50)
-
默认参数是一个可变数据类型
def demo(a,l = []):
l.append(a)
print(l)
demo('abc')
demo('123')
-
动态参数
def demo(*args,**kwargs): print(args,type(args)) print(kwargs,type(kwargs)) demo('aaron',1,3,[1,3,2,2],{'a':123,'b':321},country='china',b=1) #动态参数,也叫不定长传参,就是你需要传给函数的参数很多,不定个数,那这种情况下,你就用*args,**kwargs接收,args是元祖形式,接收除去键值对以外的所有参数,kwargs接收的只是键值对的参数,并保存在字典中。
python中函数的参数有位置参数、默认参数、可变参数、命名关键字参数和关键字参数,这个顺序也是定义函数时的必须顺序。
命名空间和作用域
代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做命名空间;
在函数的运行中开辟的临时的空间叫做局部命名空间
命名空间一共分为三种:
- 全局命名空间
- 局部命名空间
- 内置命名空间
取值顺序:
- 在局部调用:局部命名空间->全局命名空间->内置命名空间
- 在全局调用:全局命名空间->内置命名空间
作用域
- 全局作用域:包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效
- 局部作用域:局部名称空间,只能在局部范围内生效
globals和locals方法
print(globals())
print(locals())
def func():
a = 12
b = 20
print(globals())
print(locals())
func()
global关键字
- 声明一个全局变量。
- 在局部作用域想要对全局作用域的全局变量进行修改时,需要用到global(限于字符串,数字)。
def func():
global a
a = 3
func()
print(a)
count = 1
def search():
global count
count = 2
search()
print(count)
对可变数据类型(list,dict,set)可以直接引用不用通过global
li = [1,2,3]
dic = {'name':'aaron'}
def change():
li.append(4)
dic['age'] = 18
print(dic)
print(li)
change()
print(dic)
print(li)
nonlocal
- 不能修改全局变量。
- 在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。
def add_b():
b = 1
def do_global():
b = 10
print(b)
def dd_nolocal():
nonlocal b # 应用了上一层的变量b
b = b + 20
print(b) # 发生了改变
dd_nolocal() # 调用函数,导致do_global的命名空间b也改变了
print(b)
do_global()
print(b)
add_b() # 最上面一层没有变化
运行结果
10
30
30
1
函数的嵌套和作用域链
def mymax(x,y):
m = x if x > y else y
return m
def maxx(a,b,c,d):
res1 = mymax(a,b)
res2 = mymax(res1,c)
res3 = mymax(res2,d)
return res3
ret = maxx(23,453,12,-13)
print(ret)
def f1():
print("in f1")
def f2():
print("in f2")
f2()
f1()
函数名的本质
函数名本质上就是函数的内存地址
- 可以被引用
def func():
print('in func')
f = func
print(f)
f()
- 可以被当做容器类型的元素
def f1():
print('f1')
def f2():
print('f2')
def f3():
print('f3')
l = [f1,f2,f3]
d = {'f1':f1,'f2':f2,'f3':f3}
# 调用
l[0]()
d['f2']()
- 可以当做函数的参数和返回值
def f1():
print('f1')
def func(argv):
argv()
return argv
f = func(f1)
f()
闭包
def func():
name = 'aaron'
def inner():
print(name)
return inner
f = func()
f()
内部函数包括对外部作用域而非全局作用域变量的引用,该内部函数称为闭包函数
判断闭包函数的方法closure(属性)
def func():
name = 'aaron'
def inner():
print(name)
print(inner.__closure__)
return inner
f = func()
f()
# 最后运行的结果里面有cell就是闭包
name = 'aaron'
def func():
def inner():
print(name)
print("inner函数",inner.__closure__)
return inner
f = func()
f()
# 输出结果为None,说明不是闭包
def wrapper():
money = 1000
def func():
name = 'apple'
def inner():
print(name,money)
print("inner函数", inner.__closure__)
return inner
print("func函数", func.__closure__)
return func
f = wrapper()
i = f()
i()
运行结果:
func函数 (<cell at 0x000002EE8790D738: int object at 0x000002EE87D9BE90>,)
inner函数 (<cell at 0x000002EE8790D738: int object at 0x000002EE87D9BE90>, <cell at 0x000002EE87A13BE8: str object at 0x000002EE879342B0>)
apple 1000
def func(a,b):
def inner(x):
return a*x + b
return inner
func1 = func(4,5)
func2 = func(7,8)
print(func1(5),func2(6))
25 50
from urllib.request import urlopen
def func():
content = urlopen('http://myip.ipip.net').read()
def get_content():
return content
return get_content
code = func()
content = code()
print(content.decode('utf-8'))
content2 = code()
print(content2.decode('utf-8'))
装饰器
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
装饰器的形成过程
如果要测试某个函数的执行时间
import time
def func1():
print('in func1')
def timer(func):
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
func1 = timer(func1)
func1()
但是如果有多个函数,我都想让你测试他们的执行时间,你每次是不是都得func1 = timer(func1)?这样 还是有点麻烦,因为这些函数的函数名可能是不相同,有func1,func2,graph,等等,所以更简单的方 法,python给你提供了,那就是语法糖。
import time
def timer(func):
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
@timer # func1 = timer(func1)
def func1():
time.sleep(1)
print('in func1')
func1()
装饰一个带参数的函数
import time
def timer(func):
def inner(a):
start = time.time()
func(a)
print(time.time() - start)
return inner
@timer
def func1(a):
time.sleep(1)
print(a)
func1('hello world')
装饰一个带各种参数的函数
import time
def timer(func):
def inner(*args, **kwargs):
start = time.time()
func(args, kwargs)
print(time.time() - start)
return inner
@timer
def func1(*args, **kwargs):
print(args, kwargs)
func1('hello world','abc',123,432)
查看函数的相关信息,在加上装饰器后就失效了
def index():
'''这是一条注释信息'''
print('from index')
print(index.__doc__) # 查看函数注释
print(index.__name__) # 查看函数名称
导入wraps装饰器
from functools import wraps
def deco(func):
@wraps(func)
def wrapper(*args,**kwargs):
return func(*args,**kwargs)
return wrapper
@deco
def index():
'''这是一条注释信息'''
print('from index')
print(index.__doc__) # 查看函数注释
print(index.__name__) # 查看函数名称
开放封闭原则
一句话,软件实体应该是可扩展但是不可修改的
- 对于扩展是开放的
- 对于修改是封闭的
装饰器完美的遵循了这个开放封闭原则
装饰器的主要功能和固定结构
def timer(func):
def inner(*args, **kwargs):
'''执行函数之前要做的'''
re = func(*args, **kwargs)
'''执行函数之后要做的'''
return re
return inner
# 下面是加上wraps的固定结构
from functools import wraps
def timer(func):
@wraps(func)
def wrapps(*args, **kwargs)
return func(*args, **kwargs)
return wrapper
带参数的装饰器
加上一个outer函数,可以携带一个flag的值,然后控制装饰器是否生效
def outer(flag):
def timer(func):
def inner(*args,**kwargs):
if flag:
print('函数开始执行')
re = func(*args,**kwargs)
if flag:
print('函数执行完毕')
return re
return inner
return timer
@outer(True)
def func():
print('test')
func()
多个装饰器装饰一个参数
def wrapper1(func):
def inner():
print('第一个装饰器,在程序运行之前')
func()
print('第一个装饰器,在程序运行之后')
return inner
def wrapper2(func):
def inner():
print('第二个装饰器,在程序运行之前')
func()
print('第二个装饰器,在程序运行之后')
return inner
@wrapper1
@wrapper2
def f():
print('Hello')
f()
第一个装饰器,在程序运行之前
第二个装饰器,在程序运行之前
Hello
第二个装饰器,在程序运行之后
第一个装饰器,在程序运行之后
迭代器
迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
可迭代对象
已知list、tuple、str等类型的数据使用for…in…的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也叫迭代。
# 整型数据可以迭代吗?
for i in 100:
print(i)
判断一个对象是否可以迭代
可以使用isinstance()判断一个对象是否是iterable对象:
# 字符串、列表、元组、字典、集合都可以被for循环,可以说明他们都是可迭代的
from collections import Iterable # from collections.abc import Iterable
l = [1, 2, 3, 4]
t = (1, 2, 3, 4)
d = {1: 2, 3: 4}
s = {1, 2, 3, 4}
print(isinstance(l, Iterable))
print(isinstance(t, Iterable))
print(isinstance(d, Iterable))
print(isinstance(s, Iterable))
可迭代对象的本质
分析对可迭代对象进行迭代使用的过程,发现每迭代一次(即在for…in…中每循环一次)都会返回对象中的下一条数据,一直向后读取数据直到迭代了所有数据后结束。那么,在这个过程中就应该有一个“人”去记录每次访问到了第几条数据,以便每次迭代都可以返回下一条数据。我们把这个能帮助我们进行数据迭代的“人”称为迭代器。
可迭代对象的本质就是可以向我们提供一个这样的中间“人”即迭代器帮助我们对其进行迭代遍历使用。
可迭代对象通过____iter____方法向我们提供一个迭代器,我们在迭代一个可迭代对象时,实际上就是先获取该对象提供的一个迭代器,然后通过这个迭代器来依次获取对象中的每一个数据。
也就是说,一个具备了iter方法的对象,就是一个可迭代对象
可以被迭代要满足的要求就叫做可迭代协议。可迭代协议的定义非常简单,就是内部实现了____iter____方法。
l = [1, 2, 3, 4]
t = (1, 2, 3, 4)
d = {1: 2, 3: 4}
s = {1, 2, 3, 4}
print(dir(l))
print(dir(t))
print(dir(d))
print(dir(s))
可迭代的:内部必须含有一个____iter____方法
____iter____方法与____next____方法
迭代器遵循迭代器协议:必须拥有iter方法和next方法
list、tuple等都是可迭代对象,我们可以通过iter()函数获取这些可迭代对象的迭代器。然后我们可以对获取到的迭代器不断使用next()函数来获取下一条数据。iter()函数实际上就是调用了可迭代对象的____iter____方法。
l = [1, 2, 3, 4]
l_iter = l.__iter__()
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
for循环的内部
l = [1, 2, 3, 4]
l_iter = l.__iter__()
while True:
try:
item = l_iter.__next__()
print(item)
except StopIteration:
break
判断对象是否为迭代器
可以使用isinstance()判断一个对象是否是iterator对象
from collections.abc import Iterator
isinstance([], Iterator)
False
isinstance(iter([]), Iterator)
True
isinstance(iter"abc", Iterator)
True
为什么要有for循环
for循环就是基于迭代器协议提供了一个统一的可以遍历所有对象的方法,即在遍历之前,先调用对象的____iter____方法将其转换成一个迭代器,然后使用迭代器协议去实现循环访问,这样所有的对象都可以通过for循环来遍历了。
最重要的一点,转化成迭代器,在循环时,同一时刻在内存中只出现一条数据,极大限度的节省了内存。
for item in iterable循环的本质就是先通过iter()函数获取可迭代对象iterable的迭代器,然后对获取到的迭代器不断调用next()方法获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。
生成器
python中提供的生成器
- 生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行
- 生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
生成器Generator
- 本质:迭代器(所以自带了iter方法和next方法,不需要我们去实现)
- 特点:惰性运算,开发者自定义
生成器函数
一个包含yield关键字的函数就是一个生成器函数
yield可以为我们从函数中返回值,但是yield又不同与return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。
import time
def generator_func1():
a = 1
print('将a赋值')
yield a
b = 2
print('将b赋值')
yield b
g1 = generator_func1()
print(g1, next(g1))
print(next(g1))
生成器不会一次在内存中生成太多数据
比如我想卖包子。让包子工厂开始加工10000个包子,但是如果一下子全部生产好,没地方放,而且容易坏。
那么可以让包子工厂在我需要的时候再生产
def produce():
'''生产包子'''
for i in range(10000):
yield '生产了第%s个包子'%i
produce_g = produce()
print(produce_g.__next__())
print(produce_g.__next__())
print(produce_g.__next__())
# 需要一批包子
num = 0
for i in produce_g:
print(i)
num += 1
if num == 5:
break
总结
- 使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)
- yield关键字有两点作用:
- 保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
- 将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作业
- 可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)
send
send获取下一个值的效果和next基本一致
只是在获取下一个值的时候,给上一yield的位置传递一个数据
使用send的注意事项
- 第一次使用生成器的时候 是用next获取下一个值
- 最后一个yield不能接受外部的值
def generator():
print(123)
content = yield 1
print('=========', content)
print(456)
yield 2
g = generator()
ret = g.__next__()
print('***', ret)
ret = g.send('hello')
print('***', ret)
各种推导式
习题
- 过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母
- 求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元组列表
list1 = ['dcserw', 'cserwe', 'sd', 'daefs']
print([i.upper() for i in list1 if len(i) >= 3])
print([(x, y) for x in range(6) for y in range(6) if x % 2 = 0 if y % 2 = 1])
print([(x, y) for x in range(6) if x % 2 == 0 for y in range(6) if y % 2 != 0])
递归与二分法
初始递归
-
递归的定义——在一个函数里再调用这个函数本身
-
为了防止递归无限进行,通常我们会指定一个退出条件
-
递归的最大深度——997
def foo(n):
print(n)
n += 1
foo(n)
foo(1)
997是python为了我们程序的内存优化所设定的一个默认值,我们当然还可以通过一些手段去修改它。
import sys
print(sys.setrecursionlimit(10000))
def foo(n):
print(n)
n += 1
foo(n)
foo(1)
将python允许的递归深度设置为了1w,至于实际可以达到的深度就取决于计算机的性能了。
汉诺塔问题
从左到右 A B C 柱 大盘子在下, 小盘子在上, 借助B柱将所有盘子从A柱移动到C柱, 期间只有一个原则: 大盘子只能在小盘子的下面.
我们只需要考虑如果有64层,先将A柱上的63层移动到B柱上,然后将A柱的第64个移动到C柱上,然后 将B柱上的63层移动到C柱上即可。
那怎么把63层都移到B柱上,这个问题可以用上面相同的方法解决。
def move(n,a,b,c):
if n == 1:
print(a,'->',c)
else:
# 将n-1个盘子从a --> b
move(n-1,a,c,b)
# 将剩余的最后一个盘子从a --> c
print(a,'->',c)
# 将剩余的n-1个盘子从 b --> c
move(n-1,b,a,c)
n = int(input('请输入汉诺塔的层数:'))
move(n,'A','B','C')
递归实现三级菜单
menu = {
'山东': {
'青岛': ['四方', '黄岛', '崂山', '李沧', '城阳'],
'济南': ['历城', '槐荫', '高新', '长青', '章丘'],
'烟台': ['龙口', '莱山', '牟平', '蓬莱', '招远']
},
'江苏': {
'苏州': ['沧浪', '相城', '平江', '吴中', '昆山'],
'南京': ['白下', '秦淮', '浦口', '栖霞', '江宁'],
'无锡': ['崇安', '南长', '北塘', '锡山', '江阴']
},
'浙江': {
'杭州': ['西湖', '江干', '下城', '上城', '滨江'],
'宁波': ['海曙', '江东', '江北', '镇海', '余姚'],
'温州': ['鹿城', '龙湾', '乐清', '瑞安', '永嘉']
},
'安徽': {
'合肥': ['蜀山', '庐阳', '包河', '经开', '新站'],
'芜湖': ['镜湖', '鸠江', '无为', '三山', '南陵'],
'蚌埠': ['蚌山', '龙子湖', '淮上', '怀远', '固镇']
},
'广东': {
'深圳': ['罗湖', '福田', '南山', '宝安', '布吉'],
'广州': ['天河', '珠海', '越秀', '白云', '黄埔'],
'东莞': ['莞城', '长安', '虎门', '万江', '大朗']
},
'测试': {}
}
def threeLM(dic):
while True:
for k in dic:print(k)
key = input('input>>').strip()
if key == 'b' or key == 'q':return key
elif key in dic.keys() and dic[key]:
ret = threeLM(dic[key])
if ret == 'q': return 'q'
threeLM(menu)
# l = [menu]
# while l:
# for key in l[-1]:print(key)
# k = input('input>>').strip() # 北京
# if k in l[-1].keys() and l[-1][k]:l.append(l[-1][k])
# elif k == 'b':l.pop()
# elif k == 'q':break
二分查找算法
如果想在列表中查找某个数字,可以排序后从中间开始查找
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oSMBGt1i-1614480844600)(D:\QQ文件\2652516133\Image\SharePic\20210221211543.png)]
l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]
# def func(l,aim):
# mid = (len(l)-1)//2
# if l:
# if aim > l[mid]:
# func(l[mid+1:],aim)
# elif aim < l[mid]:
# func(l[:mid],aim)
# elif aim == l[mid]:
# print("找到了",mid)
# else:
# print('找不到')
# func(l,66)
# func(l,6)
def search(num,l,start=None,end=None):
start = start if start else 0
end = len(l)-1 if end is None else end
mid = (end - start)//2 + start
if start > end:
return None
elif l[mid] > num :
return search(num,l,start,mid-1)
elif l[mid] < num:
return search(num,l,mid+1,end)
elif l[mid] == num:
return mid
ret = search(18,l)
print(ret)