python模塊導入細節

来源:https://www.cnblogs.com/f-ck-need-u/archive/2018/11/13/9955485.html
-Advertisement-
Play Games

python模塊導入細節 官方手冊:https://docs.python.org/3/tutorial/modules.html 可執行文件和模塊 python源代碼文件按照功能可以分為兩種類型: 1. 用於執行的可執行程式文件 2. 不用與執行,僅用於被其它python源碼文件導入的模塊文件 例 ...


python模塊導入細節

官方手冊:https://docs.python.org/3/tutorial/modules.html

可執行文件和模塊

python源代碼文件按照功能可以分為兩種類型:

  1. 用於執行的可執行程式文件
  2. 不用與執行,僅用於被其它python源碼文件導入的模塊文件

例如文件a.py和b.py在同一目錄下,它們的內容分別是:

# b.py
x="var x in module b"
y=5

# a.py:
import b
import sys
print(b.x)
print(b.y)

a.py導入其它文件(b.py)後,就可以使用b.py文件中的屬性(如變數、函數等)。這裡,a.py就是可執行文件,b.py就是模塊文件,但模塊名為b,而非b.py。

python提供了一些標準庫,是預定義好的模塊文件,例如上面的sys模塊。

在此有幾個註意點,在後面會詳細解釋:

  1. 模塊b的文件名為b.py,但import導入的時候,使用的名稱為b,而非b.py
  2. a.py和b.py是在同一個目錄下的,如果不在同目錄下能否導入?
  3. 在a.py中訪問b.py模塊中的屬性時,使用的是b.xb.y
  4. 上面都是直接以模塊名導入的,python還支持更複雜的包導入方式,例如導入abc/b.py時,使用import abc.b。下一篇文章會詳細解釋包的導入方式

python模塊搜索路徑

在a.py中導入模塊b的時候,python會做一系列的模塊文件路徑搜索操作:b.py在哪裡?只有找到它才能讀取、運行(裝載)該模塊。

在任何一個python程式啟動時,都會將模塊的搜索路徑收集到sys模塊的path屬性中(sys.path)。當python需要搜索模塊文件在何處時,首先搜索內置模塊,如果不是內置模塊,則搜索sys.path中的路徑列表,搜索時會從該屬性列出的路徑中按照從前向後的順序進行搜索,並且只要找到就立即停止搜索該模塊文件(也就是說不會後搜索的同名模塊覆蓋先搜索的同名模塊)。

例如,在a.py文件中輸出一下這個屬性的內容:

# a.py:
import sys
print(sys.path)

結果:

['G:\\pycode', 'C:\\Program Files (x86)\\Python36-32\\python36.zip', 'C:\\Program Files (x86)\\Python36-32\\DLLs', 'C:\\Program Files (x86)\\Python36-32\\lib', 'C:\\Program Files (x86)\\Python36-32', 'C:\\Users\\malong\\AppData\\Roaming\\Python\\Python36\\site-packages', 'C:\\Program Files (x86)\\Python36-32\\lib\\site-packages']

python模塊的搜索路徑包括幾個方面,按照如下順序搜索:

  • 程式文件(a.py)所在目錄,即G:\\pycode
  • 環境變數PYTHONPATH所設置的路徑(如果定義了該環境變數,則從左向右的順序搜索)
  • 標準庫路徑
  • .pth文件中定義的路徑

需要註意,上面sys.path的結果中,除了.zip是一個文件外,其它的搜索路徑全都是目錄,也就是從這些目錄中搜索模塊X的文件X.py是否存在。

程式所在目錄

這個目錄是最先搜索的,且是python自動搜索的,無需對此進行任何設置。從互動式python程式終輸出sys.path的結果:

