Python第五周 学习笔记(1)

高阶函数


  • First Class Object
  • 函数也是对象,可调用的对象
  • 函数可以作为普通变量、参数、返回值等等

  • 数学概念 y=g(f(x))
  • 在数学和计算机科学中,高阶函数应当是至少满足下面一个条件的函数
  • 接受一个或多个函数作为参数
  • 输出一个函数

内建高阶函数

  • sorted(iterable[, key][, reverse])

    • 排序
  • filter(function, iterable) --> filter object
    • 过滤数据
  • map(func, *iterables) --> map object
    • 映射

sorted(iterable[, key][, reverse]) 排序

  • 返回一个新的列表,对一个可迭代对象的所有元素排序,排序规则为key定义的函数,reverse表示是否排序翻转

filter(function, iterable)

  • 过滤可迭代对象的元素,返回一个迭代器
  • function一个具有一个参数的函数,返回bool

map(function, *iterables) --> map object

  • 对多个可迭代对象的元素按照指定的函数进行映射,返回一个迭代器

柯里化Currying

  • 指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数
    z = f(x, y) ==> z = f(x)(y)
  • 举例
    将加法函数柯里化
    def add(x, y):
    return x + y

    转换如下

    def add(x):
    def _add(y):
    return x+y
    return _add
    add(5)(6)

    通过嵌套函数就可以把函数转换成柯里化函数

装饰器



在OOP设计模式中,装饰器属于一种装饰模式
装饰器语法是一个语法糖

def logger(func):
    def wrapper(*args, **kwargs):
        print(‘call ‘ + func.__name__)
        return func(*args, **kwargs)
    return wrapper

@logger # 等价于add = logger(add)
def add(x,y):
return x + y

装饰器(无参)

  • 它是一个函数
  • 函数作为它的形参
  • 返回值也是一个函数
  • 可以使用@functionname方式,简化调用

装饰器是高阶函数,但装饰器是对传入函数的功能的装饰(功能增强)

文档字符串

  • 在函数语句块的第一行,且习惯是多行的文本,所以多使用三引号
  • 惯例是首字母大写,第一行写概述,空一行,第三行写详细描述
  • 可以使用特殊属性doc访问这个文档

装饰器副作用

  • 原函数对象的属性都被替换了(执行后返回的是wrapper的属性),而使用装饰器,我们的需求是查看被封装函数的属性

解决方法:

1.提供一个函数,被封装函数属性 ==copy==> 包装函数属性

def copy_properties(src, dst): # 可以改造成装饰器
    dst.__name__ = src.__name__
    dst.__doc__ = src.__doc__
  • 通过copy_properties函数将被包装函数的属性覆盖掉包装函数
  • 凡是被装饰的函数都需要复制这些属性,这个函数很通用
  • 可以将复制属性的函数构建成装饰器函数,带参装饰器

2.使用functools.update_wrapper(wrapper, wrapped)

import datetime, time, functools

def logger(duration, func=lambda name, duration: print(‘{} took {}s‘.format(name, duration))):
    def _logger(fn):
        def wrapper(*args,**kwargs):
            start = datetime.datetime.now()
            ret = fn(*args,**kwargs)
            delta = (datetime.datetime.now() - start).total_seconds()
            if delta > duration:
                func(fn.__name__, duration)
            return ret
        return functools.update_wrapper(wrapper, fn)
    return _logger

@logger(5) # add = logger(5)(add)
def add(x,y):
    time.sleep(1)
    return x + y
print(add(5, 6), add.__name__, add.__wrapped__, add.__dict__, sep=‘\n‘)

3.使用@functools.wraps(wrapped)

import datetime, time, functools

def logger(duration, func=lambda name, duration: print(‘{} took {}s‘.format(name, duration))):
    def _logger(fn):
        @functools.wraps(fn)
        def wrapper(*args,**kwargs):
            start = datetime.datetime.now()
            ret = fn(*args,**kwargs)
            delta = (datetime.datetime.now() - start).total_seconds()
            if delta > duration:
                func(fn.__name__, duration)
            return ret
        return wrapper
    return _logger

