Python学习笔记

时间:2021-04-13 22:41:17

把酒祝东风,且共从容
垂杨紫陌洛城东
总是当时携手处,游遍芳丛

聚散苦匆匆,此恨无穷
今年花胜去年红
可惜明年花更好,知与谁同?

浪淘沙双调小令 欧阳修

有人说在python中一切都是对象,让我们今天来共同探寻一下Python中的函数究竟是怎么对象化的

本文的参考文献有:

本文目录

Python中的神奇对象——函数

在英文中,根据词尾,名词的复数形式也会不同。
让我们先看一下三段代码。
第一段是比较常规的思维,传入这个名词,然后进行判断,得到复数形式。

def plural(noun):          
if re.search('[sxz]$', noun):
return re.sub('$', 'es', noun)
elif re.search('[^aeioudgkprt]h$', noun):
return re.sub('$', 'es', noun)
elif re.search('[^aeiou]y$', noun):
return re.sub('y$', 'ies', noun)
else:
return noun + 's'

上面这段代码用到了re模块,如果你对这个模块还不太熟悉,可以看这里
当然先不理解也可以,我们的重点是了解python中函数的特性不是么?
很平淡无奇,不是么?

看看下面这段抽象了一点的:

函数也可以做对象~

import re

def match_sxz(noun):
return re.search('[sxz]$', noun)

def apply_sxz(noun):
return re.sub('$', 'es', noun)

def match_h(noun):
return re.search('[^aeioudgkprt]h$', noun)

def apply_h(noun):
return re.sub('$', 'es', noun)

def match_y(noun):
return re.search('[^aeiou]y$', noun)

def apply_y(noun):
return re.sub('y$', 'ies', noun)

def match_default(noun):
return True

def apply_default(noun):
return noun + 's'

rules = ((match_sxz, apply_sxz),
(match_h, apply_h),
(match_y, apply_y),
(match_default, apply_default)
)

def plural(noun):
for matches_rule, apply_rule in rules:
if matches_rule(noun):
return apply_rule(noun)
#测试
a=['apple','watch','agency']
for i in a:
print('{0} : {1}'.format(i,plural(i)))

这里引入了python的一个概念——函数也是的对象!
可以将一对对的函数存在一个tuple中(rules)。

但是我们在这里是不是还可以继续简化?比如说:自动构建rule这个函数tuple?。
答案是可以,rule中每一对函数都是同样的返回类型。那么:

自动构建一组函数

import re

def build_match_and_apply_functions(pattern, search, replace):
def matches_rule(word):
return re.search(pattern, word)
def apply_rule(word):
return re.sub(search, replace, word)
return (matches_rule, apply_rule)

在函数中定义两个函数,然后返回。

这样,我们只需要定义一个这样的数据结构:

patterns = \                                                        
(
('[sxz]$', '$', 'es'),
('[^aeioudgkprt]h$', '$', 'es'),
('(qu|[^aeiou])y$', 'y$', 'ies'),
('$', '$', 's')
)
rules = [build_match_and_apply_functions(pattern, search, replace)
for (pattern, search, replace) in patterns]

从文件中读取函数“配置”

你问我,可不可以再简单一点?
可以,比如这样:
从文件中读取这个patterns:

[sxz]$               $    es
[^aeioudgkprt]h$ $ es
[^aeiou]y$ y$ ies
$ $ s

import re

def build_match_and_apply_functions(pattern, search, replace):
def matches_rule(word):
return re.search(pattern, word)
def apply_rule(word):
return re.sub(search, replace, word)
return (matches_rule, apply_rule)

rules = []
with open('plural4-rules.txt', encoding='utf-8') as pattern_file:
for line in pattern_file:
pattern, search, replace = line.split(None, 3)
rules.append(build_match_and_apply_functions(
pattern, search, replace))

你问:这个还可以写得再精炼一点么?
这个。。。还真可以。我们把从文件到rule的这一部分可以函数化:

用生成器来生成函数

def rules(rules_filename):
with open(rules_filename, encoding='utf-8') as pattern_file:
for line in pattern_file:
pattern, search, replace = line.split(None, 3)
#看这里!
yield build_match_and_apply_functions(pattern, search, replace)

def plural(noun, rules_filename='plural5-rules.txt'):
for matches_rule, apply_rule in rules(rules_filename):
if matches_rule(noun):
return apply_rule(noun)
raise ValueError('no matching rule for {0}'.format(noun))

