[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.pswp.cn/web/62189.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/62189.shtml
英文地址,請注明出處:http://en.pswp.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)的二…

【學習路線】Java

Java基礎 基礎 基礎語法 面向對象 集合框架 JCF 進階 并發編程 JVM 企業級開發 框架 Spring Boot Spring Cloud 分布式 高性能 高可用 安全 基建 Docker 實戰 數據庫 MySQL Redis 計算機基礎 計算機組成原理 操作系統 計算機網絡 數據結構與算法 設計模式 參考&#xff1a;…

學生公寓智能限電系統的功能和作用

學生公寓智能限電系統?是一種用于管理和限制學生公寓用電的設備和技術&#xff0c;旨在確保用電安全、防止火災事故&#xff0c;并促進節能減排。以下是關于學生公寓智能限電系統的詳細介紹&#xff1a; 1、功能和作用 智能限電系統通過以下功能來管理和限制用電&#xff1a…

【開發語言】層次狀態機(HSM)介紹

層次狀態機&#xff08;Hierarchical State Machine, HSM&#xff09;&#xff0c;從基本原理、結構設計、實現方法以及如何結合 Qt 進行具體實現等方面進行分析。 1. 層次狀態機的基本原理 層次狀態機是一種用于管理復雜系統行為的狀態機模型&#xff0c;它通過將狀態組織成…

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…

深入解析 Loss 減少方式:mean和sum的區別及其在大語言模型中的應用 (中英雙語)

深入解析 Loss 減少方式&#xff1a;mean 和 sum 的區別及其在大語言模型中的應用 在訓練大語言模型&#xff08;Large Language Models, LLM&#xff09;時&#xff0c;損失函數&#xff08;Loss Function&#xff09;的處理方式對模型的性能和優化過程有顯著影響。本文以 re…

基于 AutoFlow 快速搭建基于 TiDB 向量搜索的本地知識庫問答機器人

導讀 本文將詳細介紹如何通過 PingCAP 開源項目 AutoFlow 實現快速搭建基于 TiDB 的本地知識庫問答機器人。如果提前準備好 Docker、TiDB 環境&#xff0c;整個搭建過程估計在 10 分鐘左右即可完成&#xff0c;無須開發任何代碼。 文中使用一篇 TiDB 文檔作為本地數據源作為示…

生信技能63 - 構建gnomAD變異位點的SQLite查詢數據庫

將數據量巨大的gnomAD數據庫,通過SQLite數據庫尋找gnomAD中存在的各種變異注釋信息(如等位基因計數,深度,次要等位基因頻率等),查詢300.000個變量的查詢需要大約40秒,通過染色體編號+位置+REF+ALT即可進行快速查詢。 1. gnomAD變異注釋VCF文件字段 gnomAD VCF各版本包…

【前端】將vue的方法掛載到window上供全局使用,也方便跟原生js做交互

【前端】將vue的方法掛載到window上供全局使用&#xff0c;也方便跟原生js做交互 <template><div><el-button click"start">調用方法</el-button></div> </template> <script> // import { JScallbackProc } from ./JScal…

基于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 消費端的負載均…

yocto的xxx.bb文件在什么時候會拷貝文件到build目錄

在 Yocto 中&#xff0c;.bb 文件用于描述如何構建和安裝一個軟件包&#xff0c;而文件在構建過程中的拷貝操作通常會在某些特定的步驟中進行。具體來說&#xff0c;文件會在以下幾個階段被拷貝到 build 目錄&#xff08;或者更準確地說&#xff0c;拷貝到目標目錄 ${D}&#x…

主打極致性價比,AMD RX 8600/8800顯卡定了

*以下內容僅為網絡爆料及傳聞&#xff0c;一切以官方消息為準。 這誰能想到&#xff0c;率先掏出下一代桌面獨立顯卡的不是老大哥 NVIDIA&#xff0c;也不是 AMD&#xff0c;反而是三家中存在感最弱的 Intel&#xff01; 就在 12 月 3 日&#xff0c;Intel 正式發布了自家第二…

數組哪些方法會觸發Vue監聽,哪些不會觸發監聽

發現寶藏 前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。【寶藏入口】。 在 Vue 中&#xff0c;數組的變化是通過 響應式 系統來監聽的。Vue 使用 getter 和 setter 來追蹤數組的變化&#xff0c;并在數…

npm, yarn, pnpm之間的區別

前言 在現代化的開發中&#xff0c;一個人可能同時開發多個項目&#xff0c;安裝的項目越來越多&#xff0c;所隨之安裝的依賴包也越來越臃腫&#xff0c;而且有時候所安裝的速度也很慢&#xff0c;甚至會安裝失敗。 因此我們就需要去了解一下&#xff0c;我們的包管理器&#…

工業檢測基礎-工業相機選型及應用場景

以下是一些常見的工業檢測相機種類、檢測原理、應用場景及選型依據&#xff1a; 2D相機 檢測原理&#xff1a;基于二維圖像捕獲&#xff0c;通過分析圖像的明暗、紋理、顏色等信息來檢測物體的特征和缺陷.應用場景&#xff1a;廣泛應用于平面工件的外觀檢測&#xff0c;如檢測…

C語言連接數據庫

文章目錄 一、初始化數據庫二、創建數據庫連接三、執行增刪改查語句1、增刪改2、查 四、執行增刪改查語句 接下來我簡單的介紹一下怎么用C語言連接數據庫。 初始化數據庫創建數據庫連接執行增刪改查語句關閉數據庫連接 一、初始化數據庫 // 數據庫初始化 MYSQL mysql; MYSQL* r…