• python学习21天----析构、item、hash、eq、模块


    1.析构方法【__del__】

    1)当申请一个对象,并且用完之后,不需要自己去释放,python会自动的去释放

    class A:pass
    a = A()
    print(a)
    del a             #删除对象(包括对象的所有引用)
    print(a)         #删除户打印就会出错
    输出:
    <__main__.A object at 0x00000266AB8A5C88>
    NameError: name 'a' is not defined
    View Code

    2)当通过del删除对象是,内部调用的就是__del__析构方法(先执行__del__,再删除对象)

    class A:
        def __del__(self):      #析构方法内部,虽然没有任何的操作,但是仍然删除了对象
            print("析构方法执行")
    a = A()
    del a
    print(a)
    输出:
    析构方法执行
    NameError: name 'a' is not defined
    View Code

    3)作用:去归还/释放一些在创建对象的时候借用的资源(这些资源python解释器无法自动回收,如打开的文件等)

      若对象不涉及到操作系统的一些资源或者网络连接等,python解释器都会自动回收

    class File:
        def __init__(self,file_path):
            self.f = open(file_path)
        def read(self):
            self.f.read(1024)
        def __del__(self):        #当程序结束时,要去释放打开的系统资源
            self.f.close()
    f = File('文件名')
    f.read()
    View Code

    注:不管是主动还是被动,这个f对象总会被清理掉,被清理掉就触发__del__方法,触发这个方法就会归还操作系统的文件资源

    4)__del__析构方法执行的时间

    ①在手动【del 对象】的时候执行;由程序员触发

    ②python解释器的垃圾回收机制,回收这个对象所占的内存的时候执行;由python解释器触发

    补充:python解释器在内部就能做的事:申请一块内存空间(内存空间由操作系统分配),而在申请的这一块内存空间中的所有事,都是由python解释器来管理的

    python是没有权限直接操控硬盘的文件,必须借助操作系统,才能操作文件(即python给操作系统发一条指令,告诉操作系统,要打开某个文件,此时操作系统给与python一个权限,操作系统替python打开文件,并给python一个文件操作符[文件句柄]),要想在程序中使用这个文件操作父,必须给这个文件操作符分配一块内存,然后通过变量f指向这个文件操作符的地址)所以正常情况下都需要在关闭文件描述符之前,都需要close一个文件

    【del 对象】释放的是python解释器存储的内存

     【f.close】释放的是操作系统打开的文件资源

    总结:某个对象借用了操作系统的资源,还要通过析构方法归还回去(包括文件资源、网络资源等),如果不归还,有时会引发巨大错误

    补充:对于开发文件的方式,【with open() as f】虽然可以自动打开和关闭一个文件,凡是不如【f.open()、f.close()】靠谱,因为如果使用with的打开文件的过程中,程序出现了异常等,那么获取到的资源就得不到释放

    总结:在清除一个对象在内存中的使用的时候,会触发这个对象所在类中的析构方法

    2.item系列方法

    item系类的方法和对象使用中括号,即【对象[]】访问值有联系、

    1)__getitem__方法用于对象[]获取值

    class A:
        def __getitem__(self, item):
            print("getitem方法执行。。。",item)
            return "AAAAAA"
    a = A()
    print(a['b'])
    输出:
    getitem方法执行。。。 b
    AAAAAA
    View Code

    2)__setitem__方法用于对象[]改变值(改变值打印其实还是__setitem__中的内容,两个语法本身是没有联系的,获取值和改变值调用的是两个不同的方法)

    class A:
        def __getitem__(self, item):
            print("getitem方法执行。。。",item)
            return "AAAAAA"
        def __setitem__(self, key, value):
            print(key,value)
    a = A()
    print(a['b'])
    a['b'] = '阿狸'    #通过对象[key] = 'value'设置值时,自动调用__setitem__方法
    print(a['b'])
    输出:
    getitem方法执行。。。 b
    AAAAAA
    b 阿狸
    getitem方法执行。。。 b
    AAAAAA
    View Code

    #让两个方法产生联系(即通过getitem获取的值为setitem改变的值)

    class A:
        def __getitem__(self, item):
            return getattr(self,item)
        def __setitem__(self, key, value):  #setarrt(self,'k1','v1')等价self.k1='v1'
            setattr(self,key,value)
    a = A()
    a['b'] = 'Value'
    print(a['b'])
    输出:
    Value
    View Code

    注:在一些内置的模块中,有一些特殊的方法,要求对象必须实现__getitem__、__seritem__才能使用

    3)__delitem__用于删除对象[]的值(和析构方法不同,如果在__delitem__不进行任何操作,是删除不了值得)

    class A:
        def __getitem__(self, item):
            return getattr(self,item)
        def __setitem__(self, key, value):
            setattr(self,key,value)
        def __delitem__(self, key):
            delattr(self,key)
    a = A()
    a['b'] = 'Value'
    del a['b']
    print(a['b'])
    报错:
    AttributeError: 'A' object has no attribute 'b'
    View Code

    #通过操作一个自定义得对象,操纵了对象中得某个列表(使得操作更加简便),即实现了通过操作对象,操作了对象中得某个数据类型

    class A:
        def __init__(self,lst):
            self.lst =lst
        def __getitem__(self, item):
            return self.lst[item]
        def __setitem__(self, key, value):
            self.lst[key] = value
        def __delitem__(self, key):
            self.lst.pop(key)
    a =A(['111','222','aaa','bbb'])
    print(a.lst[0])  #普通获取值
    print(a[0])  #通过getitem方法获取值
    a[3] = '阿狸' #通过setitem改变值
    print(a.lst) 
    del a[2]    #通过delitem删除值
    输出:
    111
    111
    ['111', '222', 'aaa', '阿狸']
    ['111', '222', '阿狸']
    View Code

    3.__hash__方法

    补充:底层数据结构基于hash值寻址得优化操作

      HASH是一个算法,能够把某一个要存在内存里的值通过一系列计算生成一个唯一的值(hash算法保证不同值得hash结果是不一样的)

      对同一个值在多次执行python代码的时候hash值是不同的;但是对同一个值,在同一次执行python代码的时候hash值永远不变(执行一次run,算一次执行)

    print(hash("abc"))
    print(hash("abc"))
    print(hash("abc"))
    print(hash("abc"))
    输出:
    -4026252806826000686
    -4026252806826000686
    -4026252806826000686
    -4026252806826000686
    View Code

    2)字典的寻址:字典的寻址要比列表的寻址块,因为在内部使用了hash的机制

      对于一个字典{'key':'value'},先对 key做一个hash【hash(key)】计算得出一个hash值,然后把字典中kei对应得value值直接放置在之前计算出得hash值对应的内存地址处,当下次通过【dic[key]】获取key对应的值时,直接对key进行一个【hash(key)】值得计算,然后将这个值和字典中key算出得hash值做比较,相等时直接获取key得hash值所在内存得值

    3)set集合

             集合去重时也利用的hash算法,对集合中的每一个元素进行hash计算,当有重复的值时,它们的hash值是一样的,那么就会把值存在同一个内存地址(两个值存在了一个内存地址中,相当于进行了覆盖),实现了去重。

      通过hash的结果找到一块内存地址,只要这块内存地址上没有数据,就说明之前没有重复的数据;但是如果这块地址上有一个数据存在了,才判断这个值和将要存储的值是否一样,如果一样,就进行覆盖去重,如果 不一样,二次寻址给这个值换个地方存储

    注:对于hash算法,如果两个字符串,通过【hash()】计算出的hash值是一样的,那么还会再次判断两个字符串是否相等【A == B】,如果不相等,再开辟一块内存空间去存放另一个值(进行二次寻址),如果相等,就覆盖。(即先算hash,再判断值)

      python描述的整数值是有限的,但是可hash得值是无限的,所以无法保证每一个字符串不同得字符串都有一个不同得hash值,基于这种情况,只能先判断hash,再判断值(先判断值是否相等,再判断hash,虽然结果准确,但是慢)

    4.__eq__方法

    1)__eq__方法和==是相关的

    2)如果两个实例化的对象的值是相等的,就返回True,否则返回False

    class A:
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def __eq__(self, other):
            if self.name == other.name and self.age ==other.age:
                return True
            else:
                return False
    a1 = A("阿狸",20)
    a2 = A("阿狸",20)
    a3 = A("史迪仔",6)
    print(a1 == a2)
    print(a1 == a3)
    输出:
    True
    False
    View Code

    5.面试题:

    1)有一个类,对象的属性有:姓名、性别、年龄、部门,有一个场景:早期的时候有一个员工管理系统,所有员工的姓名、性别、年龄都是存起来的,但是有些员工内部转岗了,此时将员工的姓名、性别、年龄、新部门又存了一遍(即相当于同一个人,他之前在研发部有一条信息,但是转岗到运维部后,还有一条信息);需求:如果有1000个员工,要求对这1000个员工进行去重,如果几个员工对象的姓名和性别相同,表示这是一个人,此时对这些员工去重(如果有一个人存储了2条信息,去重后还有999条员工信息)

    class Employee:
        def __init__(self,name,age,sex,partment):
            self.name = name
            self.age = age
            self.sex = sex
            self.partment = partment
        def __hash__(self):           #自定义的hash
            return hash('%s%s' %(self.name,self.sex))
        def __eq__(self, other):     #只有hash值相同的情况下,才会执行__eq__方法
            if self.name == other.name and self.sex ==self.sex:
                return True
    employ_lst = []
    #生成1000个对象
    for i in range(200):
        employ_lst.append(Employee("希维尔",18,'',"战争学院"))
    for i in range(200):
        employ_lst.append(Employee("拉克丝",20,'',"德玛西亚"))
    for i in range(200):
        employ_lst.append(Employee("维鲁斯",25,'',"艾欧尼亚"))
    for i in range(200):
        employ_lst.append(Employee("",19,'',"均衡教派"))
    for i in range(200):
        employ_lst.append(Employee("德莱文",23,'',"诺克萨斯"))
    employ_set = set(employ_lst) #set去重时,就是对集合中的每一个元素,做了一个hash计算,此时通过set去重,调用了__hash__方法
    for person in employ_set:
        print(person.__dict__)
    #对于上面程序,使用set集合去重,一定会调用__hash__方法,再通过hash值是否一样
    判断是否调用__eq__方法,如果程序中没有自定义hash方法,程序也不会报错,因为在object类中也有一个内置的hash方法
    输出:
    {'name': '拉克丝', 'sex': '', 'age': 20, 'partment': '德玛西亚'}
    {'name': '德莱文', 'sex': '', 'age': 23, 'partment': '诺克萨斯'}
    {'name': '希维尔', 'sex': '', 'age': 18, 'partment': '战争学院'}
    {'name': '维鲁斯', 'sex': '', 'age': 25, 'partment': '艾欧尼亚'}
    {'name': '', 'sex': '', 'age': 19, 'partment': '均衡教派'}
    View Code

    总结:set的去重机制:先调用__hash__,再调用__eq__;eq不是每次都触发,只有hash值相等的时候才触发

    6.模块

    0)模块分类

    ①内置模块:安装python解释器时,跟着安装的一些方法,存放在python安装目录下的lib目录

    ②自定义模块:自己写的功能,如果是一个通用的功能,可当成一个模块

    ③第三方模块/扩展模块:没在安装python解释器的时候安装的那些功能;存放在python安装目录下的lib/site-packages目录;如爬虫、Django、flask框架等

    1)  什么是模块:

    ①有的功能开发者自己无法完成,需要借助已经实现的函数、类来完成这些功能;即自己无法实现的功能,都由别人实现了;

    ②例如和操作系统打交道、和时间打交道、在1000个数中取随机数、压缩一个文件、和网络通信等,自己无法实现,都由别人实现,但是如果把这些功能都放在一个文件,不管运行什么程序,都会将所有文件加载到内存,会浪费很大内存

    ③所以模块就是将别人写好的一组功能进行分类管理、达到节省内存并且给用户提供了更多的功能(一般为文件夹、py文件或者C语言编译好的一些编译文件)

    2)为什么要有模块:对功能进行分类管理、达到节省内存并且给用户提供了更多的功能

    3)模块的导入和使用

    ①语法

    import 模块名          #要导入一个py文件的名字,但是不加.py后缀名

    ②import这个模块就相当于执行了这个模块所在的py文件

    ---------------------------------------
    moudle_name.py
    print("我是一个模块")
    ------------------------------------------
    import moudle_name
    输出:
    我是一个模块
    View Code

    注:一个模块不会被重复导入

    4)使用模块

    ①模块的名字必须要满足变量的命名规范,一般情况下,模块都是小写字母开头的名字

    my_moudle.py模块
    def login():
        print("这是一个登陆函数")
    name = "阿狸"
    主程序:
    import my_moudle
    my_moudle.login()
    print(my_moudle.name)
    View Code

    ②导入一个模块时,发生了那些事

    1' 先找到my_moudle这个模块

    2' 创建一个属于my_moudle的内存空间

    3' 执行这个my_moudle,把里面的变量等存入这个命名空间(模块有自己独立的命名空间)

    4' 将这个模块所在的命名空间建立一个和my_module之间的引用关系

    5)模块的重命名

    ①语法

    import 模块名 as 新名字

    ②使用

    import my_moudle as m      #模块的重命名
    m.login()
    my_moudle.login()      #此时再通过原来的模块名就会出错
    输出:
    这是一个登陆函数
    NameError: name 'my_moudle' is not defined
    View Code

    注:不是把模块名改了,而是把内存中引用模块的变量名改了

    6)模块导入注意事项

    ①可以在一行导入多个模块

    import os,sys

    ②PEP8规范中,模块不应该在同一行导入

    ③所有的模块导入都应该尽量放在这个文件的开头

    ④模块的导入也是有顺序的:先导入内置模块,再倒入第三方模块,最后导入自定义模块

  • 相关阅读:
    .net 5.0
    多线程synchronized锁
    多线程(Thread、线程创建、线程池)
    电商秒杀方法
    sb @EnableAsync与@Async 20210310
    spring boot @EnableAsync 异步调用
    五代十国军事人物
    唐朝末年,七大割据军阀势力
    盘点万历之后,镇守辽东的8位军事统帅,堪称有军事作为的仅三人
    Cookie-Session or JWT
  • 原文地址:https://www.cnblogs.com/piaolaipiaoqu/p/13917263.html
Copyright © 2020-2023  润新知