竞博体育 > 软件 > 本质是函数,装饰器(本身是个函数)

本质是函数,装饰器(本身是个函数)

武道之路-炼体期五重天中期,之路五重

高阶函数:函数本身被当做参数传给另一个函数,运行返回一个函数。

递归:函数内允许调用其它函数,调用本身的是递归函数(效率不算高),1.明确函数结束条件,每次调用自身相应的运算量和问题规模应减少,调用次数不宜过多(python默认999次,会造成栈溢出)

 

装饰器(本身是个函数): 对函数进行添加一些功能(不进行修改函数的调用方式和代码),而函数本身没什么变化,感受不到影响。

 

在调用函数之前,就已经调用了装饰器。       @装饰器,放在需要装饰的函数前,注意传参!

 

(懒惰是罪恶之源)

高阶函数:函数本身被当做参数传给另一个函数,运行返回一个函数。 递归:函数内允许调用其它...

装饰器

定义:本质是函数,(装饰其他函数)就是为其他函数添加附加功能。
竞博体育 ,原则:1.不能修改被装饰的函数的源代码
   2.不能修改被装饰的函数的调用方式

实现装饰器的知识储备:
  1.函数即“变量”
  2.高阶函数(满足下列条件之一就是高阶函数)

    • a:把一个函数名当作形参传给另一个函数(在不修改被装饰函数源代码的情况下修改其功能,但是调用方式变了)
    • b:返回值中包含函数名(不修改函数的调用方式,而修改函数功能)

  3.嵌套函数

 

总结:

高阶函数+嵌套函数=>装饰器


在上一篇文章中,我们介绍了 Python 的数据类型,现在我们介绍Python 的函数式编程。

1. 函数即变量

def bar():  #这里定义个函数
    print('this is bar...')

func = bar  #将函数当变量一样赋值给func

func()  #这是func加上()就可以当函数用了

  这就是函数即变量。

查看上一篇文章请点击:

2. 高阶函数

a:把一个函数名当做形参传给另一函数(在不修改被装饰函数源代码的情况下修改其他功能,但是调用方式变了)

def bar():
    print('this is bar...')
bar()
print('----------------------------------')
def test(func): #在这里增加了功能
    print(func)
test(bar)  #但是这里修改了调用方式

b:返回值中包含函数名(不修改函数的调用方式,而修改函数功能)

def bar():
    print('this is bar...')
bar()
print('----------------------------------')
def test(func):   #在这里增加了功能
    print(func)
    return func     #这里返回了函数地址
bar = test(bar)   #将函数地址赋给一个变量,该变量和传入函数同名
bar()       #这个变量加上括号,就可以当函数用了,同时又没有修改函数的调用方式

函数式编程

3. 嵌套函数

x = 1
def test1():
    def test2():
        def test3():
            x = 3
            print(x)
        test3()
    test2()
test1()

  像这样在一个函数里面定义一个函数就叫做嵌套函数。

 

现在三个知识点都已经解释过了,下面先实现一个原始版的装饰器。

原始版装饰器

import time

def test1():  #这里是被装饰的函数
    time.sleep(1)
    print("this is test1....")

def timer(func):  #这里是装饰器函数
    def deco():
        strat = time.time()
        func()
        stop = time.time()
        print("run time %s" %(stop-strat))
    return deco

test1 = timer(test1)

test1()

  这个装饰器的功能是测试test1函数的运行时间,deco函数有这个功能,所以这里其实我想运行的是deco函数,timer()函数的返回值也就是deco函数的地址;同时deco函数想要运行也要满足另一个条件,那就是需要调用timer函数,只有当timer函数运行的时候timer函数里面写的那些东西才会生效或者说被激活,deco函数才会定义成功。到此,test=timer(test1)这句代码的意思就是,先调用timer函数让deco函数的定义生效,同时将要被装饰的函数test1函数的地址传入,让deco函数里面的func函数生效,这是timer函数又会将deco函数的地址返回,并将地址赋值给test1这个变量。然后下面的test1后面加上括号,变成test1()的形式,就可以运行了。这样一个原始版的装饰器就写完了。