>>> sys.path
['', 'C:\\WINDOWS\\system32', 'C:\\Program Files (x86)\\Python36-32\\Lib\\idlelib', 'C:\\Program Files (x86)\\Python36-32\\python36.zip', 'C:\\Program Files (x86)\\Python36-32\\DLLs', 'C:\\Program Files (x86)\\Python36-32\\lib', 'C:\\Program Files (x86)\\Python36-32', 'C:\\Users\\malong\\AppData\\Roaming\\Python\\Python36\\site-packages', 'C:\\Program Files (x86)\\Python36-32\\lib\\site-packages']

其中第一個''表示的就是程式所在目錄。

註意程式所在目錄和當前目錄是不同的。例如,在/tmp/目錄下執行/pycode中的a.py文件

cd /tmp
python /pycode/a.py

其中/tmp為當前目錄,而/pycode是程式文件a.py所在的目錄。如果a.py中導入b.py,那麼將首先搜索/pycode,而不是/tmp。

環境變數PYTHONPATH

這個變數中可以自定義一系列的模塊搜索路徑列表,這樣可以跨目錄搜索(另一種方式是設置.pth文件)。但預設情況下這個環境變數是未設置的。

在windows下,設置PYTHONPATH環境變數的方式:命令行中輸入:SystemPropertiesAdvanced-->環境變數-->系統環境變數新建

如果是多個路徑,則使用英文格式的分號分隔。以下是臨時設置當前命令行視窗的PYTHONPATH:

set PYTHONPATH='D:\pypath; d:\pypath1'

在unix下,設置PYTHONPATH環境變數的方式,使用冒號分隔多個路徑:另外,必須得export導出為環境變數

export PYTHONPATH=/tmp/pypath1:/tmp/pypath2

如果要永久生效,則寫入配置文件中:

echo 'export PYTHONPATH=/tmp/pypath1:/tmp/pypath2' >/etc/profile.d/pypth.sh
chmod +x /etc/profile.d/pypth.sh
source /etc/profile.d/pypth.sh

標準庫路徑

在Linux下,標準庫的路徑一般是在/usr/lib/pythonXXX/下(XXX表示python版本號),此目錄下有些分了子目錄。

例如:

