python进阶-你是否真的懂函数,装饰器,闭包,一等对象

函数的定义:
函数是一段具有特定功能的、可重用的语句组,通过函数名来表示和调用。经过定义,一组语句等价于一个函数,在需要使用这组语句的地方,直接调用函数名称即可。

函数的使用包括两部分:函数的定义和函数的使用。
在这里插入图片描述

以上是函数的通用定义,无可厚非。那面向对象中的函数,又有什么独特的功能呢,首先,它是函数也是对象。

在 Python 中,函数是一等对象。编程语言理论家把“一等对象”定义为满足下述条件的程序实体:
在运行时创建
能赋值给变量或数据结构中的元素
能作为参数传给函数
能作为函数的返回结果

看来函数和其他对象的属性并没有什么不同,以下将重点讨论,在python中,函数作为一个一等对象,所具有的特性。

>>> def func(n):
    '''这是一个递归阶乘函数'''
    return  1 if n<2 else n*func(n-1)

>>> func(10)
3628800
>>> func.__doc__
'这是一个递归阶乘函数'
>>> type(func)
<class 'function'>
>>> help(func)
Help on function func in module __main__:

func(n)
    这是一个递归阶乘函数
__doc__ 是函数对象众多属性中的一个。
func 是 function 类的实例。
__doc__ 属性用于生成对象的帮助文本。

把函数作为对象,传入另一个函数map

>>> abc = func
>>> type(abc)
<class 'function'>
>>> abc
<function func at 0x7f5661eba1e0>
>>> map(abc,range(10))
<map object at 0x7f5660d76940>
>>> list(map(abc,range(10)))
[1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]

有了一等函数,就可以使用函数式风格编程。函数式编程的特点之一是使用高阶函数。
lambda 关键字在 Python 表达式内创建匿名函数。
Python 简单的句法限制了 lambda 函数的定义体只能使用纯表达式。换句话
说,lambda 函数的定义体中不能赋值,也不能使用 while 和 try 等 Python 语句。

使用 lambda 表达式反转拼写,然后依此给单词列表排序,如下:

>>> fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']
>>> sorted(fruits, key=lambda word: word[::-1])
['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']

再思考下一个段代码,想想结果是多少,然后自己试一试吧,相信你会有所发现:

>>> x = 10
>>> a = lambda y: x + y
>>> x = 20
>>> b = lambda y: x + y

这其中的奥妙在于 lambda 表达式中的 x 是一个自由变量,在运行时绑定值,而不是定义时就绑定,这跟函数的默认值参数定义是不同的。因此,在调用这个 lambda 表达式的时候,x 的值是执行时的值。

>>> a(10)
30
>>> b(10)
30
>>>

如果你想让某个匿名函数在定义时就捕获到值,可以将那个参数值定义成默认参数即可,就像下面这样:

>>> x = 10
>>> a = lambda y, x=x: x + y
>>> x = 20
>>> b = lambda y, x=x: x + y
>>> a(10)
20
>>> b(10)
30
>>>

在这里列出来的问题是新手很容易犯的错误,有些新手可能会不恰当的使用lambda 表达式。比如,通过在一个循环或列表推导中创建一个lambda 表达式列表,并期望函数能在定义时就记住每次的迭代值。例如:

>>> funcs = [lambda x: x+n for n in range(5)]
>>> for f in funcs:
... print(f(0))
...
4
4
4
4
4
>>>

现在我们用另一种方式修改一下:

>>> funcs = [lambda x, n=n: x+n for n in range(5)]
>>> for f in funcs:
... print(f(0))
...
0
1
2
3
4
>>>

lambda 句法只是语法糖:与 def 语句一样,lambda 表达式会创建函数对象。这是Python 中几种可调用对象的一种。

可调用对象

除了用户定义的函数,调用运算符(即 ())还可以应用到其他对象上。如果想判断对象能否调用,可以使用内置的 callable() 函数。Python 数据模型文档列出了 7 种可调用对象。
用户定义的函数
  使用 def 语句或 lambda 表达式创建。
内置函数
  使用 C 语言(CPython)实现的函数,如 len 或 time.strftime。
内置方法
  使用 C 语言实现的方法,如 dict.get。
方法
  在类的定义体中定义的函数。

  调用类时会运行类的 new 方法创建一个实例,然后运行 init 方法,初始化实例,最后把实例返回给调用方。因为 Python 没有 new 运算符,所以调用类相当于调用函数。(通常,调用类会创建那个类的实例,不过覆盖 new 方法的话,也可能出现其他行为。)
类的实例
  如果类定义了 call 方法,那么它的实例可以作为函数调用。
生成器函数
  使用 yield 关键字的函数或方法。调用生成器函数返回的是生成器对象。

>>> abs, str, 13
(<built-in function abs>, <class 'str'>, 13)
>>> [callable(obj) for obj in (abs,str,int,12)]
[True, True, True, False]

用户定义的可调用类型

不仅 Python 函数是真正的对象,任何 Python 对象都可以表现得像函数。为此,只需实现实例方法 call


import random

class BingoCage:
    '''
    BingoCage 类。这个类的实例使用任何可迭代对象构建,
    而且会在内部存储一个随机顺序排列的列表。调用实例会取出一个元素。
    '''
    def __init__(self, items):
         self._items = list(items) 
         random.shuffle(self._items) 
    def pick(self): 
         try:
             return self._items.pop()
         except IndexError:
             raise LookupError('pick from empty BingoCage') 
    def __call__(self): 
         return self.pick()
>>> bingo = BingoCage(range(3))
>>> bingo.pick()
1
>>> bingo()
0
>>> callable(bingo)
True

实现 call 方法的类是创建函数类对象的简便方式,此时必须在内部维护一个状态,让它在调用之间可用,例如 BingoCage 中的剩余元素。装饰器就是这样。装饰器必须是函数,而且有时要在多次调用之间“记住”某些事 [ 例如备忘(memoization),即缓存消耗大的计算结果,供后面使用 ]。创建保有内部状态的函数,还有一种截然不同的方式——使用闭包。

函数内省

除了 doc,函数对象还有很多属性。使用 dir 函数可以探知 factorial 具有下述属性:

>>> dir(func)
['__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__']
>>
  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~白+黑

真乃人中龙凤,必成大器,

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值