Python一等函数简析

导语:本文章记录了本人在学习Python基础之函数篇的重点知识及我的心得,打算入门Python的朋友们能够来一块儿学习并交流。

本文重点:html

一、了解函数在Python中是一等对象;
二、了解Python中的可调用对象;
三、掌握正肯定义函数参数的方法;
四、了解operator和functools中支持函数式编程的方法。

1、函数是一等对象

一、一等对象

定义:一等对象是知足以下条件的程序实体:python

  • 在运行时建立;
  • 能赋值给变量或数据结构中的元素;
  • 能做为参数传给函数;
  • 能做为函数的返回结果。

在Python中,全部函数都是一等对象。程序员

二、高阶函数

定义:接受函数为参数,或者把函数做为结果返回的函数是高阶函数。
在Python中传统的高阶函数有map,filter,reduce;经常使用的高阶函数有内置函数sorted、min、max和functools.partial。正则表达式

  • map(function, iterable, ...):
    map返回一个迭代器,迭代器是经过function处理可迭代对象中的每一个元素产生的返回值的集合。
  • filter(function, iterable):
    filter至关于一个过滤器,以函数返回值为断定条件,筛选出True的元素并放入迭代器中返回。
  • functools.reduce(function, iterable[, initializer])
    reduce对可迭代对象中从左开始元素选出两个进行函数运算,将返回的运算值做为一个参数继续与第三个元素进行函数运算,直至迭代完成返回运算值。

三、归约函数:

定义:可以接受一个可迭代对象并返回单个结果的函数是归约函数。
reduce就是归约函数的一种,sum也是一种归约函数。本章额外介绍两个内置的归约函数。express

all(iterable):
可迭代对象中每个元素都是真值则返回True,不然False。
any(iterable)
可迭代对象中存在一个元素是真值则返回True,不然False。
归约函数会在第14章中讨论可迭代对象时重点讲解。编程

四、匿名函数:

匿名函数:使用lambda表达式建立的函数,函数自己没有名字来辨识,于是叫作匿名函数。
句法特色:lambda函数只能使用纯表达式,不能赋值,也不能使用while和try等语句。
语法:lambda [arg1 [,arg2,.....argn]]:expression。
优势:建立方便,简化代码工做量。
缺点:代码可读性下降。数组

五、函数内省

在计算机编程中,自省是指这种能力:检查某些事物以肯定它是什么、它知道什么以及它能作什么。自省向程序员提供了极大的灵活性和控制力。性能优化

2、函数参数与注解

一、函数参数

在函数的定义过程可能须要传入参数,对于函数涉及到的参数分为如下四种:数据结构

  • 必填参数
    调用函数必须填写的参数。在参数中居于靠前位置。
  • 默认参数
    当必填参数设置默认值时可选填。注意默认值是不可变对象,不然有逻辑错误。
  • 可变参数
    用单星号*args表示,即传入的参数是不定的。*args把参数收到元组中接受。
    传入方式既能够是直接传入,如func(1, 2, 3);也能够用列表或元组传入,如func(*(1,2,3))。
  • 强制关键字参数
    此类参数只能捕获经过指定关键字传入的参数,没法按照位置顺序读参。定义时前面需放一个*。
  • 关键字参数
    此参数可填可不填。传入方式分两种,一是”传递参数名=传递参数值”形式的参数,这种方式传入对位置无要求;二是不写参数名,按照位置顺序传入参数值。
    当关键字参数不定时用双星号**kw表示。**kw把关键字参数收到字典中接受。关键字参数既能够直接传入:func(a=1, b=2),又能够先组装dict,再经过**kw传入:func(**{'a': 1, 'b': 2})。

参数组合:当函数涉及到使用多种参数时,定义和传入的参数需按照的顺序:
必填参数、默认参数、可变参数、强制关键字参数、关键字参数.并发

def func(name,country="China",*,age,**rest):
    print("name :",name," country :",country," age :",age," rest :",rest)
func("Jack","US",age=20)
func("Hoya",age=15,male="Boy",height="178",grade="excellent",country="UK")

#output:
name : Jack  country : US  age : 20  rest : {}
name : Hoya  country : UK  age : 15  rest : {'male': 'Boy', 'height': '178', 'grade': 'excellent'}
#强制关键字参数错误传参
 func("Hoya",country="UK",20)

SyntaxError: positional argument follows keyword argument
#强制关键字参数只能利用关键字传入参数

注:函数参数知识引自
做者:东皇Amrzs

二、获取参数:inspect模块

能够经过A=inspect.signature(object)提取关于函数参数的信息;
signature支持signature.parameter方法返回关于参数的有序映射。
signature支持signature.bind(args, *kwargs)方法,此方法可将多个实参绑定到签名的形参来接受。

三、函数注解:

注解(annotation)从Python3开始存在,用于为函数声明中的参数和返回值附加元数据。只有inspect.signature()能够提取注解。
本人目前把注解简单理解为一种标签。

3、可调用对象

定义:支持调用运算符()的对象叫作可调用对象。
判断方法:利用内置的callable()函数判断。

Python数据模型包含7种可调用对象:

  • 用户定义的函数
    使用def语句或lambda表达式建立。
  • 内置函数
    使用CPython实现的函数,如len。
  • 内置方法
    使用CPython实现的方法,如list.pop。
  • 方法
    在类的定义体中定义的函数。
  • 类的实例
    若是类定义了__call__方法,这个类的实例能够做为函数调用。
  • 生成器函数
    使用yield关键字的函数或方法。调用生成器函数返回的对象是生成器。

下面针对类的实例为示范进行调用操做:

class Text:
    def __init__(self,text):
        self.text=str(text)
    def number_search(self):
        import re
        num_search=re.compile(r"\d+")
        return print("number search :",num_search.findall(self.text))
    def __call__(self, *args, **kwargs):
        return self.number_search()
a=Text("asdljlj55fsa56af6af66f598as5asf6af59nf3asf830fa3s")
a.number_search()

#输出:
number search : ['55', '56', '6', '66', '598', '5', '6', '59', '3', '830', '3']

从中能够看出,建立函数类对象的简便方式是实现__call__方法。

4、支持函数式编程的包

1. 函数式编程:

相比较命令式编程,函数式编程是经过函数来保存程序的状态的。或者更准确一点,它是经过函数建立新的参数或返回值来保存程序的状态的。

函数式编程与命令式编程对比

认识函数式编程应掌握的两个本质:

  • 高阶函数(higher-order functions)
    函数式编程是经过高阶函数(higher-order functions)的特性来使其具备更丰富多变的表达能力。如map,filter。
    高阶函数和一等函数让基于函数演变的函数式语言表达能力大增,使其可以用函数构建起更高层更抽象的模块来解决复杂的问题。
  • 没有反作用(no side effect)
    函数全部功能就是返回一个新的值,没有其余行为,尤为是不得修改外部变量的值。
    使得函数式编程各个独立的部分的执行顺序能够随意打乱。 而这在命令式编程风格的代码中是不可能的。
    执行顺序的自由使其得以衍生出一大堆很是有用的特性,好比无锁(lock-free)的并发操做、惰性求值(lazy evaluation),还有在编译器级别上的各类性能优化技术。 特别在并行技术上,Clojure, Haskell, F#, Scala, Erlang这些函数式语言都无一例外地支持强大的并发功能。
    固然函数式语言不可能真的就不执行I/O,但它经过一些手段来把I/O的影响限制到最小,好比经过Continuations, Monad等技术。

注:函数式编程知识引自
做者:Jan Fan

Python的目标不是变成函数式编程语言,但经过operator和functools等包也能够进行函数式编程,下面开始介绍这两个模块。

2. operator

本节介绍operator中的mul、itemgetter、attrgetter、methodcaller四种方法。

  • operator.mul(a,b)
    返回数字a和b的乘积。
import operator
from _functools import reduce
#计算阶乘
def fact1():
    list1=filter(lambda x: x%2,range(8))
    return reduce(operator.mul,list1)
print(fact1())#输出:105
  • operator.itemgetter(item or *items)
    建立一个接受集合的函数,返回指定索引对应的元素。若是指定索引至少为2个,以元组形式返回查询结果。
    After f = itemgetter(2), the call f(r) returns r[2].
    After g = itemgetter(2, 5, 3), the call g(r) returns (r[2], r[5], r[3]).
metro_data =[('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833))]
a=itemgetter(1,0)
for i in metro_data:
    print(a(i))#注意分清楚a和i谁是参数,被处理的可迭代对象是参数。

#输出:
('JP', 'Tokyo')
('IN', 'Delhi NCR')
('MX', 'Mexico City')
('US', 'New York-Newark')
('BR', 'Sao Paulo')
  • operator.attrgetter(attr or *attrs)
    建立一个函数,根据名称访问对象的属性,以元组的形式返回。
    After f = attrgetter('name'), the call f(b) returns b.name.
    After f = attrgetter('name', 'date'), the call f(b) returns (b.name, b.date).
    After f = attrgetter('name.first', 'name.last'), the call f(b) returns (b.name.first, b.name.last).

eg:按照城市经度顺序输出城市名和城市经度

from collections import namedtuple
from _operator import attrgetter

latlong=namedtuple("latlong","lat long")
citydata=namedtuple("citydata", "city ID pop coord")
city=[citydata(city,ID,pop,latlong(lat,long)) for city, ID,pop,(lat,long) in metro_data]
#拆包+列表推导。拆包注意city,ID,pop,latlong(lat,long)和citydata具名元组的结构对应关系,至于拆包用的变量名字是什么并不重要,保证可读性便可。
b=attrgetter("city","coord.lat")

#方法1
for i in sorted(city,key=attrgetter("coord.lat")):
    print(b(i))

#方法2
for i in sorted(city,key=lambda x: x[3][0]):
    print(b(i))

#输出
('Sao Paulo', -23.547778)
('Mexico City', 19.433333)
('Delhi NCR', 28.613889)
('Tokyo', 35.689722)
('New York-Newark', 40.808611)
  • operator.methodcaller(name[, args...])
    自行建立函数,使用对象中支持的方法。args表明函数所需传入的参数。
    After f = methodcaller('name'), the call f(b) returns b.name().
    After f = methodcaller('name', 'foo', bar=1), the call f(b) returns b.name('foo', bar=1).
from operator import methodcaller
c=methodcaller("upper")
d=methodcaller("islower")
print(c("apple"),d("apple"))
#输出
APPLE True.

3. functools.partial

语法:functools.partial(func, *args, **keywords)
functools.partial适用于函数冻结参数的状况。冻结参数是指咱们欲调用的函数中的部分或所有参数已经固定,只需补齐剩下的参数调用便可。能够按照word编辑中的格式刷来理解。

from functools import partial
from unicodedata import normalize
clean=partial(normalize,"NFC")#字符串格式化,我以为很像格式刷啊。
e="café"
f="cafe\u0301"
print(e==f) #False
print(clean(e)==clean(f))#True

使用技巧总结:operator中的itemgetter、attrgetter和functools.partial在使用上都须要先构建相似正则表达式的compile partern,即构建对应的itemgetter,attrgetter和partial,而后在partern基础上传入待处理对象。以刚才的partial举例就是clean(e),而不是e(clean)。

相关文章
相关标签/搜索