DC's blog DC's blog
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)

DC

愿我一生欢喜,不为世俗所及.
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)
  • python面向过程

  • python面向对象

    • OOP基本
    • 继承和组合
    • 多态与多态性
    • 封装与接口
    • 绑定与非绑定方法
    • 简单总结
    • 反射、内置方法
    • 元类
      • 元类介绍
      • 创建类的两种方式
        • 用默认元类type
        • 内置函数exec
        • 创建类的三要素
        • 自定义的元类
        • class创建类的流程
        • 添加一些判断
      • 自定义类的实例化
        • __call__
        • 原理
        • 元类中的__call__
        • 调用元类产生的类
        • 属性查找
      • 实践
        • 小试身手
        • 单例模式
        • 方式一:类方法
        • 方式二:装饰器
        • 方式三:元类
      • 补充
    • 异常处理
    • 小项目之course_select
    • 复习
  • 网络并发编程

  • 不基础的py基础

  • 设计模式

  • python_Need
  • python面向对象
DC
2023-08-16
目录

元类

# 元类介绍

在python中一切皆对象,则我们用class关键字定义的类本身也是一个对象.
负责产生该对象的类称之为元类, 即 元类是我们自定义类的类.
换个说法, 我们用class定义的类本质就是在实例化元类.

元类是负责产生类的,我们学习元类/自定义元类的 目的 在于:
1> 控制class定义类的过程;   2> 控制 调用类(该类是class定义的)产生实例化对象的过程. 
元类里的 __init__ 控制元类产生的实例化对象 (即类) 初始化的过程 ; 元类里的 __call__ 控制实例化对象"实例化/调用"产生实例的过程.


# 创建类的两种方式

大前提: Python一切皆对象,那么类也是一个对象.则用class关键字去创建类的过程也是一个实例化的过程;
该实例化的过程是为了得到一个类,调用的是元类..

1> 默认的元类type;     2> 自定义的元类.

# 用默认元类type

即用关键字class 进行创建.

# -- 方式一: 用的默认的元类type
class People:  # -- People = type('People',..,..)
    country = "China"

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

    def eat(self):
        print("%s is eating." % self.name)


obj = People('dc', 22)
print(type(obj))     # <class '__main__.People'> -- obj对象是通过People类实例化得到的!!
print(type(People))  # <class 'type'> -- 所有用class关键字创建的类都默认是type类实例化得到的!!
print(obj)           # <__main__.People object at 0x7f91cde7eac0>
print(People)        # <class '__main__.People'>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 内置函数exec

exec(object[, globals[, locals]]) :
     参数一: 包含一系列python代码的 字符串
     参数二: 全局作用域(字典形式), 如果不指定, 默认为globals()
     参数三: 局部作用域(字典形式), 如果不指定, 默认为locals()

可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中.

g_dic = {'x': 1, 'y': 2}
l_dic = {'m': 3, 'n': 4, 'x': 5, 'y': 6}
x, y, m, n = 10, 20, 30, 40

exec(
    '''
global x,z
x=100
z=200
m=300
print(m,n,x,y) # 300 4 100 6
               # -- x的值为100,而不是l_dic中的5,是因为global关键字
               #    它已经告诉python解释器,函数里但凡对x变量的引用,都会解析成全局变量.. 
               #    它还使得对x变量的赋值也是在对全局变量进行操作
''', g_dic, l_dic)

print(g_dic)  # {'x': 100, 'y': 2,'z':200,...}
print(l_dic)  # {'m': 300, 'n': 4, 'x': 5, 'y': 6} -- 可以看到m变量的值被覆盖了

# --- --- ---

cmd = """
x = 1
y = 2
print('>>>>:')
"""
local_dic = {}
exec(cmd, {}, local_dic)
print(local_dic)  # -- 将执行过程中产生的名字都丢到local_dic里面去啦!
"""运行结果如下:
>>>>:
{'x': 1, 'y': 2}
"""

# --- --- --- 模拟类定义阶段,开辟命名空间,将类体执行过程中产生的名字放到命名空间里的过程.
class Foo:
    x = 1
    def fun(self):pass
    
