• 面向对象高级进阶


      一、反射

      定义:通俗的理解其实反省,自省的意思

              反射值的时一个对象具备在增 删  改 查 得的属性你能力

      操作方法:通过操作字符串操作属性 

      反射:设计的四个函数:hasattr ,get attr, setattr , delattr  者四个 函数就是普通内置函数,没有双下划线,就像print,sort,len()  区别于__getattr__  函数操作


    # 使用场景:
    # 反射其实就是对属性增删改查,如果直接使用内置dict,太过于繁琐,不好理解
    # 反射是框架的基石,为什么


    1.1 实列一:
    class Person:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
    
    p = Person("Sen", 18)
    
    if hasattr(p, 'name'):
        # print(getattr(p,'name'))
        setattr(p, 'id', 123)  # 注意setattr 设置值的时候是有三个值的(对象,‘key’ ,value)
        print(p.id)
        # delattr(p, 'id')  # ttributeError: 'Person' object has no attribute 'id'
        # print(p.id)  # 此时‘id’已被删除
      print(p, 'id') 此时是不会取到对象的值的

    1.2 案例二:默认Djiang中间件 实现对象 的调用

    前戏:

    # 使用场景:
        # 反射其实就是对属性增删改查,如果直接使用内置dict,太过于繁琐,不好理解
        # 反射是框架的基石,为什么因位框架设计者补课不可能提前知道你的对象到底是怎么
        # 设计的所以你提供给框架的对象 必须通过判断验证之后才能正常使用
        # 判断就是反射要做的事情,当然也可以通过__dict__也可以实现的,其实这些方法也就对__dict__的操作进行了封装
    
    
    # 需求:要实现一个用于处理用户的终端指令的小框架
    # 框架:已经实现了最基础的架构代码逻辑,就是所有项目都是一样的部分
    # 框架 的根据配置 文件拿到所需的类
    class WinCmd:
        def cd(self):
            print('windcmd 切换目录。。。' )
    
        def delete(self):
            print('windcmd ..。删库')
    
        def dir(self):
            print('wind 列出当前的所有文件')
    
    
    class LinCmd:
        def cd(self):
            print('lindcmd 切换目录。。。' )
    
        def rm(self):
            print('lindcmd ..。删库')
    
        def ls(self):
            print('lincmd  列出所有文件')

    通过模块导入对象 实列化 调用

    # 导入刚才的库
    from day24.lib import my_work
    
    
    def run(my_work):
        while True:
            cmd = input('输入指令')
            if cmd == 'exit':
                break
            if hasattr(my_work,cmd):
                func = getattr(my_work,cmd)
                func() #
    
            else:
                print('不支持')
        print('再见!!!')
    
    
    wind = my_work.WinCmd()
    run(wind)

    很明显上述中我们的设计框架中写死必须用某个类,这是不合理的 以为我们无法提前预知对方的类在什么地方以及类名 所以我们应该为框架的使用者提供一个配置文件,要求对方将类写入文件 然后框架自己可以加载需要的模块

     实现代码:

      二、元类


    """该文件为框架的配置文件"""
    # 作为框架的使用者,在配置文件中指定你配合框架的类是哪个

    CLASS_PATH = 'lib.my_work.WinCmd'
    # 导入刚才的库
    import importlib
    
    from day24.lib import my_work
    from day24 import settings
    
    def run(my_work):
        while True:
            cmd = input('输入指令')
            if cmd == 'exit':
                break
            if hasattr(my_work,cmd):
                func = getattr(my_work,cmd)
                func() #
    
            else:
                print('不支持')
        print('再见!!!')
    # 创建一个插件对象 调用库框架来使用
    # wind = my_work.WinCmd()
    # run(wind)
    # 很明显这种实现不是完全的合理

    # 所以需要 根据文件拿到需要的类
    path = settings.CLASS_PATH


    # 从配中单独拿出来 模块路径 和 类名称
    print(path.rsplit('.',1)) # ['lib.my_work', 'WinCmd'] 者按照点切割的列表解压赋值
    module_path, class_name = path.rsplit('.', 1)

    # res = path.rsplit('.',1)
    print(class_name)
    # 拿到模块
    mk = importlib.import_module(module_path)
    print(mk) # <module 'lib.my_work' from 'D:\datas\day24\lib\my_work.py'>
    print(class_name)
    # 拿到类
    cls = getattr(mk,class_name)
    print(cls)
    obj = cls()

    # 调用框架
    run(obj) 实现代码 调
     obj 也就是这个类的实列对象class WinCmd  
    """
    在框架设计中 我们不可能提前知道 框架的用户要提供类相关的信息
    """
    import importlib
    import abc
    
    # 拿到模块 根据模块的路径
    pl = importlib.import_module("libs.plugins")
    print(pl)
    # 从模块中取出类
    cls = getattr(pl,"WinCMD")
    print(cls)
    
    
    # 实例化产生对象
    obj = cls()
    obj.cd()

    二、元类

      2.1 定义:什么元类:用于创建类的类,万物皆对象,类也是对象(类对象)对象是通过 类实列化产生,类对象也是另一个了实例化产生的

           默认情况下所有类的元类都是type  类的类是type

      

    # type 可以自定义类 拦截的类的产生
    
    # 我们的需求是创建类对象做一些限制
    # 实现方法一 既然是创建想到了初识化方法 我们只要找到类对象的类(元类)
    # 覆盖init 方法就能实现需求
    
    # 只要继承type类 那么这个类就变成了一个元类
    # 自定义类 first

    实现代码:

    lass MyClass(type):  # 元类
        # 拦截在父类产生Student 初始化走 的__init__
        def __init__(self,class_name,bases,dict):  # self Student 类对象、
            print(self)  # 类 对象 <class '__main__.Student'>
            print(class_name)  # 类名
            print(bases)
            print(dict)
            super().__init__(class_name, bases, dict)  # 重写父类放入方法
            if not class_name.istitle():  # 限制类的产生过程 必需满足我们限制的条件
                raise Exception('大哥不能呐必需是首字母大写')
            else:
                print('创建类成功')

    我们要自定义的类

    class Student(metaclass=MyClass):  # 自定义这个类怎么 重新type
       def __init__(self,name,age,b):
           self.name = name
           self.age = age
           self.b = b
    
    a = Student('KOKO',(1,2),b=1)  # 实列化
    #  __new__
    print(a.__dict__)  # {'name': 'KOKO', 'age': (1, 2), 'ui': {}}

      方法二其实就是内部本质先走元类自己的__new__ 1.元类中构建类再  返回类对象

    # 默认继承type 的类称为元类
    
    # 类的创建第二种方法__new__
    
    # __new__是帮我们创建类对象的初始化进行添加限制
    class Myself(type):  # 继承type >>>元类
        def __new__(cls, *args, **kwargs):
            # cls 是Myself
            # 方法一
            # return super().__new__(cls,*args,**kwargs)  # 必需返回给__init 生成相应的类,并返回
            # 方法二
            obj = type.__new__(cls, *args, **kwargs)  # 在元类中该函数用来构建类本身
            return obj  # 返回一个对象  给__init__  # 在类中,该函数用来构建类实例
    
        def __init__(self, a, b, c):
            super().__init__(a, b, c)
            print('实列创建类')
    
    
    class Teacher(metaclass=Myself):  # 限制老师类的产生
        pass
       属性
    
    Teacher()

    分为两个阶段:
    1.类的生成:由元类生成;
    2.实例的生成:由上述生成的类接着生成。

    具体描述:

    结合上一篇文章中,我们总结出生成一个类实例的全部过程:
    1.类首先查找内部__metaclass__属性是否被自定义元类赋值,若赋值则准备用该自定义元类生成类,否则用type作为元类生成类;
    2.解释器调用该元类的__new__函数(该函数为静态函数),并将要实例化的类中定义的各种属性传递给该函数固定的四个参数:其中cls是该元类本身,name是要被实例化的类的类名,bases是该类父类组成的元组,attrs则是该类{属性名:属性值,函数名:函数对象}组成的字典。
    3.最终通过type类生成该类,并返回。
    4.该类生成后,调用类中的__new__函数(该函数是静态函数)创建该类的实例,并返回该实例;
    5.该实例接着调用它的__init__函数初始化实例,这样一个完整的实例就被生成出来了。

    5,自定义元类的核心
    由于是元类构建了类,因此若要更改某些固定类(str,int等)的用法,就必须在元类中做文章了。而元类的核心是__new__函数,自然在该函数内做文章。比如可以通过atrris字典为元类添加新的属性和函数,或改变以往的属性或函数等。这样的改变会传播到所有用该元类创建的类中。

    总结:
    __new__函数:
    1.在元类中该函数用来构建类本身;
    2.在类中,该函数用来构建类实例;
    ---------------------
       元类中__call__  

    当你调用类对象时会自动触发元类中的__call__方法 ,并将这个类本身作为第一个参数传入,以及后面的一堆参数

    覆盖元类中的call之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建
    并返回其返回值

    使用场景:

    当你想要控制对象的创建过程时,就覆盖call方法  对象创建过程__call__

    当你想要控制类的创建过程时,就覆盖init方法     控制类的创建  __init__

    案例:



    # 元类中的__call__方法 在对象加括号 会自动触发类中的__call__ 方法
    # 覆盖父类的__call__ 这个类无法差生对象 必须调用super().__call__
    # 来完成对象的创建,并返回其返回值



    #实现将对象的所有属性名称转为大写
    # 实现一

    class Mytype(type):
    # 控制对象的产生
    def __call__(self, *args, **kwargs):
    if args:
    raise Exception('不能用位置参数')
    return super().__call__(*args,**kwargs) # 必须返回这个对象



    class Person(metaclass=Mytype):
    def __init__(self,name,age):
    self.name =name
    self.age =age


    a = Person(name='jcsk',age=19)

    print(a.name)
    print(a.age)

     

     

     

    单列的实现

    class Single(type):
    def __call__(self, *args, **kwargs):
    print(self) # Student 类
    if hasattr(self, 'obj'):
    return getattr(self, 'obj')

    obj = super().__call__(*args, **kwargs) # 没有则有创建
    print(obj) # 实例化的对象
    print('新开的试试看看')
    self.obj = obj # 并保存到self 对象自己的名称空间中
    return obj


    class Student(metaclass=Single):
    def __init__(self, name, age):
    self.name = name
    self.age = age


    a = Student('jj', 12) #
    Student('mm', 12)
    Student('kk', 12)
    Student('pp', 12)
    Student('tt', 12)
    
    
    # l = [2, 1, 3, 5]  # 冒泡算是遍历里里面的元素 由第一个和第二个做比较 如是:按大到小进行编排

    # 列表的个数
    #
    # 先比较 2,1 得到2 1 1 3 1 5
    # 第一圈 0 索引
    # >>> 元素比较元素次数 次数len-1 3

    # 第二圈 1 索引
    # 2 3 2 5 2和1已经再第一次拿到最小值了 不用再比较 比较元素次数 为2 len -1 -1
    # l2 = [3, 5, 2, 1]


    # 第三圈 2 索引
    # 3 5 3和2 再 第二圈已经比较过了

    # l3 = [5, 3, 2, 1]

    # 需求要用函数的方法实现 找关系
    s = [2, 3, 5, 1,10,309]
    for i in range(len(s) - 1): # 元素比较的次数 第一次 顾头不顾尾range(3) 0 1 2 三次比较
    # print(i) # 0 1 2 3 元素的索引下标
    for j in range(len(s) - 1 - i): # i相当于圈数的索引i =1 第二次 顾头不顾尾range(2) 0 1 三次比较
    if s[j] < s[j+1]:
    # 解压赋值
    s[j], s[j+1] = s[j+1], s[j]
    print(s)


    class Num:
    def __init__(self,name,age):
    self.age = name
    self.age = age
    def __str__(self):
    return self.age

    def __gt__(self, other):

    return self.age > other.age


    a1 = Num('json','48')
    a2 = Num('json','19')
    a3 = Num('json','26')

    排序的 ????
    stu = [a1.age, a2.age, a3.age]
    for i in range(len(stu)-1):
    for j in range(len(stu)-1-i):
    if stu[j]>stu[j+1]:
    stu[j],stu[j+1] = stu[j],stu[j+1]
    print(stu)


    单列2:
    # 利用__new__ 在创建对象的时后先走
    会帮我们构建以及返回一个对象
    
    # 对象给__init__ 对象进行初始赋值
    
    """
    # 利用__new__ 在创建对象的时后先走  >>>么有创建 有返回
    会帮我们构建以及返回一个对象
    
    # 对象给__init__ 对象进行初始赋值
    
    """
    
    
    class Mysql(object):  # 默认继承object
        _instance = None  # 可以定义变量
    
        def __init__(self, name):
            self.name = name
            print(self)
            print('run init')
    
        def __new__(cls, *args, **kwargs):
            if not cls._instance:
                print(cls)
                print('run _new_')
                cls._instance = object.__new__(cls)  # 如果没有 重写object 的__new__ 将对象存到名称空间
    
                print('1111')
            return cls._instance
    
    
    obj = Mysql('Jack')
    obj2 = Mysql('rose')
    # obj3 = Mysql('kk
    print(id(obj), id(obj2))
    # 2705130166368  2705130166368
    # <class '__main__.Mysql'>
    # run _new_  >>>先走
    # 1111
    # <__main__.Mysql object at 0x0000017048A5C240>
    # run init  >>>再走
    # <__main__.Mysql object at 0x0000017048A5C240>
    # run init  >>>一样的

    单列3基于元类:对象调用__call__   么有值 重写父类的__call__

    # 单列实现一 利用元类 既然是对象的产生 >>>__call__
    
    
    class MyClass(type):  # 继承type
        # 控制类的产生__call__ 覆盖父类的__call__ 对象调用走__call__ 方法
        def __call__(self, *args, **kwargs):
            if hasattr(self, 'obj'):
                return getattr(self, 'obj')
            # 没有创建重写父类的__call__
            obj = super().__call__(*args, **kwargs)
            # 将对象保存
            print('new=====')
            self.obj = obj
            return obj
    
    
    class Student(metaclass=MyClass):  # 继承MyClass 父类
    
       def __init__(self,name,age):
           self.name = name
           self.age = age
    
    
    a1 = Student('jack',18)
    a2 = Student('rose',20)
    a3 = Student('roe',20)
    a4 = Student('roye',20)
    print(a1)
    print(a2)
    print(a3)
    print(a4)
    # new=====  只走了一次 
    # <__main__.Student object at 0x0000024DBFC1D160>
    # <__main__.Student object at 0x0000024DBFC1D160>
    # <__main__.Student object at 0x0000024DBFC1D160>
    # <__main__.Student object at 0x0000024DBFC1D160>

    单列4:装饰器 实现

    def singleton(cls):
        # 该对象在类Mysql被装饰上singleton的时候就已经实例化完毕
        _instance = cls('127.0.0.1',3306)
        def inner(*args,**kwargs):
            # 判断是否传入参数,传入参数表示要实例化新的,不传表示用默认的
            if args or kwargs:
                obj = cls(*args,**kwargs)
                return obj
            return _instance
        return inner
    
    @singleton
    class Mysql:
        def __init__(self,ip,port):
            self.ip = ip
            self.port = port
    
    obj1 = Mysql()
    obj2 = Mysql()
    obj3 = Mysql()
    print(obj1,obj2,obj3)def singleton(cls):
        # 该对象在类Mysql被装饰上singleton的时候就已经实例化完毕
        _instance = cls('127.0.0.1',3306)
        def inner(*args,**kwargs):
            # 判断是否传入参数,传入参数表示要实例化新的,不传表示用默认的
            if args or kwargs:
                obj = cls(*args,**kwargs)
                return obj
            return _instance
        return inner
    
    @singleton
    class Mysql:
        def __init__(self,ip,port):
            self.ip = ip
            self.port = port
    
    obj1 = Mysql()
    obj2 = Mysql()
    obj3 = Mysql()
    print(obj1,obj2,obj3)

    单列5基于

    基于classmethod)

    class Mysql(object):
        _instance = None
    
        def __init__(self, ip, port):
            self.ip = ip
            self.port = port
    
        @classmethod
        def singleton(cls):
            if not cls._instance:
                cls._instance = Mysql('127.0.0.1', 3306)
            return cls._instance
    
    
    obj1 = Mysql.singleton()
    obj2 = Mysql.singleton()
    print(obj1)
    print(obj2)
    单列6:
    # 单独在一个py文件中定义一个类,并实例化一个对象,之后在其他文件导入这一对象,实现单例
    # 单独在一个py文件中定义一个类,并实例化一个对象,之后在其他文件导入这一对象,实现单例
    class Singleton(object):
        def __init__(self,host,port):
            self.host = host
            self.port = port
    
    singleton = Singleton('127.0.0.1',3306)


  • 相关阅读:
    java框架--Spring XML 配置基础(一)
    工具的使用与安装--oracle卸载
    java web--jsp(4)
    java web--JSP(3)
    洛谷 P3384 【模板】轻重链剖分
    洛谷 P1103 书本整理
    洛谷 P1977 出租车拼车
    洛谷 P1129 [ZJOI2007]矩阵游戏
    洛谷 P2319 [HNOI2006]超级英雄
    洛谷 P1640 [SCOI2010]连续攻击游戏
  • 原文地址:https://www.cnblogs.com/mofujin/p/11272117.html
Copyright © 2020-2023  润新知