[Python学习日记-70] 元类

[Python学习日记-70] 元类

简介

什么是元类

关键字 class 创建类的流程分析

自定义元类控制类的创建

自定义元类控制类的调用

自定义元类的属性查找

自定义元类的应用与练习

简介

        在上一篇章当中我们已经了解了面向对象的各种内置函数了,本篇我们将讲述“元类”,它是 Python 面向对象编程的深层次知识,学会了元类可以做到很多神奇的姿势,这次就带大家一起来探讨一下什么是元类,我们应该如何定制自己的元类,我们应该怎么调用我们自己的元类。

什么是元类

       在介绍什么是元类之前我们先定义一个类作为我们的分析对象,如下

class Chinese:    # Python3 中默认就是新式类,即 Chinese(object)country = 'China'def __init__(self,name,age):self.name = nameself.age = agedef talk(self):print('%s is talking' % self.name)c1 = Chinese('jove',28)
print(type(c1))    # 查看对象c1的类是 ——> <class '__main__.Chinese'>

代码输出如下:

        在前面的许多篇博客当中我们都提过一个特性:Python 中的一切皆为对象;而所有的对象都是实例化而得到的,就像上面的 c1 是调用类 Chinese 实例化后得到的,那对象可以怎么用呢?对象有以下四点特性:

  1. 都可以被引用,即 x = obj
  2. 都可以当作函数的参数传入
  3. 都可以作当函数的返回值
  4. 都可以当作容器类的元素,即 l = [func,time,obj,1,...]

        只要是对象就会拥有上面的四点特性,上面的 c1 很明显是一个对象了,其实我们所创建的类 Chinese 的本质也是一个对象,它也拥有以上四点特性。

        既然所有的对象都是调用类得到的,那么 Chinese 也必然是调用了一个类得到的,我们把这个被 Chinese 调用的类就称为元类,总的来说产生 Chinese 的过程一定发生了:Chinese = 元类(...),即产生类的类就是元类;我们可以通过以下代码查看 Chinese 的元类是什么,如下

print(type(Chinese))

代码输出如下:

        从输出来看 Chinese 的产生是调用了 type 这个元类,即默认的元类为 type,类与对象的产生过程如下图所示

关键字 class 创建类的流程分析

        在分析创建类的流程之前我们要先补充一下 exec 方法的用法,exec 方法在创建类时起到了关键的作用,它是用于执行动态生成的代码,并会生成相应的作用域/名称空间,exec 方法的语法如下

exec(code,globals_dic,locals_dic)

  • code:一系列python代码的字符串
  • globals_dic:全局作用域(字典形式),如果不指定,默认为globals()
  • ocals_dic:局部作用域(字典形式),如果不指定,默认为locals()

        我们可以把 exec 方法的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中,演示代码如下

g = {'x':1,'y':2
}l = {}exec("""
global x,m
x = 10
m = 100
z = 3
""",g,l)print(g)
print(l)

代码输出如下:

{'x': 10, 'y': 2, ..., 'm': 100}

{'z': 3}

        补充完 exec 方法的使用后我们书接上文,前面我们说到,使用关键字 class 创建的类 Chinese 本身也是一个对象,负责产生该对象的类被我们称之为元类,而在 Python 中内置的元类就是 type。关键字 class 在帮我们创建类的时候必然会帮我们调用元类 type,即 Chinese = type(...),元类 type 进行实例化的时候会依次传入以下三个参数,这三个参数就是类的关键组成部分,分别是

  1. 类名:class_name = 'Chinese'
  2. 基类(父类)们:class_bases = (object,)
  3. 类的名称空间:class_dic,类的名称空间是执行类体代码而得到的

        总的来说,关键字 class 帮我们创建一个类分为以下四个过程:

        到这里我们知道了,其实我们用关键字 class 创建的类也只是一个用元类 type 创建的对象而已,那也就是说其实我们也可以自己用元类 type 来创建类,并不需要使用关键字 class,总的来说在 Python 中定义类有两种方式:

  • 方式一:关键字 class 创建
  • 方式二:由元类 type 创建

        为了验证我们分析的正确性,我们分别使用两种创建类的方式来创建两个类来对比一下,代码如下