@logger(5) # add = logger(5)(add)
def add(x,y):
    time.sleep(1)
    return x + y

print(add(5, 6), add.__name__, add.__wrapped__, add.__dict__, sep=‘\n‘)

带参装饰器

  • 它是一个函数 函数作为它的形参
  • 返回值是一个不带参的装饰器函数
  • 使用@functionname(参数列表)方式调用
  • 可以看做在装饰器外层又加了一层函数

将记录的功能提取出来,这样就可以通过外部提供的函数来灵活的控制输出

参数注解



形如:

def add(x:int , y:int) -> int :
‘‘‘
:param x: int
:param y: int
:return: int
‘‘‘
return x + y
  • Python 3.5引入
  • 对函数的参数进行类型注解
  • 对函数的返回值进行类型注解
  • 只对函数参数做一个辅助的说明,并不对函数参数进行类型检查
  • 提供给第三方工具,做代码分析,发现隐藏的bug
  • 函数注解的信息,保存在annotations属性中
  • 键是参数名,值是class类型

变量注解使用较少

inspect模块



signature(callable),返回一个Signature类对象。获取签名(函数签名包含了一个函数的信息,包括函数名、它的参数类型、它所在的类和名称空间及其他信息)

sig = signature(fn)
params = sig.parameters

Signature类对象的parameters方法返回的是一个orderedDict,其key值为fn的形参名,value值为Parameter类对象

Parameter对象

保存在元组中,是只读的

  • name,参数的名字
  • annotation,参数的注解,可能没有定义
  • default,参数的缺省值,可能没有定义
  • empty,特殊的类,用来标记default属性或者注释annotation属性的空值
  • kind,实参如何绑定到形参,就是形参的类型
    • POSITIONAL_ONLY,值必须是位置参数提供 #Python没有实现次形参类型
    • POSITIONAL_OR_KEYWORD,值可以作为关键字或者位置参数提供
    • VAR_POSITIONAL,可变位置参数,对应*args
    • KEYWORD_ONLY,keyword-only参数,对应或者args之后的出现的非可变关键字参数
    • VAR_KEYWORD,可变关键字参数,对应**kwargs

业务应用

  • 函数参数类型检查
  • 思路
    • 函数参数的检查,一定是在函数外
    • 函数应该作为参数,传入到检查函数中
    • 检查函数拿到函数传入的实际参数,与形参声明对比
    • annotations属性是一个字典,其中包括返回值类型的声明。假设要做位置参数的判断,无法和字典中的声明对应。使用inspect模块
  • inspet模块提供获取对象信息的函数,可以检查函数和类、类型检查
  • 代码实现:
import inspect
def check(fn):
    def wrapper(*args,**kwargs):
        sig = inspect.signature(fn)
        params = sig.parameters #是orderedDict
        values = list(params.values()) #可迭代对象转化成列表,处理后也是有序的
        for k,v in enumerate(args): #因为values、args都有序
            if values[k].annotation is not inspect._empty and not isinstance(v,values[k].annotation):
                return ‘{}`s input is {} type , expected {}‘.format(v,type(v),values[k].annotation)
        for k,v in kwargs.items(): #kwargs与params都为字典形式,键值相同
            if params[k].annotation is not inspect._empty and not isinstance(v, params[k].annotation):
                return ‘{}`s input is {} type , expected {}‘.format(k,type(v),params[k].annotation)
        return fn(*args,**kwargs)
    return wrapper

@check
def add(x:int,y:int):
    return x + y

functools模块


偏函数partial

  • 把函数部分的参数固定下来,相当于为部分的参数添加了一个固定的默认值,形成一个新的函数并返回
  • 从partial生成的新函数,是对原函数的封装

partial源代码逻辑