cmd = """
x = 1
def fun(self):pass
"""
class_dic = {}  # -- 定义类的局部命名空间
exec(cmd, {}, class_dic)
print(class_dic)  # {'x': 1, 'fun': <function fun at 0x7fcfdfe799d0>}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 创建类的三要素

创建类的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)   # People
print(class_bases)  # (<class 'object'>,)
# {
#   'country': 'China',
#   '__init__': <function __init__ at 0x7fb7b45799d0>,
#   'eat': <function eat at 0x7fb7b4579940>
# }
print(class_dic)

# -- type(类名,基类,类的名称空间)
#    Ps:一般会将type赋值给的变量的名字,命名的跟class_name一样
Peo = type(class_name, class_bases, class_dic)
print(Peo)  # <class '__main__.People'>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 自定义的元类

# class创建类的流程

在用默认元类type创建类的方式中, 我们可以分析出 class的运行原理:(而非元类的运行原理)
1> 拿到一个字符串格式的类名 class_name = "People"
2> 拿到一个类的基类们 class_bases = (object, )
3> 执行类体代码, 拿到一个类的名称空间 class_dic = {...}
4> 调用 People = type(class_name, class_bases, class_dic)
class关键字是上述四个步骤的封装.

class Mymeta(type):  # -- 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类

    def __init__(self, class_name, class_bases, class_dic):
        print(self)         # <class '__main__.People'> 注意哦,Mymeta实例化出的是一个类
        print(class_name)   # People
        print(class_bases)  # (<class 'object'>,)
        # {
        #     '__module__': '__main__',
        #     '__qualname__': 'People',
        #     'country': 'China',
        #     '__init__': <function People.__init__ at 0x7f9ce3555700>,
        #     'eat': <function People.eat at 0x7f9ce3555a60>
        # }
        print(class_dic)
        # (<class '__main__.Mymeta'>, <class 'type'>, <class 'object'>)
        print(Mymeta.__mro__)  
        # -- People = Mymeta(...)  self是People,其所在类是Mymeta,看Mymeta类的mro链
        #    super()会得到一个特殊对象,此处是绑定方法,所以不用写self.
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)  # -- 重用父类功能