# 定义类的两种方式:
# 方式一: class
class Chinese:  # Chinese = type(...)country = 'China'def __init__(self,name,age):self.name = nameself.age = agedef talk(self):print('%s is talking' % self.name)obj = Chinese('jove',28)
print(obj,obj.name,obj.age)
# print(type(obj.talk))
# print(Chinese.__bases__)# 方式二: type
# 定义类的三要素
class_name = 'Chinese'  # 类名
class_bases = (object,)  # 基类(父类)
# 类里的代码
class_body = """
country = 'China'def __init__(self,name,age):self.name = nameself.age = agedef talk(self):print('%s is talking' % self.name)
"""class_dic = {}
exec(class_body,globals(),class_dic)  # 执行类里的代码,并把类里面的属性(非功能性代码)都放到dict --> locals() 里面
# print(class_dic)Chinese1 = type(class_name,class_bases,class_dic)  # 最后传入所需的要素到type()当中obj1 = Chinese1('jove',28)
print(obj1,obj1.name,obj1.age)

代码输出如下:

自定义元类控制类的创建

        经过前面一大轮的分析,我们已经清楚了 Python 当中默认的元类是 type,而我们能使用 metaclass 关键字参数为一个类指定元类,在默认的情况下如下

class Chinese(object,metaclass=type):    # 默认metaclass就等于typecountry = 'China'def __init__(self,name,age):self.name = nameself.age = agedef talk(self):print('%s is talking' % self.name)

        而我们可以通过继承 type 来自定义元类,然后使用 metaclass 关键字参数为一个类指定自定义元类即可,如下

class Mymeta(type):    # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类def __init__(self,class_name,class_bases,class_dic):print(class_name)print(class_bases)print(class_dic)super(Mymeta,self).__init__(class_name,class_bases,class_dic)    # 重用父类的功能class Chinese(object,metaclass=Mymeta):  # Chinese = Mymeta(class_name,class_bases,class_dic)country = 'China'def __init__(self,name,age):self.name = nameself.age = agedef talk(self):print('%s is talking' % self.name)

代码输出如下:

        从输出可以看出,在创建类 Chinese 的时候同时调用了自定义元类 Mymeta,从这可以看出自定义元类可以控制类的产生过程,而类的产生过程其实就是元类的调用过程,即 Chinese = Mymeta(class_name,class_bases,class_dic),在调用 Mymeta 时会先产生一个空对象 Chinese,然后连同调用 Mymeta 括号内的参数一同传给 Mymeta 下的 __init__ 方法来完成初始化,这样我们可以基于这个调用机制来做一些关于创建类的限制,例如限制类名的书写格式,代码如下

class Mymeta(type):def __init__(self,class_name,class_bases,class_dic):if not class_name.istitle():  # 类名开头必须为大写raise TypeError('类名的首字母必须大写')    # 异常处理后面会有专门的篇章介绍if '__doc__' not in class_dic or not class_dic['__doc__'].strip():  # strip()如果为空则自带布尔值 falseraise TypeError('类必须写注释,且不能为空')super(Mymeta,self).__init__(class_name,class_bases,class_dic)class Chinese(object,metaclass=Mymeta):    # Chinese = Mymeta(class_name,class_bases,class_dic)'''中国人的类'''country = 'China'def __init__(self,name,age):self.name = nameself.age = agedef talk(self):print('%s is talking' % self.name)cn = Chinese('jove',28)
cn.talk()

代码输出如下:

        当代码没有按照要求类名的首字母大写时

        当代码当中没有注释说明时

        当所有要求都符合时

