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

时间:2022-07-09 17:25:20

目录

一、面向对象编程

二、类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__)

 

第八章 Python——面向对象编程第八章 Python——面向对象编程
#属性查找
#需求:查看生成了多少次对象

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):

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

绑定效果:

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

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

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推崇的是鸭子类型,只要你叫的声音像鸭子,并且你走路的样子也像鸭子,那你就是鸭子。

第八章 Python——面向对象编程第八章 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
第八章 Python——面向对象编程第八章 Python——面向对象编程
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):

第八章 Python——面向对象编程第八章 Python——面向对象编程
#储备知识:内置函数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()
第八章 Python——面向对象编程第八章 Python——面向对象编程
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)