yield的意思是暂停当前函数,并返回调用者。
这里我们需要引入一个新的概念:Generator。
这个概念比较重要,但是我们现在在这里先不探讨了。

整理一下思路

我们发现,其实我们说了这么多,就是想证明一点:函数原来也可以像一个整数、字符串这样使用。这是一个重要的目标,我们必须意识到这一点:从今往后,一个函数不再是我们在c中理解的那种,而是一个向整数,字符串那样的对象!!

让我们稍微休息一下,我们马上就要看一下,在python中,函数作对象的意义:

内置的高阶函数

python中内置了几种比较常用的以函数为参数的函数(是不是有点绕口~),分别是map、reduce、filter和sort。在看看他们的定义之后,你其实已经可以将他们写出来了。

map

def func(x):
return x+3

result=map(func,[1,2,3,5,7])

返回结果result是:

[4,5,6,8,10]

你其实已经猜出来了,这无非是在[1,2,3,5,7]上进行了一次遍历,将每一个的值都传入第一个参数:func上。

我们来尝试完成一个自己的map试试:

#朴素的my_map :)
def my_map(fun,lst):
res=[]
for i in lst:
res.append(fun(i))
return res

尝试一下:

result=my_map(func,[1,2,3,4,5])
>>> result
[4, 5, 6, 7, 8]

成功了!

reduce

至于reduce,嗯。它的效果是这样的:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

首先计算temp=f(x1,x2),然后计算temp=f(temp,3),再然后temp=f(temp,x4)。。。
这个写出来也很简单:

def my_reduce(fun,lst):
temp=lst[0]
for i in range(1,len(lst)):
temp=fun(temp,i)
return temp

我们攻克两个了!还有filter和sort!
如果你觉得累了没关系,作者写这篇博文可是用了将近一天的时间呢~

filter

我想你甚至可以从名字上猜测数来它是做什么的了:
没错,过滤

def is_odd(n):
return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))

我们照例来实现一下,但是我这里用点小技巧简化一下:

def my_filter(fun,lst):
return [i for i in lst if fun(i)]

(其实上面的map也可以这样写哦)

sorted

我们根据名字就可以猜得一二
的确:

sorted([36, 5, -12, 9, -21])
[-21, -12, 5, 9, 36]

这是一个排序高阶函数:
我们可以自定义排序的key

 sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']

请注意key=str.lower,所以并不是以默认的ABCD…abc这样的顺序。而是以其小写决定的。

key就是一个函数。我们也可以自定义其返回值。

还有什么?

如果你还没有累,建议一直读下去,就算忘记了前面的大部分内容也没关系。只要保有这样一个印象就好:函数是对象。
接下来,我们要看这样几个问题:

  • 函数作为返回值
  • 匿名函数
  • 装饰器函数
  • 偏函数
    其实第一点我们之前的已经用到了,我们不再深入。先看看后三点:

匿名函数

先看看这样一段代码:

list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]

这里的lambda x: x*x效果等同于定义一个函数

def fun(x):
return x*x

但是明显简洁了很多,你没有必要再去为这个函数起一个名字了。
这叫做lambda表达式,在c++ 11中也已经支持了它。
lambda作为返回值也没有问题:

def build(x, y):
return lambda: x * x + y * y

深呼吸一下——装饰器

装饰器比前面几个略微复杂一些:
我们现在知道函数是对象,那么我们可以用函数给对象赋值,就像一个整形或者字符串一样:

def power(x):
return x*x
#请使用函数名来赋值
f=power
#调用
f(5)
#输出25

现在看看这个函数:

def get_new_fun(fun):
def new_fun(x):
return 1+fun(x)
return new_fun

发生了什么?我们传入一个函数fun,构建了一个新函数。这就是所谓了装饰器模式:在不改变原函数的基础上构建新的函数。

我们可以通过

f=get_new_fun(power)

得到新的函数了!

还有一种方法,可以将函数直接定义为新函数

@get_new_fun
def fun(x):
return x*x
fun(5)
#结果是26

最后的话

其实作者本人接触函数编程也只有一天,我写python的博客大概也坚持了将近两周,我只是以自己最好理解的方式来整理一下目前我看到的资料。如果有什么错误,欢迎指正~