第5章 一等函数

在Python中,函数是一等对象。符合一等对象的定义需要满足以下要求:
  • 在运行时创建
  • 能给变量或数据结构的元素赋值
  • 给函数做参数
  • 给函数做返回值
除了函数外,整数、字符串和字典也是一等对象,满足上述要求。

5.1 把函数视作对象
#创建并测试一个函数,然后读取它的__doc__属性,再检查它的类型
>>> def factorial(n):
    '''returns n!'''
    return 1 if n < 2 else n * factorial(n-1)

>>> factorial(42)
1405006117752879898543142606244511569936384000000000
>>> factorial(1)
1
>>> help(factorial)
Help on function factorial in module __main__:

factorial(n)
    returns n!

>>> factorial.__doc__
'returns n!'
>>> type(factorial)
<class 'function'>
__doc__属性用于生成对象的帮助文本。

#通过别的名称使用函数,再把函数作为参数传递
>>> fact = factorial
>>> fact
<function factorial at 0x02D370C0>
>>> fact(5)
120
>>> map(factorial, range(11))
<map object at 0x02D21FB0>
>>> list(map(fact, range(11)))
[1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]
5.2 高阶函数
接受函数作为参数或返回值的函数,称之为“高阶函数”。

#根据单词的长度排序,只需将len函数传给key参数
>>> fruits = ['strawberry','fig','apple','cherry','raspberry','banana']
>>> sorted(fruits, key=len)
['fig', 'apple', 'cherry', 'banana', 'raspberry', 'strawberry']
#根据反向拼写给一个单词列表排序
>>> def reverse(word):
    return word[::-1]

>>> reverse('testing')
'gnitset'
>>> sorted(fruits, key=reverse)
['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']
5.3 匿名函数
lambda关键字在Python表达式内创建匿名函数。由于Python语法的限制,匿名函数很少使用,比较多作为参数传递给高阶函数。

#使用lambda表达式反转拼写,然后依次给单词列表排序
>>> fruits = ['strawberry','fig','apple','cherry','raspberry','banana']
>>> sorted(fruits, key=lambda word: word[::-1])
['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']
5.4 可调用对象
可调用对象有:
  • 用户定义的函数
  • 内置函数  使用CPython实现的函数,如len。
  • 内置方法      使用C语言实现的方法,如dict.get。
  • 方法
  • 类的实例        如果类定义了__call__方法,那么它的实例可作为函数调用。
  • 生成器函数     使用yield关键字的函数或方法。

5.5 用户定义的可调用类型
不仅Python函数都是对象,任何Python对象都可以表现得像函数,只需要实现实例方法__call__。

5.6 函数内省
除了__doc__,函数对象还有很多属性。使用dir函数可以探知factorial具有下述属性:
>>> dir(factorial)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
5.7 从定位参数到仅限关键字参数
定位参数和关键字参数的区别:
    参考一个例子:函数调用tag(a,b=2)。在这个例子中,a是定位参数,它是通过位置的先后顺序给形参传参;b=2是关键字参数。

5.8 获取关于参数的信息
调用函数的属性或方法获取参数的信息
def clip(text, max_len=80):
    """在max_len前面或后面的第一个空格处截断文本
    """

    end = None
    if len(text) > max_len:
        space_before = text.rfind(' ', 0, max_len)
        if space_before >= 0:
            end = space_before
        else:
            space_after = text.rfind(' ', max_len)
            if space_after >= 0:
                end = space_after
        if end is None: #没找到空格           
            end = len(text)
        return text[:end].rstrip()