['', '/usr/lib/python35.zip', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-x86_64-linux-gnu', '/usr/lib/python3.5/lib-dynload', '/usr/local/lib/python3.5/dist-packages', '/usr/lib/python3/dist-packages']

其中/usr/lib/python3.5和其內的幾個子目錄都是標準庫的搜索路徑。

註意其中/usr/lib/python35.zip,它是ZIP文件組件,當定義此文件為搜索路徑時,將自動解壓縮該文件,並從此文件中搜索模塊。

Windows下根據python安裝位置的不同,標準庫的路徑不同。如果以預設路徑方式安裝的python,則標準庫路徑為C:\\Program Files (x86)\\Python36-32及其分類的子目錄。

.pth文件自定義路徑

可以將自定義的搜索路徑放進一個.pth文件中,每行一個搜索路徑。然後將.pth文件放在python安裝目錄或某個標準庫路徑內的sitepackages目錄下即可。

這是一種替換PYTHONPATH的友好方式,因為不同操作系統設置環境變數的方式不一樣,而以文件的方式記錄是所有操作系統都通用的。

例如,windows下,在python安裝目錄C:\\Program Files (x86)\\Python36-32下新增一個mypath.pth文件,內容如下:

d:\pypath1
d:\pypath2

再去輸出sys.path,將可以看到這兩個路徑已經放進了搜索列表中。

修改搜索路徑

除了上面環境變數和.pth文件,還可以直接修改sys.path或者site.getsitepackages()的結果。

例如,在import導入sys模塊之後,可以修改sys.path,向這個列表中添加其它搜索路徑,這樣之後導入其它模塊的時候,也會搜索該路徑。

例如:

import sys
sys.path.append('d:\\pypath3')
print(sys.path)

sys.path的最後一項將是新添加的路徑。

導入模塊的細節

導入模塊時的過程

python的import是在程式運行期間執行的,並非像其它很多語言一樣是在編譯期間執行。也就是說,import可以出現在任何地方,只有執行到這個import行時,才會執行導入操作。且在import某個模塊之前,無法訪問這個模塊的屬性。

python在import導入模塊時,首先搜索模塊的路徑,然後編譯並執行這個模塊文件。雖然概括起來只有兩個過程,但實際上很複雜。

前文已經解釋了import的模塊搜索過程,所以這裡大概介紹import的其它細節。

以前面的a.py中導入模塊文件b.py為例:

import b

import導入模塊時,搜索到模塊文件b.py後:

1.首先在記憶體中為每個待導入的模塊構建module類的實例:模塊對象。這個模塊對象目前是空對象,這個對象的名稱為全局變數b

註意細節:module類的對象,變數b。

輸出下它們就知道:

print(b)
print(type(b))

輸出結果:

<module 'b' from 'g:\\pycode\\b.py'>
<class 'module'>

因為b是全局變數,所以當前程式文件a.py中不能重新對全局變數b進行賦值,這會使導入的模塊b被丟棄。例如,下麵是錯誤的:

import b
b=3
print(b.x)   # 已經沒有模塊b了

另外,因為import導入時是將模塊對象賦值給模塊變數,所以模塊變數名不能是python中的一些關鍵字,比如if、for等,這時會報錯。雖然模塊文件名可以為list、keys等這樣的內置函數名,但這會導致這些內置函數不可用,因為根據變數查找的作用域規則,首先查找全局變數,再查找內置作用域。也就是說,模塊文件的文件名不能是這些關鍵字、也不應該是這些內置函數名

  File "g:/pycode/new.py", line 11
    import if
            ^
SyntaxError: invalid syntax

2.構造空模塊實例後,將編譯、執行模塊文件b.py,並按照一定的規則將一些結果放進這個模塊對象中

註意細節,編譯、執行b.py、將結果保存到模塊對象中。

模塊第一次被導入的時候,會進行編譯,並生成.pyc位元組碼文件,然後python執行這個pyc文件。當模塊被再次導入時,如果檢查到pyc文件的存在,且和源代碼文件的上一次修改時間戳mtime完全對應(也就是說,編譯後源代碼沒有進行過修改),則直接裝載這個pyc文件並執行,不會再進行額外的編譯過程。當然,如果修改過源代碼,將會重新編譯得到新的pyc文件。

註意,並非所有的py文件都會生成編譯得到的pyc文件,對於那些只執行一次的程式文件,會將記憶體中的編譯結果在執行完成後直接丟棄(多數時候如此,但仍有例外,比如使用compileall模塊可以強制編譯成pyc文件),但模塊會將記憶體中的編譯結果持久化到pyc文件中。另外,運行位元組碼pyc文件並不會比直接運行py文件更快,執行它也一樣是一行行地解釋、執行,唯一快的地方在於導入裝載的時候無需重新編譯而已。

執行模塊文件(已完成編譯)的時候,按照一般的執行流程執行:一行一行地、以代碼塊為單元執行。一般地,模塊文件中只用來聲明變數、函數等屬性,以便提供給導入它的模塊使用,而不應該有其他任何操作性的行為,比如print()操作不應該出現在模塊文件中,但這並非強制。

總之,執行完模塊文件後,這個模塊文件將有一個自己的全局名稱空間,在此模塊文件中定義的變數、函數等屬性,都會記錄在此名稱空間中。

最後,模塊的這些屬性都會保存到模塊對象中。由於這個模塊對象賦值給了模塊變數b,所以通過變數b可以訪問到這個對象中的屬性(比如變數、函數等),也就是模塊文件內定義的全局屬性。

只導入一次

假設a.py中導入了模塊b和模塊sys,在b.py中也導入了模塊sys,但python預設對某個模塊只會導入一次,如果a.py中先導入sys,再導入b,那麼導入b並執行b.py的時候,會發現sys已經導入了,不會再去導入sys。

實際上,python執行程式的時候,會將所有已經導入的模塊放進sys.module屬性中,這是一個dict,可以通過下麵的方式查看已導入的模塊名:

>>> import sys
>>> list(sys.module.keys())

如果某個程式文件中多次使用import(或from)導入同一個模塊,雖然不會報錯,但實際上還是直接使用記憶體中已裝載好的模塊對象。

例如,b.py中x=3,導入它之後修改該值,然後再次導入,發現b.x並不會發生改變:

import b
print(b.x)   # 3

b.x=33
print(b.x)  # 33

import b 
print(b.x)  # 33

但是python提供了reload進行多次重覆導入的方法,見後文。

使用別名

import導入時,可以使用as關鍵字指定一個別名作為模塊對象的變數,例如:

import b as bb
bb.x=3
print(bb.x)

這時候模塊對象將賦值給變數bb,而不是b,b此時不再是模塊對象變數,而僅僅只是模塊名。使用別名並不會影響性能,因為它僅僅只是一個賦值過程,只不過是從原來的賦值對象變數b變為變數bb而已。

from導入部分屬性

import語句是導入模塊中的所有屬性,並且訪問時需要使用模塊變數來引用。例如:

import b
print(b.x)

除了import,還有一個from語句,表示從模塊中導入部分指定的屬性,且使得可以直接使用這些屬性的名稱來引用這些屬性,而不需要加上模塊變數名。例如原來import導入時訪問變數x使用b.x,from導入時只需使用x即可。實際上,from導入更應該稱為屬性的再次賦值(拷貝)。

例如,b.py中定義了變數x、y、z,同時定義了函數f()和g(),在a.py中導入這個模塊文件,但只導入x變數和f函數:

# a.py文件內容:
from b import x,f

print(x)
f()

# b.py文件內容:
x=3
y=4
z=5
def f():
    print("function f in b.py")

def g():
    print("function g in b.py")

註意上面a.py中引用模塊b中屬性的方式沒有加上b.X,而是直接使用x和f()來引用。這和import是不一樣的。至於from和import導入時的變數名稱細節,在下麵的內容中會詳細解釋。

雖然from語句只導入模塊的部分屬性,但實際上仍然會完整地執行整個模塊文件。

同樣的,from語句也可以指定導入屬性的變數別名,例如,將b.py中的屬性x賦值給xx,將y賦值給yy:

from b import x as xx,y as yy
print(xx)
print(yy)

from語句還有一個特殊導入統配符號*,它表示導入模塊中的所有屬性。

# a.py文件:
from b import *
print(x,y,z)
f()
g()

多數時候,不應該使用from *的方式,因為我們可能會忘記某個模塊中有哪些屬性拷貝到了當前文件,特別是多個from *時可能會出現屬性覆蓋的問題。

重載模塊:imp.reload()

無論時import還是from,都只導入一次模塊,但使用reload()可以強制重新裝載模塊。

reload()是imp模塊中的一個函數,所以要使用imp.reload()之前,必須先導入imp。

from imp import reload
reload(b)

reload()是一個函數,它的參數是一個已經成功被導入過的模塊變數(如果使用了別名,則應該使用別名作為reload的參數),也就是說該模塊必須在記憶體中已經有自己的模塊對象。

reload()會重新執行模塊文件,並將執行得到的屬性完全覆蓋到原有的模塊對象中。也就是說,reload()會重新執行模塊文件,但不會在記憶體中建立新的模塊對象,所以原有模塊對象中的屬性可能會被修改

例如,模塊文件b.py中x=3,導入b模塊,修改其值為33,然後reload這個模塊,會發現值重新變回了3。

import b
print(b.x)  # 3

b.x=33
print(b.x)  # 33

from imp import reload
reload(b)

print(b.x)  # 3

有時候reload()很有用,可以讓程式無需重啟就執行新的代碼。例如,在python的互動式模式下導入模塊b,然後修改python源碼,再reload導入:

>>> import b
>>> b.x
3

# 不要關掉互動式解釋器,直接修改源代碼中的b=3333

>>> from imp import reload
>>> reload(b)
<module 'b' from 'G:\\pycode\\b.py'>
>>> b.x
3333

但正因為reload()重載模塊會改變原始的值,這可能是很危險的行為,一定要清楚地知道它是在乾什麼。

導入模塊時的變數名稱細節

import導入的變數

import導入時,模塊對象中的屬性有自己的名稱空間,然後將整個模塊對象賦值給模塊變數。

例如,在a.py中導入b:

import b
print(b.x)

這個過程唯一和當前文件a.py作用域有關的就是模塊對象變數b,b.py中聲明的屬性和當前文件無任何關係。無論是訪問還是修改,都是直接修改這個模塊對象自身作用域中的值。所以,只要模塊變數b不出現衝突問題,可以放心地修改模塊b中的屬性。

另一方面,因為每個進程都有自己的記憶體空間,所以在a.py、c.py中都導入b時,a.py中修改b的屬性值不會影響c.py中導入的屬性,a.py和c.py中模塊對象所保存的屬性都是執行b.py後得到的,它們相互獨立

from導入的變數

from導入模塊時,會先執行完模塊文件,然後將指定的部分屬性重新賦值給當前程式文件的同名全局變數。

例如,在模塊文件b.py中定義了x、y、z變數和f()、g()函數:

# b.py:
x=3
y=4
b=5
def f():
    print("function f in b.py")

def g():
    print("function g in b.py")

當在a.py中導入b模塊時,如果只導入x、y和f():

# a.py:
from b import x, y, f

實際上的行為是構造模塊對象後,將這個模塊對象對應的名稱空間中的屬性x、y和f重新賦值給a.py中的變數x、y和f,然後丟棄整個模塊對象以及整個名稱空間。換句話說,b不再是一個有效的模塊變數(所以和import不一樣),來自b的x,y,z,f和g也都被丟棄

這裡有幾個細節,需要詳細解釋清楚,只有理解了才能搞清楚它們是怎麼生效的。

假設現在模塊文件b.py的內容為,並且a.py中導入x,y,f屬性:

# b.py:
x=3
y=[1,2]
z=5
def f():
    print("function f in b.py")

def g():
    print("function g in b.py")

# a.py:
from b import x,y,f

首先在執行模塊文件b.py時,會構造好自己的模塊對象,並且模塊對象有自己的名稱空間(作用域),模塊對象構造完成後,它的名稱空間大致如下:

然後python會在a.py的全局作用域內創建和導入屬性同名的全局變數x,y和f,並且通過賦值的方式將模塊的屬性賦值給這些全局變數,也就是:

x = b.x
y = b.y
f = b.f

上面的b只是用來演示,實際上變數b是不存在的。

賦值完成後,我們和構造的整個模塊對象就失去聯繫了,因為沒有變數b去引用這個對象。但需要註意,這個對象並沒有被刪除,僅僅只是我們無法通過b去找到它。

所以,現在的示意圖如下:

因為是賦值的方式傳值的,所以在a.py中修改這幾個變數的值時,是直接在模塊對象作用域內修改的:對於不可變對象,將在此作用域內創建新對象,對於可變對象,將直接修改原始對象的值。

另一方面,由於模塊對象一直保留在記憶體中,下次繼續導入時,將直接使用該模塊對象。對於import和from,是直接使用該已存在的模塊對象,對於reload,是覆蓋此模塊對象。

例如,在a.py中修改不可變對象x和可變對象y,之後import或from時,可變對象的值都會隨之改變,因為它們使用的都是原來的模塊對象:

from b import x,y

x=33
y[0]=333

from b import x,y
print((x,y))        # 輸出(3, [333, 2])

import b
print((b.x,b.y))    # 輸出(3, [333, 2])

from導入時,由於b不再是模塊變數,所以無法再使用reload(b)去重載對象。如果想要重載,只能先import,再reload:

from b import x,y
...CODE...

# 想要重載b
import b
from imp import reload
reload(b)

查看模塊中的屬性

內置函數dir可用於列出某模塊中定義了哪些屬性。

import b
dir(b)

輸出結果:

['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f', 'g', 'x', 'y', 'z']

可見,模塊的屬性中除了自己定義的屬性外,還有一些內置的屬性,比如上面以__開頭和結尾的屬性。

如果dir()不給任何參數,則輸出當前環境下定義的名稱屬性:

>>> import b
>>> x=3
>>> aaa=333
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'aaa', 'b', 'x']

