第八章 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
(二)绑定方法与非绑定方法
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)
创建类的两种方法:
大前提:如果说类也是对象的化,那么用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__)
十一、单例模式
什么是单例模式(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)