Python高级编程--02

支持函数式编程的包 functools和operator

https://docs.python.org/3/library/functools.html https://docs.python.org/3/library/operator.html

# reduce函数和匿名函数实现阶乘计算
from functools import reduce
def fact(n):
    print(reduce(lambda a, b: a * b, range(1, n+1)))
    return reduce(lambda a, b: a * b, range(1, n+1))
fact(6)

运行结果:

720
# reduce函数和匿名函数实现阶乘计算
from functools import reduce
from operator import mul
def fact(n):
    print(reduce(mul, range(1, n+1)))
fact(6)

运行结果:

720

使用functools.partial 固定参数

# 使用functools.partial 固定参数
from functools import partial
from operator import mul
triple = partial(mul, 3)
print(triple(7))

运行结果:

21

- 将partial 应用于 make_element上

# 使用functools.partial 固定参数
from functools import partial
from operator import mul


# 生产HTML标签
def make_element(name, *contents, cls=None, **attrs):
    if cls:
        attrs['class'] = cls

    pairs = [f"{attr}={value}" for attr, value in attrs.items()]
    attr_str = ' '.join(pairs)

    if not contents:
        return f"<{name} {attr_str}/>"

    elements = [f"<{name} {attr_str}>{content}</{name}>" for content in contents]

    return '\n'.join(elements)

picture = partial(make_element, 'img', cls='pic-frame')
print(picture(src='car.jpg'))
item = partial(make_element, 'item', size='large')
print(item('a', 'b', 'c'))

运行结果:

<img src=car.jpg class=pic-frame/>
<item size=large>a</item>
<item size=large>b</item>
<item size=large>c</item>

 

解包与多返回值

任何序列或可迭代对象都可以通过一个简单的赋值操作来分解为单独的变量

def test():
    return 1, 2
a, b = test()
a, b, c = [1, 2, 3]
d1 = {"a": 1, "b": 2}
d2 = {"c": 3, "d": 4}
d = {**d1, **d2}
print(d)
{'a': 1, 'b': 2, 'c': 3, 'd': 4}

装饰器

# 举例: 我们想获得函数的运行时间
import time


def record_time(func):
    print("Run decorator.")

    def wrapper():
        start = time.time()
        result = func()
        end = time.time()
        print(func.__name__, end - start)
        return result

    return wrapper
def func_foo():
    time.sleep(2)
    print("Run func_foo.")
func = record_time(func_foo)
func()

运行结果:

Run decorator.
Run func_foo.
func_foo 2.000586986541748

- 装饰器语法糖@

import time 
from functools import wraps

import time

def build_func_with_record(func):
    print("Run decorator.")

    def wrapper():
        start = time.time()
        result = func()
        end = time.time()
        print(func.__name__, end - start)

    return wrapper

@build_func_with_record
def func_bar():
    time.sleep(1)
    print("Run func_bar.")

func_bar()

运行结果:

Run decorator.
Run func_bar.
func_bar 1.0002179145812988

- 带参数

"""

重点强调:
- 装饰器一般来说不会修改函数的签名

- 也不会修改被包装函数的返回结果

- 使用*args, **kwargs是为了确保可以接受任何形式的输入参数

- 装饰器的返回值几乎总是同调用func(*args, **kwargs)的结果一致
"""
import time

