在python中,函数是“一等对象”。那么什么是一等对象?
一等对象需要满足以下四个条件:1.在运行是创建;2.能赋值给变量或数据结构中的元素;3.能作为参数传给函数;4.能作为函数的返回结果。函数本身是function类的实例(python2和python3结果不同)
高阶函数:接受函数作为参数,或者把函数作为结果返回的函数称为高阶函数。比如内置函数sorted,map,filter
除了函数可以调用,对象也可以调用,前提是对象的类定义了__call__方法。
# -*- coding: utf-8 -*-
def factorial(n):
""" return n!"""
return 1 if n<2 else n*factorial(n-1)
fruits = ['apple', 'banana', 'strawbarry']
class aa:
def __init__(self,name):
self._name = name
def get_name(self):
return "name is " + self._name
def __call__(self, *args, **kwargs):
return self.get_name()
if __name__ == '__main__':
print(factorial(3))
print(help(factorial))
print(type(factorial))
print(sorted(fruits, key=len))
print(dir(factorial))
tom = aa('tom')
print(tom())
结果:
6
Help on function factorial in module __main__:
factorial(n)
return n!
None
<type 'function'>
['apple', 'banana', 'strawbarry']
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
name is tom
如上,可以看到函数对象有很多属性,其中__dict__用来存储用户属性。
函数的参数处理:在调用函数时使用*和**展开可迭代的对象,可以看以下例子:
def tag(name, *content, **attrs):
"""生成一个或多个htnl标签"""
if attrs:
attr_str = ''.join(' %s="%s"'%(attr, value)for attr,value in sorted(attrs.items()))
else:
attr_str = ''
if content:
return '\n'.join('<%s%s>%s</%s>'%(name, attr_str, c, name) for c in content)
else:
return '<%s%s/>'% (name, attr_str)
if __name__ == '__main__':
print(tag('p','hello', id=33))
结果:
<p id="33">hello</p>
函数变量的作用域:python不要求申明变量,但是假定在函数定义体中赋值的变量是局部变量,要想在函数体中使用全局变量,需要用到global关键字。
函数的装饰器:
@decorate
def target():
print('running target()')
#等价于
target = decorate(target)
所以,被装饰器修饰的函数会被替换。如下:
def deco(func):
def inner():
print('running inner()')
return inner
@deco
def target():
print('running target()')
if __name__ == '__main__':
target()
print(target)
---------------------------------response
running inner()
<function inner at 0x1062332a8>
装饰器的两大特性:1.能把被装饰的函数替换成其他函数;2.装饰器在加载模块时立即执行。
严格来说,装饰器只是语法糖,它可以像常规的可调用的对象一样调用,期参数是另一个函数。
registry = []
def register(func):
print('running register(%s)'%func)
registry.append(func)
return func
@register
def f1():
print('running f1()')
@register
def f2():
print('running f2()')
if __name__ == '__main__':
print('running main()')
print('registry ->', registry)
f1()
f2()
------------------------response
running register(<function f1 at 0x109831d70>)
running register(<function f2 at 0x109831de8>)
running main()
('registry ->', [<function f1 at 0x109831d70>, <function f2 at 0x109831de8>])
running f1()
running f2()
装饰器一般的使用情况:1.装饰器一般会在一个模块中被定义,然后应用到其他模块的函数上;2.大多数装饰器会在内部定义一个函数然后将其返回。
装饰器使用实例:
promos = []
def promotion(promo_func):
promos.append(promo_func)
return promo_func
@promotion
def fidelity(order):
return order.total*0.05 if order.fidelity >= 1000 else 0
@promotion
def once_reduce(order):
return 300 if order.total>1000 else 0
@promotion
def cut(order):
return order.total * 0.1
def best_promo(order):
return max(promo(order) for promo in promos)
if __name__ == '__main__':
print(promos)
print(best_promo(order(999,1000)))
--------------------------------------------response
[<function fidelity at 0x105a80f50>, <function once_reduce at 0x105a89050>, <function cut at 0x105a890c8>]
100.0
实例2:实现一个avg函数,这个函数返回历史平均值:
class Averager():
def __init__(self):
self.series = []
def __call__(self, new_value):
self.series.append(new_value)
return sum(self.series)/len(self.series)
def make_averager():
series = []
#a = 0
def averager(new_value):
#a+=1#不能这样用,这样会隐式创建局部变量
series.append(new_value)
return sum(series)/len(series)
return averager
if __name__ == '__main__':
avg1 = Averager()
avg2 = make_averager()
print(avg1(10))
print(avg1(20))
print(avg1(30))
print(avg2(10))
print(avg2(20))
print(avg2(30))
print(avg2.__code__.co_varnames)
print(avg2.__code__.co_freevars)
------------------------------response
10
15
20
10
15
20
('new_value',)
('series',)
函数可 以嵌套定义,即在一个函数内部可以定义另一个函数,有了嵌套函数这种结构,便会产生闭包问题。在函数式语言中,当内嵌函数体内引用到体外的变量时,将会把定义时涉及到的引用环境和函数体打包成一个整体(闭包)返回。现在给出引用环境的定义就 容易理解了:引用环境是指在程序执行中的某个点所有处于活跃状态的约束(一个变量的名字和其所代表的对象之间的联系)所组成的集合。闭包的使用和正常的函 数调用没有区别。
注意make_averager函数,这个函数用到了闭包的概念。在averager函数中,series是自由变量,指的是没有在本地作用域中绑定的变量,如上是averager函数的局部变量和自由变量。由此可见,闭包是一种函数,它会保留定义函数时的存在的自由变量的绑定,这样在调用函数时,虽然定义作用域不可用了,但是仍能使用这些绑定。(只有嵌套函数才有可能处理不再全局变量中的外部变量)
对不可变对类型,只能读,不能更新,如果尝试重新绑定,例如a+=1,则会隐式创建局部变量a,这样a就不是自由变量了,因此不会保存在闭包中。
闭包的使用:1.当闭包执行完之后,会保存当前的运行环境;2.
def get_sum(initial_sum):
def avg(count):
initial_sum[0] += count
return initial_sum[0]
return avg
if __name__ == '__main__':
avg = get_sum([0])
print(avg(1))
print(avg(2))
print(avg(4))
--------------------------response
1
3
7
实现一个简单的装饰器:
import time
from functools import wraps
def clock(func):
@wraps(func)
def clocked(*args):
t0 = time.time()
result = func(*args)
user_time = time.time()-t0
name = func.__name__
print('[%0.8fs]%s -> %r'%(user_time, name, result))
return result
return clocked
@clock
def snooze(seconds):
time.sleep(seconds)
return 'aa' + str(seconds)
if __name__ == '__main__':
snooze(2)
------------------------------response
[2.00354099s]snooze -> 'aa2'
标准库中的三个用来装饰方法的函数:property, classmethod, staticmethod
一个很好用的装饰器:@lru_cache
叠放装饰器:
@d1
@d2
def func()
...
等价于:
f = d1(d2(func))
参数化装饰器:
def decorate(func):
print('decorate')
print(func)
def clocked(*_args):
print('----')
print(_args)
t0 = time.time()
print(func.__name__)
_result = func(*_args)
elapsed = time.time() - t0
name = func.__name__
args = ', '.join(repr(arg) for arg in _args)
result = repr(_result)
print(DEFAULT_FMT.format(**locals()))
return _result
return clocked
def clock(fmt=DEFAULT_FMT):
def decorate(func):
print('decorate')
print(func)
def clocked(*_args):
print('----')
print(_args)
t0 = time.time()
print(func.__name__)
_result = func(*_args)
elapsed = time.time() - t0
name = func.__name__
args = ', '.join(repr(arg) for arg in _args)
result = repr(_result)
print(fmt.format(**locals()))
return _result
return clocked
return decorate
@clock()#注意:带参数的有括号
def snooze1(seconds):
print('snoozee1')
time.sleep(seconds)
@decorate
def snooze2(seconds):
print('snoozee2')
time.sleep(seconds)
if __name__ == '__main__':
print('main')
snooze1(2)
snooze2(3)
print(snooze1.__name__)
print(snooze2.__name__)
-------------------------------response
decorate
<function snooze1 at 0x10bab06e0>
decorate
<function snooze2 at 0x10bab0668>
main
snooze1
clocked
----
(2,)
snooze1
snoozee1
[2.00051999s] snooze1(2) -> None
----
(3,)
snooze2
snoozee2
[3.00413394s] snooze2(3) -> None
<function hahaha at 0x10bab0938>
这里可以看到@wrap的作用。