流畅的python--函数及装饰器

在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的作用。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值