每個屬性都對應一個對象,例如x對應的是int對象,b對應的是module對象:

>>> type(x)
<class 'int'>
>>> type(b)
<class 'module'>

既然是對象,那麼它們都會有自己的屬性。例如:

>>> dir(x)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

所以,也可以直接dir某個模塊內的屬性:

import b
dir(b.x)
dir(b.__name__)

dir()不會列出內置的函數和變數,如果想要輸出內置的函數和變數,可以去標準模塊builtins中查看,因為它們定義在此模塊中:

import builtins
dir(buildins)

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 全指材料(SH000987) - 2018-11-13日,當前值:12.4646,平均值:30.54,中位數:26.09865,當前 接近歷史新低。全指材料(SH000987)的歷史市盈率PE詳情 內地地產(SH000948) - 2018-11-13日,當前值:8.2563,平均值:16.92,中 ...
  • 關於php的開發工具很多,目前市面上最好用最強大的莫過於PhpStorm這款開發神器了,但是鑒於很多開發者朋友在網站上下載的PhpStorm開發工具不能用,或者使用起來很不方便,筆者把最好用的下載地址及免費激活教程共用出來。 一、安裝並配置集成環境XAMPP 具體可以參考我的這篇博客:PHP開發環境 ...
  • ## 大綱: ## 一、正則概述 1.正則是什麼 正則就是一套規則,或者語法 2.正則的作用 讓我們判斷是否符合我們的的規則,或者根據規則找到符合規則的數據 3.使用場景 可以用正則判斷我們輸入的郵箱是否合法 可以用正則去獲取整個網頁的照片 4.適合語言 所有語言都可以通用 ## 二、正則表達式簡單 ...
  • 1 class ShenXian: # 神仙 2 3 def fei(self): 4 print("神仙都會⻜") 5 6 class Monkey: # 猴 7 8 def chitao(self): 9 print("猴⼦喜歡吃桃⼦") 10 11 class SunWukong(ShenXi ...
  • 解決方案: 在mybatis配置文件中聲明setting屬性的useActualParamName 參數值為false ** 這種方法解決mybatis3.4.2之後的版本產生該問題的解決方法** ...
  • 結論來看,是一個簡單又朴素的道理——打開文件句柄用完了得給關上。表現在現象上卻是著實讓人費解,以至於有人還懷疑起了微軟的Winodws系統來了,可笑至極。還是那句話,先把自己的屁股先給擦乾凈嘍再懷疑別人吧! 引申到另一個話題 ,一個較大型程式存在此類文件句柄耗盡的問題,該如何去排查呢?一個簡單原始的 ...
  • [toc] 引言 今天學習一個Java集合的一個抽象類 AbstractMap ,AbstractMap 是 Map 介面的 實現類之一,也是HashMap、TreeMap、ConcurrentHashMap 等的父類,它提供了 Map 介面中方法的基本實現(關於Map介面有疑惑的同學可參考 "Ja ...
  • 前言 在前文中我們瞭解了幾種常見的數據結構,這些數據結構有著各自的應用場景,並且被廣泛的應用於編程語言中,其中,Java中的集合類就是基於這些數據結構為基礎。 Java的集合類是一些非常實用的工具類,主要用於存儲和裝載數據 (包括對象),因此,Java的集合類也被成為容器。在Java中,所有的集合類 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...