接下来要写一个真实装饰器的实例

import time

def timer(func):
    def deco():
        start_time = time.time()
        func()
        stop_time = time.time()
        print('the func run time is %s' %(stop_time-start_time))
    return deco

@timer  #这里等于 test = timer(test),要装饰哪个函数,就在哪个函数前面加上这个
def test():
    time.sleep(2)
    print('this is test...')

test()

  上面这些都是一些比较基本的,如果装饰有参数的函数且参数的个数还都不一定怎么办呢?下面写一个高级案例 -- 有参数的装饰器

高级案例 -- 有参数装饰器

import time

def timer(func):
    def deco(*args,**kwargs):  #在这里接收参数
        start_time = time.time()
        func(*args,**kwargs)
        stop_time = time.time()
        print('the func run time is %s' %(stop_time-start_time))
    return deco

@timer
def test(name):
    time.sleep(2)
    print('this is test...',name)

test('vector')

  如果原test函数里面有返回值,被装饰后我们打印它print(test()),结果会是none。因为这时的test=deco,而deco没有返回值,所以打印不出来。这样就会出现一个问题,如果test函数有返回值,test函数的一个功能(返回值)就会被弄丢了。

解决办法看下面案例。

高高级案例 -- 有返回值的装饰器

import time
def timer(func):
    def deco(*args,**kwargs):
        start_time = time.time()
        res = func(*args,**kwargs)
        stop_time = time.time()
        print('the func run time is %s' %(stop_time-start_time))
        return res  #这里将func的返回值返回
    return deco

@timer
def test(name):
    time.sleep(2)
    print('this is test...',name)
    return 'asdfasdfgdfgd'

print(test('vector'))

  这样就可以打印出返回值了。

还有些时候我们需要给装饰器进行传参,给内部使用。解决方案看下面。

高高高级案例 -- 给装饰器传参

import time
def timer(temp):
    print('this is ',temp)  #这里是加进来的参数
    def out(func):
        def deco(*args,**kwargs):
            start_time = time.time()
            res = func(*args,**kwargs)
            stop_time = time.time()
            print('the func run time is %s' %(stop_time-start_time))
            return res
        return deco
    return out

@timer('temp')
def test(name):
    time.sleep(2)
    print('this is test...',name)
    return 'asdfasdfgdfgd'

print(test('vector'))

  实际上就是整体又在外面加了一层内嵌函数。

 

函数式编程

函数式 (Functional Programming) 是一种以函数为基础的编程风格。
函数编程的一个关键部分是高阶函数 (Highter-order function)。在关于函数作为对象的文章中,我们简单地看到了这个想法的例子。高阶函数接受其他函数作为参数,甚至允许将函数作为结果返回。

