zoukankan      html  css  js  c++  java
  • python学习第49天索引、元类、单例

    一、索引

    1、什么是索引

    搜索引导, 所以是一种单独的,物理的 有序的 存储结构,用于加速查询

    是mysql中一种专门的数据结构,称为key
    例如: 字典 书的目录 车票上的车厢号

    索引的本质原理就是通过不断地缩小查询范围,来降低io次数从而提升查询性能
    强调:一旦为表创建了索引,以后的查询都会先查索引,再根据索引定位的结果去找数据

    2、为什么用索引

    因为项目运行中,查询操作非常频繁,为了提高用户体验,要提高查询的速度,
    如何提高就靠索引

    索引的影响:

                 1.加速查询
                 2.降低写入(增加 删除 修改)速度
                 3.会额外占用磁盘空间

    3、怎么用索引

    先了解索引的原理和数据结构http://www.cnblogs.com/linhaifeng/articles/7274563.html#_label3

    (1)聚焦索引

    通常我们所说的主键

     特点:叶子节点存放的一整条数据

    (2)辅助索引:除了主键的索引

    特点:
    如果是按照这个字段创建的索引,
    那么叶子节点存放的是:{名字:名字所在那条记录的主键的值}

    覆盖索引:只在辅助索引的叶子节点中就已经找到了所有我们想要的数据

    (3)mysql中的索引:主键、外键、index索引

    举个例子来说,比如你在为某商场做一个会员卡的系统。
    
    这个系统有一个会员表
    有下列字段:
    会员编号 INT
    会员姓名 VARCHAR(10)
    会员身份证号码 VARCHAR(18)
    会员电话 VARCHAR(10)
    会员住址 VARCHAR(50)
    会员备注信息 TEXT
    
    那么这个 会员编号,作为主键,使用 PRIMARY
    会员姓名 如果要建索引的话,那么就是普通的 INDEX
    会员身份证号码 如果要建索引的话,那么可以选择 UNIQUE (唯一的,不允许重复)
    
    #除此之外还有全文索引,即FULLTEXT
    会员备注信息 , 如果需要建索引的话,可以选择全文搜索。
    用于搜索很长一篇文章的时候,效果最好。
    用在比较短的文本,如果就一两行字的,普通的 INDEX 也可以。
    但其实对于全文搜索,我们并不会使用MySQL自带的该索引,而是会选择第三方软件如Sphinx,专门来做全文搜索。

    (4)索引的两大类型hash与btree

    #我们可以在创建上述索引的时候,为其指定索引类型,分两类
    hash类型的索引:查询单条快,范围查询慢
    btree类型的索引:b+树,层数越多,数据量指数级增长(我们就用它,因为innodb默认支持它)
    
    #不同的存储引擎支持的索引类型也不一样
    InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
    MyISAM 不支持事务,支持表级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
    Memory 不支持事务,支持表级别锁定,支持 B-tree、Hash 等索引,不支持 Full-text 索引;
    NDB 支持事务,支持行级别锁定,支持 Hash 索引,不支持 B-tree、Full-text 等索引;
    Archive 不支持事务,支持表级别锁定,不支持 B-tree、Hash、Full-text 等索引;

    (5)创建/删除索引的语法

    #方法一:创建表时
          CREATE TABLE 表名 (
                    字段名1  数据类型 [完整性约束条件…],
                    字段名2  数据类型 [完整性约束条件…],
                    [UNIQUE | FULLTEXT | SPATIAL ]   INDEX | KEY
                    [索引名]  (字段名[(长度)]  [ASC |DESC]) 
                    );
    
    
    #方法二:CREATE在已存在的表上创建索引
            CREATE  [UNIQUE | FULLTEXT | SPATIAL ]  INDEX  索引名 
                         ON 表名 (字段名[(长度)]  [ASC |DESC]) ;
    
    
    #方法三:ALTER TABLE在已存在的表上创建索引
            ALTER TABLE 表名 ADD  [UNIQUE | FULLTEXT | SPATIAL ] INDEX
                                 索引名 (字段名[(长度)]  [ASC |DESC]) ;
                                 
    #删除索引:DROP INDEX 索引名 ON 表名字;

    (6)索引的使用

    1.无论索引如何设计 无法降低范围查询的查询速度
    select count(*) from usr where id > 1;
    即使命中索引也无法提高效率

    2.索引不应该加在值重复度很高的字段上 应该加在重复度低的字段
    3. 使用and时 当 条件中出现多个索引命中时 会自定找一个区分度最高的索引来使用


    4.使用or的时候 如果两边都有索引 会使用索引,但是注意 or两边都要执行 顺序依然从左往右
    只有一边有索引会不会使用索引? 不会使用 无法加速查询

    5.优化查询 不仅仅要加索引,sql语句也需要优化 使其能命中索引
    你的条件中应该使用区别度高的索引

    6.联合索引
    为是什么使用它
    降低资源的占用 , 降低增删改的时间 会比单个字段的索引快

    建立联合索引时 应该把区分度高放最左边 区分度低的依次往右放
    按照区分度的高低 从左往右 依次排列

    查询中 应该优先使用左边的索引
    使用and时 无所谓书写顺序 会自动找区分度最高的
    注意联合索引在查询时 如果压根没用到最左侧索引 不能加速查询

    正常开发时
    优先使用聚集索引
    再次 使用联合索引 如果你的条件不包含最左侧索引 不能加速查询 这时候就应该使用单个字段索引

    创建索引:
    create index 索引名称 on usr(索引字段);

     二、元类

    1、什么是元类:

    一切皆为对象,那么类本质也是一个对象,既然所有的对象都是调用类得到的,那么类必然也是调用了另外一个类得到的,这个另外的一个类称为元类

    class OldboyTeacher(object):
        school='oldboy'
    
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def say(self):
            print('%s says welcome to the oldboy to learn Python' %self.name)
    所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化),比如对象t1是调用类OldboyTeacher得到的
    
    t1=OldboyTeacher('egon',18)
    print(type(t1)) #查看对象t1的类是<class '__main__.OldboyTeacher'>
    如果一切皆为对象,那么类OldboyTeacher本质也是一个对象,既然所有的对象都是调用类得到的,那么OldboyTeacher必然也是调用了一个类得到的,这个类称为元类
    
    于是我们可以推导出===>产生OldboyTeacher的过程一定发生了:OldboyTeacher=元类(...)
    
    print(type(OldboyTeacher)) # 结果为<class 'type'>,证明是调用了type这个元类而产生的OldboyTeacher,即默认的元类为type

    2、class关键字创建类的流程分析

    1. 先拿到类名:'OldboyTeacher'
    # 2. 再拿到类的基类们:(object,)
    # 3. 然后拿到类的名称空间???(执行类体代码,将产生的名字放到类的名称空间也就是一个字典里,补充exec)
    # 4. 调用元类实例化得到自定义的类: OldboyTeacher=type('OldboyTeacher',(object,),{...})
    # class OldboyTeacher: #OldboyTeacher=type(...)
    #     school = 'Oldboy'
    #     def __init__(self,name,age,sex):
    #         self.name=name
    #         self.age=age
    #         self.sex=sex
    #
    #     def score(self):
    #         print('%s is scoring' %self.name)
    # print(OldboyTeacher)
    exec的用法
    #exec:三个参数 #参数一:包含一系列python代码的字符串 #参数二:全局作用域(字典形式),如果不指定,默认为globals() #参数三:局部作用域(字典形式),如果不指定,默认为locals() #可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中 g
    ={ 'x':1, 'y':2 } l={} exec(''' global x,z x=100 z=200 m=300 ''',g,l) print(g) #{'x': 100, 'y': 2,'z':200,......} print(l) #{'m': 300}

    3、为什么用

    一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类

     class Mymeta(type): #但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
    #     def __init__(self,class_name,class_bases,class_dic):
    #         print(self)
    #         print(class_name)
    #         print(class_bases)
    #         print(class_dic)
    #
    # class OldboyTeacher(object,metaclass=Mymeta): #OldboyTeacher=Mymeta('OldboyTeacher',(object,),{...})
    #     school = 'Oldboy'
    #
    #     def __init__(self,name,age,sex):
    #         self.name=name
    #         self.age=age
    #         self.sex=sex
    #
    #     def score(self):
    #         print('%s is scoring' %self.name)

    (1)控制类的产生过程

    类的产生过程其实就是元类的调用过程

    比如我们可以通过元类控制类名必须使用驼峰体,类体必须要有文档注释,且不能为空

    class Mymeta(type): #但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
        def __init__(self,class_name,class_bases,class_dic):
            if class_name.islower():
                raise TypeError('类名必须使用驼峰体')
    
            doc=class_dic.get('__doc__')
            if doc is None or len(doc) == 0 or len(doc.strip('
     ')) == 0:
                raise TypeError('类体中必须有文档注释,且文档注释不能为空')
    
    class OldboyTeacher(object,metaclass=Mymeta): #OldboyTeacher=Mymeta('OldboyTeacher',(object,),{...})
        school = 'Oldboy'
    
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
        def score(self):
            print('%s is scoring' %self.name)
    
    
    print(OldboyTeacher.__dict__)

    (2)自定义元类来控制调用的过程

    了解--call--:

    class OldboyTeacher(object): #OldboyTeacher=Mymeta('OldboyTeacher',(object,),{...})
        school = 'Oldboy'
    
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
        def score(self):
            print('%s is scoring' %self.name)
    
        def __call__(self, *args, **kwargs):
            print(self)
            print(args)
            print(kwargs)
    tea1=OldboyTeacher('egon',18,'male')
    
    tea1(1,2,a=1,b=2) #__call__(tea1,(1,2).{'a':1,'b':2})

    对象之所以可以调用,是因为对象的类中有一个函数__call__
    #推导:如果一切皆对象,那么OldboyTeacher也是一个对象,该对象之所可以调用,肯定是这个对象的类中也定义了一个函数__call__。

    思路:

    # 实例化OldboyTeacher,或者说调用OldboyTeacher会
    # 1. 先产生一个空对象
    # 2. 执行__init__方法,完成对象的初始属性操作
    # 3. 返回初始化好的那个对象
    # 推导:调用OldboyTeacher(...)就是在调用OldboyTeacher的类中的__call__,那么在该__call__中就需要做上述三件事

    #自定义元类来控制类的调用(即类的实例化过程)
    
    class Mymeta(type): #但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
        def __call__(self, *args, **kwargs): #self=OldboyTeacher这个类,args=('egon',18,'male'),kwargs={}
            # 1. 先产生一个空对象
            tea_obj=self.__new__(self) #tea_obj是OldboyTeacher这个类的对象
            # 2. 执行__init__方法,完成对象的初始属性操作
            self.__init__(tea_obj,*args,**kwargs)
            # print(tea_obj.__dict__)
            tea_obj.__dict__={('_%s__%s' %(self.__name__,k)):v for k,v in tea_obj.__dict__.items()}
            # 3. 返回初始化好的那个对象
            return tea_obj
    
    class OldboyTeacher(object,metaclass=Mymeta): #OldboyTeacher=Mymeta('OldboyTeacher',(object,),{...})
        school = 'Oldboy'
    
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
        def score(self):
            print('%s is scoring' %self.name)
    
    tea1=OldboyTeacher('egon',18,'male') # 会触发OldboyTeacher的类(即元类)中的__call__函数
    # print(tea1)
    print(tea1.__dict__)

    4、属性查找

    结合python继承的实现原理+元类重新看属性的查找应该是什么样子呢???

    在学习完元类后,其实我们用class自定义的类也全都是对象(包括object类本身也是元类type的 一个实例,可以用type(object)查看),我们学习过继承的实现原理,如果把类当成对象去看,将下述继承应该说成是:对象OldboyTeacher继承对象Foo,对象Foo继承对象Bar,对象Bar继承对象object

    class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
        n=444
    
        def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
            obj=self.__new__(self)
            self.__init__(obj,*args,**kwargs)
            return obj
    
    class Bar(object):
        n=333
    
    class Foo(Bar):
        n=222
    
    class OldboyTeacher(Foo,metaclass=Mymeta):
        n=111
    
        school='oldboy'
    
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def say(self):
            print('%s says welcome to the oldboy to learn Python' %self.name)
    
    
    print(OldboyTeacher.n) #自下而上依次注释各个类中的n=xxx,然后重新运行程序,发现n的查找顺序为OldboyTeacher->Foo->Bar->object->Mymeta->type

    三、单例

    1、什么是单例

    指的是 一个类有且仅有一个实例 就叫单例

    2、为什么要使用单例这种模式

    之前在创建对象时 每个对象中的数据不相同 对象实际上数据和处理数据的方法的结合体
    当对象中的数据是 相同的 共享的 时候 使用单例

    3、单例的四种方法

    实例化类配置端口、IP

    settings是配置文件

    (1)

    import settings
    
    class MySQL:
        __instance=None
        def __init__(self, ip, port):
            self.ip = ip
            self.port = port
    
        @classmethod
        def from_conf(cls):
            if cls.__instance is None:
                cls.__instance=cls(settings.IP, settings.PORT)
            return cls.__instance
    obj1=MySQL.from_conf()
    obj2=MySQL.from_conf()
    obj3=MySQL.from_conf()
    # obj4=MySQL('1.1.1.3',3302)
    print(obj1)
    print(obj2)
    print(obj3)
    # print(obj4)

    (2)

    import settings
    
    def singleton(cls):
        _instance=cls(settings.IP,settings.PORT)
        def wrapper(*args,**kwargs):
            if len(args) !=0 or len(kwargs) !=0:
                obj=cls(*args,**kwargs)
                return obj
            return _instance
        return wrapper
    
    @singleton #MySQL=singleton(MySQL) #MySQL=wrapper
    class MySQL:
        def __init__(self, ip, port):
            self.ip = ip
            self.port = port
    
    # obj=MySQL('1.1.1.1',3306) #obj=wrapper('1.1.1.1',3306)
    # print(obj.__dict__)
    
    obj1=MySQL() #wrapper()
    obj2=MySQL() #wrapper()
    obj3=MySQL() #wrapper()
    obj4=MySQL('1.1.1.3',3302) #wrapper('1.1.1.3',3302)
    print(obj1)
    print(obj2)
    print(obj3)
    print(obj4)

    (3)

    import settings
    
    class Mymeta(type):
        def __init__(self,class_name,class_bases,class_dic):
            #self=MySQL这个类
            self.__instance=self(settings.IP,settings.PORT)
    
        def __call__(self, *args, **kwargs):
            # self=MySQL这个类
            if len(args) != 0 or len(kwargs) != 0:
                obj=self.__new__(self)
                self.__init__(obj,*args, **kwargs)
                return obj
            else:
                return self.__instance
    
    class MySQL(metaclass=Mymeta): #MySQL=Mymeta(...)
        def __init__(self, ip, port):
            self.ip = ip
            self.port = port
    
    
    obj1=MySQL()
    obj2=MySQL()
    obj3=MySQL()
    obj4=MySQL('1.1.1.3',3302)
    print(obj1) 
    print(obj2)
    print(obj3)
    print(obj4)

    (4)

    def f1():
        from singleton import instance
        print(instance)
    
    def f2():
        from singleton import instance,My
        SQL
        print(instance)
        obj=MySQL('1.1.1.3',3302)
        print(obj)  
    
    f1()
    f2()
  • 相关阅读:
    九度oj 题目1525:子串逆序打印
    九度oj 题目1516:调整数组顺序使奇数位于偶数前面
    九度oj 题目1490:字符串链接
    九度oj 题目1438:最小公倍数
    九度oj 题目1181:遍历链表
    九度oj 题目1179:阶乘
    九度oj 题目1077:最大序列和
    九度oj 题目1075:斐波那契数列
    九度oj 题目1074:对称平方数
    九度oj 题目1073:杨辉三角形
  • 原文地址:https://www.cnblogs.com/ye-hui/p/10029189.html
Copyright © 2011-2022 走看看