from tag import clip
print(clip.__defaults__)    #它的值是一个元组,保存定位参数和关键字参数的默认值。
print(clip.__code__)        #它的值是一个code对象引用,自身也有很多属性。
print(clip.__code__.co_varnames) #含有函数创建的局部变量
print(clip.__code__.co_argcount) #不明白
结果:
(80,)
<code object clip at 0x03091D30, file "d:\ProgramFilesWer\python3.6\tag.py", line 30>
('text', 'max_len', 'end', 'space_before', 'space_after')
2
5.9 函数注解
用于为函数声明中的参数和返回值附加元数据,可以对参数做检查。
def clip(text:str, max_len:'int > 0'=80) -> str:
    """在max_len前面或后面的第一个空格处截断文本
    """

    end = None
    if len(text) > max_len:
        space_before = text.rfind(' ', 0, max_len)
        if space_before >= 0:
            end = space_before
        else:
            space_after = text.rfind(' ', max_len)
            if space_after >= 0:
                end = space_after
        if end is None: #没找到空格
            end = len(text)
        return text[:end].rstrip()
函数声明中的各个参数可以在:之后增加注解表达式。如果参数有默认值,注解放在参数名和=号之间。如果想返回值,在)和函数声明末尾的:之间添加 ->和一个表达式。这个表达式可以是任何类型。

Python对注解所做的唯一的事情是,把它们存储在函数的__annotations__属性里,不做任何处理,注解对Python解释器也没有任何意义。注解只是元数据,可供IDE、框架和装饰器等工具使用。在标准库里,唯有inspect.signature()函数知道如何提取注解。

5.10 支持函数式编程的包
虽然Guido明确表明,Python的目标不是变成函数式编程语言,但是得益于operator和functools等包的支持,接下来介绍这两个包。

operator模块
#使用reduce函数和一个匿名函数计算阶乘
>>> from functools import reduce
>>> def fact(n):
    return reduce(lambda a, b: a*b, range(1, n+1))

>>> fact(3)
6
#使用reduce和operator.mul函数计算阶乘
>>> from functools import reduce
>>> from operator import mul
>>> def fact(n):
    return reduce(mul, range(1, n+1))

>>> fact(4)
24
operator模块的itemgetter函数(自行构建函数)
    用法:根据元组下标获取对于元组的值
#演示使用itemgetter排序一个元组列表
>>> metro_data = [
    ('Tokyo', 'JP', 36.933, (35.4664, 139.654)),
    ('Delhi NCP', 'IN', 53.654,(45.654, 25.644 )),
    ('Mexico City', 'MX', 21.354,(-19.654, 23.546 )),
    ('New York-Newark', 'US', 65.654,(-140.225, 45.3621 )),
    ('Sao Paulo', 'BR', 98.654,(112.312, 10.231)),
]
>>> from operator import itemgetter
>>> for city in sorted(metro_data, key=itemgetter(1)):
    print(city)


('Sao Paulo', 'BR', 98.654, (112.312, 10.231))
('Delhi NCP', 'IN', 53.654, (45.654, 25.644))
('Tokyo', 'JP', 36.933, (35.4664, 139.654))
('Mexico City', 'MX', 21.354, (-19.654, 23.546))
('New York-Newark', 'US', 65.654, (-140.225, 45.3621))
#如果把多个参数传给itemgetter,它构建的函数会返回提取的值构成的元组:
>>> cc_name = itemgetter(1,0)
>>> for city in metro_data:
    print(city)

('Tokyo', 'JP', 36.933, (35.4664, 139.654))
('Delhi NCP', 'IN', 53.654, (45.654, 25.644))
('Mexico City', 'MX', 21.354, (-19.654, 23.546))
('New York-Newark', 'US', 65.654, (-140.225, 45.3621))
('Sao Paulo', 'BR', 98.654, (112.312, 10.231))

operator模块的attrgetter函数(自行构建函数)
    它创建的函数根据名称提取对象的属性,如果把多个属性名传给它,则会返回提取的值构成的元组。
#定义一个namedtuple,名为metro_data,演示使用attrgetter处理它
>>> from collections import namedtuple
>>> LatLong = namedtuple('LatLong', 'lat long')
>>> Metropolis = namedtuple('Metropolis', 'name cc pop coord')
>>> metro_areas = [Metropolis(name, cc, pop, LatLong(lat, long)) for name, cc, pop, (lat, long) in metro_data]
>>> metro_areas[0]
Metropolis(name='Tokyo', cc='JP', pop=36.933, coord=LatLong(lat=35.4664, long=139.654))
>>> metro_areas[0].coord.lat
35.4664
>>> from operator import attrgetter
>>> name_lat = attrgetter('name', 'coord.lat')
>>> for city in sorted(metro_areas, key=attrgetter('coord.lat')):
    print(name_lat(city))