自定义元类控制类的调用

        在学习自定义元类的调用之前我们需要先掌握 __call__ 方法的使用,这在之前已经介绍过了,可以点击链接查看。了解完 __call__ 方法之后,我们知道调用一个对象,就是触发对象所在类中的 __call__ 方法的执行,如果把 Chinese 也当做一个对象,那么在 Chinese 这个对象的类中也必然存在一个 __call__ 方法,即 Chinese 的元类里面也应该有一个 __call__ 方法,会在 Chinese() 调用时触发,如下

class Mymeta(type):def __init__(self,class_name,class_bases,class_dic):if not class_name.istitle():  # 类名开头必须为大写raise TypeError('类名的首字母必须大写')    # 异常处理后面会有专门的篇章介绍if '__doc__' not in class_dic or not class_dic['__doc__'].strip():  # strip()如果为空则自带布尔值 falseraise TypeError('类必须写注释,且不能为空')super(Mymeta,self).__init__(class_name,class_bases,class_dic)def __call__(self, *args, **kwargs):  # 如果没有写这个,将会找父类的__call__方法 obj = Chinese('egon', 18)print(self)  # self = Chineseprint(args)  # arge = ('jove',)print(kwargs)  # kwarge = {'age': 28}class Chinese(object,metaclass=Mymeta):    # Chinese = Mymeta(class_name,class_bases,class_dic)'''中国人的类'''country = 'China'def __init__(self,name,age):self.name = nameself.age = agedef talk(self):print('%s is talking' % self.name)cn = Chinese('jove',28)      # Chinese.__call__(Chinese,'jove',28)
print(cn)

代码输出如下:

        从输出结果来看,调用 Chinese 对象的时候就是在调用 Chinese 类中的 __call__ 方法,然后会把 Chinese 传递给 self,而溢出的位置参数和关键字参数分别传递给 *args 和 **kwargs,最后调用 Chinese 的返回值就是调用的 __call__ 方法的返回值,这里的 __call__ 方法没有指定返回值所以打印 cn 时就是 None。

        很明显的看出,我们自定义的 __call__ 还没有实现实例化对象 cn 的功能,那应该怎么做呢?默认地,在调用 cn = Chinese('jove',28) 时 __call__ 应该做以下三件事:

  1. 产生一个空对象 obj
  2. 调用 __init__ 方法初始化对象 obj
  3. 返回初始化好的 obj

        实现代码如下

class Mymeta(type):def __init__(self,class_name,class_bases,class_dic):if not class_name.istitle():  # 类名开头必须为大写raise TypeError('类名的首字母必须大写')    # 异常处理后面会有专门的篇章介绍if '__doc__' not in class_dic or not class_dic['__doc__'].strip():  # strip()如果为空则自带布尔值 falseraise TypeError('类必须写注释,且不能为空')super(Mymeta,self).__init__(class_name,class_bases,class_dic)def __call__(self, *args, **kwargs):  # 如果没有写这个,将会找父类的__call__方法 obj = Chinese('egon', 18)# 第一件事: 调用__new__造出一个空对象objobj = object.__new__(self)    # 此处的self是类Chinese,必须传参,代表创建一个Chinese的对象obj# 第二件事: 调用__init__方法初始化空对象objself.__init__(obj, *args, **kwargs)# 第三件事: 返回初始化好的 objreturn objclass Chinese(object,metaclass=Mymeta):    # Chinese = Mymeta(class_name,class_bases,class_dic)'''中国人的类'''country = 'China'def __init__(self,name,age):self.name = nameself.age = agedef talk(self):print('%s is talking' % self.name)cn = Chinese('jove',28)      # Chinese.__call__(Chinese,'jove',28)
print(cn)

代码输出如下:

         从输出结果来看已经看到 cn 已经变成了类 Chinese 的一个对象了,这个时候已经完成了实例化,而上面代码当中的 __call__ 其实只相当于一个模版而已,我们还能在此基础上改写 __call__ 的逻辑从而控制调用 Chinese 的过程,例如把 Chinese 实例化的对象的所有属性都变成私有属性,如下

