zoukankan      html  css  js  c++  java
  • 面向对象编程,高级

    一.面向过程和面向对象(oop)

    1 面向过程:将一个复杂的问题,拆分成多个简单的小问题(流程化,按照固定的实现流程)从而简单化

    优点:复杂的问题简单化

    缺点:流程固定,牵一发动全身,扩展性差,维护性差

    应用场景:对扩展性要求低的软件,比如系统内核,脚本程序,文本编辑器这些

     

    2 面向对象:实际上在寻找一堆对象,让他们来完成任务

    优点:扩展性高,维护性高,各对象耦合度低(一个对象有问题,不会对其他对象产生影响)

    缺点:复杂程度比面向过程高,无法预知执行的结果(对于比如lol这样的游戏,不能把程序写死)

    应用场景:需要较高扩展性的软件(比如直接与用户交互的程序, qq与微信)

    当然对于一些不需要扩展的程序而言,使用面向对象反而增加了复杂度

     

    二.类与对象

    什么是类 
      是一个抽象概念,不是具体存在的,
      类是通过提取一系列对象的相同特征和技能得到的
      类的作用是用于标识对象与对象之间的差异,通过类就能大致了解一个对象的特征和行为
    什么是对象
      具备某些特征和技能的结合体,是具体存在的某个物体,(一切皆对象)

    在生活中,先有对象再有类
    再程序中,先有类再有对象
    # 在类中描述对象的特征和行为
    class Person:#类名首字母要大写
      # 用变量来描述特征
      name = "李四"
      sex = "man"
      age = 20


    # 得到对象 通过调用类 ,也称之为实例化 或 创建对象
    obj = Person()
    print(obj)
    #<__main__.Person object at 0x00000279452B7C50>
    # 模块名为main 其包含一个Person类 通过Person类产生了一个对象 地址为0x00000279452B7C50
    # 这是一个Person类的对象 其地址为0x00000279452B7C50

    # 使用对象的属性(说的就是特征)
    print(obj.name)
    print(obj.age)
    print(obj.sex)

    # 通过__dict__可以获取一个对象中包含的内容
    print(obj.__dict__)
    {}


    # 获取类中包含的内容
    print(Person.__dict__)
    {'__module__': '__main__', 'name': '李四', 'sex': 'man', 'age': 20, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

    三.初始化函数

    # 带有__开头__结尾的函数 是一些特殊内置函数,会在某个时间点自动触发执行
    class Person:
      # 初始化函数名称是固定 该函数会在调用类是时自动执行,self参数必须有,表示要进行初始化的对象,系统会自动传值
      def __init__(self,name,age):
          print("执行了 __init__")
          print(self)
          self.name = name
          self.age =age

    p1 = Person("张三丰",80)

    print(p1.__dict__)
    执行了 __init__
    <__main__.Person object at 0x0000025BB20CD4A8>
    {'name': '张三丰', 'age': 80}

    p2 = Person("李狗蛋",20)
    print(p2.__dict__)
    执行了 __init__
    <__main__.Person object at 0x0000025BB20CD2B0>
    {'name': '李狗蛋', 'age': 20}

    # init 函数用于初始化对象,它会在创建对象时,自动执行,并传入调用类时传递的参数,第一个参数表示要初始化的对象本身,
    # self(第一个)参数不需要手动传递
    # self表示对象自己 是一个形式参数,名字可以随便取,但是不建议修改

    四.属性的访问顺序

    "属性的访问顺序"
    class Car:
      c_type = "破鞋"
      color = "red"
      price = 400000


    c1 = Car()
    c2 = Car()

    print(c1.__dict__) # {}
    print(c2.__dict__) # {}
    print(c1.c_type) # 破鞋

    # 当对象中不存在是会到类中去找

    c1.c_type = "法拉利"
    print(c1.__dict__)   # {'c_type': '法拉利'}
    print(c2.__dict__) # {}
    print(c1.c_type) # 法拉利

    print(c2.c_type) #破鞋
    # 如果对象中存在这个属性,优先问对象中的属性
    print(Car.__dict__) # {'__module__': '__main__', 'c_type': '破鞋', 'color': 'red', 'price': 400000, '__dict__': <attribute '__dict__' of 'Car' objects>, '__weakref__': <attribute '__weakref__' of 'Car' objects>, '__doc__': None}


    # 查找顺序为 对象 -> 类

    # 当创建一个类的时候 会产生名称空间,存储类中名称和值的绑定关系
    # 当创建一个对象的时候 会产生名称空间,存储对象中名称和值的绑定关系
    # 类还有另一个作用 就是 作为对象的模板,所有属于同一个类的对象,都具备类中的公共内容

    五.绑定方法

    """
      绑定方法
      什么是绑定 把两个东西捆绑在一起
      什么是方法 方法 就是 函数
          函数是专业术语,不好理解,面向对象编程思想,是要我们模仿现实生活中的抽象概念,为了方便理解就把函数称之为方法

      绑定方法就是 把对象与函数进行绑定
      为什么要把把对象与函数进行绑定
          调用函数 就变成了调用对象的方法

      对象本质上就是一种存放数据的容器
      函数是用于处理数据的代码
      绑定方法就是将数据与处理数据的函数绑定在一起

      最终问题是 为什么要把数据与处理数据的函数绑定在一起?

      如何使用绑定方法
    """
    class Student:
      school = "BeiJing"

      def __init__(self,name,sex,age):
          self.name = name
          self.sex = sex
          self.age = age

      def learning(self):
          print("正在学习..")

      def sayHI(self):
          print("hello my name is %s my age:%s my sex:%s" % (self.name,self.age,self.sex))

      # 默认情况下 在类中定义的函数都是绑定方法,共同点是,都会讲对象作为第一个参数self

    stu1 = Student("一个学生","man",18)
    stu1.sayHI()
    Student.sayHI(stu1)

    # 当用用对象来调用类中的方法时,默认把对象传入方法中
    # 而用类名来调用时,则需要手动传入对象


    # print(stu1.sayHI)
    #<bound method Student.sayHI of <__main__.Student object at 0x000001784F889C50>>
    # 这是一个绑定方法,本质上是Student类中的sayHI函数 现在把这个函数绑定给了地址为0x000001784F889C50的对象


    #
    # stu2 = Student("李四","女",19)
    # stu2.sayHI()
    # print(stu2.sayHI)

    # 只要拿到对象 就同时拿到了数据和处理数据的方法



    class Student:
      school = "beijing"

      def __init__(self,name,age,sex):
          self.name = name
          self.age = age
          self.sex = sex

      # 绑定方法分为两种 一种是绑定给对象的,一种绑定给类的


      # 绑定给类的方法 使用一个装饰器叫classmethod,必须有一个参数,表示当前类,参数名也可以自己定义,建议不要修改
      @classmethod
      def print_school(cls): # 输出类里面叫school的属性
          print(cls.school)

      def print_school2(self): # 输出类里面叫school的属性
          print(self.school)

      # 这是绑定给对象的方法
      def sayHello(self):
          print(self.name, " 说: 你好")



    # Student.print_school_name()

    Student.print_school()

    # 对象绑定方法 可以使用对象来调用 也可以使用类名来调用
    # 在对象调用时会自动传入对象自己
    # 类调用时不会自动传参

    # 类的绑定方法,对象和类都能调用,并且都会自动传入这个类
    Student.print_school()
    stu1 = Student("印度阿三","woman",20)
    stu1.print_school()

    # 如何定义对象绑定方法 以及类的绑定方法
    # 在调用时的区别

    # 一个方法到底应该绑定给对象还是帮对给类?
    # 当要处理的数据包含在类中时,就应该绑定给类
    # 当要处理的数据包含在对象中时,就应该绑定给对象



    # 有一个Dog类 每一个Dog对象都应该会叫 会跑 请用面向对象来完成

    class Dog:
      def __init__(self,nikename,gender,age):
          self.nikename = nikename
          self.gender = gender
          self.age = age

      def run(self):
          print("不好了 ",self.nikename,"跑了 ")

      def bark(self):
          print("听",self.nikename,"在瞎叫...")

    d1 = Dog("大金毛","母的",2)
    d2 = Dog("大黄","公的",3)

    d1.run()
    d2.bark()
    Dog.run(d1)



    """
    # 类的绑定方法和对象的绑定方法的相同与不同
    相同点:
      1.都会自动传值
      2.都可以被类和对象调用

    不同点:
      1.对象绑定方法再对象调用时 传的是对象自己 而类绑定方法字自动传的是类自己
      2.第一个参数 个cls 一个叫self


    为什么要绑定?

    # 第一个问题传递参数,必须手动传递,很有可能传参顺序而发生错误
    # 第二个问题每次处理数据 都需要手动传参数
    # 第三个问题 当要处理的数据特别的多 就不能再定义为变量了 你可以使用列表出来存储要处理的数据
    但是 每次处理 都需要先获取数据 在传递给处理数据的函数
    之所以绑定 ,简化代码,提高效率
    """

    非绑定方法

    class Teacher:

      def __init__(self,name,sex):
          self.name = name
          self.sex = sex

      # @staticmethod 用于定义个非绑定方法
      @staticmethod
      def test_func(num):
          print("test_func run!")
          print(num)

    Teacher.test_func(1)

    t1 = Teacher("矮根","男")
    t1.test_func(100)
    print(t1.test_func)


    # 什么是非绑定方法 再类中 即不绑定给类 也不绑定给对象
    # 特点:没有自动传参数的效果 ,类和对象向都能调用,就是一个普通函数
    # 当你的这个功能不需要访问类的数据 也不需要访问对象的数据,就可以作为一个非绑定方法
    # 使用场景较少

    练习

    1.创建Student类

    2.拥有以下属性: 姓名    性别    年龄    学校    班级

    3.拥有以下方法

    save(name) 其作用是将这个对象序列化到文件中

    get_obj(name) 其作用是根据name从文件中反序列化为得到一个对象

    分析save方法和get_obj 应该作为绑定给对象还是绑定给类

    import json

    class Student:

      school = "beijing"

      def __init__(self,name,sex,age,classes):
          self.name = name
          self.age = age
          self.sex = sex
          self.classes = classes


      def save(self):
          dic = {"name":self.name,"sex":self.sex,
                  "age":self.age,"classes":self.classes}
          with open(self.name,"wt",encoding="utf-8") as f:
              json.dump(dic,f)

      @classmethod
      def get_obj(cls,name):
          with open(name,"rt",encoding="utf-8") as f:
              dic = json.load(f)
          obj = cls(dic["name"],dic["sex"],dic["age"],dic["classes"])
          return obj




    # stu1 = Student("阿尔法","man",20,"py5期")
    # stu2 = Student("张三","woman",20,"py5期")


    # stu1.save()
    # stu2.save()

    stu = Student.get_obj("阿尔法")
    print(stu)
    print(stu.name)

    六.面向对象中的常用方法

    """
    
    
    
    """
    # 判断某个对象是不是某个类的实例
    # isinstance()
    
    class Person:
        pass
    class Student(Person):
        pass
    
    stu = Student()
    
    #判断 两个对象是不是同一个类型
    print(type(1) == type(1))
    
    # 判断stu对象是不是Student类的实例
    print(isinstance(stu,Student))
    
    # 是不是子类
    # issubclass()
    
    # 判断一个类是不是 另一个类子类 (所有类都是object的子类或子子类)
    print(issubclass(Student,Person))
    View Code

    七.反射

    其实就是反省,简单的说就是对象要具备一种修正错误的能力

     

    hasattr 是否存在某个属性

    getattr 获取某个属性的指

    setattr 设置某个属性的指

    delattr 删除某个属性

    这个几个方法都有一个共同点: 都是通过字符串来操作属性,

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

      def study(self):
          print("学生正在学习...")
           
    stu = Student("杨彬","woman",38)
           
    # 当你获取到一个对象 但是并不清楚搞对象的内部细节时 就需要使用反射了
    def test(obj):
      if hasattr(obj,"name"):
          print(getattr(obj,"name","没有name属性"))


    test(stu) # 杨彬


    setattr(stu,"school","beijing") # 为stu这个对象设置school为beijing

    delattr(stu,"school") # 删除学校这个属性

    print(getattr(stu,"school","没有学校属性")) # 这样打印出来就会出现没有学校属性

    delattr(stu,"age") # 删除age这个属性

    print(stu.age) # 由于被删除会报错   AttributeError: 'Student' object has no attribute 'age'

    八.str

    这个主要作用时自定义打印的内容,要注意的是,这个方法必须返回一个字符串  返回的是什么,就打印什么

    class Test:
      def __init__(self,name):
          self.name = name
      def __str__(self):
          print("str run....")
          return self.name
       
    t = Test("安米")

    print(t)
    #str run....
    #安米

    # 在讲一个对象转换字符串时 本质就是在调用这个对象 __str__方法
    print(str(t))
    #str run....
    #安米

    九.del   (析构函数)

    将对象从内存中删除时会自动执行 但是注意  并不说用这个方法来删除对象  删除对象时解释器干的
    该方法仅仅是 通知你的程序 这个对象即将被删除
    在python中 有自动内存管理机制 所以 python自己创建的数据 不需要我们做任何操作
    ​    但是有一种情况 我们使用python打开了一个不属于python管理的数据
    ​    比如打开了一个文件  这个文件一定是操作系统在打开 会占用系统内存  而python解释器无法操作系统内存的
    ​    所以 当你的python解释器运行结束后  文件依然处于打开状态  这时候就需要使用del来关闭系统资源
    简单地说 当程序运行结束时 需要做一些清理操作 就使用del

    十.​exec 方法

    """
        exec
        execute的缩写
        表示执行的意思
    
        其作用 是帮你解析执行python代码 并且将得到的名称 存储到制定的名称空间  解释器内部也是调用它来执行代码的
    
    """
    # 参数一 需要一个字符串对象 表示需要被执行的python语句
    # 参数二 是一个字典 表示全局名称空间
    # 参数三 也是一个字典 表示局部名称空间
    
    globalsdic = {}
    localsdic = {}
    
    exec("""
    aaaaaaaaaaaaaaaaaaaa = 1
    bbbbbbbbbbbbbbbbbbbbbbbbbbbb = 2
    def func1():
        print("我是func1")
    """,globalsdic,localsdic)
    
    
    # 如果同时制定了 全局和局部 则 会字符串中包含名称 解析后存到局部中
    # print(globalsdic)
    print(localsdic)
    localsdic["func1"]()
    
    
    
    
    
    # # 如果只传了一个传参数 则 将字符串中包含名称 解析后存到全局中
    # exec("""
    # aaaaaaaaaaaaaaaaaaaa = 1
    # bbbbbbbbbbbbbbbbbbbbbbbbbbbb = 2
    #
    # """,localsdic)
    View Code

    十一.元类

    """
        一切皆对象
        元类是指 用于产生类的类  type就是元类
        所有的自定义类都是通过type实例化得来
    
    """
    
    #创建模块的过程 1.创建一个空的名称空间 2.执行内部的代码 3.将得到的名字放到名称空间中
    
    # class也是一个对象
    class Student(object):
    
        school = "北京大学!"
    
        def study(self):
            print("学习中...")
    
    
    
    # 使用type可以发现 类其实是type类型的实例(对象)
    print(type(Student))
    
    # 我们可以自己调用type来实例化产生一个类
    
    
    
    # myclass 包含的代码
    code = """
    name = "张三"
    age = 18
    def hello(self):
        print("hello %s" % self.name)
    """
    
    
    
    #类的名字
    class_name = "MyClass"
    #类的的父类们
    base_classes = (object,)
    #类的名称空间
    namespace = {}
    
    exec(code,{},namespace)
    
    
    res = type(class_name,base_classes,namespace)
    
    print(Student)
    print(res.name)
    print(res.age)
    print(res.hello)
    
    
    
    # 1.类是由type实例化产生的
    # 2.我们可以使用type来产生一个类
    # 3.一个类是由 类名字 类的父类元祖 类的名称空间 三个部分组成
    
    
    class Test(object): #Test = type("Test",(object,),{})
        pass
    View Code
    class MyMeta(type):
    
        # 用于新建类对象
        def __new__(cls, *args, **kwargs):
            print("new run")
            # print(MyMeta)
            # print(*args)
    
            # 调用type通过的__new__方法来创建一个空的类对象 已经将三个组成部分都放到类对象中了
            res = type.__new__(cls,*args)
            return res
    
        def __init__(self,class_name,bases,namespace):
            print("init run")
            print(self)
    
    
    
    class Student(metaclass=MyMeta):
        pass
    
    
    print(Student)
    """
    new  与 init的区
    __new__ 比__init__先执行 其作用是创建一个空的类对象
    作为一个类对象 必须具备是三个组成部分  所以调用type中的__new__来完成组装 
    得到这个类对象后需要将其返回 以供__init__来使用 
    
    """
    View Code

     

    十二.通过__call__方法来控制对象的实例化过程

    """
    
        __call__
    
        调用的意思
        在在对象被调用时 执行
    
        函数 类
    
    
        自定义元类 的目的
        1.可以通过__call__ 来控制对象的创建过程
        2.可用控制类的创建过程
    
    
    """
    
    # 自定义一个元类 元类也是一个类   但是需要继承type
    class MyMeta(type):
    
        # self 表示要创建对象的那个类(Person)  *args是调用Person类时传入的参数
        def __call__(self, *args, **kwargs):
    
            print("MyMte中的 call run'")
            print(self,*args,**kwargsl)
    
            # 下面的三步是固定写法 一个模板 只要你需要控制对象的创建过程 就应该先把模板写出来
    
            # 1.创建空对象
            obj = object.__new__(self)
            # 2.调用初始化方法
            self.__init__(obj,*args,**kwargs)
            # self.__init__(obj)
            # 3.得到一个完整的对象
            return obj
    
    
    
    # 修改Person类的元类为MyMeta
    class Person(metaclass=MyMeta):
    
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def __call__(self, *args, **kwargs):
            print("call run...")
    
    
    #调用Person这个对象时 执行的是 Person的类(type)中__call__ 方法
    p = Person("张三疯",80)
    
    print(p)
    # 当调用对象时 会执行该对象所属类中的__call__方法
    # p()
    
    print(p.name)
    print(p.age)
    
    
    # class People:
    #     def __init__(self,name):
    #         self.name = name
    #     pass
    #
    #
    # p = People()
    # p.anme = 1
    View Code

    十三.通过元类控制类的创建过程

    # 要控制类的创建过程 只要找到类所属的类 中的__init__即可
    
    
    class MyMeta(type):
    
        # self 刚建出来的类
        # 第二个 类的名字
        # 第三个 类的父类们 元组
        # 第四个 这个类传进来的名称空间
        def __init__(self,class_name,bases,namespace):
            print("============================")
            #print(self.__dict__)
            # 我要控制 类的名字  必须 是大写开头
            if not class_name.istitle():
                print("类名 必须大写开头...... ")
                # 该代码是主动抛出异常
                raise TypeError("类名 必须大写开头...... ")
            #要空类的创建 必须包含__doc__这个属性
            if not self.__doc__:
                raise TypeError("类中必须有文档注释.....")
    
            pass
    
    class Student(metaclass=MyMeta):   # Student = MyMeta("Student",(object,),{})
        """
            这是文档注释  可以通过__doc__来获取
            这是一个学生类
        """
    
        # 在类的__init__中可以控制该类对象的创建过程
        def __init__(self,name):
            print("-----------------------")
            print(self.__dict__)
            self.name = name
    
    print(Student.__doc__)
    
    # 元类使用总结:
    """
    元类是用于创建类的类
    学习元类是为了 能控制类的创建过程 以及 类实例化对象的过程
    一.
    控制类的创建过程
        1.创建一个元类 (需要继承type)
        2.覆盖__init__方法  该方法 会将新建的类对象  类名 父类们 名称空间 都传进来 ,
            可以利用这些信息在做处理
        3.对于需要被控制的类 需要指定metaclass 为上面的元类 
        
    二.
    控制类实例化对象的过程
         1.创建一个元类 (需要继承type)
         2.覆盖__call__方法 会将 正在实例化对象的类  调用类是传入的参数  都传进来
         3.在__call__方法中 必须要先编写模板代码
            3.1创建空对象
            3.2调用类的__init__方法来初始化这个空对象
            3.3返回该对象
         4.加入你需要控制的逻辑 
         
    类的三个组成部分
    类名 父类们 名称空间
    
    元类 -> 实例化产生 -> 类 -> 实例化产生 -> 对象
    
    
    """
    View Code

    十四.单例模式

    单例(一种设计模式(套路))
    是指一个类中只有一个对象
    什么时候使用单例
    
    当要处理的数据只有一份时
    当所有对象的属性都相同时
    
    1.通过classmethod   会有问题  依然可以通过调用类 产生新的对象
    
    2.通过元类中的__call__方法 来实现
    
    
    
    class MyMeta(type):
    
        obj = None
        def __call__(self, *args, **kwargs):
            if not MyMeta.obj:
                obj = object.__new__(self)
                self.__init__(obj,*args,**kwargs)
                MyMeta.obj = obj
            return MyMeta.obj
    
    
    #打印机类
    class  Printer(metaclass=MyMeta):
        """
        这是一个单例类 请不要直接实例化 使用get方法来获取实例
        """
    
        obj = None
        def __init__(self,name,brand,type):
            self.name = name
            self.brand = brand
            self.type = type
    
        def printing(self,text):
            print("正在打印 %s"  % text)
    
        # 通过该方法来获取对象 可以保证只有一个对象
        # 但是这还不够 因为 还是可以通过调用类产生新对象
        # 就应该使用元类 来控制实例化的过程 __call__
        # 在__call__ 中编写代码 保证每次调用call 都返回同一个实例 即可
    
        @classmethod
        def get_printer(cls):
            if not cls.obj:
                obj = cls("ES005","爱普生","彩色打印机")
                cls.obj = obj
                print("创建了新的对象")
    
            return cls.obj
    
    # 以下三个对象 的数据完全相同 但是却 占用三分内存空间
    # p1 = Printer("ES005","爱普生","彩色打印机")
    # p2 = Printer("ES005","爱普生","彩色打印机")
    # p3 = Printer("ES005","爱普生","彩色打印机")
    
    # 现在要处理问题就是  如何能够限制该类 只能实例化一个对象
    
    p = Printer.get_printer()
    print(p)
    
    p = Printer.get_printer()
    print(p)
    
    p = Printer.get_printer()
    print(p)
    
    p = Printer.get_printer()
    print(p)
    
    p1 = Printer("ES005","爱普生","彩色打印机")
    p2 = Printer("ES005","爱普生","彩色打印机")
    
    print(p1)
    print(p2)
    View Code

     

     

  • 相关阅读:
    js基础 数组reduce
    js基础 数组every some
    js基础 数组forEach
    js基础 数组排序
    js基础 数组findIndex
    nodejs+koa2微信app支付,小程序支付
    Iview-datePicker获取选中的日期,如果没有选,提示错误,选了,错误隐藏
    mongoose+koa2 按照_id更新多条数据,删除数组中的字段,然后添加新的字段,$pull和$or结合使用
    json数组去除重复的值
    mongoose根据_id更新,且如果数组中没有元素就添加
  • 原文地址:https://www.cnblogs.com/liubinliuliu/p/10375018.html
Copyright © 2011-2022 走看看