python中一些奇奇怪怪的高级函数用法

python中一些奇奇怪怪的高级函数用法

  • 1. 高阶函数
    • 案例一
  • 2. 匿名函数 lambda 函数表达式 (语法糖)
    • 案例一
    • 案例二
  • 3. 闭包
    • 一、定义
    • 二、作用
      • 2.1 当闭包执行完后,仍然能够保持住当前的运行环境。
        • 案例一
        • 案例二
      • 2.2 闭包可以根据外部作用域的局部变量来得到不同的结果
        • 案例一
    • 三、小结
  • 4. 装饰器
    • 装饰器函数
      • 案例一 计时装饰器
      • 案例二 带参数的decorate
    • 装饰器类(可以当作装饰器的类)
      • 案例一
      • 案例二 带参数的decorate
    • 类装饰器(可以装饰类的装饰器)
    • 小结
    • 一个应用技巧
  • 5. 偏函数
  • 6. 其他
    • sort() 该方法用来对列表中的元素进行排序,==改变原有序列==
    • sorted() 可以对任意的序列进行排序,==不改变原有序列==

1. 高阶函数

高阶函数:接收函数作为参数,或者将函数作为返回值的函数

案例一

# 创建一个列表
l = [1,2,3,4,5,6,7,8,9,10]

# 定义一个函数,用来检查一个任意的数字是否是偶数
def fn2(i) :
    if i % 2 == 0 :
        return True
    return False    

def fn(func , lst) :
    '''
        fn()函数可以将指定列表中的所有偶数获取出来,并保存到一个新列表中返回
        参数:
            lst:要进行筛选的列表
    '''
    # 创建一个新列表
    new_list = []
    # 对列表进行筛选
    for n in lst :
        # 判断n的奇偶
        if func(n) :
            new_list.append(n)
        if n > 5 :
            new_list.append(n)
   # 返回新列表
    return new_list
print(fn(fn2,l)) # [2, 4, 6, 6, 7, 8, 8, 9, 10, 10]

2. 匿名函数 lambda 函数表达式 (语法糖)

lambda函数表达式专门用来创建一些简单的函数,是函数创建的又一种方式;
匿名函数一般都是作为参数使用,其他地方一般不会使用

语法:lambda 参数列表 : 返回值
fn6 = lambda a,b : a + b
print(fn6(10,30)) # 40

案例一

filter(func,l) 可以从序列中过滤出符合条件的元素,保存到一个新的序列中

参数:
1.函数,根据该函数来过滤序列(可迭代的结构)
2.需要过滤的序列(可迭代的结构)
返回值:
过滤后的新序列(可迭代的结构)

l = [1,2,3,4,5,6,7,8,9,10]
r = filter(lambda i : i > 5 , l)
print(list(r)) # [6, 7, 8, 9, 10]

案例二

map(func,l) 函数可以对对象中的所有元素做指定的操作,然后将其添加到一个新的对象中返回

l = [1,2,3,4,5,6,7,8,9,10]
r = map(lambda i : i ** 2 , l)
print(list(r)) 	# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
123

3. 闭包

一、定义

python中的闭包从表现形式上定义(解释)为:

如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure).

二、作用

2.1 当闭包执行完后,仍然能够保持住当前的运行环境。

案例一

比如说,如果你希望函数的每次执行结果,都是基于这个函数上次的运行结果。我以一个类似棋盘游戏的例子来说明。假设棋盘大小为50*50,左上角为坐标系原点(0,0),我需要一个函数,接收2个参数,分别为方向(direction),步长(step),该函数控制棋子的运动。棋子运动的新的坐标除了依赖于方向和步长以外,当然还要根据原来所处的坐标点,用闭包就可以保持住这个棋子原来所处的坐标。

origin = [0, 0]  # 坐标系统原点
legal_x = [0, 50]  # x轴方向的合法坐标
legal_y = [0, 50]  # y轴方向的合法坐标
def create(pos=origin):
    def player(direction,step):
        # 这里应该首先判断参数direction,step的合法性,比如direction不能斜着走,step不能为负等
        # 然后还要对新生成的x,y坐标的合法性进行判断处理,这里主要是想介绍闭包,就不详细写了。
        new_x = pos[0] + direction[0]*step
        new_y = pos[1] + direction[1]*step
        pos[0] = new_x
        pos[1] = new_y
        #注意!此处不能写成 pos = [new_x, new_y],原因在上文有说过
        return pos
    return player
 
player = create()  # 创建棋子player,起点为原点
print player([1,0],10)  # 向x轴正方向移动10步
print player([0,1],20)  # 向y轴正方向移动20步
print player([-1,0],10)  # 向x轴负方向移动10步
案例二

持续添加数,求平均

def make_averager():
    # 创建一个列表,用来保存数值
    nums = []

    # 创建一个函数,用来计算平均值
    def averager(n) :
        # 将n添加到列表中
        nums.append(n)
        # 求平均值
        return sum(nums)/len(nums)

    return averager

averager = make_averager()

print(averager(10)) # 10.0
print(averager(20)) # 15.0
print(averager(30)) # 20.0
print(averager(40)) # 25.0

2.2 闭包可以根据外部作用域的局部变量来得到不同的结果

这有点像一种类似配置功能的作用,我们可以修改外部的变量,闭包根据这个变量展现出不同的功能。比如有时我们需要对某些文件的特殊行进行分析,先要提取出这些特殊行。

案例一
def make_filter(keep):
    def the_filter(file_name):
        file = open(file_name)
        lines = file.readlines()
        file.close()
        filter_doc = [i for i in lines if keep in i]
        return filter_doc
    return the_filter

如果我们需要取得文件"result.txt"中含有"pass"关键字的行,则可以这样使用例子程序