class Mymeta(type):def __init__(self, class_name, class_bases, class_dic):if not class_name.istitle():  # 类名开头必须为大写raise TypeError('类名的首字母必须大写')  # 异常处理后面会有专门的篇章介绍if '__doc__' not in class_dic or not class_dic['__doc__'].strip():  # strip()如果为空则自带布尔值 falseraise TypeError('类必须写注释,且不能为空')super(Mymeta, self).__init__(class_name, class_bases, class_dic)def __call__(self, *args, **kwargs):  # 如果没有写这个,将会找父类的__call__方法 obj = Chinese('egon', 18)# 第一件事: 调用__new__造出一个空对象objobj = object.__new__(self)  # 此处的self是类Chinese,必须传参,代表创建一个Chinese的对象obj# 第二件事: 调用__init__方法初始化空对象objself.__init__(obj, *args, **kwargs)# 在初始化之后,obj.__dict__里就有值了print(obj.__dict__)obj.__dict__ = {'_%s__%s' % (self.__name__, k): v for k, v in obj.__dict__.items()}# 第三件事: 返回初始化好的 objreturn objclass Chinese(object, metaclass=Mymeta):  # Chinese = Mymeta(class_name,class_bases,class_dic)'''中国人的类'''country = 'China'def __init__(self, name, age):self.name = nameself.age = agedef talk(self):print('%s is talking' % self.name)cn = Chinese('jove', 28)  # Chinese.__call__(Chinese,'jove',28)
print(cn.__dict__)

代码输出如下:

自定义元类的属性查找

        到这里基本就介绍完元类了,在学习完元类之后再来看看结合了继承和元类之后的属性查找应该是怎么样的一个查找顺序呢?我们先来写一段代码,如下

class Mymeta(type):n=444def __call__(self, *args, **kwargs):obj=self.__new__(self)self.__init__(obj,*args,**kwargs)return objclass Bar(object):n=333class Foo(Bar):n=222class Chinese(Foo,metaclass=Mymeta):n=111country = 'China'def __init__(self, name, age):self.name = nameself.age = agedef talk(self):print('%s is talking' % self.name)print(Chinese.n)    # 自下而上依次注释各个类中的n=xxx,然后重新运行程序,发现n的查找顺序为Chinese -> Foo -> Bar -> object -> Mymeta -> type

代码输出如下:

        注释掉 Chinese 下的 n=111 后

·        注释掉 Foo 下的 n=222 后

        注释掉 Bar 下的 n=333 后

        最后注释掉 Mymeta 下的 n=444 后直接报错找不到了

        在前面我们学习过继承的实现原理,如果把类当成对象去看,上面代码的继承关系是:对象 Chinese 继承对象 Foo,对象 Foo 继承对象 Bar,对象 Bar 继承对象 object。我们应该把属性的查找分成两层,一层是对象层的查找,即基于 c3 算法的 MRO 方法调用顺序;另一层则是类层的查找,即对元类层的查找,查找顺序如下

当对对象中的属性进行查找时会按以下顺序进行查找:

  1. 对象层:Chinese -> Foo -> Bar -> object
  2. 元类层:Mymeta -> type

        通过上面的分析,我们现在知道在属性查找的时候元类也会参与其中,我们在之前使用 __call__ 方法实现实例化的时候用到了 self.__new__,下面我们来分析一下这个 __new__ 到底是调用了哪里的,我们先写下一段代码运行看看,如下

class Mymeta(type):n=444def __call__(self, *args, **kwargs):obj=self.__new__(self)print(self.__new__ is object.__new__)   # 当前面的__new__都注释掉之后这个就是True了class Bar(object):n=333# def __new__(cls, *args, **kwargs):#     print('Bar.__new__')class Foo(Bar):n=222# def __new__(cls, *args, **kwargs):#     print('Foo.__new__')class Chinese(Foo,metaclass=Mymeta):n=111country = 'China'def __init__(self, name, age):self.name = nameself.age = agedef talk(self):print('%s is talking' % self.name)# def __new__(cls, *args, **kwargs):#     print('Chinese.__new__')Chinese('jove',28)

