设为首页 收藏本站
查看: 1322|回复: 0

[经验分享] Python基础笔记7

[复制链接]

尚未签到

发表于 2018-8-16 09:18:31 | 显示全部楼层 |阅读模式
  1.返回函数
  如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数:
def lazy_sum(*args):  
    def sum():
  
        ax = 0
  
        for n in args:
  
            ax = ax + n        return ax    return sum
  当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:
>>> f = lazy_sum(1, 3, 5, 7, 9)  
>>> f
  

  调用函数f时,才真正计算求和的结果:
>>> f()25  2.
  在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
  3.
>>> f1 = lazy_sum(1, 3, 5, 7, 9)  
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
  
>>> f1==f2
  
False
  4.闭包
  另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。我们来看一个例子:
def count():  
    fs = []
  
    for i in range(1, 4):
  
        def f():
  
           return i*i
  
        fs.append(f)
  
    return fs
  

  
f1, f2, f3 = count()
  在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都返回了。
  你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是:
>>> f1()  
9
  
>>> f2()
  
9
  
>>> f3()
  
9
  全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。
  返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
  5.
  如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
def count():  
    def f(j):
  
        def g():
  
            return j*j
  
        return g
  
    fs = []
  
    for i in range(1, 4):
  
        fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
  
    return fs
  再看看结果:
>>> f1, f2, f3 = count()  
>>> f1()
  
1
  
>>> f2()
  
4
  
>>> f3()
  
9
  缺点是代码较长,可利用lambda函数缩短代码。
  6.匿名函数
  关键字lambda表示匿名函数,冒号前面的x表示函数参数。
  匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
  用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:
>>> f = lambda x: x * x  
>>> f
  

  
>>> f(5)25
  同样,也可以把匿名函数作为返回值返回,比如:
def build(x, y):  
    return lambda: x * x + y * y
  7.装饰器
  函数对象有一个__name__属性,可以拿到函数的名字:
>>> def now():  
...     print('2015-3-25')
  
...
  
>>> f = now
  
>>> now.__name__
  
'now'
  
>>> f.__name__
  
'now'
  现在,假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
  8.
  本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:
def log(func):  
    def wrapper(*args, **kw):
  
        print('call %s():' % func.__name__)
  
        return func(*args, **kw)
  
    return wrapper
  
  9.
  我们要借助Python的@语法,把decorator置于函数的定义处:
@logdef now():  
    print('2015-3-25')
  调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:
>>> now()  
call now():
  
2015-3-25
  把@log放到now()函数的定义处,相当于执行了语句:
now = log(now)  10.
  如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:
def log(text):  
    def decorator(func):
  
        def wrapper(*args, **kw):
  
            print('%s %s():' % (text, func.__name__))
  
            return func(*args, **kw)
  
        return wrapper
  
    return decorator
  这个3层嵌套的decorator用法如下:
@log('execute')  
def now():
  
    print('2015-3-25')
  执行结果如下:
>>> now()  
execute now():
  
2015-3-25
  11.
  以上两种decorator的定义都没有问题,但还差最后一步。因为我们讲了函数也是对象,它有__name__等属性,但你去看经过decorator装饰之后的函数,它们的__name__已经从原来的'now'变成了'wrapper':
>>> now.__name__'wrapper'  因为返回的那个wrapper()函数名字就是'wrapper',所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。
  不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下:
import functoolsdef log(func):  
    @functools.wraps(func)
  
    def wrapper(*args, **kw):
  
        print('call %s():' % func.__name__)
  
        return func(*args, **kw)
  
    return wrapper
  或者针对带参数的decorator:
import functoolsdef log(text):  
    def decorator(func):
  
        @functools.wraps(func)
  
        def wrapper(*args, **kw):
  
            print('%s %s():' % (text, func.__name__))
  
            return func(*args, **kw)
  
        return wrapper
  
    return decorator
  12.
  在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。
  decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便。
  13.
import functools  
def SKLog(SKInput):
  
    def decorator(func):
  
        def inner(args,**kw):
  
            if isinstance(SKInput,str):
  
                print('%s %s():' %(SKInput,func.name))
  
                result = func(args,kw)
  
            else:
  
                print('begin %s():' % func.name)
  
                result = func(*args,kw)
  
                print('end   %s():' % func.name)
  
            return result
  
        return inner
  
    if isinstance(SKInput,str):
  
        return decorator
  
    else:
  
        return decorator(SKInput)
  
@SKLog
  
def SKtestLog1():
  
    print('我是来测试不带字符串的装饰器装饰器的!!!')
  
@SKLog('excute')
  
def SKtestLog2():
  
    print('我是来测试带字符串的装饰器装饰器的!!!')
  
SKtestLog1()
  
print('--------------------------------------')
  
SKtestLog2()
  14.
#! /usr/bin/env python3  
# -*- coding: utf-8 -*-
  

  
import functools
  

  
def log(text):
  
    if isinstance(text, str):
  
        def decorator(func):
  
            @functools.wraps(func)
  
            def test(*args, **kw):
  
                print('%s %s()' % (text, func.__name__))
  
                func(*args, **kw)
  
                print('end call')
  
            return test
  
        return decorator
  
    else:
  
        @functools.wraps(text)
  
        def test(*args, **kw):
  
            print('%s()' % text.__name__)
  
            text(*args, **kw)
  
            print('end call')
  
        return test
  
    return log
  

  
@log
  
def now():
  
    pass
  

  
now()
  

  
@log('excute')
  
def now():
  
    pass
  

  
now()
  注:@functools.wraps(text)总是放在decorator之前,并且有参数时text就是func,没有参数时text就是text。(个人体会)
  15.偏函数
  int()函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做N进制的转换:
>>> int('12345', base=8)5349>>> int('12345', 16)74565  假设要转换大量的二进制字符串,每次都传入int(x, base=2)非常麻烦,于是,我们想到,可以定义一个int2()的函数,默认把base=2传进去:
def int2(x, base=2):  
    return int(x, base)
  16.
  functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2:
>>> import functools  
>>> int2 = functools.partial(int, base=2)
  
>>> int2('1000000')
  
64
  
>>> int2('1010101')
  
85
  所以,简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
  17.
  创建偏函数时,实际上可以接收函数对象、*args和**kw这3个参数,当传入:
int2 = functools.partial(int, base=2)  实际上固定了int()函数的关键字参数base,也就是:
int2('10010')  相当于:
kw = { 'base': 2 }int('10010', **kw)  当传入:
max2 = functools.partial(max, 10)  实际上会把10作为*args的一部分自动加到左边,也就是:
max2(5, 6, 7)  相当于:
args = (10, 5, 6, 7)  
max(*args)
  结果为10。



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-552476-1-1.html 上篇帖子: python 之根据自己的需求配置hostname-Ttr 下篇帖子: Python常用内置函数(二)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表