Python:What the f*ck Python(上)

来源:https://www.cnblogs.com/gl1573/archive/2018/12/08/10086353.html
-Advertisement-
Play Games

GitHub 上有一個名為《What the f*ck Python!》的項目,這個有趣的項目意在收集 Python 中那些難以理解和反人類直覺的例子以及鮮為人知的功能特性, 並嘗試討論這些現象背後真正的原理! ...


GitHub 上有一個名為《What the f*ck Python!》的項目,這個有趣的項目意在收集 Python 中那些難以理解和反人類直覺的例子以及鮮為人知的功能特性,並嘗試討論這些現象背後真正的原理!
原版地址:https://github.com/satwikkansal/wtfpython

最近,一位名為“暮晨”的貢獻者將其翻譯成了中文。
中文版地址:https://github.com/leisurelicht/wtfpython-cn

我將所有代碼都親自試過了,加入了一些自己的理解和例子,所以會和原文稍有不同

1. Strings can be tricky sometimes

>>> a = '!'
>>> b = '!'
>>> a is b
True

>>> a = 'some_string'
>>> id(a)
140420665652016
>>> id('some' + '_' + 'string') # 註意兩個的id值是相同的.
140420665652016

>>> a = 'wtf'
>>> b = 'wtf'
>>> a is b
True

>>> a = 'wtf!'
>>> b = 'wtf!'
>>> a is b
False

>>> a, b = 'wtf!', 'wtf!'
>>> a is b
True

>>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
True
>>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
False

說明:
這些行為是由於 Cpython 在編譯優化時,某些情況下會嘗試使用已經存在的不可變對象而不是每次都創建一個新對象。(這種行為被稱作字元串的駐留[string interning])。發生駐留之後, 許多變數可能指向記憶體中的相同字元串對象從而節省記憶體。

