元类与单例模式
一、元类
元类是什么
有一句话在编程语言圈中流行,python一切皆对象。在之前初学面向对象的时候,我们举过例子,之前使用的函数都是通过类造出来的对象。但是我们没有思考过,既然一切皆对象,那么类是否也是一个对象?如果他是一个对象,那么他又是被哪个类造出来的?
这个就是今天要学习的内容,元类,即类这个对象的类.
我们查看某个对象属于哪个类,是利用type,那么我们可以使用相同方法,查看类的类是什么。
class MyClass:
def __init__(self,name,age):
self.name = name
self.age = age
obj = MyClass('yang',18)
print(type(obj))
print(type(MyClass))
----------------------
<class '__main__.MyClass'>
<class 'type'> # 此处可知元类是type
默认的元类是type,在默认情况下,我们用class关键字定义的类都是由type产生。
模拟class关键字
对于一个类来说,组成部分有三个,类名,基类,类体代码。在下面,我们模拟class关键字产生一个类。
# 1、先拿到一个类名
class_name = "Teacher"
# 2、然后拿到类的父类
class_bases = (object,) # 如果未继承类,则默认为object
# 3、再运行类体代码,将产生的名字放到名称空间中
class_dic = {}
class_body = """
school = 'qinghua'
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('%s says welcome' % self.name)
"""
exec(class_body,{},class_dic) # exec 执行储存在字符串或文件中的Python语句,相比于eval,exec可以执行更复杂的 Python 代码。
# print(class_dic)
# 4、调用元类(传入类的三大要素:类名、基类、类的名称空间)得到一个元类的对象,然后将元类的对象赋值给变量名Teacher,Teacher就是我们用class自定义的那个类
MyClass = type(class_name,class_bases,class_dic)
自定义元类
既然知道了class关键字是如何创建类的,那么我们就清楚了类的实现过程,那么我们可以自定义一个元类了,自定义元类的好处有很多,在下面的例子中你可以看到.
基本格式
class Mymeta(type): # 必须要继承type
pass
class MyClass(metaclass=Mymeta): # 指明利用自定义元类创建类
pass
obj = MyClass()
print(obj)
----------------------
<__main__.MyClass object at 0x7fd442328c70>
在自定义元类中,我们可以加以对类创建的控制,如名字必须大写开头,必须要有类的注释,把利用此类生成的对象的所有属性和方法都设置为私有等等。在这里我们使用到了raise,主动抛出异常。
import re
class Mymeta(type): # 只有继承了type类的类才是自定义的元类
def __init__(self, class_name, class_bases, class_dic):
if not re.match("[A-Z]", class_name):
raise BaseException("类名必须用驼峰体")
if len(class_bases) == 0:
raise BaseException("至少继承一个父类")
# print("文档注释:",class_dic.get('__doc__'))
doc=class_dic.get('__doc__')
if not (doc and len(doc.strip()) > 0):
raise BaseException("必须要有文件注释,并且注释内容不为空")
class Teacher(object,metaclass=Mymeta):
"""
文档注释必须有,不然要抛出异常
"""
addr = 'Shanghai'
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('%s says welcome to the oldboy to learn Python' % self.name)
obj = Teacher('yang',18)
print(obj)
-----------------------------
<__main__.Teacher object at 0x7fb8c4428b80>
一个对象加括号,会触发魔术方法__call__方法,既然类作为一个对象,肯定在加括号运行时候也触发了这个方法,所以我们可以推测在type中必定有__call__方法,我们也可以自己写这个方法,来控制类加括号运行时发生的事情。比如,将一个在实例化的时候就把所有的属性和方法都设置为私有。
import re
class Mymeta(type): # 只有继承了type类的类才是自定义的元类
def __init__(self, class_name, class_bases, class_dic):
if not re.match("[A-Z]", class_name):
raise BaseException("类名必须用驼峰体")
if len(class_bases) == 0:
raise BaseException("至少继承一个父类")
# print("文档注释:",class_dic.get('__doc__'))
doc = class_dic.get('__doc__')
if not (doc and len(doc.strip()) > 0):
raise BaseException("必须要有文件注释,并且注释内容不为空")
def __call__(self, *args, **kwargs):
# 1、先创建一个老师的空对象,利用__new__魔术方法,利用父类创建对象
tea_obj = object.__new__(self)
# 2、调用老师类内的__init__函数,然后将老师的空对象连同括号内的参数的参数一同传给__init__
self.__init__(tea_obj, *args, **kwargs)
# 利用字典生成式,直接一行代码解决私有化
tea_obj.__dict__ = {"_%s__%s" %(self.__name__,k): v for k, v in tea_obj.__dict__.items()}
# 3、将初始化好的老师对象赋值给变量名res
return tea_obj
class Teacher(object, metaclass=Mymeta):
"""
必须写注释不然要报错
"""
school = 'MIT'
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('%s says welcome to the oldboy to learn Python' % self.name)
res = Teacher('yang', 18)
print(res.__dict__)
----------------------------------
{'_Teacher__name': 'yang', '_Teacher__age': 18}
二、多线程下的单例
1、多线程下数据安全的单例(重要)
使用threading.Lock方法,把创建对象的线程串行化。在学习过程中,博主发现添加了time.sleep(1)这句后,多线程下无法正常实现单例,由此引发了下面与chatgpt的询问过程
def synchronized(func):
func.__lock__ = threading.Lock()
def lock_func(*args, **kwargs):
with func.__lock__:
return func(*args, **kwargs)
return lock_func
class Singleton(object):
instance = None
def __init__(self, name):
self.name = name
@synchronized
def __new__(cls, *args, **kwargs):
# 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象
if not cls.instance:
time.sleep(1)
cls.instance = super(Singleton, cls).__new__(cls)
return cls.instance
def task(name):
ob = Singleton(name)
print(ob)
for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()
以下为关于线程安全的chatgpt对话内容:
**王寄鱼:** __new__方法的使用
**Ai.小助手:** `__new__` 方法是在创建一个新对象时调用的特殊方法。它是一个静态方法,与其他类方法不同,它不会自动传递类实例作为第一个参数(通常是 `self`)。`__new__` 方法的主要作用是控制对象的创建过程,可以在对象创建之前进行一些自定义的操作。
下面是一个简单的示例,演示了 `__new__` 方法的使用:
```python
class MyClass:
def __new__(cls, *args, **kwargs):
print("Creating a new instance")
instance = super().__new__(cls)
return instance
def __init__(self, name):
self.name = name
# 创建对象
obj = MyClass("example")
```
在上面的示例中,我们定义了一个 `MyClass` 类,并重写了 `__new__` 方法。在 `__new__` 方法内部,我们打印了一条消息,表示正在创建一个新实例。然后,我们使用 `super().__new__(cls)` 调用父类的 `__new__` 方法来创建实例。最后,我们将创建的实例返回。
需要注意的是,`__new__` 方法返回的是一个对象实例,而 `__init__` 方法则是用来初始化这个实例。在上面的示例中,我们还定义了 `__init__` 方法来初始化 `name` 属性。
总结一下,`__new__` 方法用于控制对象的创建过程,而 `__init__` 方法用于初始化对象的属性。
**王寄鱼:** 以下代码在多线程情况为何依然数据安全?
class Singleton(object):
instance = None
def __init__(self, name):
self.name = name
time.sleep(1)
# @synchronized
def __new__(cls, *args, **kwargs):
# 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象
if not cls.instance:
cls.instance = super(Singleton, cls).__new__(cls)
return cls.instance
def task(name):
ob = Singleton(name)
print(ob)
for i in range(5):
t = threading.Thread(target=task,args=[i,])
t.start()
**Ai.小助手:** 在给定的代码中,虽然存在多线程,但数据仍然是安全的,这是因为使用了单例模式来创建对象。
在单例模式中,只有一个实例对象可以被创建和访问。在这个例子中,`Singleton` 类的 `__new__` 方法确保只有一个实例被创建并返回。具体来说,`instance` 属性在第一次实例化时被赋值为 `None`,然后在 `__new__` 方法中检查 `instance` 是否为 `None`,如果是,则使用 `super().__new__(cls)` 创建一个新的实例,并将其赋值给 `instance`。之后,无论多少线程尝试实例化 `Singleton` 类,都会返回同一个实例。
因为只有一个实例被创建和共享,所以在多线程环境下,每个线程都会得到相同的实例对象。这就保证了数据的安全性,因为所有线程都在访问和操作同一个对象。
需要注意的是,单例模式并不一定适用于所有情况,特别是在多线程环境下。在某些情况下,可能需要采取其他措施来确保数据的安全性,例如使用锁机制或其他并发控制手段。然而,在给定的代码中,由于使用了单例模式,因此数据是安全的。
**王寄鱼:** 为何仅仅调整了time.sleep(1)的位置,就造成了数据不安全?比如一下代码:
class Singleton(object):
instance = None
def __init__(self, name):
self.name = name
# @synchronized
def __new__(cls, *args, **kwargs):
# 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象
if not cls.instance:
time.sleep(1)
cls.instance = super(Singleton, cls).__new__(cls)
return cls.instance
def task(name):
ob = Singleton(name)
print(ob)
for i in range(5):
t = threading.Thread(target=task,args=[i,])
t.start()
**Ai.小助手:** 在给定的修改后的代码中,将 `time.sleep(1)` 移动到 `__new__` 方法的条件判断之后。这样做会导致在多线程环境下数据不安全。
在原始代码中,`time.sleep(1)` 在 `__init__` 方法中,表示在每次实例化时都会休眠1秒。这样,即使多个线程同时尝试实例化 `Singleton` 类,由于每个线程都会在 `__init__` 方法中休眠1秒,确保了每个线程都能够安全地完成实例化。
但是,在修改后的代码中,将 `time.sleep(1)` 移动到 `__new__` 方法的条件判断之后。这样做会导致以下问题:
1. 多个线程同时进入 `__new__` 方法,并发现 `cls.instance` 为 `None`。
2. 第一个线程开始休眠1秒。
3. 在第一个线程休眠期间,其他线程继续执行,并进入 `__new__` 方法。
4. 其他线程发现 `cls.instance` 仍然为 `None`,于是也开始休眠1秒。
5. 第一个线程完成休眠后,创建了一个实例并将其赋值给 `cls.instance`。
6. 其他线程完成休眠后,也创建了一个实例,但由于 `cls.instance` 已经被第一个线程赋值,它们不会再次赋值。
因此,由于多个线程都能够创建实例并赋值给 `cls.instance`,所以数据不再安全。每个线程将拥有自己的实例,而不是共享同一个实例。
为了确保数据的安全性,应该将 `time.sleep(1)` 保留在 `__init__` 方法中,以确保每个线程都能够按顺序休眠并实例化对象。
**王寄鱼:** 第一个单例和以下单例有什么区别?为什么以下单例线程不安全?
class Singleton(object):
def __init__(self):
import time
time.sleep(1)
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
import threading
def task(arg):
obj = Singleton.instance()
print(obj)
for i in range(5):
t = threading.Thread(target=task,args=[i,])
t.start()
**Ai.小助手:** 第一个单例实现和第二个单例实现之间有以下区别:
1. 在第一个单例实现中,使用了 `__new__` 方法来控制对象的创建过程,而在第二个单例实现中,使用了一个类方法 `instance` 来获取单例对象。
2. 第一个单例实现中,使用了一个类变量 `instance` 来保存单例对象,而第二个单例实现中,使用了一个类属性 `_instance` 来保存单例对象。
以下单例实现是线程不安全的,原因如下:
1. 多个线程同时进入 `instance` 方法,并发现 `Singleton._instance` 为 `None`。
2. 第一个线程开始执行 `Singleton(*args, **kwargs)` 创建实例,并将其赋值给 `Singleton._instance`。
3. 在第一个线程执行期间,其他线程继续执行,并进入 `instance` 方法。
4. 其他线程发现 `Singleton._instance` 仍然为 `None`,于是也开始执行 `Singleton(*args, **kwargs)` 创建实例。
5. 其他线程完成创建实例后,将实例赋值给 `Singleton._instance`,覆盖了第一个线程创建的实例。
因此,多个线程可以同时创建实例并赋值给 `Singleton._instance`,导致每个线程拥有自己的实例,而不是共享同一个实例。这就导致了线程不安全的问题。
为了确保线程安全,可以使用线程锁(例如 `threading.Lock`)来保护关键部分的代码,确保只有一个线程可以同时访问和操作 `Singleton._instance`。这样可以避免多个线程同时创建实例的问题。
**王寄鱼:** 把time.sleep(1)注释后,为什么又线程安全了?
class Singleton(object):
def __init__(self):
import time
# time.sleep(1)
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
import threading
def task(arg):
obj = Singleton.instance()
print(obj)
for i in range(5):
t = threading.Thread(target=task,args=[i,])
t.start()
**Ai.小助手:** 在给定的代码中,将 `time.sleep(1)` 注释掉后,代码在多线程环境下仍然是线程不安全的。
虽然注释掉了 `time.sleep(1)`,消除了每个线程之间的休眠时间差异,但问题仍然存在。具体原因如下:
1. 多个线程同时进入 `instance` 方法,并发现 `Singleton._instance` 为 `None`。
2. 第一个线程开始执行 `Singleton(*args, **kwargs)` 创建实例,并将其赋值给 `Singleton._instance`。
3. 在第一个线程执行期间,其他线程继续执行,并进入 `instance` 方法。
4. 其他线程发现 `Singleton._instance` 仍然为 `None`,于是也开始执行 `Singleton(*args, **kwargs)` 创建实例。
5. 其他线程完成创建实例后,将实例赋值给 `Singleton._instance`,覆盖了第一个线程创建的实例。
因此,多个线程可以同时创建实例并赋值给 `Singleton._instance`,导致每个线程拥有自己的实例,而不是共享同一个实例。这就导致了线程不安全的问题。
为了确保线程安全,同样可以使用线程锁(例如 `threading.Lock`)来保护关键部分的代码,确保只有一个线程可以同时访问和操作 `Singleton._instance`。这样可以避免多个线程同时创建实例的问题。
**王寄鱼:** 以下两个单例,为什么一个线程安全,另一个不安全?
例子1:
class Singleton(object):
def __init__(self):
import time
time.sleep(1)
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
例子2:
class Singleton(object):
instance = None
def __init__(self, name):
self.name = name
time.sleep(1)
# @synchronized
def __new__(cls, *args, **kwargs):
# 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象
if not cls.instance:
cls.instance = super(Singleton, cls).__new__(cls)
return cls.instance
**Ai.小助手:** 在例子1中,虽然存在多线程,但代码仍然是线程不安全的。原因是在 `instance` 方法中,多个线程可以同时检查 `hasattr(Singleton, "_instance")` 为 `False`,然后同时创建实例并赋值给 `Singleton._instance`。
具体过程如下:
1. 多个线程同时进入 `instance` 方法,并发现 `hasattr(Singleton, "_instance")` 为 `False`。
2. 第一个线程开始执行 `Singleton(*args, **kwargs)` 创建实例,并将其赋值给 `Singleton._instance`。
3. 在第一个线程执行期间,其他线程继续执行,并进入 `instance` 方法。
4. 其他线程同样发现 `hasattr(Singleton, "_instance")` 为 `False`,于是也开始执行 `Singleton(*args, **kwargs)` 创建实例。
5. 其他线程完成创建实例后,将实例赋值给 `Singleton._instance`,覆盖了第一个线程创建的实例。
因此,多个线程可以同时创建实例并赋值给 `Singleton._instance`,导致每个线程拥有自己的实例,而不是共享同一个实例。这就导致了线程不安全的问题。
在例子2中,虽然也存在多线程,但代码是线程安全的。原因是在 `__new__` 方法中使用了互斥锁(`super().__new__(cls)`)来保护关键部分的代码,确保只有一个线程可以同时访问和操作 `cls.instance`。
具体过程如下:
1. 多个线程同时进入 `__new__` 方法,并发现 `cls.instance` 为 `None`。
2. 第一个线程获得互斥锁,开始执行 `super().__new__(cls)` 创建实例,并将其赋值给 `cls.instance`。
3. 在第一个线程执行期间,其他线程无法获得互斥锁,因此无法执行 `super().__new__(cls)`,只能等待。
4. 第一个线程完成创建实例后,释放互斥锁。
5. 其他线程竞争获得互斥锁,其中一个线程获得锁后发现 `cls.instance` 已经被赋值,直接返回该实例。
6. 其他线程无法获得锁的线程会继续等待,直到锁被释放。
由于使用了互斥锁,确保了同一时间只有一个线程可以执行关键部分的代码,从而避免了多个线程同时创建实例的问题,保证了线程安全性。
三、简单单例模式实现
单例模式,是一个设计思路,既然是一种设计思路,那么肯定就有很多种实现方法。
首先介绍一下单例模式的思路,我们的目的是确保某一个类只有一个实例存在,减少内存的损耗。比如我们要读取一个配置文件的时候,可能很多地方都需要读取这个文件,那么可能会创建出十几个对象都在读这个文件,并且他们的功能都是相同的。那么我们不需要创建那么多对象了,每次都把相同的对象返回给他。
单例模式常用写法:懒汉式(懒汉模式在多线程下有数据安全问题,需要使用线程锁),饿汉式,注册式,序列化式。
本文介绍四种实现单例模式的方法。
1、纯单例模式的实现
记住单例的实现思路,无论看什么方法实现单例都是一样的。首先在类中定义一个属性如x为空,然后再调用类的时候就可以判断了,如果是第一次进来,x必定为空,那么就正常的创建一个对象给他。那么我们类中的x属性现在就不是空了,而是一个对象,因为我们把创建出来的对象赋值给了x,那么之后每次进来,因为x已经不是空了,所以都不会走创建对象的分支,而是走直接return x的分支,我们又没有对x进行修改,他永远都是第一次创建出来的对象。
1、利用__new__魔术方法
在调用的时候若没有这个对象再临时创建这个对象,属于懒汉式
class MyClass:
__obj = None
def __new__(cls, *args, **kwargs):
if cls.__obj:
return cls.__obj
cls.__obj = object.__new__(cls)
return cls.__obj
obj1 = MyClass()
obj2 = MyClass()
print(obj1)
print(obj2)
---------------------------
<__main__.MyClass object at 0x7f9e22a45460>
<__main__.MyClass object at 0x7f9e22a45460>
2、直接利用类方法
在调用的时候若没有这个对象再临时创建这个对象,属于懒汉式
class MyClass:
__obj = None
@classmethod
def singleton(cls):
if cls.__obj:
return cls.__obj
cls.__obj = cls()
return cls.__obj
obj1 = MyClass.singleton()
obj2 = MyClass.singleton()
print(obj1)
print(obj2)
------------------------
<__main__.MyClass object at 0x7fb321340460>
<__main__.MyClass object at 0x7fb321340460>
3、利用元类
思路是在调用前就先把这个对象创建出来,然后要调用的时候直接返回这个对象。属于饿汉式
class MyMeta(type):
__obj = None
def __init__(self,class_name,class_bases,class_dic):
self.__obj = object.__new__(self) # 先把对象创建出来
def __call__(self, *args, **kwargs):
return self.__obj # 不用判断是否有这个对象了,直接创建即可
class MyClass(metaclass=MyMeta):
pass
obj1 = MyClass()
obj2 = MyClass()
print(obj1)
print(obj2)
------------------------
<__main__.MyClass object at 0x7ffbc4a28c70>
<__main__.MyClass object at 0x7ffbc4a28c70>
4、利用函数装饰器
思路是在调用前就先把这个对象创建出来,然后要调用的时候直接返回这个对象。属于饿汉式
def wrapper(func):
__obj = func()
def inner(*args,**kwargs):
return __obj
return inner
@wrapper
class MyClass():
pass
obj1 = MyClass()
obj2 = MyClass()
print(obj1)
print(obj2)
--------------------
<__main__.MyClass object at 0x7fefc632db80>
<__main__.MyClass object at 0x7fefc632db80>
# 这个单例的分析思路:
博主曾经被这个单例困惑了几个月,直到现在和朋友讨论才发现了这个单例的实现流程,博主的问题是:
我们用装饰器的时候,装饰器里面的代码肯定是会走的,而我们执行了两次MyClass(),也就是__obj = func(),那么在wrapper这个
装饰器里,func()这个代码应该是走两次的,那么生成的对象肯定应该是两个不一样的,但是结果却是实现了单例。
在解决这个困惑的过程中,加入了一些代码:
def wrapper(func):
__obj = func()
print(1)
def inner(*args,**kwargs):
print(2)
return __obj
return inner
@wrapper
class MyClass():
pass
上述的代码,执行结果是,1走了一遍,2走了两遍。当然,只有这样的结果才是符合单例的,但是却和我们的思路不一样。
然后我把@wrapper拆分了出来
代码就变成了
def wrapper(func):
__obj = func()
def inner(*args,**kwargs):
return __obj
return inner
class MyClass():
pass
MyClass = wrapper(MyClass)
obj1 = MyClass()
obj2 = MyClass()
print(obj1)
print(obj2)
然后我瞬间懂了,那个print(1),是在MyClass = wrapper(MyClass)走的,然后,这个MyClass其实变成了inner
后面其实就是直接执行inner,根本就和wrapper没关系了。
5、利用python导包实现(最常用)
# s1.py 文件中
class Foo(object):
def test(self):
print("123")
v = Foo()
# v是Foo的实例
------
# s2.py 文件中
from s1 import v as v1
print(v1,id(v1)) #<s1.Foo object at 0x0000000002221710> 35788560
from s1 import v as v2
print(v1,id(v2)) #<s1.Foo object at 0x0000000002221710> 35788560
# 两个的内存地址是一样的
# 文件加载的时候,第一次导入后,再次导入时不会再重新加载。
6、利用类装饰器
def Singleton(cls):
"""
使用一个字典,实现能装饰多个不同的类实现单例
:param cls:
:return:
"""
_instance = {}
def _singleton(*args, **kargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
return _instance[cls]
return _singleton
@Singleton
class Test(object):
t = 1
def __init__(self, x=0):
self.x = x
@Singleton
class Test1(object):
t = 1
def __init__(self, x=0):
self.x = x
t1 = Test(2)
t2 = Test(3)
t3 = Test1(4)
t4 = Test1(5)
2、单例模式的实际使用
以下也是一种设计的方案,当使用者实例化时传了参数,那么说明他想创建一个新的对象,如果不传参,我们默认使用单例模式。下面提供了三种方式。
1、利用类方法
IP = 1.1.1.1
PORT = 3306
class MySQL:
__instance = None
def __init__(self, ip, port):
self.ip = ip
self.port = port
@classmethod
def singleton(cls):
if cls.__instance:
return cls.__instance
cls.__instance = cls(IP, PORT)
return cls.__instance
obj1=MySQL("1.1.1.1",3306)
obj2=MySQL("1.1.1.2",3306)
obj3 = MySQL.singleton()
obj4 = MySQL.singleton()
print(obj1)
print(obj2)
print(obj3)
print(obj4)
2、利用装饰器
IP = "192.168.11.10"
PORT = 3306
import settings
def outter(func): # func = MySQl类的内存地址
_instance = func(IP,PORT)
def wrapper(*args,**kwargs):
if args or kwargs:
res=func(*args,**kwargs)
return res
else:
return _instance
return wrapper
@outter # MySQL=outter(MySQl类的内存地址) # MySQL=》wrapper
class MySQL:
def __init__(self, ip, port):
self.ip = ip
self.port = port
obj1 = MySQL("1.1.1.1", 3306)
obj2 = MySQL("1.1.1.2", 3306)
obj3 = MySQL()
obj4 = MySQL()
print(obj1)
print(obj2)
print(obj3 is obj4)
3、利用元类
IP = 1.1.1.1
PORT = 3306
class Mymeta(type):
__instance = None
def __init__(self,class_name,class_bases,class_dic):
self.__instance=object.__new__(self) # Mysql类的对象
self.__init__(self.__instance,IP,PORT)
def __call__(self, *args, **kwargs):
if args or kwargs:
obj = object.__new__(self)
self.__init__(obj, *args, **kwargs)
return obj
else:
return self.__instance
class MySQL(metaclass=Mymeta):
def __init__(self, ip, port):
self.ip = ip
self.port = port
obj1 = MySQL("1.1.1.1", 3306)
obj2 = MySQL("1.1.1.2", 3306)
obj3 = MySQL()
obj4 = MySQL()
print(obj1)
print(obj2)
print(obj3 is obj4)
'''
关于为何要在Mymeta中书写__call__方法,那是因为我们现在要控制Mymeta实现单例模式,那么如果不自己实现__call__就会使用type的__call__方法,type是不可能帮我们实现单例模式的。
'''