• 第八章 Python——面向对象编程


    目录

    一、面向对象编程

    二、类class与对象object

    三、继承与派生

    四、多态与多态性(抽象类)

    五、封装

    六、绑定方法

    七、反射

    八、与面向对象有关的内置函数

    九、自定义内置方法

    十、元类

    十一、单例模式

    一、面向对象编程

    【面向过程编程】
    核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么然后干什么。。。
    基于该思想编写程序好比在设计一条流水线,是一种机械式的思维方式

    优点:复杂的问题流程化、进而简单化
    缺点:扩展性差

    【面向对象编程】
    核心对象二字,对象是特征与技能的结合体
    基于该思想编写程序就好比是在创造一个世界,你就是这个世界的上帝,是一种上帝式的思维方式

    优点:可扩展性强
    缺点:编程的复杂度要高于面向过程

    二、类class与对象object

    (一)类class

    什么是类(what):

    类可以理解为种类、分类、类别

    对象是特征与技能的结合体,类是一系列对象相似的特征与技能的结合体
    强调:站的角度不同,总结出的类是截然不同的

    在现实世界中:先有的一个个具体存在的对象,然后随着人类文明的发展才了分类的概念
    在程序中:必须先定义类,后调用类来产生对象

    站在老男孩选课系统的角度,先总结现实世界中的老男孩的学生对象
    对象1:
    特征:
    学校='oldboy'
    姓名='耗哥'
    年龄=18
    性别='male'
    技能:
    选课

    对象2:
    特征:
    学校='oldboy'
    姓名='猪哥'
    年龄=17
    性别='male'
    技能:
    选课

    对象3:
    特征:
    学校='oldboy'
    姓名='帅翔'
    年龄=19
    性别='female'
    技能:
    选课

    站在老男孩选课系统的角度,先总结现实世界中的老男孩学生类
    老男孩学生类:
    相似的特征:
    学校='oldboy'
    相似的技能
    选课
    #在程序中
    #1、先定义类
    class OldboyStudent:
        school='oldboy'
    
        def choose_course(self):
            print('is choosing course')
    
    #类体代码会在类定义阶段就立刻执行,会产生一个类的名称空间
    
    # 类的本身其实就是一个容器/名称空间,是用来存放名字的,这是类的用途之一
    print(OldboyStudent.__dict__)
    print(OldboyStudent.__dict__['school'])
    print(OldboyStudent.__dict__['choose_course'])
    OldboyStudent.__dict__['choose_course']()
    
    #
    print(OldboyStudent.school) 
    #OldboyStudent.__dict__['school']
    print(OldboyStudent.choose_course) #OldboyStudent.__dict__['choose_course']
    
    #
    OldboyStudent.country='China' #OldboyStudent.__dict__['country']='China'
    
    #
    OldboyStudent.country='CHINA' #OldboyStudent.__dict__['country']='China'
    
    #
    del OldboyStudent.school
    print(OldboyStudent.__dict__)
    
    
    
    #2、后调用类产生对象,调用类的过程,又称为类的实例化,实例化的结果称为类的对象/实例
    stu1=OldboyStudent() 
    # 调用类会得到一个返回值,该返回值就是类的一个具体存在的对象/实例
    stu2=OldboyStudent() 
    # 调用类会得到一个返回值,该返回值就是类的一个具体存在的对象/实例
    stu3=OldboyStudent() 
    # 调用类会得到一个返回值,该返回值就是类的一个具体存在的对象/实例

     

    (二)对象object

    什么是对象(what):

    对象本质也就是一个名称空间而已,对象名称空间是用存放对象自己独有的名字/属性,而类中存放的是对象们共有的属性

    #类的属性应用与实例化
    class OldboyStudent:
        school='oldboy'
    
    
        def __init__(obj, name, age, male): #会在调用类时自动触发
            obj.name = name #stu1.name='耗哥'
            obj.age = age  #stu1.age=18
            obj.sex = male #stu1.sex='male'
    
        def choose_course(self):
            print('is choosing course')
    
    #调用类时发生两件事
    #1、创造一个空对象stu1
    #2、自动触发类中__init__功能的执行,将stu1以及调用类括号内的参数一同传入
    stu1=OldboyStudent('耗哥',18,'male')
    #OldboyStudent.__init__(stu1,'耗哥',18,'male')
    stu2=OldboyStudent('猪哥',17,'male')
    stu3=OldboyStudent('帅翔',19,'female')
    
    print(stu1.__dict__)
    print(stu2.__dict__)
    print(stu3.__dict__)
    #属性查找
    #需求:查看生成了多少次对象
    
    class OldboyStudent:
        school='oldboy'
        count=0
    
        def __init__(self, x, y, z): #会在调用类时自动触发
            self.name = x #stu1.name='耗哥'
            self.age = y  #stu1.age=18
            self.sex = z #stu1.sex='male'
            OldboyStudent.count+=1
    
        def choose_course(self):
            print('is choosing course')
    
    
    # 先从对象自己的名称空间找,没有则去类中找,如果类也没有则报错
    stu1=OldboyStudent('耗哥',18,'male')
    stu2=OldboyStudent('猪哥',17,'male')
    stu3=OldboyStudent('帅翔',19,'female')
    
    print(OldboyStudent.count)
    print(stu1.count)
    print(stu2.count)
    print(stu3.count)
    属性查找顺序

     Tips:

    ①类名称空间中定义的数据属性和函数属性都是共享给所有对象用的。对象名称空间中定义的只有数据属性,而且时对象所独有的数据属性。

    ②类中定义的函数是类的函数属性,类可以使用,但使用的就是一个普通的函数而已,意味着需要完全遵循函数的参数规则,该传几个值就传几个参数。

    (三)初识绑定方法

    什么是绑定方法(what):

    类中定义的函数是共享给所有对象的,对象也可以使用,而且是绑定给对象用的。

    绑定效果:

    绑定给谁,就应该由谁来调用,谁来调用就会将谁当作第一个参数自动传入。

    Tips:

    ①类中定义的函数,类确实可以使用,但其实类定义的函数大多情况下都是绑定给对象用的,所以在类中定义的函数都应该自带一个参数self。

    (四)类即类型

    在python3中统一了类与类型的概念,类就是类型。

    class OldboyStudent:
        school='oldboy'
    
        def __init__(self, name, age, male): #会在调用类时自动触发
            self.name = name #stu1.name='耗哥'
            self.age = age  #stu1.age=18
            self.sex = male #stu1.sex='male'
    
        def choose_course(self,x):
            print('%s is choosing course' %self.name)
    
    stu1=OldboyStudent('耗哥',18,'male')
    l=[1,2,3] #l=list([1,2,3])
    print(type(l))
    l.append(4) #list.append(l,4)
    list.append(l,4)
    print(l)

    三、继承与派生

    (一)继承

    什么是继承(what):

    继承是一种新建类的方式,新建的类称为子类,被继承的类称为父类
    继承的特性是:子类会遗传父类的属性
    强调:继承是类与类之间的关系

    为什么用继承(why):

    继承的好处就是可以减少代码的冗余。

    怎么用继承(how):

    在python中支持一个类同时继承多个父类
    在python3中
    如果一个类没有继承任何类,那默认继承object类
    在python2中:
    如果一个类没有继承任何类,不会继承object类

    (二)菱形继承问题

    什么是菱形继承(what):

    当一个子继承多个父类时,多个父类最终继承了同一个类,称之为菱形继承。

    菱形继承问题划分:

    【新式类】
    但凡继承了object的类以及该类的子类,都是新式类。

    查找属性方法:广度优先查找
    【经典类】
    没有继承object的类以及该类的子类,都是经典类。

    查找属性方法:深度优先查找

    在python3中都是新式类,只有在python2中才区别新式类与经典类。

    Tips:python内置mro()(C3算法)查找广度优先顺序。

    (三)派生

    什么是派生(what):

    子类中新定义的属性,子类在使用时始终以自己的为准。

    对象查找属性的顺序:对象自己-》对象的类-》父类-》父类。。。

    (四)子类中调用父类的方法

    在子类派生出的新功能中重用父类功能的方式有两种:
    1、指名道姓访问某一个类的函数:该方式与继承无关

    # 方式一:与继承无关
    #指名道姓法,直接用:类名.函数名
    class OldboyPeople:
        school = 'oldboy'
    
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
    class OldboyStudent(OldboyPeople):
        def __init__(self,name,age,sex,stu_id):
            OldboyPeople.__init__(self,name,age,sex)
            self.stu_id=stu_id
    
        def choose_course(self):
            print('%s is choosing course' %self.name)

    2、严格用继承关系查找

    super()会得到一个特殊的对象,该对象就是专门用来访问父类中的属性的(按照继承的关系)
    super().__init__(不用为self传值)
    Tips:
    super的完整用法是super(自己的类名,self),在python2中需要写完整,而python3中可以简写为super()

    # 方式二:严格以来继承属性查找关系
    
    class OldboyPeople:
        school = 'oldboy'
    
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
    class OldboyStudent(OldboyPeople):
        def __init__(self,name,age,sex,stu_id):
            # OldboyPeople.__init__(self,name,age,sex)
            super(OldboyStudent,self).__init__(name,age,sex)
            self.stu_id=stu_id
    
        def choose_course(self):
            print('%s is choosing course' %self.name)
    
    
    stu1=OldboyStudent('猪哥',19,'male',1)
    print(stu1.__dict__)

    (五)组合

     什么是组合(what):

    组合就是一个类的对象具备某一个属性,该属性的值是指向另外外一个类的对象。

    为什么用组合(why):

    组合也是用来解决类与类直接代码冗余问题的。

    如何用组合(how):

    class Course:
        def __init__(self,name,period,price):
            self.name=name
            self.period=period
            self.price=price
    
        def tell_info(self):
            msg="""
            课程名:%s
            课程周期:%s
            课程价钱:%s
            """ %(self.name,self.period,self.price)
            print(msg)
    
    class OldboyPeople:
        school = 'oldboy'
    
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
    class OldboyStudent(OldboyPeople):
        def __init__(self,name,age,sex,stu_id):
            OldboyPeople.__init__(self,name,age,sex)
            self.stu_id=stu_id
    
        def choose_course(self):
            print('%s is choosing course' %self.name)
    
    class OldboyTeacher(OldboyPeople):
    
        def __init__(self, name, age, sex, level):
            OldboyPeople.__init__(self,name,age,sex)
            self.level=level
    
        def score(self,stu,num):
            stu.score=num
            print('老师[%s]为学生[%s]打分[%s]' %(self.name,stu.name,num))
    
    # 创造课程
    python=Course('python全栈开发','5mons',3000)
    linux=Course('linux运维','5mons',800)
    
    
    # 创造学生与老师
    stu1=OldboyStudent('猪哥',19,'male',1)
    tea1=OldboyTeacher('egon',18,'male',10)
    
    
    # 将学生、老师与课程对象关联/组合
    stu1.course=python
    tea1.course=linux
    
    stu1.course.tell_info()
    tea1.course.tell_info()

    四、多态与多态性(抽象类)

    什么是多态(what):

     多态指的是同一种事物的多种形态

    水-》冰、水蒸气、液态水
    动物-》人、狗、猪

    为什么要用多态(why):

    多态性:
    继承同一个类的多个子类中有相同的方法名
    那么子类产生的对象就可以不用考虑具体的类型而直接调用功能

    如何用多态(how):

    #抽象类

    import
    abc class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod def speak(self): pass @abc.abstractmethod def eat(self): pass # Animal() #强调:父类是用来指定标准的,不能被实例化 class People(Animal): def speak(self): print('say hello') def eat(self): pass class Dog(Animal): def speak(self): print('汪汪汪') def eat(self): pass class Pig(Animal): def speak(self): print('哼哼哼') def eat(self): pass peo1=People() dog1=Dog() pig1=Pig()

    Tips:python推崇的是鸭子类型,只要你叫的声音像鸭子,并且你走路的样子也像鸭子,那你就是鸭子。

    class Disk:
        def read(self):
            print('disk read')
    
        def write(self):
            print('disk wirte')
    
    
    class Process:
        def read(self):
            print('process read')
    
        def write(self):
            print('process wirte')
    
    
    class File:
        def read(self):
            print('file read')
    
        def write(self):
            print('file wirte')
    
    
    
    obj1=Disk()
    obj2=Process()
    obj3=File()
    鸭子类型

      

    五、封装

    什么是封装(what):

    封:属性对外是隐藏的,但对内是开放的
    装:申请一个名称空间,往里装入一系列名字/属性

    为什么要封装(why):

    封装数据属性的目的:
      首先定义属性的目的就是为了给类外部的使用使用的,
      隐藏之后是为了不让外部使用直接使用,需要类内部开辟一个接口
      然后让类外部的使用通过接口来间接地操作隐藏的属性。
    精髓在于:我们可以在接口之上附加任意逻辑,从而严格控制使用者对属性的操作

    封装函数属性:
      首先定义属性的目的就是为了给类外部的使用使用的,
      隐藏函数属性是为了不让外不直接使用,需要类内部开辟一个接口
      然后在接口内去调用隐藏的功能
    精髓在于:隔离了复杂度

    如何封装(how):

    如何隐藏:在属性前加上__开头

    1、 这种隐藏仅仅只是一种语法上的变形操作
    2、 这种语法上的变形只在类定义阶段发生一次,因为类体代码仅仅只在类定义阶段检测一次
    3、 这种隐藏是对外不对内的,即在类的内部可以直接访问,而在类的外则无法直接访问,原因是 在类定义阶段,类体内代码统一发生了一次变形

    4、 如果不想让子类的方法覆盖父类的,可以将该方法名前加一个__开头

    #需求:通过封装控制修改对象的方式
    class People:
        def __init__(self,name,age):
            self.__name=name
            self.__age=age
    
        def tell_info(self):
            print('%s:%s' %(self.__name,self.__age))
    
        def set_info(self,name,age):
            if type(name) is not str:
                # print('用户名必须为str类型')
                # return
                raise TypeError('用户名必须为str类型')
    
            if type(age) is not int:
                # print('年龄必须为int类型')
                # return
                raise TypeError('年龄必须为int类型')
            self.__name=name
            self.__age=age
    
    peo1=People('egon',18)
    peo1.set_info('egon',19)
    peo1.tell_info()

    六、property与绑定方法

    (一)特性property

     property装饰器用于将被装饰的方法伪装成一个数据属性,在使用时可以不用加括号而直接引用。

    #需求:计算BMI
    class People:
        def __init__(self,name,weight,height):
            self.name=name
            self.weight=weight
            self.height=height
    
        @property
        def bmi(self):
            return self.weight / (self.height ** 2)
    
    peo1=People('egon',75,1.8)
    
    peo1.height=1.85
    print(peo1.bmi)
    #property内置方法
    class People:
        def __init__(self,name):
            self.__name=name
    
        @property # 查看obj.name
        def name(self):
            return '<名字是:%s>' %self.__name
    
        @name.setter #修改obj.name=值
        def name(self,name):
            if type(name) is not str:
                raise TypeError('名字必须是str类型')
            self.__name=name
    
        @name.deleter #删除del obj.name
        def name(self):
            # raise PermissionError('不让删')
            print('不让删除')
            # del self.__name
    
    peo1=People('egon')
    # print(peo1.name)
    
    # print(peo1.name)
    
    # peo1.name='EGON'
    # print(peo1.name)
    
    del peo1.name
    class People:
        def __init__(self,name):
            self.__name=name
    
    
        def tell_name(self):
            return '<名字是:%s>' %self.__name
    
        def set_name(self,name):
            if type(name) is not str:
                raise TypeError('名字必须是str类型傻叉')
            self.__name=name
    
        def del_name(self):
            print('不让删除傻叉')
    
        name=property(tell_name,set_name,del_name)
    
    
    peo1=People('egon')
    
    print(peo1.name)
    peo1.name='EGON'
    print(peo1.name)
    del peo1.name
    property内置方法古老写法

     (二)绑定方法与非绑定方法

    1、绑定方法
    特性:绑定给谁就应该由谁来调用,谁来调用就会将谁当作第一个参数自动传入
    《《《精髓在于自动传值》》》

    1、绑定方法分为两类:
    1.1 绑定给对象方法
    在类内部定义的函数(没有被任何装饰器修饰的),默认就是绑定给对象用的
    1.2 绑定给类的方法(@classmethod):
    在类内部定义的函数如果被装饰器@classmethod装饰,
    那么则是绑定给类的,应该由类来调用,类来调用就自动将类当作第一个参数自动传入

    2、非绑定方法(@staticmethod)
    类中定义的函数如果被装饰器@staticmethod装饰,那么该函数就变成非绑定方法
    既不与类绑定,又不与对象绑定,意味着类与对象都可以来调用
    但是无论谁来调用,都没有任何自动传值的效果,就是一个普通函数

    3、应用
    如果函数体代码需要用外部传入的类,则应该将该函数定义成绑定给类的方法
    如果函数体代码需要用外部传入的对象,则应该将该函数定义成绑定给对象的方法
    如果函数体代码既不需要外部传入的类也不需要外部传入的对象,则应该将该函数定义成非绑定方法/普通函数

    '''
    settings.py文件内容
    IP = '10.13.24.245
    PORT = 3306
    '''
    
    #需求:
    一种新的实例化方式:从配置文件中读取配置完成实例化
    class Mysql:
        def __init__(self,ip,port):
            self.uid=self.create_uid()
            self.ip=ip
            self.port=port
    
        def tell_info(self):
            print('%s:%s' %(self.ip,self.port))
    
        @classmethod
        def from_conf(cls):
            return cls(settings.IP, settings.PORT)
    
        @staticmethod
        def func(x,y):
            print('不与任何人绑定')
    
        @staticmethod
        def create_uid():
            return uuid.uuid1()
    
    # 默认的实例化方式:类名(..)
    obj=Mysql('10.10.0.9',3307)

     七、反射

    什么是反射(what):

    通过字符串来操作类或者对象的属性。

    如何使用反射(how):

    hasattr
    getattr
    setattr
    delattr

    class People:
        country='China'
        def __init__(self,name):
            self.name=name
    
        def eat(self):
            print('%s is eating' %self.name)
    
    peo1=People('egon')
    
    
    print(hasattr(peo1,'eat')) #peo1.eat
    
    print(getattr(peo1,'eat')) #peo1.eat
    print(getattr(peo1,'xxxxx',None))
    
    setattr(peo1,'age',18) #peo1.age=18
    print(peo1.age)
    
    print(peo1.__dict__)
    delattr(peo1,'name') #del peo1.name
    print(peo1.__dict__)

    八、与面向对象有关的内置函数

    class Foo:
        pass
    
    obj=Foo()
    
    print(isinstance(obj,Foo))
    
    # 在python3中统一类与类型的概念
    d={'x':1} #d=dict({'x':1} #)
    
    print(type(d) is dict)
    print(isinstance(d,dict))
    
    issubclass()
    
    
    class Parent:
        pass
    
    class Sub(Parent):
        pass
    
    print(issubclass(Sub,Parent))
    print(issubclass(Parent,object))

    九、自定义内置方法

    # 1、__str__方法
    class People:
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        #在对象被打印时,自动触发,应该在该方法内采集与对象self有关的信息,然后拼成字符串返回
        def __str__(self):
            # print('======>')
            return '<name:%s age:%s>' %(self.name,self.age)
    obj=People('egon',18)
    obj1=People('alex',18)
    print(obj)  #obj.__str__()
    print(obj)  #obj.__str__()
    print(obj)  #obj.__str__()
    print(obj1)  #obj1.__str__()
    
    
    d={'x':1} #d=dict({'x':1})
    print(d)
    
    
    #2、__del__析构方法
    # __del__会在对象被删除之前自动触发
    class People:
        def __init__(self,name,age):
            self.name=name
            self.age=age
            self.f=open('a.txt','rt',encoding='utf-8')
    
        def __del__(self):
            # print('run=-====>')
            # 做回收系统资源相关的事情
            self.f.close()
    
    obj=People('egon',18)
    
    print('')

     十、元类

    什么是元类(what):

    在python中一切皆对象,那么我们用class关键字定义的类本身也是一个对象
    负责产生该对象的类称之为元类,即元类可以简称为类的类
    class Foo: # Foo=元类()
      pass

    为什么用元类(why):

    元类是负责产生类的,所以我们学习元类或者自定义元类的目的:
    是为了控制类的产生过程,还可以控制对象的产生过程。

    如何用元类(how):

    #储备知识:内置函数exec的用法
    cmd="""
    x=1
    def func(self):
        pass
    """
    class_dic={}
    exec(cmd,{},class_dic)
    
    print(class_dic)
    内置函数exec

    创建类的两种方法:

    大前提:如果说类也是对象的化,那么用class关键字的去创建类的过程也是一个实例化的过程
    该实例化的目的是为了得到一个类,调用的是元类

    方式一:用的默认的元类type

    #2.1 方式一:用的默认的元类type
    class People: #People=type(...)
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def eat(self):
            print('%s is eating' %self.name)
    
    print(type(People))
    
    #2.1.1 创建类的3个要素:类名,基类,类的名称空间
    class_name='People'
    class_bases=(object,)
    class_dic={}
    class_body="""
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age
    
    def eat(self):
        print('%s is eating' %self.name)
    """
    exec(class_body,{},class_dic)
    
    #准备好创建类的三要素
    print(class_name)
    print(class_bases)
    print(class_dic)
    
    #People=type(类名,基类,类的名称空间)
    People1=type(class_name,class_bases,class_dic)
    print(People1)
    obj1=People1('egon',18)
    print(People)
    obj=People('egon',18)
    
    obj1.eat()
    obj.eat()

    方式二:用的自定义元类

    #2.2 方式二:用的自定义的元类
    class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
        def __init__(self,class_name,class_bases,class_dic):
            print(self) #现在是People
            print(class_name)
            print(class_bases)
            print(class_dic)
            super(Mymeta,self).__init__(class_name,class_bases,class_dic) #重用父类的功能
    
    # 分析用class自定义类的运行原理(而非元类的的运行原理):
    #1、拿到一个字符串格式的类名class_name='People'
    #2、拿到一个类的基类们class_bases=(obejct,)
    #3、执行类体代码,拿到一个类的名称空间class_dic={...}
    #4、调用People=type(class_name,class_bases,class_dic)
    class People(object,metaclass=Mymeta): #People=Mymeta(类名,基类们,类的名称空间)
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def eat(self):
            print('%s is eating' %self.name)
    
    
    # 应用:自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程
    
    class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
        def __init__(self,class_name,class_bases,class_dic):
            if class_dic.get('__doc__') is None or len(class_dic.get('__doc__').strip()) == 0:
                raise TypeError('类中必须有文档注释,并且文档注释不能为空')
            if not class_name.istitle():
                raise TypeError('类名首字母必须大写')
            super(Mymeta,self).__init__(class_name,class_bases,class_dic) #重用父类的功能
    
    class People(object,metaclass=Mymeta): #People=Mymeta('People',(object,),{....})
        """这是People类"""
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def eat(self):
            print('%s is eating' %self.name)
    #3 储备知识:__call__
    class Foo:
        def __call__(self, *args, **kwargs):
            print(self)
            print(args)
            print(kwargs)
    
    
    obj=Foo()
    #
    # # 要想让obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法__call__方法
    # # 该方法会在调用对象时自动触发
    obj(1,2,3,x=1,y=2)
    
    
    4、自定义元类来控制类的调用的过程,即类的实例化过程
    class Mymeta(type):
    
        def __call__(self, *args, **kwargs):
            # print(self) # self是People
            # print(args)
            # print(kwargs)
            # return 123
    
            # 1、先造出一个People的空对象
            obj=self.__new__(self)
            # 2、为该对空对象初始化独有的属性
            # print(args,kwargs)
            self.__init__(obj,*args,**kwargs)
    
            # 3、返回一个初始好的对象
            return obj
    
    
    class People(object,metaclass=Mymeta):
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def eat(self):
            print('%s is eating' %self.name)
    
        def __new__(cls, *args, **kwargs):
            print(cls)
            # cls.__new__(cls) # 错误
            obj=super(People,cls).__new__(cls)
            return obj
    
    # 分析:调用Pepole的目的
    #1、先造出一个People的空对象
    #2、为该对空对象初始化独有的属性
    # obj1=People('egon1',age=18)
    # obj2=People('egon2',age=18)
    # print(obj1)
    # print(obj2)
    
    obj=People('egon',age=18)
    print(obj.__dict__)
    print(obj.name)
    obj.eat()
    class Mymeta(type):
        def __init__(self,class_name,class_bases,class_dic):
            #控制类Foo的创建
            super(Mymeta,self).__init__(class_name,class_bases,class_dic)
    
        def __call__(self, *args, **kwargs):
            #控制Foo的调用过程,即Foo对象的产生过程
            obj = self.__new__(self)
            self.__init__(obj, *args, **kwargs)
            obj.__dict__={'_%s__%s' %(self.__name__,k):v for k,v in obj.__dict__.items()}
    
            return obj
    
    class Foo(object,metaclass=Mymeta):  # Foo=Mymeta(...)
        def __init__(self, name, age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
    
    obj=Foo('egon',18,'male')
    print(obj.__dict__)
    练习:通过原来控制Foo的调用过程,隐藏属性

    十一、单例模式

    什么是单例模式(what):

    基于某种方法实例化多次得到实例是同一个

    为什么用单例模式(why):

    当实例化多次得到的对象中存放的属性都一样的情况,应该将多个对象指向同一个内存,即同一个实例。

    如何用单例模式(how):

    方式一:绑定classmethod装饰器

    import settings
    
    class Mysql:
        __instacne=None
    
        def __init__(self,ip,port):
            self.ip=ip
            self.port=port
    
        @classmethod
        def from_conf(cls):
            if cls.__instacne is None:
                cls.__instacne=cls(settings.IP,settings.PORT)
            return cls.__instacne
    # obj=Mysql('1.1.1.10',3306)
    
    obj1=Mysql.from_conf()
    obj2=Mysql.from_conf()
    obj3=Mysql.from_conf()
    
    print(obj1)
    print(obj2)
    print(obj3)
    
    obj4=Mysql('10.10.10.11',3307)

    方式二:自定义一个装饰器控制单例模式

    import settings
    def singleton(cls):
        cls.__instance=cls(settings.IP,settings.PORT)
        def wrapper(*args,**kwargs):
            if len(args) == 0 and len(kwargs) == 0:
                return cls.__instance
            return cls(*args,**kwargs)
        return wrapper
    
    @singleton #Mysql=singleton(Mysql) #Mysql=wrapper
    class Mysql:
        def __init__(self,ip,port):
            self.ip=ip
            self.port=port
    
    
    obj1=Mysql() #wrapper()
    obj2=Mysql() #wrapper()
    obj3=Mysql() #wrapper()
    print(obj1 is obj2 is obj3)
    print(obj1)
    print(obj2)
    print(obj3)
    obj4=Mysql('1.1.1.4',3308)
    print(obj4)

    方式三:控制元类类实现单例模式 

    import settings
    
    class Mymeta(type):
        def __init__(self,class_name,class_bases,class_dic): #self=Mysql
            super(Mymeta,self).__init__(class_name,class_bases,class_dic )
            self.__instance=self.__new__(self) #造出一个Mysql的对象
            self.__init__(self.__instance,settings.IP,settings.PORT) #从配置文件中加载配置完成Mysql对象的初始化
    
            # print(self.__instance)
            # print(self.__instance.__dict__)
    
        def __call__(self, *args, **kwargs): #self=Mysql
            if len(args) == 0 and len(kwargs) == 0:
                return self.__instance
    
            obj=self.__new__(self)
            self.__init__(obj,*args,**kwargs)
            return obj
    
    
    
    class Mysql(object,metaclass=Mymeta): #Mysql=Mymeta(...)
        def __init__(self,ip,port):
            self.ip=ip
            self.port=port
    
    
    obj1=Mysql()
    obj2=Mysql()
    obj3=Mysql()
    obj4=Mysql('10.10.10.11',3308)
    
    print(obj1)
    print(obj2)
    print(obj3)
    print(obj4)
  • 相关阅读:
    heat模板
    Leetcode812.Largest Triangle Area最大三角形面积
    Leetcode812.Largest Triangle Area最大三角形面积
    Leetcode811.Subdomain Visit Count子域名访问计数
    Leetcode811.Subdomain Visit Count子域名访问计数
    Leetcode806.Number of Lines To Write String写字符串需要的行数
    Leetcode806.Number of Lines To Write String写字符串需要的行数
    Leetcode819.Most Common Word最常见的单词
    Leetcode819.Most Common Word最常见的单词
    Leetcode783.Minimum Distance Between BST Nodes二叉搜索树结点最小距离
  • 原文地址:https://www.cnblogs.com/neymargoal/p/9225035.html
Copyright © 2020-2023  润新知