代码输出如下:

        上面代码中注释的 __new__ 方法可以自己运行一下,这样可以更加清楚的看到 self.__new__ 的查找情况 。一番操作后发现 Mymeta 下的 __call__ 里的 self.__new__ 的查找顺序是 Chinese -> Foo -> Bar -> object,而且经过 self.__new__ 和 object.__new__ 的比对之后发现这两个是一样的,那就是说 self.__new__ 的查找并没有找到元类当中,而是会去找 object 里的 __new__,而 object 下默认就有一个 __new__,所以即便是之前的类均未实现 __new__,也一定会在 object 中找到一个,根本不会再去找元类 Mymeta 和 type 中查找 __new__。

        那我们是否可以在元类的 __call__ 中用 object.__new__(self) 代替 self.__new__(self) 去造对象呢?原则上是可以的,因为通过属性查找最终还是会找到 object.__new__,但是并不推荐这样做,因为直接使用 object.__new__ 会直接跳过之前的 Chinese、Foo 和 Bar 三个类的检索,如果后期在他们当中想做 __new__ 的自定义的话会造成一定的麻烦。

        那什么情况下才会去元类层查找 __new__ 呢?在产生类 Chinese 的过程就是在调用 Mymeta,而 Mymeta 也是 type 类的一个对象,那么 Mymeta 之所以可以调用,一定是在元类 type 中也有一个 __call__ 方法,而这个 __call__ 方法也同样需要做至少三件事,如下

class type:def __call__(self, *args, **kwargs):    # self=<class '__main__.Mymeta'>obj=self.__new__(self,*args,**kwargs)    # 产生Mymeta的一个对象self.__init__(obj,*args,**kwargs) return obj

        这个时候 type 中的 self.__new__ 进行检索的时候就会先对 Mymeta 进行检索,然后再对 type 进行检索,所以我们可以通过这个逻辑来定制我们的自定义元类 Mymeta,如下

class Mymeta(type):n=444def __new__(cls, *args, **kwargs):obj=type.__new__(cls,*args,**kwargs) # 必须按照这种传值方式print(obj.__dict__)return obj  # 只有在返回值是type的对象时,才会触发下面的__init__# return 123def __init__(self,class_name,class_bases,class_dic):print('run。。。')class Chinese(Foo,metaclass=Mymeta):n=111country = 'China'def __init__(self, name, age):self.name = nameself.age = agedef talk(self):print('%s is talking' % self.name)print(type(Mymeta))

代码输出如下:

        当返回 type 的对象时

        当返回的不是 type 的对象时

自定义元类的应用与练习

一、单例模式

        在详细介绍完元类之后我们再来讲讲元类的一些实际应用——单例模式。什么是单例模式呢?在 Python 当中我们定义两个不同的变量,但是值是一样的,如下

obj1 = int(1)    # obj1 = 1
obj2 = int(1)    # obj2 = 1
print(obj1 is obj2)

代码输出如下:

        obj1 和 obj2 两个对象是指向相同的内存地址的,而单例模式要做的事就是把相同特征的对象只产生一个内存地址,从而节约内存空间,下面我们会用两种方式来实现单例模式,如下

配置文件 setting.py:

HOST = '127.0.0.1'
PORT = 1000

实现方式一:不使用元类

import settingsclass MySQL:__instance = Nonedef __init__(self,host,port):self.host = hostself.port = port@classmethoddef singleton(cls):if not cls.__instance:  # 这样的赋值方法只适用于self的特征写死的情况下obj = cls(settings.HOST,settings.PORT)cls.__instance = objreturn cls.__instancedef conn(self):passdef execute(self):pass# 对象的特征(属性)不同,内存地址不同
obj1 = MySQL('1.1.1.2',3306)
obj2 = MySQL('1.1.1.3',3307)
print(obj1 is obj2)# 对象的特征(属性)相同,内存地址相同
obj1 = MySQL.singleton()
obj2 = MySQL.singleton()
print(obj1 is obj2)

