高阶函数
- 一个函数作为另一个的返回值
def demo():print('我是test函数')return 'hello'def demo():print('我是demo函数')return testdef bar():print('我是bar函数')return test()x = test() # 我是test函数
print(x) # helloy = demo() # 我是demo函数
print(y) # <function test at 0x000002136781CB80>
z = y() # 我是test函数
print(z) # helloa = bar() # 我是bar函数 我是test函数
print(a) # hello
- 一个函数作为另一个函数的参数(lambda 匿名函数)
# sort filter map reduce方法
# 主要还是 lambda 匿名函数的使用
- 函数内部再定义一个函数
# 函数嵌套
def outer(x):def inner():print('我是inner函数')if x > 18:inner()print('我是outer函数')return 'hello'outer(12) # 我是outer函数
outer(21) # 我是inner函数 我是outer函数
闭包
- 首先需要外部函数嵌套内部函数
- 其次需要外部函数返回内部函数(不是返回内部函数的调用)
- 在外部函数定义一个变量,是一个局部变量
- 在内部函数中,会操作这个定义的局部变量
- 外部函数的返回值是内部函数
- 闭包 = (函数块+引用环境)
- 如果在一个内部函数中,对外在的作用域(但不是在全局作用域)的变量进行引用,则内部的这个函数就可以被认为是闭包
如何在内部函数中修改外部函数的局部变量
def outer():x = 10def inner():# 在内部函数中如何修改外部函数的局部变量的值nonlocal x # 此处声明之后,修改的x就是外部的x,而不是内部新定义的变量xy = x + 1x = 20 # 当前语句会导致内部函数inner新建一个名为x的局部变量,因此上一句会报错return innerouter()()
装饰器
计算一段代码的执行时间
- 在代码执行之前获取当前时间
- 在代码执行之后获取当前时间
- 使用time模块可以获取当前的时间戳
import time# 在代码运行之前,获取一下时间
start = time.time()
# 时间戳是从1970-01-01 00:00:00 UTC(国际标准时)时间到现在时间经过的秒数
# 如需查看当前时间,需要将东八区时间转换成当前的国际标准时
print('start =',start)x = 0
for i in range(1,10000000):x += 1print(x)
# 代码完成之后,在获取一下当前时间戳
end = time.time()
print('end =',end)
print('当前程序运行耗时秒数:',end-start)
- 求任意代码的运行时间,可以用定义函数的形式实现
import timedef get_used_time(fn):start = time.time()print('start =',start)fn()end = time.time()print('end =',end)print('当前程序运行耗时秒数:',end-start)def test():x = 0 for i in range(1,10000000):x += 1print(x)def sleep_time():print('hello')time.sleep(3)print('hi')get_used_time(sleep_time)
get_used_time(test)
装饰器的使用
- 装饰器使用的例子
import timedef cal_time(fn):def inner():start = time.time()fn()end = time.time()print('代码耗时',end-start)return inner@cal_time # 第一件事,调用cal_time函数,第二件事,将demo函数当做cal_time的参数传递给fn
def demo():x = 0for i in range(1,100000):x += 1print(x)@cal_time
def foo():print('hello')time.sleep(3)print('hi')demo() # 第三件事,调用inner函数,此时的demo函数,已经是被cal_time函数装饰过之后的inner函数
foo()# 99999
# 代码耗时 0.00999903678894043
# hello
# hi
# 代码耗时 3.0013046264648438
装饰器的详解
- 装饰器的编写:
- 定义一个函数(decorate),有一个形参(fn)
- 该函数内部还有一个内部函数(inner)
- 内部函数(inner)对形参(fn)进行处理(装饰),并调用形参(fn)传进来的函数
- 外部函数(decorate)返回内部函数(inner)
- 使用时,在需要被装饰的函数(demo)开头,使用@符号引用装饰器(@decorate)
- 解释器在对该函数(demo)进行处理时,会先调用装饰器函数(decorate)
- 将需要被装饰的函数(demo)作为装饰器函数的参数传递给形参(fn)
- 在装饰器函数内部(inner)对形参(fn)进行调用并装饰
- 装饰器函数处理并返回内部函数(inner),此时原函数(demo)就变为了被装饰器装饰之后的函数(inner)
- 在调用原函数(demo)时,实际上调用的就是被装饰器函数(decorate)装饰之后返回的(inner)
- 如果需要被装饰的函数(demo)有返回值,而装饰器函数(decorate)的内部函数(inner)并没有返回值,则会导致原函数(demo)无法正常返回,因此需要在内部函数中获取原函数的返回值,并返回,如下示例:
import timedef cal_time(fn):def inner():start = time.time()fn()end = time.time()print('代码耗时',end-start)return inner@cal_time
def demo():x = 0for i in range(1,100000):x += 1return xx = demo()
print(x)# 代码耗时 0.007985830307006836
# None
# 此时demo()实际上返回的是inner函数,因此返回值为None
- 针对如上问题,可以在内部函数(inner)中,定义一个变量获取原函数(demo)的返回值,并返回,如下示例:
import timedef cal_time(fn):def inner():start = time.time()x = fn() # 定义一个x变量,获取原函数的返回值end = time.time()print('代码耗时',end-start)return x # 返回xreturn inner@cal_time
def demo():x = 0for i in range(1,100000):x += 1return xx = demo()
print(x)# 代码耗时 0.008002519607543945
# 99999
- 同理,如果原函数(demo)有形参,就需要在内部函数(inner)中定义相应的形参,并在内部函数(inner)中,调用原函数(demo)时传参,如果需要多个参数,可以使用inner(x,*args,**kwargs):接受多个参数
装饰器的高级使用
def can_play(fn):def inner(x,y,*args,**kwargs):clock = kwargs.get('clock',23)if clock >= 22:fn(x,y)else:print('太晚了赶紧睡')return inner@can_play
def play_games(name,game):print('{}正在玩{}'.format(name,game))play_games('张三','王者荣耀',m='hello',n='good',clock=18) # 太晚了赶紧睡
play_games('王五','吃鸡') # 王五正在玩吃鸡
模块
- 在python中,一个py文件,就可以理解为一个模块
- 不是所有的py文件都能作为一个模块来导入
- 如果想要一个py文件可以被导入,文件名必须要遵守命名规则
- 由数字,字母,下划线组成
- 不能以数字开头
导入模块的语法
- python为了方便开发,提供了很多的内置模块
- 模块导入的方法
import 模块名
直接导入一个模块from 模块名 import 函数名
导入一个模块中某一个方法或者变量from 模块名 import *
导入这个模块中"所有"的变量和方法- 相比于第一种import 模块名
- 该方法可以直接使用模块中的变量,如print(pi)
- 第一种方法需要使用模块名.变量名,如print(math.pi)
from 模块名 as 别名
导入这个模块,并将这个模块起个别名from 模块名 import 方法名 as 别名
导入模块中的某个方法,将这个方法起个别名
# 导入这个模块之后,就可以使用这个模块里面的方法
import time # 使用import 模块名 直接导入一个模块
from random import randint # from 模块名 import 函数名 导入一个模块中某一个方法或者变量
from math import * # from 模块名 import * 导入这个模块中"所有"的变量和方法(相比import 模块名,可以直接使用变量或者方法,而不需要模块名.变量名)
from datetime as dt # from 模块名 as 别名 导入这个模块,并将这个模块起个别名
from copy import deepcopy as dp # from 模块名 import 方法名 as 别名 导入模块中的某个方法,将这个方法起个别名
常见的内置模块
os模块
- os模块是用来调用操作系统里的方法
import os
os.getcwd() # 获取当前的工作目录,即当前python脚本工作的目录
os.chdir('test') # 改变当前脚本工作目录,相当于shell下的cd命令
os.rename('毕业论文.txt','毕业论文-最终版.txt') # 文件重命名
os.remove('毕业论文.txt') # 删除文件
os.rmdir('demo') # 删除空文件夹
os.removedirs('demo') # 删除空文件夹
os.mkdir('demo') # 创建一个文件夹
os.chdir('C:\\') # 切换工作目录
os.listdir('C:\\') # 列出指定目录里的所有文件和文件夹
os.name # nt->widonws posix->Linux/Unix或者MacOS
os.environ # 获取到环境配置
os.environ.get('PATH') # 获取指定的环境配置os.path.abspath(path) # 获取Path规范会的绝对路径
os.path.exists(path) # 如果Path存在,则返回True
os.path.isdir(path) # 如果path是一个存在的目录,返回True。否则返回False
os.path.isfile(path) # 如果path是一个存在的文件,返回True。否则返回False
os.path.splitext(path) # 用来将指定路径进行分隔,可以获取到文件的后缀名
sys模块
- 系统相关的功能
import sys
sys.path # 模块的查找路径,结果是一个列表
sys.argv # 传递给Python脚本的命令行参数列表
sys.exit(code) # 让程序以指定的退出码结束sys.stdin # 标准输入。可以通过它来获取用户的输入,和input相关
sys.stdout # 标准输出。可以通过修改它来改变默认输出位置
sys.stderr # 错误输出。可以通过修改它来改变错误删除
math模块
- 数学计算相关的模块
import math
print(math.fabs(-100)) # 取绝对值
print(math.ceil(34.01)) #向上取整
print(math.factorial(5)) # 计算阶乘
print(math.floor(34.98)) # 向下取整
print(math.pi) # π的值,约等于 3.141592653589793
print(math.pow(2, 10)) # 2的10次方
print(math.sin(math.pi / 6)) # 正弦值
print(math.cos(math.pi / 3)) # 余弦值
print(math.tan(math.pi / 2)) # 正切值
random模块
- 和随机数相关的模块
print(random.random()) # 生成 [0,1)的随机浮点数
print(random.uniform(20, 30)) # 生成[20,30]的随机浮点数
print(random.randint(10, 30)) # 生成[10,30]的随机整数,等价于randrange(a,b+1)
print(random.randrange(20, 30)) # 生成[20,30)的随机整数
print(random.choice('abcdefg')) # 从列表里随机取出一个元素
print(random.sample('abcdefghij', 3)) # 从列表里随机取出指定个数的元素
datetime模块
- date类: 用来显示日期
- time类: 用来显示时间
- datetime类: 用来显示时间日期
- timedelta: 用来计算时间
import datetime
print(datetime.date(2020, 1, 1)) # 创建一个日期
print(datetime.time(18, 23, 45)) # 创建一个时间
print(datetime.datetime.now()) # 获取当前的日期时间
print(datetime.datetime.now() + datetime.timedelta(3)) # 计算三天以后的日期时间
time模块
- time模块用来操作时间
print(time.time()) # 获取从1970-01-01 00:00:00 UTC 到现在时间的秒数
print(time.strftime("%Y-%m-%d %H:%M:%S")) # 按照指定格式输出时间
print(time.asctime()) #Mon Apr 15 20:03:23 2019 给一个元组,转换成标准时间
print(time.ctime()) # Mon Apr 15 20:03:23 2019 给一个指定的时间戳可以转化成标准时间print('hello')
print(time.sleep(10)) # 让线程暂停10秒钟
print('world')
calendar模块
- 和日历相关的操作
calendar.setfirstweekday(calendar.SUNDAY) # 设置每周起始日期码。周一到周日分别对应 0 ~ 6
calendar.firstweekday()# 返回当前每周起始日期的设置。默认情况下,首次载入calendar模块时返回0,即星期一。
c = calendar.calendar(2019) # 生成2019年的日历,并且以周日为其实日期码
print(c) #打印2019年日历
print(calendar.isleap(2000)) # True.闰年返回True,否则返回False
count = calendar.leapdays(1996,2010) # 获取1996年到2010年一共有多少个闰年
print(calendar.month(2019, 3)) # 打印2019年3月的日历
hashlib模块和hmac模块
这两个模块都是用来进行数据加密的
hashlib模块里主要支持两种算法 md5 和 sha 加密
import hashlib# 待加密信息
str = '这是一个测试'# 创建md5对象
hl = hashlib.md5('hello'.encode(encoding='utf8'))
print('MD5加密后为 :' + hl.hexdigest())h1 = hashlib.sha1('123456'.encode())
print(h1.hexdigest())
h2 = hashlib.sha224('123456'.encode())
print(h2.hexdigest())
h3 = hashlib.sha256('123456'.encode())
print(h3.hexdigest())
h4 = hashlib.sha384('123456'.encode())
print(h4.hexdigest())
# sha算法后面的数字代表加密后的数字长度x = hashlib.md5()
x.update('abc'.encode('utf-8'))
print(x) # <md5 HASH object @ 0x0000020F9C19D9F0>
print(x.hexdigest()) # 900150983cd24fb0d6963f7d28e17f72
hmac模块加密,可以指定加密的密钥
h = hmac.new('h'.encode(),'你好'.encode())
result = h.hexdigest()
print(result) # 获取加密后的结果
uuid模块
用来生成一个全局唯一的id
方法 | 作用 |
---|---|
uuid.uuid1() | 基于MAC地址,时间戳,随机数来生成唯一的uuid,可以保证全球范围内的唯一性 |
uuid.uuid2() | 算法与uuid1相同,不同的是把时间戳的前四位置换为POSIX的UID,需要注意的是python中没有基于DEC的算法,因此python的uuid模块中没有uuid2这个方法 |
uuid.uuid3(namespace,name) | 通过计算一个命名空间的名字的md5散列值来给出一个uuid,可以保证不同的namespace中不通的name有不同的uuid,但是相同的名字就是相同的uuid了。namespace并不是一个自己手动指定的字符串或其他量,而是在uuid模块中本身给出的一些值。比如uuid.NAMESPACE_DNS,uuid.NAMESPACE_OID,uuid.NAMESPACE_OID这些值。这些值本身也是UUID对象,根据一定的规则计算得出。 |
uuid.uuid4() | 通过伪随机数得到uuid,是有概率重复的 |
uuid.uuid5(namespace,name) | 和uuid3基本相同,采用的算法换成了sha1 |
一般来说,在对uuid的需求不是很复杂的时候,uuid1和uuid4方法就已经够使用了
import uuidprint(uuid.uuid1()) # 根据时间戳和机器码生成uuid,可以保证全球唯一
print(uuid.uuid4()) # 随机生成uuid,可能会有重复# 使用命名空间和字符串生成uuid.
# 注意一下两点:
# 1. 命名空间不是随意输入的字符串,它也是一个uuid类型的数据
# 2. 相同的命名空间和想到的字符串,生成的uuid是一样的
print(uuid.uuid3(uuid.NAMESPACE_DNS, 'hello'))
print(uuid.uuid5(uuid.NAMESPACE_OID, 'hello'))
pip的使用
- 在cmd中直接运行pip,如果报错,检查是否配置环境变量或者python安装时没有安装pip
- pip命令:
pip install flask
(pip install <包名> 安装指定的包)pip uninstall flask
(pip uninstall <包名> 卸载指定的包)pip list
(用于列出当前环境安装了哪些模块)pip freeze
(列出当前环境安装了哪些模块,模块名和版本号用等号连接)- (可以使用该命令,将当前服务器上安装的所有包列表导出重定向到一个文件requirements.txt,在目标服务器上使用列表进行安装)
pip install -r requirements.txt
(读取requirements.txt文件中的内容进行安装)pip install <包名> -i <包源地址>
(临时修改,pip从指定的包源地址下载指定的包)- 永久修改pip下载位置:
- 在当前系统用户文件夹下新建pip文件夹,在pip文件夹下新建pip.ini配置文件,写入以下内容
- 永久修改pip下载位置:
[global]
index-url=https://pypi.douban.com/simple
[install]
trusted-host=pypi.douban.com
使用自定义模块
- 一个模块本质上就是一个py文件
- 自己定义一个模块,实际上就是自己写一个py文件
- 如果想要一个py文件可以被导入,文件名必须要遵守命名规则
- 由数字,字母,下划线组成
- 不能以数字开头
- 导入一个模块之后,就可以使用这个模块中的变量和方法了
- 使用
from <module_name> import *
导入这个模块中"所有"的变量和函数,可以直接使用 - 本质上是读取模块中的
_all_
属性,看该属性中定义了哪些变量和函数 - 如果模块中没有定义
_all_
属性,才会导入所有不以_
开头的变量和函数
# module_1
__all__ = ['n','test']
n = 100
m = 1def test():print('我是test方法')# 如果当前的模块被使用from <module_name> import * 方法进行调用
# 只能使用该模块中的n变量和test方法# module_2
x = 1000
y = 'teszxt'_age = 19# 当前module_2在被导入时,由于没有设置_all_属性,以_开头的变量都不会呗引用
# _age变量不能被导入
# 使用import module_2 module_2._age
# 仍然可以对该变量进行调用
- 针对_开头的函数,在使用
import <module_name>
方式仍可以被调用的情况, - 可以在自定义的模块最后使用
del <module_name>
的方法,禁止本模块外调用该变量
__name__的使用
__name__
当直接运行这个py文件的时候,值是__main__
- 如果作为一个模块导入后,
__name__
的值是文件名 - 因此如果需要在模块内部进行调试时,可以使用
__name__
保证只有在运行当前模块时候才运行,如下示例:
if __name__ == '__main__':print('这是一个测试')
包的使用
- 和模块的导入语法类似,可以导入各种系统内置或者外部的包