def record_time(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(func.__name__, end - start)
        return result

    return wrapper


@record_time
def make_element(name, *contents, cls=None, **attrs):
    """
    生成html标签元素
    """
    if cls:
        attrs['class'] = cls

    pairs = [f"{attr}={value}" for attr, value in attrs.items()]
    attr_str = ' '.join(pairs)

    if not contents:
        return f"<{name} {attr_str}/>"

    elements = [f"<{name} {attr_str}>{content}</{name}>" for content in contents]

    return '\n'.join(elements)

make_element('img', cls='pic-frame', src='a.jpg')

运行结果:

make_element 0.0010006427764892578

- 保存元数据

"""
上述装饰器的使用,导致原始函数的元数据丢失
像函数名,文档字符串,调用签名等等
"""
print(make_element.__name__)
print(make_element.__doc__)

运行结果:

make_element
None

 

from inspect import signature
print(signature(make_element))

运行结果:

(*args, **kwargs)

引入@wraps

from functools import wraps
import time
from inspect import signature


def record_time(func):
    
    """
    通过事先获取func的属性,并储存
    在返回的最终的函数中设置上述属性
    """
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(func.__name__, end - start)
        return result
    return wrapper


@record_time
def make_element(name, *contents, cls=None, **attrs):
    """
    生成html标签元素
    """
    if cls:
        attrs['class'] = cls

    pairs = [f"{attr}={value}" for attr, value in attrs.items()]
    attr_str = ' '.join(pairs)

    if not contents:
        return f"<{name} {attr_str}/>"

    elements = [f"<{name} {attr_str}>{content}</{name}>" for content in contents]

    return '\n'.join(elements)

print(make_element.__name__)
print(make_element.__doc__)
print(signature(make_element))

运行结果:

make_element
    生成html标签元素
    
(name, *contents, cls=None, **attrs)

- 可接受参数的装饰器

"""
编写一个为函数添加日志功能的装饰器
允许用户指定日志的等级,以及一些其他的细节作为参数
"""
from functools import wraps
import logging

logging.basicConfig(format='%(asctime)s %(name)s %(levelname)s %(message)s')
logging.root.setLevel(logging.NOTSET)


def logged(level, name=None, message=None):
    def decorate(func):
        logname = name if name else func.__module__
        log = logging.getLogger(logname)

        logmsg = message if message else f"Function <{func.__name__}> is running."

        @wraps(func)
        def wrapper(*args, **kwargs):
            log.log(level, logmsg)
            return func(*args, **kwargs)

        return wrapper

    return decorate


@logged(logging.DEBUG)
def make_element(name, *contents, cls=None, **attrs):
    """
    生成html标签元素
    """
    if cls:
        attrs['class'] = cls

    pairs = [f"{attr}={value}" for attr, value in attrs.items()]
    attr_str = ' '.join(pairs)

    if not contents:
        return f"<{name} {attr_str}/>"

    elements = [f"<{name} {attr_str}>{content}</{name}>" for content in contents]

    return '\n'.join(elements)
print(make_element('img', cls='pic-frame', src='a.jpg'))

运行结果:

2020-12-01 16:03:15,606 __main__ DEBUG Function <make_element> is running.
<img src=a.jpg class=pic-frame/>

- 闭包

"""
闭包:指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的非全局变量

关键是它能访问定义体之外定义的非全局变量

综上,闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义作用域不可用

但是仍能使用那些绑定


"""
# 基于保存历史的版本
def make_averager():
    # 在averager中 series 一般称为自由变量,指未在本地作用域中绑定的变量
    nums = []

    def averager(new_value):
        nums.append(new_value)
        total = sum(nums)
        return total / len(nums)

    return averager
avg = make_averager()
print(avg(1))

运行结果:

1.0
# 不保存历史数据

def make_averager():
    count = 0
    total = 0
    
    def averager(new_value):
        count += 1
        total += new_value
        return total / count
    
    return averager

avg = make_averager()
avg(1)

报错:

UnboundLocalError: local variable 'count' referenced before assignment
"""
当count是不可变类型时,count += 1 其实是 count = count + 1,这个赋值操作会将count变为局部变量

但是之前的series作为列表是可变对象,不存在这个问题

对于不可变类型,只能读取,不能更新,count = count + 1其实会隐式的创建局部变量

引入nonlocal,作用是将变量标记为自由变量,即使函数中为变量赋予了新的值
"""
# 不保存历史数据

def make_averager():
    count = 0
    total = 0

    def averager(new_value):
        nonlocal count, total
        count += 1
        total += new_value
        return total / count

    return averager
avg = make_averager()
print(avg(3))

运行结果:

3.0

动态设置装饰器属性
 

from functools import wraps, partial
import logging

logging.basicConfig(format='%(asctime)s %(name)s %(levelname)s %(message)s')
logging.root.setLevel(logging.NOTSET)


def attach_wrapper(obj, func=None):
    if not func:
        return partial(attach_wrapper, obj)
    setattr(obj, func.__name__, func)
    return func


def logged(level, name=None, message=None):
    def decorate(func):
        logname = name if name else func.__module__
        log = logging.getLogger(logname)

        logmsg = message if message else f"Function <{func.__name__}> is running."

        @wraps(func)
        def wrapper(*args, **kwargs):
            log.log(level, logmsg)
            return func(*args, **kwargs)

        @attach_wrapper(wrapper)
        def set_level(newlevel):
            nonlocal level
            level = newlevel

        return wrapper

    return decorate

@logged(level=logging.INFO)
def hello():
    print("hello")

hello()

运行结果:

2020-12-01 16:50:29,771 __main__ INFO Function <hello> is running.
hello
hello.set_level(logging.DEBUG)
hello()

运行结果:

2020-12-01 16:51:44,218 __main__ DEBUG Function <hello> is running.
hello

装饰器可以叠加使用

@logged(level=logging.INFO)
@record_time
def hello():
    print("hello")

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值