代码输出如下:

实现方式二:使用元类来实现

import settingsclass Mymeta(type):def __init__(self,name,bases,dic): #定义类Mysql时就触发# 事先先从配置文件中取配置来造一个Mysql的实例出来self.__instance = object.__new__(self)  # 产生对象self.__init__(self.__instance, settings.HOST, settings.PORT)  # 初始化对象# 上述两步可以合成下面一步# self.__instance=super().__call__(*args,**kwargs)super().__init__(name,bases,dic)def __call__(self, *args, **kwargs): #Mysql(...)时触发if args or kwargs: # args或kwargs内有值obj=object.__new__(self)self.__init__(obj,*args,**kwargs)return objreturn self.__instanceclass Mysql(metaclass=Mymeta):def __init__(self,host,port):self.host=hostself.port=portobj1=Mysql() # 没有传值则默认从配置文件中读配置来实例化,所有的实例应该指向一个内存地址
obj2=Mysql()
obj3=Mysql()print(obj1 is obj2 is obj3)obj4=Mysql('1.1.1.4',3307)
print(obj4 is obj1)

代码输出如下:

二、在元类中控制把自定义类的数据属性都变成大写

class Mymeta(type):def __new__(cls, class_name, class_base, class_dict):update_class_dict = {}''.endswith('__')for k in class_dict:if not callable(class_dict[k]) and not k.startswith('__') and not k.startswith('_'):update_class_dict[k.upper()] = class_dict[k]else:update_class_dict[k] = class_dict[k]return type.__new__(cls, class_name, class_base, update_class_dict)class Chinese(metaclass=Mymeta):country = 'China'tag = 'Legend of the Dragon'  # 龙的传人__ismarry = 'yes'def __init__(self):self.name = 'Zou'def walk(self):print('%s is walking' % self.name)print(Chinese.__dict__)

代码输出如下:

{'__module__': '__main__', 'COUNTRY': 'China', 'TAG': 'Legend of the Dragon', '_Chinese__ismarry': 'yes', '__init__': <function Chinese.__init__ at 0x000001A082A48B80>, 'walk': <function Chinese.walk at 0x000001A082A499E0>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None}

三、在元类中控制自定义的类无需 __init__ 方法

  1. 元类帮其完成创建对象,以及初始化操作
  2. 要求实例化时传参必须为关键字形式,否则抛出异常 TypeError: must use keyword argument
  3. key 作为用户自定义类产生对象的属性,且所有属性变成大写
class Mymeta(type):def __call__(self, *args, **kwargs):if args:raise TypeError('must use keyword argument for key function')obj = object.__new__(self)for k in kwargs:obj.__dict__[k.upper()] = kwargs[k]return objclass Chinese(metaclass=Mymeta):country = 'China'tag = 'Legend of the Dragon'  # 龙的传人__ismarry = 'yes'def walk(self):print('%s is walking' % self.name)obj1 = Chinese(name = 'jove',age = 28)
print(obj1.__dict__)

代码输出如下:

四、在元类中控制自定义的类产生的对象相关的属性全部为隐藏属性

class Mymeta(type):def __call__(self, *args, **kwargs):# 控制Chinese对象的创建过程if args:raise TypeError('must use keyword argument for key function')obj = object.__new__(self)for k in kwargs:obj.__dict__['_%s__%s' % (self.__name__, k)] = kwargs[k]return objclass Chinese(metaclass=Mymeta):country = 'China'tag = 'Legend of the Dragon'  # 龙的传人__ismarry = 'yes'def walk(self):print('%s is walking' % self.name)p = Chinese(name = 'jove',age = 28)
print(p.__dict__)

代码输出如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/62189.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构题库11