# -- People默认继承object类,metaclass默认值为type.
#    class的运行原理第4步是用type实例化出People对象.
#    在这里,我们不使用type,使用自定义的元类Mymeta实例化出People对象.
#    People = Mymeta(类名,基类们,类的名称空间)
#        调用Mymeta类创建了一个空对象'即空的名称空间{}'后;
#        空对象People会连同Mymeta括号内的参数一起传给Mymeta下的__init__方法,完成初始化
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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 添加一些判断
# -- 应用:自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程
class Mymeta(type):

    def __init__(self, class_name, class_bases, class_dic):
        temp = class_dic.get("__doc__")
        # -- or左侧条件成立的话,or右侧就不会执行啦!!也就保证or右侧执行时,temp一定不为None.
        if temp is None or len(temp.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类!"""
    country = "China"

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

    def eat(self):
        print("%s is eating." % self.name)


print(getattr(People, "__doc__", None))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 自定义类的实例化

自定义元类来控制类的调用过程, 即类的实例化过程

# __call__

# 原理
class Foo:

    def __call__(self, *args, **kwargs):
        print(self)    # <__main__.Foo object at 0x7fa1f868cac0>
        print(args)    # (1, 2, 3)
        print(kwargs)  # {'x': 4, 'y': 5}
        return "返回值123"


obj = Foo()
# -- 要想让obj这个实例化对象变成一个可调用的对象,需要在该对象的类中定义一个__call__方法
#    该方法会在调用实例化对象obj时自动触发,调用obj的返回值就是__call__方法的返回值
print(obj(1, 2, 3, x=4, y=5))  # 返回值123  --  obj的调用会触发Foo中__call__方法的执行
1
2
3
4
5
6
7
8
9
10
11
12
13

由此得知, 调用一个对象, 就是触发对象所在类中的__call__方法的执行!
Python处处皆对象, People也是一个对象, 并且People它是可以调用的!
那么在实例化得到People对象的Mymeta类中也必然存在一个__call__方法!!!

# 元类中的__call__

Mymeta ---(实例化)--- People ---(实例化)--- obj
People实例化得到obj会对People进行调用,则Mymeta中一定会有__call__方法.

# -- 应用:自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程
class Mymeta(type):

    def __call__(self, *args, **kwargs):
        print(self)    # <class '__main__.People'>
        print(args)    # ('dc',)
        print(kwargs)  # {'age': 18}


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)


# -- 调用People就是在调用Mymeta类中的__call__方法
#    然后将People传给self,溢出的位置参数传给*,溢出的关键字参数传给**
#    调用People的返回值就是调用__call__的返回值
obj = People('dc', age=18)
print(obj)  # None
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

回顾一下,抛开元类的知识不谈, 就单纯的People('dc', 18)得到一个类的实例化对象obj, 这一过程经历了什么?
step1: 调用类创建了一个空对象 '空的名称空间' {}
step2: 自动触发类中__init__功能的执行, 将空对象传给self参数

# 调用元类产生的类

那么,自定义的元类Mymeta在__call__方法中一定会实现这三件事:
1> 产生一个People的空对象obj
2> 调用__init__方法初始化对象obj
3> 返回初始化好的obj

接下来,我们将默认元类type干的事情进行还原.

# -- 应用:自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程
class Mymeta(type):

    def __call__(self, *args, **kwargs):
        # -- !!!必须写self参数,代表创建的是该self的空对象 固定用法!!
        #    此处是self指代的是类People
        #    要找__new__这个属性,若People的namespace中没有,依次再去object、Mymeta、type中找
        obj = self.__new__(self)  # -- 【1】.通过__new__方法先造一个People的空对象
        # -- 这里也涉及到属性查找,该self(即People)中有__init__方法,就直接使用它
        #    抛开元类不谈,类的实例化对象自动调用__init__绑定方法时,不用传self.. 就是因为在这里帮忙传啦
        self.__init__(obj, *args, **kwargs)  # -- 【2】.调用__init__为该空对象初始化独有的属性
        return obj  # -- 【3】.返回一个初始化好的对象obj


# -- People = Mymeta(People,...,...) 是在创建类,主要操作的是Mymeta中的__init__
# -- People()是在调用Mymeta中的__call__创建实例化对象
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)  # <class '__main__.People'>
        # cls.__new__(cls)  # -- 直接报错,递归死循环
        # print(cls.__mro__)  # -- (<class '__main__.People'>, <class 'object'>)
        obj = super().__new__(cls)  # -- 完整写法为: super(People,cls)
        return obj


obj = People('dc', age=18)
print(obj.__dict__)  # {'name': 'dc', 'age': 18}
obj.eat()  # dc is eating.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 属性查找

结合 python继承的实现原理+元类 重新看属性的查找应该是什么样子呢???

在学习完元类后, 更进一步理解了"python处处皆对象",提一点,用class自定义的类全都是对象
包括object类本身也是元类type的 一个实例,可以用type(object)验证.

继承的实现原理 + 将类当成对象.下述继承应该说成是:
对象StanfordTeacher继承对象Foo, 对象Foo继承对象Bar, 对象Bar继承对象object

于是属性查找应该分成 两层 :
一层是对象层 (基于c3算法的MRO) 的查找, 另外一个层则是类层(即元类层)的查找!!
Ps: 若StanfordTeacher实例化出obj对象,obj通过 . 进行属性引用时,会先在自己的namespace中查找独有属性.

class Mymeta(type):
    n = 520
    def __call__(self, *args, **kwargs):  
      	# print(self)  # <class '__main__.StanfordTeacher'>
        # -- __new__的查找顺序为StanfordTeacher->Foo->Bar->object->Mymeta->type
        obj = self.__new__(self)
        # print(self.__new__ is object.__new__)  # True
        # -- !!下方这条注释的语句,也可以创建空对象,但会直接跳过StanfordTeacher->Foo->Bar这三个类的检索
        #  obj = object.__new__(self)
        self.__init__(obj, *args, **kwargs)
        return obj

class Bar(object):
    n = 333

class Foo(Bar):
    n = 222

class StanfordTeacher(Foo, metaclass=Mymeta):
    n = 111
    school = 'Stanford'
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def say(self):
        print('%s says welcome to the Stanford to learn Python' % self.name)

# -- n的查找顺序为StanfordTeacher->Foo->Bar->object->Mymeta->type
print(StanfordTeacher.n)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

obj = object.__new__(self) 也可以创建空对象,但会直接跳过StanfordTeacher->Foo->Bar这三个类的检索

提一嘴: StanfordTeacher = Mymeta('StanfordTeacher','...','...') 产生类StanfordTeacher的过程就是在调用Mymeta, 而Mymeta也是type类的一个对象, 那么Mymeta之所以可以调用, 是因为在元类type中也有一个 __call__方法!!! 该方法中也会实现那三件事!!


# 实践

# 模版分析

1     # class type:
2     #     def __call__(self, *args, **kwargs):  # -- self为Mymeta对象 args是三要素 15
3     #         obj = self.__new__(self, *args, **kwargs)
4     #         self.__init__(obj, *args, **kwargs)
5     #         return obj

6     class Mymeta(type):
7        def __new__(self, *args, **kwargs):   # -- self为Mymeta对象 args是三要素 15
8             return super(Mymeta, self).__new__(self, *args, **kwargs)

9        def __init__(self, class_name, class_bases, class_dic):  # -- self为People对象 15
10            super(Mymeta, self).__init__(class_name, class_bases, class_dic)

11       def __call__(self, *args, **kwargs):  # -- self为People对象 args是('dc',18) 21
12            obj = self.__new__(self)  # -- 这里调用的是19行的代码
13            self.__init__(obj, *args, **kwargs)  # -- 调用的是16的代码 *args解包以位置参数传递
14            return obj

15    class People(object, metaclass=Mymeta):
16        def __init__(self, name, age):
17            self.name = name
18            self.age = age

          # -- 第19、20行的代码不写也是可以的
19        def __new__(self):
20            return super(People, self).__new__(self)  # -- 最后调用的是object里的__new__
                                                        #    注意,object的__new__只有一个参数

21    p = People('dc', 18)

先说一点:
  python处处皆对象,类也是一个对象. 调用对象就会触发实例化该对象的类中的__call__方法.
  以本模版为例, Mymeta()、People()会分别触发type类和Mymeta类中的call方法;

<在第15行打个断点,就知道执行顺序啦!!从上到下,class和def语句都会先执行,但函数体代码是没有马上执行的.>
第1-5行代码是type类的伪代码.
1> 从第15行代码开始分析. 
   class关键字会收集创建类的三要素,类名People、基类们(object,)、执行类体代码得到的名称字典.
   注意一点:能收集到名称字典是因为第16行、第19行代码执行了,但是函数体代码并没有执行!!
2> 收集完成后,在背后会执行语句,People = Mymate('People',(object,),{...})
   Mymate对象的调用会触发type中__call__方法的运行.
3> 看第2行的伪代码,self是Mymeta对象. -- 因为对象调用绑定方法会自动将自己传递给方法的第一个参数.
   创建类的三要素会被*args以元祖形式接收.
4> 第3行的伪代码,开始实例化Mymeta对象,创建出一个空对象..这里涉及到__new__属性的查找.
   Mymeta对象中有__new__属性,跳转到第7行代码.该行代码中的self也是Mymeta对象.
   打印一下,会发现args元组里是创建类的三要素,kwargs是空字典..
   第8行代码使用了super(),此行代码等价于 `return type.__new__(self, *args, **kwargs)`
   注意一点!*args相当于将args元祖进行了拆包,以位置参数的形式进行了传递!!!
   ok,将空对象 return回了 第3行的伪代码, 并赋值给了obj对象. 实则此obj就是 People对象!
5> 执行第4行的伪代码,为People对象进行初始化操作,同理,涉及__init__属性的查找.
   跳转到第9行代码.. 接着执行第10行,再回到第4行,执行第5行的return语句
6> 就此,第15行代码 People = Mymate('People',(object,),{...})的路途结束!!
7> 第21行代码的执行,会执行第12行代码,再执行第20行代码; 第13行代码的执行会执行第17行代码.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

清爽版

class Mymeta(type):
    # -- 控制自定义类的创建过程 People = Mymeta('People',..,..)
    def __init__(self, class_name, class_bases, class_dic):
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)

    # -- 控制自定义类的调用 p = People('dc',18) 
    def __call__(self, *args, **kwargs):
        obj = self.__new__(self)
        self.__init__(obj, *args, **kwargs)
        return obj

class People(object, metaclass=Mymeta):
    def __init__(self, name, age):
        self.name = name
        self.age = age
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 小试身手

1> 在自定义元类Mymeta中把自定义类People的数据属性都变成大写
2> 在自定义元类Mymeta中完成People实例对象p独有属性的定制,自定义类People无需使用__init__方法

"""
class type:
    def __call__(self, *args, **kwargs):
        # print(self)  # <class '__main__.Mymeta'>
        obj = self.__new__(self, *args, **kwargs)  # -- 产生Mymeta的一个对象People
        self.__init__(obj, *args, **kwargs)
        return obj
"""
class Mymeta(type):
    def __new__(self, class_name, class_bases, class_dic):
        print(self)  # <class '__main__.Mymeta'> !!!!!!
        update_dic = {}
        for k, v in class_dic.items():
            if not callable(v) and not k.startswith("__"):
                update_dic[k.upper()] = v
            else:
                update_dic[k] = v
        # -- 这里是创建一个Mymeta的空的实例化对象People
        #    等同于 return type.__new__(self, class_name, class_bases, update_dic)
        return super(Mymeta, self).__new__(self, class_name, class_bases, update_dic)

    def __call__(self, *args, **kwargs):
        print(self)  # -- <class '__main__.People'> !!!!!!
        # -- 要求实例化时传参必须为关键字形式
        if args:
            raise TypeError("must use keyword argument for key function!")
        obj = self.__new__(self)
        for k, v in kwargs.items():
            obj.__dict__[k] = v
        return obj


class People(object, metaclass=Mymeta):  # -- People = Mymeta(People,..,..)
    country = "china"

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


print(getattr(People, 'COUNTRY'))  # china
p = People(name='dc', age=18)
print(p.__dict__)  # {'name': 'dc', 'age': 18}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

3> 在元类Mymeta中控制自定义的类People产生的对象p相关的独有属性全部为隐藏属性

class Mymeta(type):

    def __call__(self, *args, **kwargs):
        obj = object.__new__(self)
        self.__init__(obj, *args, **kwargs)
        obj.__dict__ = {f'_{self.__name__}__{k}': v for k, v in obj.__dict__.items()}
        return obj


class People(object, metaclass=Mymeta):

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


p = People('dc', 18)
print(p.__dict__)  # {'_People__name': 'dc', '_People__age': 18}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 单例模式

单例模式: 即单个实例, 指的是同一个类实例化多次的结果指向同一个对象, 用于节省内存空间

应用场景:
当实例化多次得到的多个对象中存放的属性都一样时, 应该将多个对象指向同一个内存, 即同一个实例!

settings.py配置文件内容如下:

IP = "1.1.1.10"
PORT = 3306
1
2

若我们从配置文件中读取配置来进行实例化, 在配置相同的情况, 就没必要重复产生对象浪费内存啦!
方式二和三,约定一个规则,Mysql()不传参数,默认从配置文件中读取参数!!Hhh.

# 方式一:类方法
import settings

class Mysql:
    __instance = None

    def __init__(self, ip, port):
        self.ip = ip
        self.port = port

    @classmethod
    def from_conf(cls):
        if not cls.__instance:
            cls.__instance = cls(settings.IP, settings.PORT)
        return cls.__instance

obj1 = Mysql.from_conf()
obj2 = Mysql.from_conf()
obj3 = Mysql.from_conf()

print(obj1 is obj2 is obj3)  # True
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 方式二:装饰器
import settings
"""
def singleton(cls):
    _isinstance = None
    def wrapper(*args, **kwargs):
        if args or kwargs:
            return cls(*args, **kwargs)
        nonlocal _isinstance
        if not _isinstance:
            _isinstance = cls(settings.IP, settings.PORT)
        return _isinstance
    return wrapper
"""
# -- Ps:装饰器还可以用类实现,具体参考面向对象高级部分中关于描述符的知识点!!
def singleton(cls):
    # -- 可以放到类里 cls.__isinstance
    #    注意:函数中约定一个下划线,类中约定两个下划线.
    _isinstance = cls(settings.IP, settings.PORT)
    
    def wrapper(*args, **kwargs):
        if args or kwargs:
            return cls(*args, **kwargs)
        # -- cls.__isinstance
        return _isinstance
      
    return wrapper

# -- 被装饰对象可以是任意可被调用的对象 类是可以调用的
@singleton
class Mysql:
    def __init__(self, ip, port):
        self.ip = ip
        self.port = port

obj1 = Mysql()
obj2 = Mysql()
obj3 = Mysql()
print(obj1 is obj2 is obj3)  # True
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 方式三:元类
import settings


class Mymeta(type):

    # -- 定义类Mysql时触发
    def __init__(self, class_name, class_bases, class_dic):  # -- self为Mysql
        # -- 在Mysql调用之前,事先先从配置文件中取配置来造一个Mysql的实例出来
        self.__instance = object.__new__(self)  # -- 造出一个Mysql的空对象
        self.__init__(self.__instance, settings.IP, settings.PORT)  # -- 加载配置文件初始化Mysql的空对象
        #    上述两步可以合成下面的任意一种写法(即上方的两行代码等效于下方的两行代码中的任意一条)
        #    建议不合成 难得理解 —_-""  此__call__是type中的, 看前面的·模版分析·就晓得咋个回事了.
        # self.__instance = super().__call__(settings.IP, settings.PORT)
        # self.__instance = type.__call__(self, settings.IP, settings.PORT)

        # -- 此行模版代码放到此函数的哪个位置都可以..
        #    因为type中的__call__方法里的__new__步骤已经创建好了Mymeta的空对象.
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)

    # -- Mysql调用时触发
    def __call__(self, *args, **kwargs):  # -- self为Mysql
        if args or kwargs:
            obj = self.__new__(self)
            self.__init__(obj, *args, **kwargs)
            return obj
        return self.__instance


class Mysql(object, metaclass=Mymeta):

    def __init__(self, ip, port):
        self.ip = ip
        self.port = port


# -- 没有传值则默认从配置文件中读配置来实例化,所有的实例应该指向一个内存地址
obj1 = Mysql()
obj2 = Mysql()
obj3 = Mysql()
print(obj1 is obj2 is obj3)  # True
obj4 = Mysql('127.0.0.1', 8000)
print(obj1 is obj4)  # False
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 补充

class MyType(type):
    def __init__(self, *args, **kwargs):
        print("2")
        super().__init__(*args, **kwargs)

    def __new__(cls, *args, **kwargs):
        print("1")
        new_cls = super().__new__(cls, *args, **kwargs)  # 创建类
        print(new_cls)  # <class '__main__.Foo'>
        return new_cls


class Foo(object, metaclass=MyType):  # 也遵循,先执行new再执行init
    pass


# 类创建对象先执行类中的new再执行init,对象加括号会执行类中的call方法
# 创建类的"东西"在创建类时也会先执行"东西"里的new再执行init,类加括号会执行"东西"里的call方法
# 意味着 类加括号会执行 "东西" 里的call方法??

# 先new后init是因为call里定义了的



根据类创建对象
1> 先执行__new__方法,创建空对象; ==> 构造方法
2> 执行类的__init__方法,初始化对象; ==> 初始化方法
Q: 对象是基于类创建的.那么类是由谁创建的? 
A: 类默认是由type创建的!!
class Foo(object):
    v1 = 123
    def func(self):
        return 666
等同于
Foo = type("Foo",(object,),{"v1":123,"func":lambda self:666})  # 类名、继承类、成员

type也是一个继承了object的类,type括号后面的三个参数是init的参数???

---
类默认是由type创建的,那如何让一个类的创建改成其他东西呢? 元类!!
元类,指定类由谁来创建!!通过metaclass=..来指定!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

反射、内置方法
异常处理

← 反射、内置方法 异常处理→

最近更新
01
deepseek本地部署+知识库
02-17
02
实操-微信小程序
02-14
03
教学-cursor深度探讨
02-13
更多文章>
Theme by Vdoing | Copyright © 2023-2025 DC | One Piece
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式