• python核心高级学习总结8动态性、__slots__、生成器、迭代器、装饰、闭包


    python的动态性

    什么是动态性呢,简单地来说就是可以在运行时可以改变其结构,如:新的函数、对象、代码都可以被引进或者修改,除了Python外,还有Ruby、PHP、javascript等也是动态语言。下面是python中常用动态性

    运行的过程中给对象绑定(添加)属性

    >>> class Person(object):
        def __init__(self, name = None, age = None):
        self.name = name
        self.age = age
    >>> P = Person("⼩明", "24")
    >>>
    >>> P.sex = "male"
    >>> P.sex
    'male'
    >>>
    以上的例子很好理解,就是动态给实例绑定属性。

    运行的过程给类绑定(添加)属性

    >>> P1 = Person("⼩丽", "25")
    >>> P1.sex
    >>>> Person.sex = None #给类Person添加⼀个属性
    >>> P1 = Person("⼩丽", "25")
    >>> print(P1.sex) #如果P1这个实例对象中没有sex属性的话, 那么就会访问它的类属性
    None #可以看到没有出现异常
    >>>

    运行的过程中给类绑定(添加)方法(类方法和静态方法)

    运行的过程给对象绑定(添加)方法(类方法和静态方法)

    import types
    #定义了⼀个类
    class Person(object):
    num = 0
    def __init__(self, name = None, age = None):
    self.name = name
    self.age = age
    def eat(self):
    print("eat food")
    #定义⼀个实例⽅法
    def run(self, speed):
    print("%s在移动, 速度是 %d km/h"%(self.name, speed))
    #定义⼀个类⽅法
    @classmethod
    def testClass(cls):
    cls.num = 100
    #定义⼀个静态⽅法
    @staticmethod
    def testStatic():
    print("---static method----")
    #创建⼀个实例对象
    P = Person("⽼王", 24)
    #调⽤在class中的⽅法
    P.eat()
    #给这个对象添加实例⽅法
    P.run = types.MethodType(run, P)
    #调⽤实例⽅法
    P.run(180)
    #给Person类绑定类⽅法
    Person.testClass = testClass
    #调⽤类⽅法
    print(Person.num)
    Person.testClass()
    print(Person.num)
    #给Person类绑定静态⽅法
    Person.testStatic = testStatic
    #调⽤静态⽅法
    Person.testStatic()
    注意需要导入types,这是个类或者函数,统称其为模块。要记住调用方式types.MethodType(run,P)

    运行的过程中删除属性或方法

    两种⽅法:
    1. del 对象.属性名
    2. delattr(对象, "属性名")

    __slots__

    可以看到,相对于静态语言,动态语言没有那么严谨,所以玩的时候一定要小心其坑,如果想避免这种情况,请使用__slots__,为了达到限制的⽬的, Python允许在定义class的时候, 定义⼀个特殊的__slots__变量, 来限制该class实例能添加的属性:
    >>> class Person(object):
        __slots__ = ("name", "age")
    >>> P = Person()
    >>> P.name = "⽼王"
    >>> P.age = 20
    >>> P.score = 100
    Traceback (most recent call last):
    File "<pyshell#3>", line 1, in <module>
    AttributeError: Person instance has no attribute 'score'
    >>>
    注意:
    • 使⽤__slots__要注意, __slots__定义的属性仅对当前类实例起作⽤, 对继承的⼦类是不起作⽤的

    生成器

    1. 什么是⽣成器
    通过列表⽣成式, 我们可以直接创建⼀个列表。 但是, 受到内存限制, 列表容量肯定是有限的。 ⽽且, 创建⼀个包含100万个元素的列表, 不仅占⽤很⼤的存储空间, 如果我们仅仅需要访问前⾯⼏个元素, 那后⾯绝⼤多数元素占⽤的空间都⽩⽩浪费了。 所以, 如果列表元素可以按照某种算法推算出来, 那我们是否可以在循环的过程中不断推算出后续的元素呢? 这样就不必创建完整的list, 从⽽节省⼤量的空间。 在Python中, 这种⼀边循环⼀边计算的机制, 称为⽣成器: generator。
    2. 创建⽣成器⽅法1
    要创建⼀个⽣成器, 有很多种⽅法。 第⼀种⽅法很简单, 只要把⼀个列表⽣成式的 [ ] 改成 ( )
    In [15]: L = [ x*2 for x in range(5)]
    In [16]: L
    Out[16]: [0, 2, 4, 6, 8]
    In [17]: G = ( x*2 for x in range(5))
    In [18]: G
    Out[18]: <generator object <genexpr> at 0x7f626c132db0>
    In [19]:
    创建 L 和 G 的区别仅在于最外层的 [ ] 和 ( ) , L 是⼀个列表, ⽽ G 是⼀个⽣成器。 我们可以直接打印出L的每⼀个元素, 但我们怎么打印出G的每⼀个元素呢? 如果要⼀个⼀个打印出来, 可以通过 next() 函数获得⽣成器的下⼀
    个返回值:
    In [19]: next(G)
    Out[19]: 0
    In [20]: next(G)
    Out[20]: 2
    In [21]: next(G)
    Out[21]: 4
    In [22]: next(G)
    Out[22]: 6
    In [23]: next(G)
    Out[23]: 8
    In [24]: next(G)
    ------------------------------------------------------------------------
    StopIteration Traceback (most recent call las
    <ipython-input-24-380e167d6934> in <module>()
    ----> 1 next(G)
    StopIteration:
    In [25]:
    In [26]: G = ( x*2 for x in range(5))
    In [27]: for x in G:
    ....: print(x)
    0 
    2 
    4 
    6 
    8 
    In [28]:
    
    ⽣成器保存的是算法, 每次调⽤ next(G) , 就计算出 G 的下⼀个元素的值,直到计算到最后⼀个元素, 没有更多的元素时, 抛出 StopIteration 的异常。当然, 这种不断调⽤ next() 实在是太变态了, 正确的⽅法是使⽤ for 循环,因为⽣成器也是可迭代对象。 所以, 我们创建了⼀个⽣成器后, 基本上永远不会调⽤ next() , ⽽是通过 for 循环来迭代它, 并且不需要关⼼StopIteration 异常。3. 创建⽣成器⽅法2generator⾮常强⼤。 如果推算的算法⽐较复杂, ⽤类似列表⽣成式的 for 循环⽆法实现的时候, 还可以⽤函数来实现。⽐如, 著名的斐波拉契数列( Fibonacci) , 除第⼀个和第⼆个数外, 任意⼀个数都可由前两个数相加得到:1, 1, 2, 3, 5, 8, 13, 21, 34, ...斐波拉契数列⽤列表⽣成式写不出来, 但是, ⽤函数把它打印出来却很容易:
    
    
    
    
    
    In [28]: def fib(times):
    ....: n = 0
    ....: a,b = 0,1
    ....: while n<times:
    ....: print(b)
    ....: a,b = b,a+b
    ....: n+=1
    ....: return 'done'
    ....:
    In [29]: fib(5)
    1 1 2 3 5 O
    ut[29]: 'done'
    仔细观察, 可以看出, fib函数实际上是定义了斐波拉契数列的推算规则, 可以从第⼀个元素开始, 推算出后续任意的元素, 这种逻辑其实⾮常类似generator。也就是说, 上⾯的函数和generator仅⼀步之遥。 要把fib函数变成generator,只需要把print(b)改为yield b就可以了:
    In [30]: def fib(times):
    ....: n = 0
    ....: a,b = 0,1
    ....: while n<times:
    ....: yield b
    ....: a,b = b,a+b
    ....: n+=1
    ....: return 'done'
    ....:
    In [31]: F = fib(5)
    In [32]: next(F)
    Out[32]: 1
    In [33]: next(F)
    Out[33]: 1
    在上⾯fib 的例⼦, 我们在循环过程中不断调⽤ yield , 就会不断中断。 当然要给循环设置⼀个条件来退出循环, 不然就会产⽣⼀个⽆限数列出来。 同样的, 把函数改成generator后, 我们基本上从来不会⽤ next() 来获取下⼀个返
    回值, ⽽是直接使⽤ for 循环来迭代。但是⽤for循环调⽤generator时, 发现拿不到generator的return语句的返回
    值。 如果想要拿到返回值, 必须捕获StopIteration错误, 返回值包含在StopIteration的value中。

    In [39]: g = fib(5)
    In [40]: while True:
    ....: try:
    ....: x = next(g)
    ....: print("value:%d"%x)
    ....: except StopIteration as e:
    ....: print("⽣成器返回值:%s"%e.value)
    ....: break
    ....:
    value:1
    value:1
    value:2
    value:3
    value:5
    ⽣成器返回值:done
    In [41]:
    4. send
    例⼦: 执⾏到yield时, gen函数作⽤暂时保存, 返回i的值;temp接收下次c.send("python"), send发送过来的值, c.next()等价c.send(None)
    In [10]: def gen():
    ....: i = 0
    ....: while i<5:
    ....: temp = yield i
    ....: print(temp)
    ....: i+=1
    ....:
    #使用next()方法
    In [11]: f = gen()
    In [12]: next(f)
    Out[12]: 0
    In [13]: next(f)
    None
    Out[13]: 1
    In [14]: next(f)
    None
    Out[14]: 2
    In [15]: next(f)
    None
    Out[15]: 3
    In [16]: next(f)
    None
    Out[16]: 4
    In [17]: next(f)
    None
    ------------------------------------------------------------------------
    StopIteration Traceback (most recent call las
    <ipython-input-17-468f0afdf1b9> in <module>()
    ----> 1 next(f)
    StopIteration:
    使用send()
    In [43]: f = gen()
    In [44]: f.__next__()
    Out[44]: 0
    In [45]: f.send('haha')
    haha
    Out[45]: 1
    In [46]: f.__next__()
    None
    Out[46]: 2
    In [47]: f.send('haha')
    haha
    Out[47]: 3
    In [48]:
    最后总结:⽣成器是这样⼀个函数, 它记住上⼀次返回时在函数体中的位置。 对⽣成器函数的第⼆次( 或第 n 次) 调⽤跳转⾄该函数中间, ⽽上次调⽤的所有局部变量都保持不变。⽣成器不仅“记住”了它数据状态; ⽣成器还“记住”了它在流控制构造( 在命令式编程中, 这种构造不只是数据值) 中的位置。
    ⽣成器的特点:
    1. 节约内存
    2. 迭代到下⼀次的调⽤时, 所使⽤的参数都是第⼀次所保留下的, 即是说, 在整个所有函数调⽤的参数都是第⼀次所调⽤时保留的, ⽽不是新创建的

    迭代器

    迭代器跟其他语言类似,从一个可迭代的对象中逐步遍历其值,而且只会前进,不会后退。
    1.可迭代对象Iterable
    也即可以用for循环来遍历的数据类型
    ⼀类是集合数据类型, 如 list 、 tuple 、 dict 、 set 、 str 等;
    ⼀类是 generator , 包括⽣成器和带 yield 的generator function。
    可以使⽤ isinstance() 判断⼀个对象是否是 Iterable 对象:
    In [50]: from collections import Iterable
    In [51]: isinstance([], Iterable)
    Out[51]: True
    In [52]: isinstance({}, Iterable)
    Out[52]: True
    In [53]: isinstance('abc', Iterable)
    Out[53]: True
    In [54]: isinstance((x for x in range(10)), Iterable)
    Out[54]: True
    In [50]: from collections import Iterable
    In [51]: isinstance([], Iterable)
    Out[51]: True
    In [52]: isinstance({}, Iterable)
    Out[52]: True
    In [53]: isinstance('abc', Iterable)
    Out[53]: True
    In [54]: isinstance((x for x in range(10)), Iterable)
    Out[54]: True
    ⽽⽣成器不但可以作⽤于 for 循环, 还可以被 next() 函数不断调⽤并返回下⼀个值, 直到最后抛出 StopIteration 错误表示⽆法继续返回下⼀个值了。
    2.迭代器Iterator
    In [62]: isinstance(iter([]), Iterator)
    Out[62]: True
    In [63]: isinstance(iter('abc'), Iterator)
    Out[63]: True
    可以使⽤ isinstance() 判断⼀个对象是否是 Iterator 对象:
    In [56]: from collections import Iterator
    In [57]: isinstance((x for x in range(10)), Iterator)
    Out[57]: True
    In [58]: isinstance([], Iterator)
    Out[58]: False
    In [59]: isinstance({}, Iterator)
    Out[59]: False
    In [60]: isinstance('abc', Iterator)
    Out[60]: False
    In [61]: isinstance(100, Iterator)
    Out[61]: False
    4.Iter函数
    ⽣成器都是 Iterator 对象, 但 list 、 dict 、 str 虽然是 Iterable , 却不是Iterator 。把 list 、 dict 、 str 等 Iterable 变成 Iterator 可以使⽤ iter() 函数:
    In [62]: isinstance(iter([]), Iterator)
    Out[62]: True
    In [63]: isinstance(iter('abc'), Iterator)
    Out[63]: True
    总结:
    • 凡是可作⽤于 for 循环的对象都是 Iterable 类型;
    • 凡是可作⽤于 next() 函数的对象都是 Iterator 类型集合数据类型如 list 、 dict 、 str 等是 Iterable 但不是 Iterator, 不过可以通过 iter() 函数获得⼀个 Iterator 对象。

    闭包

    什么是闭包?闭包有什么用处
    #定义⼀个函数
    def test(number):
    #在函数内部再定义⼀个函数, 并且这个函数⽤到了外边函数的变量, 那么将这个函数以及⽤到
    def test_in(number_in):
    print("in test_in 函数, number_in is %d"%number_in)
    return number+number_in
    #其实这⾥返回的就是闭包的结果
    return test_in
    #给test函数赋值, 这个20就是给参数number
    ret = test(20)
    #注意这⾥的100其实给参数number_in
    print(ret(100))
    #注意这⾥的200其实给参数number_in
    print(ret(200))
    运行结果:
    in test_in 函数, number_in is 100
    120
    in test_in 函数, number_in is 200
    220
    内部函数对外部函数作⽤域⾥变量的引⽤( ⾮全局变量) , 则称内部函数为闭包。
    # closure.py
    def counter(start=0):
        count=[start]
        def incr():
        count[0] += 1
        return count[0]
        return incr
    启动python解释器
    >>>import closeure
    >>>c1=closeure.counter(5)
    >>>print(c1())
    6 >
    >>print(c1())
    7 >
    >>c2=closeure.counter(100)
    >>>print(c2())
    101
    >>>print(c2())
    102
    nonlocal访问外部函数的局部变量(python3)
    def counter(start=0):
        def incr():
            nonlocal start
            start += 1
            return start
        return incr
    c1 = counter(5)
    print(c1())
    print(c1())
    c2 = counter(50)
    print(c2())
    print(c2())
    print(c1())
    print(c1())
    print(c2())
    print(c2())
    4,下面是闭包的一个实际例子
    def line_conf(a, b):
        def line(x):
           return a*x + b
        return line
    line1 = line_conf(1, 1)
    line2 = line_conf(4, 5)
    print(line1(5))
    print(line2(5))
    这个例⼦中, 函数line与变量a,b构成闭包。 在创建闭包的时候, 我们通过liine_conf的参数a,b说明了这两个变量的取值, 这样, 我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。 我们只需要变换参数a,b, 就可以获得不同的直线表达函数。 由此, 我们可以看到, 闭包也具有提⾼代码可复⽤性的作⽤。
    如果没有闭包, 我们需要每次创建直线函数的时候同时说明a,b,x。 这样, 我们就需要更多的参数传递, 也减少了代码的可移植性。
    闭包思考:
    1.闭包似优化了变量, 原来需要类对象完成的⼯作, 闭包也可以完成
    2.由于闭包引⽤了外部函数的局部变量, 则外部函数的局部变量没有及时释放, 消耗内存

    装饰器

    通过下面一个小例子来理解装饰器,具体的关于闭包跟装饰器的内容有篇博客写的更详细,可以看看说说python中的闭包与装饰器
    #定义函数: 完成包裹数据
    def makeBold(fn):
    def wrapped():
    return "<b>" + fn() + "</b>"
    return wrapped
    #定义函数: 完成包裹数据
    def makeItalic(fn):
    def wrapped():
    return "<i>" + fn() + "</i>"
    return wrapped
    @makeBold
    def test1():
    return "hello world-1"
    @makeItalic
    def test2():
    return "hello world-2"
    @makeBold
    @makeItalic
    def test3():
    return "hello world-3"
    print(test1()))
    print(test2()))
    print(test3()))
    运行结果:
    <b>hello world-1</b>
    <i>hello world-2</i>
    <b><i>hello world-3</i></b>
    
    是不是觉得很简单,是不是觉得SO EASY!
    装饰器一般用于下面几个场景
    1. 引⼊⽇志
    2. 函数执⾏时间统计
    3. 执⾏函数前预备处理
    4. 执⾏函数后清理功能
    5. 权限校验等场景
    6. 缓存
  • 相关阅读:
    智能实验室-杀马(Defendio) 3.0.0.670 新春贺岁版
    智能实验室-全能优化(Guardio) 4.0.0.680 beta 9
    解密:骗人的所谓“开放六位 QQ 号码免费申请”
    智能实验室-全能优化(Guardio) 4.0.0.630 beta 3
    智能实验室-全能优化(Guardio) 4.0.0.650 beta 6
    智能实验室-杀马(Defendio) 3.0.0.651 beta 9
    智能实验室-杀马(Defendio) 3.0.0.632 beta 8
    智能实验室-全能优化(Guardio) 4.0.0.691 beta 11
    智能实验室-全能优化(Guardio) 4.0.0.620 beta 2
    智能实验室-全能优化(Guardio) 4.0.0.685 beta 10
  • 原文地址:https://www.cnblogs.com/hulichao/p/python_slot.html
Copyright © 2020-2023  润新知