Python基础-09 高阶函数

高阶函数

  • 一个函数作为另一个的返回值
def demo():
    print('我是test函数')
    return 'hello'

def demo():
    print('我是demo函数')
    return test

def bar():
    print('我是bar函数')
    return test()

x = test()  # 我是test函数
print(x)  # hello

y = demo()  # 我是demo函数
print(y)  # <function test at 0x000002136781CB80>
z = y()  # 我是test函数
print(z)    # hello

a = 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 = 10

    def inner():
        # 在内部函数中如何修改外部函数的局部变量的值
        nonlocal x  # 此处声明之后,修改的x就是外部的x,而不是内部新定义的变量x
        y = x + 1
        x = 20  # 当前语句会导致内部函数inner新建一个名为x的局部变量,因此上一句会报错
    
    return inner

outer()()

装饰器

计算一段代码的执行时间

  • 在代码执行之前获取当前时间
  • 在代码执行之后获取当前时间
  • 使用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 += 1

print(x)
# 代码完成之后,在获取一下当前时间戳
end = time.time()
print('end =',end)
print('当前程序运行耗时秒数:',end-start)
  • 求任意代码的运行时间,可以用定义函数的形式实现
import time

def 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 += 1

    print(x)

def sleep_time():
    print('hello')
    time.sleep(3)
    print('hi')

get_used_time(sleep_time)
get_used_time(test)

装饰器的使用

  • 装饰器使用的例子
import time

def 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 = 0
    for i in range(1,100000):
        x += 1
    print(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 time

def cal_time(fn):
    def inner():
        start = time.time()
        fn()
        end = time.time()
        print('代码耗时',end-start)
    
    return inner

@cal_time
def demo():
    x = 0
    for i in range(1,100000):
        x += 1
    return x

x = demo()
print(x)

# 代码耗时 0.007985830307006836
# None
# 此时demo()实际上返回的是inner函数,因此返回值为None
  • 针对如上问题,可以在内部函数(inner)中,定义一个变量获取原函数(demo)的返回值,并返回,如下示例:
import time

def cal_time(fn):
    def inner():
        start = time.time()
        x = fn()  # 定义一个x变量,获取原函数的返回值
        end = time.time()
        print('代码耗时',end-start)
        return x  # 返回x
    
    return inner

@cal_time
def demo():
    x = 0
    for i in range(1,100000):
        x += 1
    return x

x = 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为了方便开发,提供了很多的内置模块
  • 模块导入的方法
    1. import 模块名 直接导入一个模块
    2. from 模块名 import 函数名 导入一个模块中某一个方法或者变量
    3. from 模块名 import * 导入这个模块中"所有"的变量和方法
      1. 相比于第一种import 模块名
      2. 该方法可以直接使用模块中的变量,如print(pi)
      3. 第一种方法需要使用模块名.变量名,如print(math.pi)
    4. from 模块名 as 别名 导入这个模块,并将这个模块起个别名
    5. 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 uuid

print(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配置文件,写入以下内容
[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 = 1

def 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('这是一个测试')

包的使用

  • 和模块的导入语法类似,可以导入各种系统内置或者外部的包

猜你喜欢

转载自blog.csdn.net/Lz__Heng/article/details/130192234