('New York-Newark', -140.225)
('Mexico City', -19.654)
('Tokyo', 35.4664)
('Delhi NCP', 45.654)
('Sao Paulo', 112.312)
operator模块的methodcaller函数(自行构建函数)
    它创建的函数会在对象上调用参数指定的方法。
#展示绑定额外参数的方式
>>> from operator import methodcaller
>>> s = 'The time has come'
>>> upcase = methodcaller('upper')
>>> upcase(s)
'THE TIME HAS COME'
>>> hiphenate = methodcaller('replace', '', '-')
>>> hiphenate(s)
'-T-h-e- -t-i-m-e- -h-a-s- -c-o-m-e-'
>>> hiphenate = methodcaller('replace', ' ', '-')
>>> hiphenate(s)
'The-time-has-come'
使用functools.partial冻结参数
functools模块提供了一系列的高阶函数,其中最为人熟知的是reduce,余下的函数最有用的是partial及其变体,partialmethod。
#使用partial把一个两参数函数改编成需要单参数的可调用对象
>>> from operator import mul
>>> from functools import partial
>>> triple = partial(mul, 3)
>>> triple(7)
21
>>> list(map(triple, range(1, 10)))
[3, 6, 9, 12, 15, 18, 21, 24, 27]
5.11 本章小结
***
<think>好的,我现在需要回答用户关于JavaScript中“一等函数”意味着什么的问题。首先,我得确保自己完全理解这个概念。一等函数(First-class Functions)在编程语言中是一个常见的术语,但具体到JavaScript中,它有哪些具体表现呢? 用户之前询问过如何使用对象字面量创建对象,现在转向函数特性,可能需要更深入的语言特性解释。我需要从基本定义入手,解释什么是一等函数,然后分点说明其具体体现,比如函数可以被赋值给变量、作为参数传递、作为返回值、存储在数据结构中等。 接下来,我应该举一些例子,帮助用户理解每个特点。例如,函数赋值给变量的例子,回调函数作为参数传递的例子,闭包作为返回值的例子,以及对象中的方法存储函数的例子。还要注意提到箭头函数和普通函数的区别,尤其是在处理this时的不同。 同时,用户可能对为什么一等函数重要感兴趣,需要结合实际应用场景,比如高阶函数函数组合、事件处理等。这样用户能明白这个概念的实际用途,而不仅仅是理论上的定义。 还要注意术语的准确性,确保解释清晰,避免误解。可能需要提到函数式编程的相关概念,但不过多深入,以免偏离主题。最后,总结一等函数带来的优势,如灵活性、代码简洁性、模块化等,让用户理解其在JavaScript开发中的重要性。 检查是否有遗漏点,比如动态创建函数函数的方法和属性(如name、length属性,call、apply方法),这些是否属于一等函数的一部分?可能需要简要提及,以展示函数的对象特性。 最后,确保语言简洁,结构清晰,分点明确,例子恰当,帮助用户从多个角度理解一等函数的意义和应用。同时避免使用过于专业的术语,用通俗易懂的中文进行解释,确保不同层次的用户都能理解。</think>以下是关于 JavaScript 一等函数的详细解释: **核心概念** JavaScript 中的一等函数(First-class Functions)意味着函数与其他数据类型(如数值、字符串、对象)具有完全平等的地位,主要表现在以下方面: ### 1. 函数可赋值变量 ```javascript // 普通函数声明 function add(a, b) { return a + b } // 将函数赋值给变量 const multiply = function(a, b) { return a * b }; // 箭头函数表达式 const divide = (a, b) => a / b; console.log(typeof multiply); // "function" ``` ### 2. 函数可作参数传递(回调函数) ```javascript // 高阶函数:接收函数作为参数 function calculate(a, b, operation) { return operation(a, b); } // 传递不同函数实现不同运算 console.log(calculate(5, 3, add)); // 8 console.log(calculate(5, 3, multiply)); // 15 ``` ### 3. 函数可作为返回值 ```javascript // 工厂函数:返回新函数 function createGreeting(language) { return function(name) { const greetings = { en: `Hello ${name}!`, es: `¡Hola ${name}!`, zh: `${name},你好!` }; return greetings[language] || 'Unsupported language'; }; } const greetChinese = createGreeting('zh'); console.log(greetChinese('小明')); // "小明,你好!" ``` ### 4. 函数可存储于数据结构 ```javascript // 在数组中存储函数 const operations = [ (a, b) => a + b, (a, b) => a - b, (a, b) => a * b, (a, b) => a / b ]; // 在对象中存储方法 const mathUtils = { PI: 3.1415926, square: x => x * x, cube: x => x * x * x }; console.log(operations[0](2,3)); // 5 console.log(mathUtils.cube(3)); // 27 ``` ### 5. 函数拥有属性和方法 ```javascript function demoFunc(param1, param2) { console.log(demoFunc.caller); // 查看调用者 } // 添加自定义属性 demoFunc.metadata = { author: '张三', version: '1.0.0' }; console.log(demoFunc.name); // "demoFunc" console.log(demoFunc.length); // 2 (参数个数) console.log(demoFunc.metadata); // {author: "张三", version: "1.0.0"} ``` ### 6. 动态创建函数 ```javascript // 使用 Function 构造函数 const sum = new Function('a', 'b', 'return a + b'); console.log(sum(2, 3)); // 5 // 动态生成函数逻辑 const operationType = 'multiply'; const dynamicFunc = new Function('x', 'y', `return x ${operationType === 'multiply' ? '*' : '+'} y`); console.log(dynamicFunc(4, 5)); // 20 ``` **关键影响** 1. **高阶函数**:支持 map/filter/reduce 等函数式编程范式 ```javascript const numbers = [1, 2, 3]; const squares = numbers.map(x => x * x); // [1, 4, 9] ``` 2. **闭包应用**:函数可以记住并访问其词法作用域 ```javascript function counter() { let count = 0; return () => ++count; } const myCounter = counter(); console.log(myCounter()); // 1 console.log(myCounter()); // 2 ``` 3. **异步编程**:支撑回调模式、Promise、async/await ```javascript // 回调示例 setTimeout(() => console.log('2秒后执行'), 2000); // Promise 链 fetch('api/data') .then(response => response.json()) .then(data => processData(data)) .catch(error => handleError(error)); ``` 4. **装饰器模式**:增强函数功能 ```javascript function logDecorator(fn) { return function(...args) { console.log(`调用 ${fn.name},参数:${args}`); const result = fn(...args); console.log(`结果:${result}`); return result; }; } const decoratedAdd = logDecorator(add); decoratedAdd(2, 3); // 输出: // 调用 add,参数:2,3 // 结果:5 ``` **重要特性对比** | 特征 | 普通函数 | 箭头函数 | |--------------------|---------------|------------------| | `this` 绑定 | 动态绑定 | 继承外层上下文 | | `arguments` 对象 | 可用 | 不可用 | | 构造函数 | 可做构造函数 | 不可做构造函数 | | `prototype` 属性 | 有 | 无 | **实际应用场景** - 事件处理程序:`button.addEventListener('click', handler)` - 模块模式:通过立即执行函数封装私有变量 - 函数组合:`const composedFunc = compose(funcA, funcB, funcC)` - 策略模式:将不同算法封装为函数对象 一等函数特性使 JavaScript 具备强大的函数式编程能力,这种设计允许开发者: - 更灵活地组织代码结构 - 实现高阶抽象和代码复用 - 创建更简洁的异步处理流程 - 构建复杂的行为组合模式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值