美文网首页Python语言自学Python入门与进阶
Python高级知识点学习(五)

Python高级知识点学习(五)

作者: kakarotto | 来源:发表于2018-10-24 11:41 被阅读3561次

    dict的子类

    首先,不建议自己编写代码继承dict和list这种原生使用c语言编写的,因为有时候,用c语言写的dict不会调用python写的覆盖的方法。

    如果确实有继承dict来写代码的需求,可以使用UserDict,继承这个UserDict。
    UserDict这个内部使用了python语言实现了c语言写的逻辑。

    from collections import UserDict
    
    class Mydict(UserDict):
        def __setitem__(self, key, value):
            super().__setitem__(key, value*2)
    
    my_dict = Mydict(one=1)
    print (my_dict)
    
    打印结果:
    {'one': 2}
    

    set和fronzenset

    set集合:

    1. 无序
    2. 不重复
    3. set 接受一个可迭代对象

    frozenset集合:

    1. 一旦设置好就无法修改。
    2. frozenset为不可变类型。
    3. 相对于可变类型来说的好处,可以作为dict的key。

    set的初始化方法:

    set([a, b, c])
    a = {a, b, c}
    两种都可以初始化
    

    dict、set实现原理

    当我们了解了背后的实现原理,就可以判断什么情况下使用dict以及为什么要使用dict。
    dict查找的性能远远大于list。

    在list中随着list数据的增大 查找时间会增大。
    在dict中查找元素不会随着dict的增大而增大。

    dict原理实际上就是利用hash算法。

    数组和链表相比来说最大的优势,就是它可以做到任何一个位置直接存取而不需要从头到尾遍历,因为数组是一段连续的空间,数组取数据的时间复杂度是O(1)。

    dict的key或者set的值,都必须是可以hash的。不可变对象都是可hash的, str, fronzenset, tuple。

    自己实现的类重载__hash__这个魔法函数,让它可以变为可哈希对象。

    dict的内存花销大,但是查询速度快, 自定义的对象或者python内部的对象都是用dict包装的。

    dict的存储顺序和元素添加顺序有关,添加数据有可能改变已有数据的顺序。

    Python中的变量是什么

    python和java中的变量本质不一样,python的变量实质上是一个指针。我们可以理解变量就是一个便利贴,

    例如:a = 1
    先成对象,然后贴便利贴,把a贴在1上面。

    a = [1, 2, 3]
    b = a
    print(id(a), id(b))
    打印结果:
    a和b的地址值一样。
    

    is 和 == 的区别

    is是判断对象的id是否相同,但是注意看下边例子:

    a = [1, 2, 3, 4]
    b = [1, 2, 3, 4]
    print (id(a), id(b))
    print (a is b)
    输出结果:
    4446597320 4446597640
    False
    

    上边这种用法得到的结果很正常,再看下边代码:

    a = 1
    b = 1
    print (id(a), id(b))
    print (a is b)
    
    运行结果:
    4325627840 4325627840
    True
    

    这种情况下,a和b指向的是同一个。这是由于Python内部机制决定的,将小正数建立一个全局唯一的对象,小段字符串也是一样的。

    a = [1, 2, 3]
    b = [1, 2, 3]
    print(a==b) 返回True

    因为:
    list里边实现了一个魔法函数 __eq__,当我们调用a==b这种模式的时候,会调用__eq__这个魔法函数,从而来判断值是否相等。
    它们的值是相等的,只是不是同一个对象而已,所有a==b是True。

    垃圾回收和del语句

    cpython中垃圾回收的算法是采用 引用计数

    a = 1
    b = a
    此时,1这个对象上就又有一个计数器,a = 1 时会在计数器上加1,b如果指向的还是1,1上边的计数器会再加1,当不使用时,执行del a,他就会将引用计数器减1,当引用计数器减到0时,python解释器会将对象回收。

    对象只有在计数器减到0时,才会被回收,del只是减计数器的功能。

    a = object()
    b = a
    del a
    print(b)
    print(a)
    
    打印结果:
    <object object at 0x104a86100>
    NameError: name 'a' is not defined
    

    可以看到,执行del后,只是把a销毁了,b还在。

    __del__魔法函数:
    可以在__del__魔法函数中实现自己的逻辑,当python解释器回收对象的时候,会调用对象的__del__魔法函数,它可以帮我们在回收对象时做一些事。

    @property的用法

    我们在读源码时,往往会看到这这种方法:

    @property
    def hello(self):
         pass
    

    @property 这个装饰器会把函数变为属性描述符,怎么说?

    看代码:

    class Allen(object):
    
        def word(self):
            return 'word'
    
        @property
        def hello(self):
            return 'hello'
    
    a = Allen()
    print(a.hello)
    print(a.word())
    
    运行结果:
    hello
    word
    

    可以发现,@property这个装饰器把取方法的模式变为取属性。

    魔法函数__getattr____getattribute__介绍

    魔法函数是python解释器内部需要用的方法,它是整个python动态特性的最根本原因。

    __getattr__:就是在查找不到属性的时候调用。
    例1:

    class User:
        def __init__(self, info):
            self.info = info
    
        def __getattr__(self, item):
            return '2'
    
    if __name__ == "__main__":
        user = User('allen')
        print(user.info)
        print(user.age)
    
    运行结果:
    allen
    2
    

    例2:

    class User:
        def __init__(self, info={}):
            self.info = info
    
        def __getattr__(self, item):
            return self.info[item]
    
    if __name__ == "__main__":
        user = User(info={"name": "allen", "age": "3"})
        print(user.name)
    
    打印结果:
    allen
    

    __getattribute__ : 比__getattr__更高级,只要查找属性,就会首先进入__getattribute__这个魔法函数,强制进入,无条件的。

    __getattribute__这个魔法函数尽量不要去重写,因为如果一旦写不好,整个类的属性访问就会崩溃掉,一般写框架时会用到这个魔法函数。

    class User:
        def __init__(self,info={}):
            self.info = info
    
        def __getattr__(self, item):
            return self.info[item]
    
        def __getattribute__(self, item):
            return "10"
    
    if __name__ == "__main__":
        user = User(info={"name":"allen"})
        print(user.name)
        print(user.test)
    
    打印结果:
    10
    10
    

    属性描述符和属性查找过程

    一个类只需要实现__get____set____delete__这三个中的任意一个方法,它就算是属性描述符。

    通过属性描述符,可以控制在赋值的时候它的行为,在属性设置的时候参数检查。

    属性描述符有两种:

    1. 数据属性描述符:实现__get____set__就是数据属性描述符。
    2. 非数据属性描述符:只实现一个__get__方法就是非数据属性描述符。

    数据属性描述符 和 非数据属性描述符 它们的属性查找过程是不一样的。

    前边提到的属性查找过程,先查找实例中的属性,然后查找类中的属性,实际上它有更加详细的查找过程。

    魔法函数__new____init__ 区别

    __new__ 魔法函数在python新式类才会有 python2.2之前没有这个。

    class User:
        def __new__(cls, *args, **kwargs):
            print(" in new ")
            return super().__new__(cls)
    
        def __init__(self):
            print(" in init")
            pass
    
    if __name__ == "__main__":
        user = User()
    
    打印结果:
     in new 
     in init
    

    __new__魔法函数允许在生成对象之前加逻辑,自定义对象生成过程,传递进来的是类。

    __init__方法传递进去的是对象。

    __new____init__ 之前调用。

    __new__中必须return super().__new__(cls)才会调用__init__方法

    def __new__(cls, *args, **kwargs):这个中的*args,和**kwargs,代表的是传入的参数。

    相关文章

      网友评论

        本文标题:Python高级知识点学习(五)

        本文链接:https://www.haomeiwen.com/subject/eacizftx.html