第五章 树和二叉树 一、单项选择题 1&#xff0e;关于二叉树的下列说法正确的是 (1)。 (1)&#xff1a;A&#xff0e;二叉树的度为2 B&#xff0e;二叉树的度可以小于2 C&#xff0e;每一个结点的度都为2 D&#xff0e;至少有一个结点的度为 2&#xff0e;设深度为h(h>0)的二…

学生公寓智能限电系统的功能和作用

学生公寓智能限电系统‌是一种用于管理和限制学生公寓用电的设备和技术&#xff0c;旨在确保用电安全、防止火灾事故&#xff0c;并促进节能减排。以下是关于学生公寓智能限电系统的详细介绍&#xff1a; 1、功能和作用 智能限电系统通过以下功能来管理和限制用电&#xff1a…

MYSQL PARTITIONING分区操作和性能测试

PARTITION OR NOT PARTITION IN MYSQl Bill Karwin says “In most circumstances, you’re better off using indexes instead of partitioning as your main method of query optimization.” According to RICK JAMES: “It is so tempting to believe that PARTITIONing wi…

基于 AutoFlow 快速搭建基于 TiDB 向量搜索的本地知识库问答机器人

导读 本文将详细介绍如何通过 PingCAP 开源项目 AutoFlow 实现快速搭建基于 TiDB 的本地知识库问答机器人。如果提前准备好 Docker、TiDB 环境&#xff0c;整个搭建过程估计在 10 分钟左右即可完成&#xff0c;无须开发任何代码。 文中使用一篇 TiDB 文档作为本地数据源作为示…

基于XML的AOP开发

AOP 为 Aspect Oriented Programming 的缩写&#xff0c;意思为面向切面编程。 AOP相关术语&#xff1a; 目标对象(Target)&#xff1a; 你要去代理的对象&#xff0c;可以理解为之前很单纯的那个对象。 代理对象(Proxy)&#xff1a; 你把你那个单纯的对象给我&#xff0c…

记录blender学习过程中遇到的问题

物体发射的方向不对 被发射物体&#xff08;例如一棵树&#xff09;n键看旋转归0 切换正视图 将被发射物体的局部坐标的Z轴 指向 全局方向的X轴时 并且把粒子系统设置的物体旋转勾选上 方向就对了 做倒角发现有问题 检查缩放应用、面朝向、有没有重合点&#xff08;融合点&am…

Ubuntu系统中Redis的安装步骤及服务配置

目录 内容概括 系统环境 安装方式 1、apt包管理器安装 &#xff08;1&#xff09;安装redis服务 &#xff08;2&#xff09;安装客户端&#xff08;进入命令行操作使用&#xff0c;包含redis-cli&#xff09; &#xff08;3&#xff09;安装检验 &#xff08;4&#xf…

半导体设备中的微型导轨应如何选择合适的润滑油?

微型导轨的润滑对于保证其高精度和高稳定性至关重要&#xff0c;尤其是在半导体设备中&#xff0c;微型导轨的润滑油选择需要考虑多个因素&#xff0c;以确保设备的最佳性能和寿命。以下是一些关键点&#xff1a; 1、黏度&#xff1a;润滑油的黏度是影响其流动性和润滑效果的重…

RocketMq详解:六、RocketMq的负载均衡机制

上一章&#xff1a;《SpringBootAop实现RocketMq的幂等》 文章目录 1.背景1.1 什么是负载均衡1.2 负载均衡的意义 2.RocketMQ消息消费2.1 消息的流转过程2.2 Consumer消费消息的流程 3.RocketMq的负载均衡策略3.1 Broker负载均衡3.2 Producer发送消息负载均衡3.3 消费端的负载均…

主打极致性价比,AMD RX 8600/8800显卡定了

*以下内容仅为网络爆料及传闻&#xff0c;一切以官方消息为准。 这谁能想到&#xff0c;率先掏出下一代桌面独立显卡的不是老大哥 NVIDIA&#xff0c;也不是 AMD&#xff0c;反而是三家中存在感最弱的 Intel&#xff01; 就在 12 月 3 日&#xff0c;Intel 正式发布了自家第二…

npm, yarn, pnpm之间的区别