def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords): # 包装函数
newkeywords = keywords.copy() #将原函数关键字参数拷贝后,
newkeywords.update(fkeywords)  #将调用新函数时的关键字参数更新
return func(*(args + fargs), **newkeywords) #调用新函数时的参数是建立偏函数时给的参
                                                                         #数,再各加上调用新函数时的参
newfunc.func = func # 保留原函数,注意:如果原函数已有装饰器,func属性内容有可能不是原函数名,而是装饰器函数名
newfunc.args = args # 保留原函数的位置参数
newfunc.keywords = keywords # 保留原函数的关键字参数参数
return newfunc
  • partial()返回一个新函数newfunc,而newfunc返回原函数,其参数是是建立偏函数时给的参数,再各加上调用新函数时的参

LRU缓存装饰器

  • @functools.lru_cache(maxsize=128, typed=False)
  • Least-recently-used装饰器。lru,最近最少使用。cache缓存
  • 如果maxsize设置为None,则禁用LRU功能,并且缓存可以无限制增长。当maxsize是二的幂时,LRU功能执行得最好
  • 如果typed设置为True,则不同类型的函数参数将单独缓存。例如,f(3)和f(3.0)将被视为具有不同结果的不同调用
  • 通过一个字典缓存被装饰函数的调用和返回值
  • 如果全为位置实参且位置与数值都相同,则视为相同实参,或者全传关键字参数则即使顺序不同也视为相同实参,即缓存能使用上;其他情况则皆视为不同实参
  • lru_cache使用_make_key生成参数缓存
  • 源码:
    def _make_key(args, kwds, typed,
             kwd_mark = (object(),),
             fasttypes = {int, str, frozenset, type(None)},
             tuple=tuple, type=type, len=len):
    """Make a cache key from optionally typed positional and keyword arguments
    
    The key is constructed in a way that is flat as possible rather than
    as a nested structure that would take more memory.
    
    If there is only a single argument and its data type is known to cache
    its hash value, then that argument is returned without a wrapper.  This
    saves space and improves lookup speed.
    
    """
    key = args
    if kwds:
        key += kwd_mark
        for item in kwds.items():
            key += item
    if typed:
        key += tuple(type(v) for v in args)
        if kwds:
            key += tuple(type(v) for v in kwds.values())
    elif len(key) == 1 and type(key[0]) in fasttypes:
        return key[0]
    return _HashedSeq(key)

lru_cache装饰器应用

  • 使用前提

    • 同样的函数参数一定得到同样的结果
    • 函数执行时间很长,且要多次执行
    • 本质是函数调用的参数=>返回值
  • 缺点
    • 不支持缓存过期,key无法过期、失效
    • 不支持清除操作
    • 不支持分布式,是一个单机的缓存
    • 适用场景,单机上需要空间换时间的地方,可以用缓存来将计算变成快速的查询

原文地址:http://blog.51cto.com/11281400/2106508

时间: 2024-08-30 10:02:35

Python第五周 学习笔记(1)的相关文章

Python第五周 学习笔记(2)