有一些方法可以用來猜測字元串是否會被駐留:

  • 所有長度為 0 和長度為 1 的字元串都被駐留(①中字元串被駐留)
  • 字元串在編譯時被實現('wtf' 將被駐留,但是 ''.join(['w', 't', 'f'] 將不會被駐留)
  • 字元串中只包含字母、數字或下劃線時將會駐留,所以 'wtf!' 由於包含 '!' 而未被駐留
  • 當在同一行將 a 和 b 的值設置為 "wtf!" 的時候,Python 解釋器會創建一個新對象,然後兩個變數同時指向這個對象。如果你在不同的行上進行賦值操作,它就不會“知道”已經有一個 wtf! 對象(因為 "wtf!" 不是按照上面提到的方式被隱式駐留的)。
  • 常量摺疊(constant folding)是 Python 中的一種窺孔優化(peephole optimization)技術。這意味著在編譯時表達式 'a' * 20 會被替換為 'aaaaaaaaaaaaaaaaaaaa' 以減少運行時的時鐘周期。只有長度小於 20 的字元串才會發生常量摺疊。(為啥?想象一下由於表達式 'a' * 10 ** 10 而生成的 .pyc 文件的大小)。

如果你在 .py 文件中嘗試這個例子,則不會看到相同的行為,因為文件是一次性編譯的。

2. Time for some hash brownies!

>>> some_dict = {}
>>> some_dict[5.5] = "Ruby"
>>> some_dict[5.0] = "JavaScript"
>>> some_dict[5] = "Python"

>>> some_dict[5.5]
"Ruby"
>>> some_dict[5.0]
"Python"
>>> some_dict[5]
"Python"

說明:
Python 字典檢查鍵值是否相等是通過比較哈希值是否相等來確定的。如果兩個對象在比較的時候是相等的,那它們的散列值必須相等,否則散列表就不能正常運行了。例如,如果 1 == 1.0 為真,那麼 hash(1) == hash(1.0) 必須也為真,但其實兩個數字(整數和浮點數)的內部結構是完全不一樣的。

3. Return return everywhere!

def some_func():
    try:
        return 'from_try'
    finally:
        return 'from_finally'

Output:

>>> some_func()
'from_finally'

說明:
函數的返回值由最後執行的 return 語句決定。由於 finally 子句一定會執行,所以 finally 子句中的 return 將始終是最後執行的語句。

4. Deep down, we're all the same.

class WTF:
    pass

Output:

>>> WTF() == WTF() # 兩個不同的對象應該不相等
False
>>> WTF() is WTF() # 也不相同
False
>>> hash(WTF()) == hash(WTF()) # 哈希值也應該不同
True
>>> id(WTF()) == id(WTF())
True

說明:
當調用 id 函數時,Python 創建了一個 WTF 類的對象並傳給 id 函數,然後 id 函數獲取其 id 值(也就是記憶體地址),然後丟棄該對象,該對象就被銷毀了。

當我們連續兩次進行這個操作時,Python會將相同的記憶體地址分配給第二個對象,因為在 CPython 中 id 函數使用對象的記憶體地址作為對象的id值,所以兩個對象的id值是相同的。

綜上,對象的 id 值僅僅在對象的生命周期內唯一,在對象被銷毀之後或被創建之前,其他對象可以具有相同的id值。

class WTF(object):
  def __init__(self): print("I")
  def __del__(self): print("D")

Output:

>>> WTF() is WTF()
I
I
D
D
False
>>> id(WTF()) == id(WTF())
I
D
I
D
True

正如你所看到的,對象銷毀的順序是造成所有不同之處的原因。

5. For what?

>>> some_string = "wtf"
>>> some_dict = {}
>>> for i, some_dict[i] in enumerate(some_string): pass
>>> some_dict
{0: 'w', 1: 't', 2: 'f'}

說明:
這一條仔細看一下很好理解,for 迴圈每次迭代都會給分配目標賦值,some_dict[i] = value 就相當於給字典添加鍵值對了。
有趣的是下麵這個例子,你可曾覺得這個迴圈只會運行一次?

for i in range(4):
    print(i)
    i = 10

6. Evaluation time discrepancy

>>> array = [1, 8, 15]
>>> g = (x for x in array if array.count(x) > 0)
>>> array = [2, 8, 22]
>>> list(g)
[8]

>>> array_1 = [1, 2, 3, 4]
>>> g1 = (x for x in array_1)
>>> array_1 = [1, 2, 3, 4, 5]

>>> array_2 = [1, 2, 3, 4]
>>> g2 = (x for x in array_2)
>>> array_2[:] = [1, 2, 3, 4, 5]

>>> list(g1)
[1, 2, 3, 4]

>>> list(g2)
[1, 2, 3, 4, 5]

說明:
在生成器表達式中 in 子句在聲明時執行,而條件子句則是在運行時執行。
①中,在運行前 array 已經被重新賦值為 [2, 8, 22],因此對於之前的 1, 8, 15,只有 count(8) 的結果是大於 0 ,所以生成器只會生成 8。
②中,g1 和 g2 的輸出差異則是由於變數 array_1 和 array_2 被重新賦值的方式導致的。

  • 在第一種情況下,array_1 被綁定到新對象 [1, 2, 3, 4, 5],因為 in 子句是在聲明時被執行的,所以它仍然引用舊對象 [1, 2, 3, 4](並沒有被銷毀)。
  • 在第二種情況下,對 array_2 的切片賦值將相同的舊對象 [1, 2, 3, 4] 原地更新為 [1, 2, 3, 4, 5]。因此 g2 和 array_2 仍然引用同一個對象[1, 2, 3, 4, 5]。

7. is is not what it is!

>>> a = 256
>>> b = 256
>>> a is b
True

>>> a = 257
>>> b = 257
>>> a is b
False

>>> a = 257; b = 257
>>> a is b
True

說明:
is 和 == 的區別

  • is 運算符檢查兩個運算對象是否引用自同一對象
  • == 運算符比較兩個運算對象的值是否相等

因此 is 代表引用相同,== 代表值相等。下麵的例子可以很好的說明這點:

>>> [] == []
True
>>> [] is []  # 這兩個空列表位於不同的記憶體地址
False

256 是一個已經存在的對象,而 257 不是
當啟動 Python 的時候,-5 到 256 的數值就已經被分配好了。這些數字因為經常使用所以適合被提前準備好。

當前的實現為 -5 到 256 之間的所有整數保留一個整數對象數組,當你創建了一個該範圍內的整數時,你只需要返回現有對象的引用。所以改變 1 的值是有可能的。

但是,當 a 和 b 在同一行中使用相同的值初始化時,會指向同一個對象。

>>> id(256)
10922528
>>> a = 256
>>> b = 256
>>> id(a)
10922528
>>> id(b)
10922528
>>> id(257)
140084850247312

>>> x = 257
>>> y = 257
>>> id(x)
140084850247440
>>> id(y)
140084850247344

>>> a, b = 257, 257
>>> id(a)
140640774013296
>>> id(b)
140640774013296

這是一種特別為互動式環境做的編譯器優化,當你在實時解釋器中輸入兩行的時候,他們會單獨編譯,因此也會單獨進行優化, 如果你在 .py 文件中嘗試這個例子,則不會看到相同的行為,因為文件是一次性編譯的。

8. A tic-tac-toe where X wins in the first attempt!

>>> row = [''] * 3
>>> board = [row] * 3
>>> board
[['', '', ''], ['', '', ''], ['', '', '']]
>>> board[0]
['', '', '']
>>> board[0][0]
''
>>> board[0][0] = "X"
>>> board
[['X', '', ''], ['X', '', ''], ['X', '', '']]

說明:
我們來輸出 id 看下:

>>> id(row[0])
7536232
>>> id(row[1])
5143216
>>> id(row[2])
5143216
>>> id(board[0])
7416840
>>> id(board[1])
7416840
>>> id(board[2])
7416840

row 是一個 list,其中三個元素都指向地址 5143216,當對 board[0][0] 進行賦值以後,row 的第一個元素指向 7536232。而 board 中的三個元素都指向 row,row 的地址並沒有改變。

我們可以通過不使用變數 row 生成 board 來避免這種情況。

>>> board = [[''] * 3 for _ in range(3)]
>>> board[0][0] = "X"
>>> board
[['X', '', ''], ['', '', ''], ['', '', '']]

這裡用了推導式,每次迭代都會生成一個新的 _ ,所以 board 中三個元素指向的是不同的變數。

9. The sticky output function

funcs = []
results = []
for x in range(7):
    def some_func():
        return x
    funcs.append(some_func)
    results.append(some_func())

funcs_results = [func() for func in funcs]

Output:

>>> results
[0, 1, 2, 3, 4, 5, 6]
>>> funcs_results
[6, 6, 6, 6, 6, 6, 6]

說明:
當在迴圈內部定義一個函數時,如果該函數在其主體中使用了迴圈變數,則閉包函數將與迴圈變數綁定,而不是它的值。因此,所有的函數都是使用最後分配給變數的值來進行計算的。

可以通過將迴圈變數作為命名變數傳遞給函數來獲得預期的結果。為什麼這樣可行?因為這會在函數內再次定義一個局部變數。

funcs = []
for x in range(7):
    def some_func(x=x):
        return x
    funcs.append(some_func)

Output:

>>> funcs_results = [func() for func in funcs]
>>> funcs_results
[0, 1, 2, 3, 4, 5, 6]

10. is not ... is not is (not ...)/is not ... 不是 is (not ...)

>>> 'something' is not None
True
>>> 'something' is (not None)
False

說明:
is not 是個單獨的二元運算符,與分別使用 is 和 not 不同。

11. The surprising comma

略過,我想沒人會在函數的最後一個參數後面再加一個逗號吧!
況且,尾隨逗號的問題已經在 Python 3.6 中被修複了。

12. Backslashes at the end of string

>>> print("\\ C:\\")
\ C:\
>>> print(r"\ C:")
\ C:
>>> print(r"\ C:\")

    File "<stdin>", line 1
      print(r"\ C:\")
                     ^
SyntaxError: EOL while scanning string literal

說明:
在以 r 開頭的原始字元串中,反斜杠並沒有特殊含義。解釋器所做的只是簡單的改變了反斜杠的行為,因此會直接傳遞反斜杠及後一個的字元。這就是反斜杠在原始字元串末尾不起作用的原因。

13. not knot!

>>> not x == y
True
>>> x == not y
  File "<input>", line 1
    x == not y
           ^
SyntaxError: invalid syntax

說明:
一句話,== 運算符的優先順序要高於 not 運算符。

14. Half triple-quoted strings

>>> print('wtfpython''')
wtfpython
>>> print("wtfpython""")
wtfpython
>>> # 下麵的語句會拋出 `SyntaxError` 異常
>>> # print('''wtfpython')
>>> # print("""wtfpython")

說明:
''' 和 """ 在 Python中也是字元串定界符,Python 解釋器在先遇到三個引號的的時候會嘗試再尋找三個終止引號作為定界符,如果不存在則會導致 SyntaxError 異常。

而 Python 提供隱式的字元串鏈接:

>>> print("wtf" "python")
wtfpython
>>> print("wtf""")  # 相當於 "wtf" ""
wtf

15. Midnight time doesn't exist?

from datetime import datetime

midnight = datetime(2018, 1, 1, 0, 0)
midnight_time = midnight.time()

noon = datetime(2018, 1, 1, 12, 0)
noon_time = noon.time()

if midnight_time:
    print("Time at midnight is", midnight_time)

if noon_time:
    print("Time at noon is", noon_time)

Output:

Time at noon is 12:00:00

midnight_time 並沒有被輸出。
說明:
在Python 3.5之前,如果 datetime.time 對象存儲的UTC的午夜0點, 那麼它的布爾值會被認為是 False。
這個我特意下了個 python 3.4 驗證了下,真是這樣。

16. What's wrong with booleans

mixed_list = [False, 1.0, "some_string", 3, True, [], False]
integers_found_so_far = 0
booleans_found_so_far = 0

for item in mixed_list:
    if isinstance(item, int):
        integers_found_so_far += 1
    elif isinstance(item, bool):
        booleans_found_so_far += 1

Output:

>>> booleans_found_so_far
0
>>> integers_found_so_far
4

說明:
布爾值是 int 的子類

>>> isinstance(True, int)
True
>>> isinstance(False, int)
True

在引入實際 bool 類型之前,0 和 1 是真值的官方表示。為了向下相容,新的 bool 類型需要像 0 和 1 一樣工作。

17. Class attributes and instance attributes

class A:
    x = 1

class B(A):
    pass

class C(A):
    pass

Output:

>>> A.x, B.x, C.x
(1, 1, 1)
>>> B.x = 2
>>> A.x, B.x, C.x
(1, 2, 1)
>>> A.x = 3
>>> A.x, B.x, C.x
(3, 2, 3)
>>> a = A()
>>> a.x, A.x
(3, 3)
>>> a.x += 1
>>> a.x, A.x
(4, 3)

class SomeClass:
    some_var = 15
    some_list = [5]
    another_list = [5]
    def __init__(self, x):
        self.some_var = x + 1
        self.some_list = self.some_list + [x]
        self.another_list += [x]

Output:

>>> some_obj = SomeClass(420)
>>> some_obj.some_list
[5, 420]
>>> some_obj.another_list
[5, 420]
>>> another_obj = SomeClass(111)
>>> another_obj.some_list
[5, 111]
>>> another_obj.another_list
[5, 420, 111]
>>> another_obj.another_list is SomeClass.another_list
True
>>> another_obj.another_list is some_obj.another_list
True

說明:

  • 類變數和實例變數在內部是通過類對象的字典來處理(__dict__ 屬性),如果在當前類的字典中找不到的話就去它的父類中尋找。
  • += 運算符會在原地修改可變對象,而不是創建新對象。因此,修改一個實例的屬性會影響其他實例和類屬性。

    18. yielding None

some_iterable = ('a', 'b')

def some_func(val):
    return "something"

Output:

>>> [x for x in some_iterable]
['a', 'b']
>>> [(yield x) for x in some_iterable]
<generator object <listcomp> at 0x7f70b0a4ad58>
>>> list([(yield x) for x in some_iterable])
['a', 'b']
>>> list((yield x) for x in some_iterable)
['a', None, 'b', None]
>>> list(some_func((yield x)) for x in some_iterable)
['a', 'something', 'b', 'something']

說明:
這是CPython在理解和生成器表達式中處理yield的一個錯誤,在Python 3.8中修複,在Python 3.7中有棄用警告。 請參閱Python錯誤報告和Python 3.7和Python 3.8的新增條目。

來源和解釋可以在這裡找到: https://stackoverflow.com/questions/32139885/yield-in-list-comprehensions-and-generator-expressions
相關錯誤報告: http://bugs.python.org/issue10544

19. Mutating the immutable!

>>> some_tuple = ("A", "tuple", "with", "values")
>>> another_tuple = ([1, 2], [3, 4], [5, 6])

>>> some_tuple[2] = "change this"
TypeError: 'tuple' object does not support item assignment
>>> another_tuple[2].append(1000) # 這裡不出現錯誤
>>> another_tuple
([1, 2], [3, 4], [5, 6, 1000])
>>> another_tuple[2] += [99, 999]
TypeError: 'tuple' object does not support item assignment
>>> another_tuple
([1, 2], [3, 4], [5, 6, 1000, 99, 999])

說明:
元組中不可變的元素的標識(即元素的地址),如果元素是引用類型,元組的值會隨著引用的可變對象的變化而變化。所以 another_tuple[2].append(1000) 是可以的。
+= 操作符在原地修改了列表。元素賦值操作並不工作,但是當異常拋出時,元素已經在原地被修改了。+= 並不是原子操作,而是 extend 和 = 兩個動作,這裡 = 操作雖然會拋出異常,但 extend 操作已經修改成功了。

20. The disappearing variable from outer scope

e = 7
try:
    raise Exception()
except Exception as e:
    pass

Output: python2

>>> print(e)
# prints nothing

Output: python3

>>> print(e)
NameError: name 'e' is not defined

說明:
當使用 as 為目標分配異常的時候,將在 except 子句的末尾清除該異常。
這就好像:

except E as N:
    foo

會被翻譯成:

except E as N:
    try:
        foo
    finally:
        del N

這意味著必須將異常分配給其他名稱才能在 except 子句之後引用它。而異常之所以會被清除,是因為附加了回溯信息(trackback),它們與棧幀(stack frame)形成一個引用迴圈,使得該棧幀中的所有本地變數在下一次垃圾回收發生之前都處於活動狀態(不會被回收)。

子句在 Python 中並沒有獨立的作用域。示例中的所有內容都處於同一作用域內,所以變數 e 會由於執行了 except 子句而被刪除。而對於有獨立的內部作用域的函數來說情況就不一樣了。下麵的例子說明瞭這一點:

def f(x):
    del(x)
    print(x)

x = 5
y = [5, 4, 3]

Output:

>>>f(x)
UnboundLocalError: local variable 'x' referenced before assignment
>>>f(y)
UnboundLocalError: local variable 'x' referenced before assignment
>>> x
5
>>> y
[5, 4, 3]

21. When True is actually False

True = False
if True == False:
    print("I've lost faith in truth!")

Output:

I've lost faith in truth!

說明:
最初,Python 並沒有 bool 型(人們用 0 表示假值, 用非零值比如 1 作為真值)。後來他們添加了 True, False, 和 bool 型,但是,為了向後相容,他們沒法把 True 和 False 設置為常量,只是設置成了內置變數。
Python 3 由於不再需要向後相容,終於可以修複這個問題了,所以這個例子無法在 Python 3.x 中執行。

22. From filled to None in one instruction...

some_list = [1, 2, 3]
some_dict = {
  "key_1": 1,
  "key_2": 2,
  "key_3": 3
}

some_list = some_list.append(4)
some_dict = some_dict.update({"key_4": 4})

Output:

>>> print(some_list)
None
>>> print(some_dict)
None

說明:
大多數修改序列/映射對象的方法,比如 list.append,dict.update,list.sort 等等,都是原地修改對象並返回 None,這樣可以避免創建對象的副本來提高性能。

23. Subclass relationships

>>> from collections import Hashable
>>> issubclass(list, object)
True
>>> issubclass(object, Hashable)
True
>>> issubclass(list, Hashable)
False

子類關係應該是可傳遞的,對吧?即,如果 A 是 B 的子類,B 是 C 的子類,那麼 A 應該 是 C 的子類。
說明:

  • Python 中的子類關係並不必須是傳遞的,任何人都可以在元類中隨意定義 __subclasscheck__。
  • 當 issubclass(cls, Hashable) 被調用時,它只是在 cls 中尋找 "__hash__" 方法或繼承自 "__hash__" 的方法。
  • 由於 object 是可散列的(hashable),而 list 是不可散列的,所以它打破了這種傳遞關係。

    24. The mysterious key type conversion

class SomeClass(str):
    pass

some_dict = {'s': 42}

Output:

>>> type(list(some_dict.keys())[0])
<class 'str'>
>>> s = SomeClass('s')
>>> some_dict[s] = 40
>>> some_dict # 預期: 兩個不同的鍵值對
{'s': 40}
>>> type(list(some_dict.keys())[0])
<class 'str'>

說明:

  • 由於 SomeClass 會從 str 自動繼承 __hash__ 方法,所以 s 對象和 "s" 字元串的哈希值是相同的。
  • 而 SomeClass("s") == "s" 為 True 是因為 SomeClass 也繼承了 str 類 __eq__ 方法。
  • 由於兩者的哈希值相同且相等,所以它們在字典中表示相同的鍵。

如果想要實現期望的功能, 我們可以重定義 SomeClass 的 __eq__ 方法.

class SomeClass(str):
  def __eq__(self, other):
      return (
          type(self) is SomeClass
          and type(other) is SomeClass
          and super().__eq__(other)
      )

  # 當我們自定義 __eq__ 方法時, Python 不會再自動繼承 __hash__ 方法
  # 所以我們也需要定義它
  __hash__ = str.__hash__

some_dict = {'s':42}

Output:

>>> s = SomeClass('s')
>>> some_dict[s] = 40
>>> some_dict
{'s': 40, 's': 42}
>>> keys = list(some_dict.keys())
>>> type(keys[0]), type(keys[1])
<class 'str'> <class '__main__.SomeClass'>

25. Let's see if you can guess this?

>>> a, b = a[b] = {}, 5
>>> a
{5: ({...}, 5)}

說明:
根據 Python 語言參考,賦值語句的形式如下:

(target_list "=")+ (expression_list | yield_expression)

賦值語句計算表達式列表(expression list)(請記住,這可以是單個表達式或以逗號分隔的列表, 後者返回元組)並將單個結果對象從左到右分配給目標列表中的每一項。

(target_list "=")+ 中的 + 意味著可以有一個或多個目標列表。在這個例子中,目標列表是 a, b 和 a[b]。表達式列表只能有一個,是 {}, 5。

這話看著非常的晦澀,我們來看一個簡單的例子:

a, b = b, c = 1, 2
print(a, b, c)

Output:

1 1 2

在這個簡單的例子中,目標列表是 a, b 和 b, c,表達式是 1, 2。將表達式從左到右賦給目標列表,上述例子就可以拆分成:

a, b = 1, 2
b, c = 1, 2

所以結果就是 1 1 2。

那麼,原例子就不難理解了,拆解開來就是:

a, b = {}, 5
a[b] = a, b

這裡不能寫作 a[b] = {}, 5,因為這樣第一句中的 {} 和第二句中的 {} 其實就是不同的對象了,而實際他們是同一個對象。這就形成了迴圈引用,輸出中的 {...} 指與 a 引用了相同的對象。
我們來驗證一下:

>>> a[b][0] is a
True

可見確實是同一個對象。

以下是一個簡單的迴圈引用的例子:

>>> some_list = some_list[0] = [0]
>>> some_list
[[...]]
>>> some_list[0]
[[...]]
>>> some_list is some_list[0]
True
>>> some_list[0][0][0][0][0][0] == some_list
True

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

-Advertisement-
Play Games
更多相關文章
  • 簡單工廠模式,屬於創建型模式,它提供了一種創建對象的最佳方式。在工廠模式中,我們創建對象時不會對客戶端暴露創建邏輯,而是通過一個統一的介面來指向新創建的對象。 介紹 在現實生活中,當我們去 4S 店購車時,我們不用考慮汽車的各個部件是如何生產的,而就可以提走一輛愛車。同樣的,在軟體開發過程中,當我們 ...
  • 為了能在以後的工作學習中少寫 BUG ,我覺得還是有必要認真複習一下所有的設計模式, 為了後續系列博客做技術儲備, 杠精 請出門右拐點擊 關閉 按鈕即可。需要說明一點的是,設計模式與語言無關,所以還請各位萌新不要陷入語言妄想症。 前言 關於什麼是設計模式,這裡簡單描述描述一下: 所謂設計模式,是指軟 ...
  • 前言 Java多線程分類中寫了21篇多線程的文章,21篇文章的內容很多,個人認為,學習,內容越多、越雜的知識,越需要進行深刻的總結,這樣才能記憶深刻,將知識變成自己的。這篇文章主要是對多線程的問題進行總結的,因此羅列了40個多線程的問題。 這些多線程的問題,有些來源於各大網站、有些來源於自己的思考。 ...
  • 給定數據利用神經網路演算法模型進行計算,利用FP、BP演算法,求得模型最優值。 神經網路初步學習使用。 ...
  • //接受數據請求public function client($pz){ //參數1是:網路協議, //AF_INET: IPv4 網路協議。TCP 和 UDP 都可使用此協議。一般都用這個,你懂的。 //AF_INET6: IPv6 網路協議。TCP 和 UDP 都可使用此協議。 //AF_UNI ...
  • 1.首先感謝同事 2.之前一直在做angularjs的項目,目前vue火熱,所以自己搭建了一個的vue框架,在此作為記錄 vue+vux-ui這裡就不介紹了,有很多博客都寫的很詳細了。 下麵簡單記錄下axios 和 mock 1.axios <1> 安裝axios <2> 使用axios 1.因為有 ...
  • 適配器模式(Adapter Pattern)又叫做變壓器模式,變壓器把一種電壓變換為另一種電壓。 定義: 將一個類的介面變換成客戶端所期待的另一種介面,從而使原本因介面不匹配而無法一起工作的兩個類能夠在一起工作。 適配器模式就是將一個介面或類轉換成其它的介面或類,適配器相當於一個包裝器,類圖如下所示 ...
  • 用CEF4Delphi取網頁元素時碰到ElementInnerText里含有"&nbsp;" 比如網頁源碼里是"內容&nbsp;"取出來顯示就變成"內容?" 搜索大部分是說把"&nbsp;"替換成其它字元即可 但實際操作怎麼也替換不了,就算變數為AnsiString也不行 最後用了以下方法解決 參考 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...