前言 在现代化的开发中&#xff0c;一个人可能同时开发多个项目&#xff0c;安装的项目越来越多&#xff0c;所随之安装的依赖包也越来越臃肿&#xff0c;而且有时候所安装的速度也很慢&#xff0c;甚至会安装失败。 因此我们就需要去了解一下&#xff0c;我们的包管理器&#…

C语言连接数据库

文章目录 一、初始化数据库二、创建数据库连接三、执行增删改查语句1、增删改2、查 四、执行增删改查语句 接下来我简单的介绍一下怎么用C语言连接数据库。 初始化数据库创建数据库连接执行增删改查语句关闭数据库连接 一、初始化数据库 // 数据库初始化 MYSQL mysql; MYSQL* r…

优化LabVIEW数据运算效率的方法

在LabVIEW中进行大量数据运算时&#xff0c;提升计算效率并减少时间占用是开发过程中常遇到的挑战。为此&#xff0c;可以从多个角度着手优化&#xff0c;包括合理选择数据结构与算法、并行处理、多线程技术、硬件加速、内存管理和界面优化等。通过采用这些策略&#xff0c;可以…

计算机的错误计算(一百七十六)

摘要 利用某一大语言模型计算 的值&#xff0c;输出为 0 . 例1. 在某一大语言模型下&#xff0c;计算 的值。其中sin中值取弧度。结果保留16位有效数字。 直接贴图吧&#xff1a; 点评&#xff1a; &#xff08;1&#xff09;以上为一个大模型给的答案。从其回答可知&…

数据结构与算法——1204—递归分治法

1、斐波那契数列优化 使用滚动变量&#xff0c;保存当前计算结果和前两项值 (1)RAB (2)更新计算对象&#xff0c;AB&#xff0c;BR #include<iostream> using namespace std;int fun(int n) {if (n 0)return 0;if (n 1 || n 2)return 1;int num11;int num21;int su…

openstack内部rpc消息通信源码分析

我们知道openstack内部消息队列基于AMQP协议&#xff0c;默认使用的rabbitmq 消息队列。谈到rabbitmq&#xff0c;大家或许并不陌生&#xff0c;但或许会对oslo message有些陌生。openstack内部并不是直接使用rabbitmq&#xff0c;而是使用了oslo.message 。oslo.message 后端的…

Postman自定义脚本Pre-request-script以及Test

这两个都是我们进行自定义script脚本的地方&#xff0c;分别是在请求执行的前后运行。 我们举两个可能经常运用到的场景。 (一)请求A先执行&#xff0c;请求B使用请求A响应结果作为参数。如果我们不用自定义脚本&#xff0c;可能得先执行请求A&#xff0c;然后手动复制响应结果…

总结的一些MySql面试题

目录 一&#xff1a;基础篇 二&#xff1a;索引原理和SQL优化 三&#xff1a;事务原理 四&#xff1a;缓存策略 一&#xff1a;基础篇 1&#xff1a;定义&#xff1a;按照数据结构来组织、存储和管理数据的仓库&#xff1b;是一个长期存储在计算机内的、有组织的、可共享 的…

116. UE5 GAS RPG 实现击杀掉落战利品功能

这一篇&#xff0c;我们实现敌人被击败后&#xff0c;掉落战利品的功能。首先&#xff0c;我们将创建一个新的结构体&#xff0c;用于定义掉落体的内容&#xff0c;方便我们设置掉落物。然后&#xff0c;我们实现敌人死亡时的掉落函数&#xff0c;并在蓝图里实现对应的逻辑&…

Excel技巧:如何批量调整excel表格中的图片?

插入到excel表格中的图片大小不一&#xff0c;如何做到每张图片都完美的与单元格大小相同&#xff1f;并且能够根据单元格来改变大小&#xff1f;今天分享&#xff0c;excel表格里的图片如何批量调整大小。 方法如下&#xff1a; 点击表格中的一个图片&#xff0c;然后按住Ct…