filter = make_filter("pass")
filter_result = filter("result.txt")

三、小结

以上两种使用场景,用面向对象也是可以很简单的实现的,但是在用Python进行函数式编程时,闭包对数据的持久化以及按配置产生不同的功能,是很有帮助的。

4. 装饰器

记住:

1、python里面万物都是object

2、记住装饰器的等价形式

装饰器函数

decorate直观不准确定义:不过是一个输入和输出都是函数的函数

def dec(f):
    pass

@dec
def double(x):
    return x**2

完全等价于:

double = dec(double)

注:输入一定是函数,输出则不一定,例如:

def dec(f):
    return 1

@dec
def double(x):
    return x**2

if __name__ == "__main__":
    a=double
    print(a)# a=1
# 原因是double = dec(double)
# dec return 1
# 因此此时double赋值为1

只是通常输出也是函数,关键在于最外层decorate return的东西

案例一 计时装饰器

用于计时(内函数只执行一次)

import time
def timeit(f):
    def wrapper(*args,**kwargs):
        start=time.time()
        result=f(*args, **kwargs)
        end=time.time()
        print("用时{:.2f}s".format(end-start))
        return result
    return wrapper

@timeit
def my_func(x):
    time.sleep(x)
# 等价于my_func=timeit(my_func)

*args,**kwargs本质上就是为了允许接收变长的函数参数,以达到,不管我装饰什么样的函数,它都能用

案例二 带参数的decorate

除上述以外,可能还有带参数的decorate

实际上也很简单,比如

@timeit(10)
def double(x):
    return x**2
# 等价于
double=timeit(10)(double)

即多了一次函数调用

import time
def timeit(iteration):
    def inner(f):

        def wrapper(*args,**kwargs):
            start=time.time()
            for _ in range(iteration):
                result=f(*args, **kwargs)
            end=time.time()
            print("用时{:.2f}s".format(end-start))
            return result
        return wrapper

    return inner


@timeit(1000)
def double(x):
    return x**2

double(2)

装饰器类(可以当作装饰器的类)

本质是装饰器本身,也即装饰器本身既可以是类,也可以是函数

案例一

import time
class Timer:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        # 魔术函数__call__让所以这个类的实例都变得callable,也即这个类的对象都可以当作函数用
        # 对象可以直接调用
        start = time.time()
        result = self.func(*args, **kwargs)
        end = time.time()
        print("用时{:.2f}s".format(end - start))
        return result


@Timer
def add(a, b):
    return a + b
# 等价于:add = Timer(add),将add这个函数转化为了一个Timer对象

result = add(2, 3)# add(Timer对象)使用了它的__call__函数
print(result)

跟函数装饰器的区别:

函数装饰器是 函数调用一个函数 返回一个函数,该函数是callable的

装饰器类是 类调用一个函数 返回一个对象 该对象是callable的

案例二 带参数的decorate

import time
class Timer:
    def __init__(self, prefix):# 有改动
        self.prefix = prefix# 有改动
        
    def __call__(self, func):# 有改动
        def wrapper(*args, **kwargs):# 有改动
            # 魔术函数__call__让所以这个类的实例都变得callable,也即这个类的对象都可以当作函数用
            # 对象可以直接调用
            start = time.time()
            result = self.func(*args, **kwargs)
            end = time.time()
            print("用时{:.2f}s".format(end - start))
            return result
        return wrapper# 有改动


@Timer(prefix="curr_time:")# 有改动
def add(a, b):
    return a + b
# 等价于:add = Timer(prefix="curr_time:"))(add),将add这个函数转化为了一个Timer对象

result = add(2, 3)# add(Timer对象)使用了它的__call__函数
print(result)

类装饰器(可以装饰类的装饰器)

本质是装饰器要装饰的对象,也即装饰器装饰的对象也既可以是函数,也可以是类

def add_str(cls):
    def __str__(self):
        return str(self.__dict__)
    cls.__str__ = __str__
    # 把cls的__str__函数替换成自定义的__str__,本质上就是重写了class的__str__函数
    return cls
# 因为class本身也是一个object,所以add_str这个函数是一个参数是class,返回值也是class的函数

@add_str
class Myobject:
    def __init__(self,a,b):
        self.a=a
        self.b=b

# 等价于 Myobject=add_str(Myobject)
# 输入一个类,返回一个类,在类里边动动手脚

o=Myobject(1,2)
print(o)#{'a': 1, 'b': 2}

同样道理,类装饰器也可以有带参数的写法

小结

核心:转换成等价形式,你就知道你需要写一个什么东西,它的输入应该是什么,输出应该是什么

一个应用技巧

objprint, 让你轻松打印python object

支持直接打印对象,原理就是使用了装饰器

另外,评论区有一个可以使得函数无需调用且全局可用的方法,也很神奇,可以看下

5. 偏函数

functools.partial()一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单

# 将数字转化为二进制
def int2(x, base=2):
    return int(x, base)
int2('1000000') # 64 

# 可以像下面这样写
int2 = functools.partial(int, base=2)
int2('1000000')  # 64

6. 其他

sort() 该方法用来对列表中的元素进行排序,改变原有序列

参数:key=len/int/str…

sorted() 可以对任意的序列进行排序,不改变原有序列

# sort()
l = ['bb','aaaa','c','ddddddddd','fff']
l.sort(key=len)
print(l) # ['c', 'bb', 'fff', 'aaaa', 'ddddddddd']

# sorted()
l = [2,5,1,3,6,4]
print('排序前:',l) # 排序前: [2, 5, 1, 3, 6, 4]
print(sorted(l,key=int)) # [1, 2, 3, 4, 5, 6]
print('排序后:',l) # 排序后: [2, 5, 1, 3, 6, 4]

你可能感兴趣的:(python,开发语言)