装饰器应用练习 一.实现一个cache装饰器,实现可过期被清除的功能 简化设计,函数的形参定义不包含可变位置参数.可变关键词参数和keyword-only参数 可以不考虑缓存满了之后的换出问题 1)原始 def cache(fn): import inspect local_cache = {} def wrapper(*args, **kwargs): sig = inspect.signature(fn) params = sig.parameters param_names = list(

20165326 java第五周学习笔记

第五周学习笔记 ch7 内部类(&外嵌类) 内部类的类体不可以声明类变量和方法 内部类如同类的变量或方法 内部类和外嵌类在编译时生成两个class文件 匿名类 异常类 断言 原文地址:https://www.cnblogs.com/Czzzz/p/8688184.html

第五周学习笔记

一·感兴趣的控制电机 这周学习的控制电动机,让我想到了这段时间一直都很感兴趣的舵机. 舵机也叫伺服电机,最早用于船舶上实现其转向功能,舵机的大小由外舾装按照船级社的规范决定,选型时主要考虑扭矩大小. 在航天方面,舵机应用广泛.航天方面,导弹姿态变换的俯仰.偏航.滚转运动都是靠舵机相互配合完成的.舵机在许多工程上都有应用,不仅限于船舶.并且由于可以通过程序连续控制其转角,因而被广泛应用智能小车以实现转向以及机器人各类关节运动中. 一般工业上船用舵机分类:船用舵机目前多用电液式,即液压设备由电动设备

Linux内核分析——第五周学习笔记

扒开系统调用的三层皮[下] 前言:以下笔记除了一些讲解视频中的概念记录,图示.图示中的补充文字.总结.分析.小结部分均是个人理解.如有错误观点,请多指教! 视频中所要求的画出流程图在图二中有体现. PS.实验操作会在提交到MOOC网站的博客中写.

《深入理解计算机系统》第五周学习笔记

第三章 程序的机器级表示 一.知识点总结 (一) 1.计算机执行机器代码,用字节序列编码低级的操作,,包括处理数据.管理存储器.读写存储设备上的数据.利用网络通信. 2.GCC C语言编译器以汇编代码的形式产生输出,汇编代码是机器代码的文本表示.给出程序中每条指令,然后GCC调用汇编器和链接器,从而根据汇编代码生成可执行的机器代码 3.逆向过程——通过研究系统和逆向工作来了解系统的创建过程 (二)程序编码 1.gcc -01 -o p p1.c p2.c  (-01告诉编译器使用第一级优化) 2

《机电传动控制》第五周学习笔记

1. 我比较感兴趣的电机 我对电影放映机里面的电机比较感兴趣,虽然现在许多的许多电影放映机都是使用电子设备来完成胶片的推送,但一些电影院仍然在使用那种轮式的电影放映机,这种放映机是使用胶片的,这就要求里面电机的转速比较均匀,且每张胶片能够保持一秒内播放规定的帧数,一般是24帧或30帧.利用所学的知识,我推测里面用的应该是步进电机,每秒转24步或30步从而达到画面流畅的要求,这需要通过协调步距角和转速来实现.电机每走一步就播放一帧,这样电机的步距角同时还必须与胶片的尺寸大小.轮盘的直径相协调.同时

学习笔记-第五周-学习笔记

传动电机或控制电机在工业或生活中的应用 伺服电机在机器人制造上的运用.比如高精度机器人会安装高精度总线型伺服电机. 开关磁阻电机(Switched Reluctance Drive :  SRD)是继变频调速系统.无刷直流电动机调速系统之后发展起来的最新一代无级调速系统.可用于高速运转.华中科技大学开关磁阻电机课题组在“九五”项目中研制出使用SRD的纯电动轿车,在“十五”项目中将SRD应用到混合动力城市公交车,均取得了较好的运行效果. 我个人对于电机用于机器人(串联多关节机器人.机械臂等)控制比

Python第三周 学习笔记(1)

字典 key-value键值对的数据的集合 可变的.无序的.key不重复 初始化: d = dict() d = {} d = dict(**kwargs) 如:d = dict(a=1,b=2) dict(iterable, **kwarg) 使用可迭代对象和name=value对构造字典,不过可迭代对象的元素必须是一个二元结构 dict(mapping, **kwarg) dict.fromkeys(iterable, value) 字典元素的访问 d[key] 返回key对应的值value

Python第七周 学习笔记(2)

面向对象 类 class 抽象概念 是属性与方法的集合 对象/实例 instance/object 类的具体实现/实例化 属性 对象状态的抽象,用数据结构描述 操作/方法 对象行为的抽象,用操作名和实现该操作的方法来描述 一切皆对象 对象是数据和操作的封装 对象是独立的,但是对象之间可以互相作用 目前OOP是最接近人类认知的编程范式 OOP三要素 封装 组装:将数据和操作组装到一起 隐藏数据:对外只暴露一些接口,通过接口访问对象 继承 复用 少修改 OCP开闭原则 对扩展开放,对修改关闭 多态