def apply: return func)def add: return x + 3print(apply

运行结果:

>>>26>>>

一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数,函数式编程就是指这种高度抽象的编程范式。

纯函数
函数式编程是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量。因此,任意一个函数只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。
这就是数学中函数的工作方式:例如,sin 对于 x 的相同值,总是返回相同的结果。
纯函数:

def func:temp = x*2 + y/4return temp /(x/4 + y*2)

非纯函数:

my_list =[]def impure_func:my_list.append

非纯函数是因为返回的结果是不固定的,上面的代码改变了列表 my_list。

纯函数的优点:

1、 容易推理和测试。
2、 能够并行运算。
3、 更有效率。一旦对输入的函数进行了求值,对于给定的参数结果都是固定的,就可以存储起来并在下次需要该函数时引用,从而减少调用函数的次数。这叫记忆化,英文叫 Memoization
使用纯函数的主要缺点是,它们是 IO 的简单任务复杂化,这种任务本身就需要副作用。在一些情况下纯函数可能更难编写。

匿名函数

到目前为止,我们编写函数的方式是使用 def 来创建函数,并指派一个 def 定义的变量名来调用。

在某些情况下,我们不需要显式地定义函数。如果函数是使用 lambda 语法创建的,这种方式创建的函数称为匿名函数

这种方法在不需要显式地定义函数时最常用。

下列示例中将显式语法,它由 lambda 关键字、参数列表、冒号和要计算和返回的表达式组成。

def func: return ffunc(lambda x:4*x*x,5)

lambda 演算是 Alonzo Church 给出的一套图灵机等价的形式计算系统。lambda 演算系统的精妙之处在于处理递归,lambda 演算也叫 λ 演算。

Python 对匿名函数的支持有限,只有一些简单的情况下可以使用,通常是一个单行的代码来写一个表达式。

# named functiondef func: return x + 5*x + 8print(func(5))#lambdaprint((lambda x:x + 5*x + 8)

运行结果:

>>>3838>>>

用匿名函数的好处:由于函数没有名字,因此不必担心函数名冲突。

匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数。

f = lambda x:x + 5*x + 8print

运行结果:

>>>20>>>

为了使程序规范化,最好使用 def 来定义函数。我们也可以把匿名函数作为返回值返回。

map 和 filter 函数

Python 内建了 mapfilter 高阶函数,它们接收一个 iterable (可迭代 ) 对象,如:列表。

map 函数

map 函数接收两个参数,一个是函数,一个是 iterable, map将传入的函数依次作用到序列的每个元素,并把结果作为新的 iterator 返回。

比如我们有一个函数 func = x + 1,要把这个函数作用在一个列表 [11,22,33,44,55] 上,可以使用 map 实现。

实现代码如下:

def add: return x + 1my_list = [11,22,33,44,55]result = list(map(add,my_list))print

运行结果:

>>>[12, 23, 34, 45, 56]>>>

也可以使用匿名函数来创建。

my_list = [11,22,33,44,55]result = list(map(lambda x:x+1,my_list))print

map 函数返回的是一个 iterator,是一个惰性序列,需要用 list 函数强制转换成列表。

filter 函数

Python 内建的 filter 函数用于过滤序列,给定的函数返回 Boolean 值。根据返回值是 True 还是 False 决定保留还是丢弃该元素。

my_list = [11,22,33,44,55]ret = list(filter(lambda x:x%3 == 0,my_list))print

运行结果:

>>>[33]>>>

filter 函数返回的是一个 iterator,需要用 list 函数强制转换成列表。

生成器
在前面,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。如果我们仅仅需要访问大列表里的几个列表,绝大多数元素占用的空间都白白浪费了。
如果列表元素可以按照某种算法推算出来,那么就有可能不用这么大的内存,在 Python 中,这种一边循环一边计算的机制,称为生成器,英文 generator生成器是一种像列表和元组的 iterable
写列表不同,它们不允许使用索引进行索引,但仍然可以使用 for 循环进行遍历。语法 yield 用来生成生成器。

def count(): i = 0 while i < 5:  yield i  i += 1for i in count(): print

运行结果:

>>>01234>>>

yield 语句用于定义生成器,将函数的返回值替换为向调用者提供结果而不破坏本地变量。

由于每次生成一个数据,生成器没有了列表的内存限制,可以无穷调用!

def func(): while True:  yield 1for i in func(): print

运行结果:

>>>1111...>>>

简而言之,生成器允许声明一个类似迭代器的函数,它可以在 for 循环中使用。

生成器做为 list 函数的参数以使用它生成列表。

def func: for i in range:  if i % 3 == 0:   yield iprint(list)

运行结果:

>>>[0, 3, 6, 9]>>>

由于具有延迟并按需生成值的特性,使用生成器可以提高性能,这也会降低内存的开销。另外,我们不需要等到所有元素生成之后才开始使用它们。

下面代码会输出什么结果?

def func(): nums = "" for x in "123456":  nums += x  yield numsprint(list

运行结果:

>>>['1', '12', '123', '1234', '12345', '123456']>>>

装饰器
在代码运行期间动态增加功能的方式,称之为装饰器 (Decorator)。
当你需要扩展一个功能,但是不想修改代码,就可以用装饰器。

def decor: def wp():  print("===============")  func()  print("===============") return wpdef text(): print("I like Python!")decorated = decordecorated()

上面例子定义了一个 decor 函数接收一个叫 func 的函数,在函数体 decor 里面我们定义了一个函数 wp。函数 wp 打印一些字符串并调用函数 func。函数 decor 返回函数 wp。我们使用 decorated 来装饰 text 函数,让其添加一些功能。
本质上,装饰器就是一个返回函数的高阶函数。所以,我们定义一个能获取运行时间的装饰器,可以定义如下:

import timedef timmer: def warpper(*args,**kwargs):  start_time = time.time  stop_time = time.time()  print('the func run time is %s' %(stop_time-start_time)) return warpper()

一个装饰器接收一个函数作为参数,并返回一个函数。之前的例子我们使用装饰器修改了 text 函数。

def text(): print("I like Python!")decorated = decordecorated()

如果我们需要一个模式可以在任何时间使用装饰器修改一个函数,那么要借助 Python 的 @ 语法,把装饰器函数置于函数的定义处。

@decordef text(): print("I like Python!")

这个版本的代码是上一版本的简写。

一个函数可以有多个装饰器。

递归函数
递归函数是函数式编程里比较重要的概念。
在函数内部,可以调用其他函数。如果一个函数在内部调用自身,这个函数就是递归函数。
举个来计算阶乘的例子 recursion = n! = 1 * 2 * 3 * … * * * n = ! * n = recursion * n
所以 recursion 可以表示为 n * recursion,最后我们需要处理一下 1! = 1 的问题。于是代码就如下所示。

def recursion: #'定义递归函数实现求阶乘功能' if n==1:  return 1 else:  return n*recursion(n-1)print(recursion

运行结果:

>>>720>>>

根据函数定义可以看到计算过程展开如下:

===> recursion(6)===> 6 * recursion(5)===> 6 * (5 * recursion(4))===> 6 * (5 * (4 * recursion(3)))===> 6 * (5 * (4 * (3 * recursion(2))))===> 6 * (5 * (4 * (3 * (2 * recursion(1)))))===> 6 * (5 * (4 * (3 * (2 * 1))))===> 6 * (5 * (4 * (3 * 2)))===> 6 * (5 * (4 * 6))===> 6 * (5 * 24)===> 6 * 120===> 720

使用递归函数需要特别关注如何退出递归。

在计算机中,函数调用是通过栈 这种数据结构实现的,每当进入一次函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小是无限的,所以,递归调用的次数过多,会导致栈溢出导致程序崩溃。下面的代码说明如果没有退出机制,程序因为内存不足而崩溃。

def recursion: #'定义递归函数实现求阶乘功能' return n * recursion(n-1)print(recursion

运行结果:

>>>RecursionError: maximum recursion depth exceeded>>>

解决递归调用栈溢出的方法可以通过尾递归优化。

尾递归是指在函数返回的时候调用自身,并且 return 语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
上面的函数由于引入了乘法表达式,所以就不是尾递归了。要改成尾递归方式,需要多一点代码,主要是要把每一步的乘积传入到递归函数中。

def recursion(num, product): return fune(num, product)def fune(num, product): if num == 1:  return product else:  return fune(num - 1, num * product)print(recursion

运行结果:

>>>720>>>

遗憾的是大多数编程语言没有针对尾递归做优化,Python 解释器也没有做优化。所以,即使上面的 recursion 函数改成尾递归方式,也会导致栈溢出。

集合

集合 是一种数据结构与字典类似,也是一组 key 的集合,但不存储 value

创建集合有两种方式:使用列表做为参数的 set 函数或或者语法 {} 创建。

nums = {1,2,3,4}msg = set(["I","like","Python","!"])print(1 in nums)print("I" not in msg)

运行结果:

>>>TrueFalse>>>

创建一个空 set 必须使用 set 函数,使用 {} 会创建字典。

集合与列表有几种不同之处,但是它们都可以使用 len 函数。

函数是无序的,这意味着它们不能被索引,在集合中也没有重复的 key

由于采用不同的数据结构,集合的 in 操作里比列表更快。使用 add 添加元素,使用 remove 删除特定的元素,pop 删除任意一个函数。

nums = {1,2,1,5,3,4}printnums.add(-5)nums.remove(1)print

运行结果:

>>>{1, 2, 3, 4, 5}{2, 3, 4, 5, -5}>>>

集合的基本用途包括元素存在测试和消除重复元素。

集合能使用一些数学概念上的操作。

并集操作符 | 合并两个集合。

交集操作符 & 返回两个集合都存在的元素。

补集操作符 - 返回元素在第一个集合,但是不在第二个集合。

对称差操作符 ^ 返回不在两个集合中都存在的元素。

nums_1 = {9,8,7,6,5}nums_2 = {5,4,3,2,1}print(nums_1 | nums_2)print(nums_1 & nums_2)print(nums_1 - nums_2)print(nums_2 - nums_1)print(nums_1 ^ nums_2)

运行结果:

>>>
{1, 2, 3, 4, 5, 6, 7, 8, 9}{5}{8, 9, 6, 7}{1, 2, 3, 4}{1, 2, 3, 4, 6, 7, 8, 9}
>>>

数据结构

先前,我们看到 Python 拥有下面的数据结构:列表 , 字典 (dictionary) , 元组 , 集合 。

当你需要唯一的元素,使用集合。

当你需要数据不能被修改,使用元组。

当你需要一个简单的、可迭代的、经常修改的集合时,或需要有序访问,请尝试选择列表。

使用字典的情况:

  • 需要一种 key-value 的这种逻辑。

  • 需要一种通过 key 快速查找数据的方式。

  • 需要经常修改数据。

    很多时候,元组与字典会结合使用,例如,元组可能代表一个键,因为它是不可变的。

itertools 模块
Python 的内建模块 itertools 提供了在函数式编程里非常有用的用于操作迭代对象的函数。
无限迭代器:count 一个计数器,可以指定起始位置和步长。cycle 会把引入的一个序列 无限重复下去。repeat 把一个元素无限重复下去,如果提供第二个参数就可以限定重复次数。

from itertools import countfor i in count(5): print if i >= 10:  break

运行结果:

>>>5678910>>>

itertools 模块包含很多函数处理 iterator 对象,就像 mapfilter 函数一样。
有限迭代器:
takewhile - 函数根据条件判断来获取序列。
chain - 把一组迭代对象串联起来,形成一个更大的迭代器。
accumulate - 迭代器返回累计求和结果。

from itertools import accumulate,takewhilenums = list(accumulate(range(5)))printprint(list(takewhile(lambda x:x<=3,nums)))

运行结果:

>>>[0, 1, 3, 6, 10][0, 1, 3]>>>

组合生成器:
product - 产生多个列表和迭代器的笛卡尔积。
permutation - 产生指定输煤

from itertools import product,permutationsmsg = ("a","b")print(list(product(msg,range(2))))print(list(permutations

运行结果:

>>>[('a', 0), ('a', 1), ('b', 0), ('b', 1)][('a', 'b'), ('b', 'a')]>>>

“我们不能用制造问题的思路去解决问题。” --阿尔伯特·爱因斯坦

  • 首页
  • 电话
  • 软件