[英]How do I remove duplicates from a list, while preserving order?
如何在保留順序的同時從列表中刪除重復項? 使用集合刪除重復項會破壞原始順序。 有內置的或 Pythonic 的成語嗎?
在這里你有一些選擇: http ://www.peterbe.com/plog/uniqifiers-benchmark
最快的一個:
def f7(seq):
seen = set()
seen_add = seen.add
return [x for x in seq if not (x in seen or seen_add(x))]
為什么將seen.add
分配給seen_add
而不僅僅是調用seen.add
? Python 是一種動態語言,每次迭代解析seen.add
比解析局部變量成本更高。 seen.add
可能在迭代之間發生了變化,並且運行時不夠聰明,無法排除這種情況。 為了安全起見,它必須每次都檢查對象。
如果您計划在同一個數據集上大量使用此函數,那么使用有序集可能會更好:http: //code.activestate.com/recipes/528878/
O (1) 每個操作的插入、刪除和成員檢查。
(小的附加說明: seen.add()
總是返回None
,所以上面的or
上面的只是作為嘗試更新集合的一種方式,而不是作為邏輯測試的組成部分。)
最佳解決方案因 Python 版本和環境限制而異:
在 PyPy 2.5.0 中首次引入,並在 CPython 3.6 中作為實現細節采用,在 Python 3.7 中成為語言保證之前,plain dict
是插入順序的,甚至比(也是 CPython 3.5 實現的 C)更有效collections.OrderedDict
。 因此,到目前為止,最快的解決方案也是最簡單的:
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(dict.fromkeys(items)) # Or [*dict.fromkeys(items)] if you prefer
[1, 2, 0, 3]
與list(set(items))
一樣,這會將所有工作推到 C 層(在 CPython 上),但由於dict
是插入排序的, dict.fromkeys
不會丟失排序。 它比list(set(items))
慢(通常需要 50-100% 的時間),但比任何其他訂單保留解決方案要快得多(大約需要在 listcomp 中使用set
的黑客時間的一半)。
重要說明: more_itertools
的unique_everseen
解決方案(見下文)在惰性和支持不可散列的輸入項方面具有一些獨特的優勢; 如果您需要這些功能,這是唯一可行的解決方案。
正如 Raymond指出的那樣,在 CPython 3.5 中, OrderedDict
是用 C 實現的,丑陋的列表理解黑客比OrderedDict.fromkeys
慢(除非您實際上需要最后的列表 - 即便如此,只有在輸入非常短的情況下)。 因此,在性能和可讀性方面,CPython 3.5 的最佳解決方案是OrderedDict
等效於 3.6+ 使用普通dict
:
>>> from collections import OrderedDict
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(OrderedDict.fromkeys(items))
[1, 2, 0, 3]
在 CPython 3.4 及更早版本上,這將比其他一些解決方案慢,因此如果分析表明您需要更好的解決方案,請繼續閱讀。
正如@abarnert 所指出的, more_itertools
庫( pip install more_itertools
)包含一個unique_everseen
函數,該函數旨在解決此問題,而不會在列表推導中出現任何不可讀( not seen.add
)的突變。 這也是最快的解決方案:
>>> from more_itertools import unique_everseen
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(unique_everseen(items))
[1, 2, 0, 3]
只需一個簡單的庫導入,無需任何技巧。
該模塊正在調整 itertools 配方unique_everseen
,如下所示:
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in filterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
但與itertools
配方不同,它支持不可散列的項目(以性能為代價;如果iterable
中的所有元素都是不可散列的,則算法變為O(n²)
,如果它們都是可散列的,則為O(n)
)。
重要提示:與這里的所有其他解決方案不同, unique_everseen
可以懶惰地使用; 峰值內存使用量將是相同的(最終,基礎set
增長到相同的大小),但如果您不list
結果,您只需對其進行迭代,您將能夠處理獨特的項目,因為它們是找到,而不是等到整個輸入被刪除后才處理第一個唯一項。
你有兩個選擇:
將unique_everseen
配方復制並粘貼到您的代碼中,並按照上面的more_itertools
示例使用它
使用丑陋的 hack 允許單個 listcomp 檢查和更新set
以跟蹤所看到的內容:
seen = set() [x for x in seq if x not in seen and not seen.add(x)]
以依賴丑陋的黑客為代價:
not seen.add(x)
這依賴於set.add
是一個就地方法,它總是返回None
所以not None
評估為True
。
請注意,上面的所有解決方案都是O(n)
(保存在不可散列項的可迭代項上調用unique_everseen
,即O(n²)
,而其他解決方案將立即失敗並出現TypeError
),因此所有解決方案在以下情況下都足夠高效它們不是最熱門的代碼路徑。 使用哪一個取決於您可以依賴的語言規范/解釋器/第三方模塊的版本,性能是否至關重要(不要假設它是關鍵;通常不是),最重要的是可讀性(因為如果維護這段代碼的人后來陷入了殺氣,那么你聰明的微優化可能就不值得了)。
在 CPython 3.6+ (以及從Python 3.7+開始的所有其他 Python 實現)中,字典是有序的,因此從可迭代對象中刪除重復項同時保持其原始順序的方法是:
>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']
在 Python 3.5及更低版本(包括Python 2.7 )中,使用OrderedDict
。 我的時間安排表明,這現在是 Python 3.5 各種方法中最快和最短的方法(當它獲得 C 實現時;在 3.5 之前,它仍然是最清晰的解決方案,盡管不是最快的)。
>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']
不要踢死馬(這個問題很老,已經有很多好的答案),但這里有一個使用 pandas 的解決方案,它在許多情況下都非常快,而且使用起來非常簡單。
import pandas as pd
my_list = [0, 1, 2, 3, 4, 1, 2, 3, 5]
>>> pd.Series(my_list).drop_duplicates().tolist()
# Output:
# [0, 1, 2, 3, 4, 5]
sequence = ['1', '2', '3', '3', '6', '4', '5', '6']
unique = []
[unique.append(item) for item in sequence if item not in unique]
唯一 → ['1', '2', '3', '6', '4', '5']
from itertools import groupby
[ key for key,_ in groupby(sortedList)]
列表甚至不必排序,充分條件是相等的值被組合在一起。
編輯:我假設“保留順序”意味着列表實際上是有序的。 如果不是這種情況,那么 MizardX 的解決方案就是正確的。
社區編輯:然而,這是“將重復的連續元素壓縮為單個元素”的最優雅的方式。
我想如果你想維持秩序,
list1 = ['b','c','d','b','c','a','a']
list2 = list(set(list1))
list2.sort(key=list1.index)
print list2
list1 = ['b','c','d','b','c','a','a']
list2 = sorted(set(list1),key=list1.index)
print list2
list1 = ['b','c','d','b','c','a','a']
list2 = []
for i in list1:
if not i in list2:
list2.append(i)`
print list2
list1 = ['b','c','d','b','c','a','a']
list2 = []
[list2.append(i) for i in list1 if not i in list2]
print list2
只是為了從外部模塊添加另一個(非常高效的)實現這種功能1 : iteration_utilities.unique_everseen
:
>>> from iteration_utilities import unique_everseen
>>> lst = [1,1,1,2,3,2,2,2,1,3,4]
>>> list(unique_everseen(lst))
[1, 2, 3, 4]
我做了一些計時(Python 3.6),這些表明它比我測試的所有其他替代方案更快,包括OrderedDict.fromkeys
、 f7
和more_itertools.unique_everseen
:
%matplotlib notebook
from iteration_utilities import unique_everseen
from collections import OrderedDict
from more_itertools import unique_everseen as mi_unique_everseen
def f7(seq):
seen = set()
seen_add = seen.add
return [x for x in seq if not (x in seen or seen_add(x))]
def iteration_utilities_unique_everseen(seq):
return list(unique_everseen(seq))
def more_itertools_unique_everseen(seq):
return list(mi_unique_everseen(seq))
def odict(seq):
return list(OrderedDict.fromkeys(seq))
from simple_benchmark import benchmark
b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
{2**i: list(range(2**i)) for i in range(1, 20)},
'list size (no duplicates)')
b.plot()
並且只是為了確保我還進行了更多重復的測試,以檢查它是否有所作為:
import random
b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
{2**i: [random.randint(0, 2**(i-1)) for _ in range(2**i)] for i in range(1, 20)},
'list size (lots of duplicates)')
b.plot()
一個只包含一個值:
b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
{2**i: [1]*(2**i) for i in range(1, 20)},
'list size (only duplicates)')
b.plot()
在所有這些情況下, iteration_utilities.unique_everseen
函數是最快的(在我的計算機上)。
此iteration_utilities.unique_everseen
實用程序.unique_everseen 函數還可以處理輸入中的不可散列值(但是當值是可散列時,性能為O(n*n)
而不是O(n)
性能)。
>>> lst = [{1}, {1}, {2}, {1}, {3}]
>>> list(unique_everseen(lst))
[{1}, {2}, {3}]
1免責聲明:我是該軟件包的作者。
對於另一個非常古老的問題的另一個很晚的答案:
itertools
recipes有一個函數seen
做到這一點,使用 see set 技術,但是:
key
功能。seen.add
來優化循環,而不是查找 N 次。 ( f7
也這樣做,但有些版本沒有。)ifilterfalse
優化循環,因此您只需遍歷 Python 中的唯一元素,而不是所有元素。 (當然,您仍然在ifilterfalse
中遍歷所有這些,但這是在 C 中,而且速度更快。) 它實際上比f7
快嗎? 這取決於您的數據,因此您必須對其進行測試並查看。 如果你最后想要一個列表, f7
使用 listcomp,這里沒有辦法做到這一點。 (您可以直接append
而不是yield
ing,或者您可以將生成器提供給list
函數,但兩者都不能像 listcomp 中的 LIST_APPEND 一樣快。)無論如何,通常擠出幾微秒是行不通的與擁有一個易於理解、可重用、已經編寫好的函數一樣重要,當你想要裝飾時不需要 DSU。
與所有食譜一樣,它也可以在more-iterools
中使用。
如果您只想要無key
的情況,您可以將其簡化為:
def unique(iterable):
seen = set()
seen_add = seen.add
for element in itertools.ifilterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
對於基於 MizardX 的無哈希類型(例如列表列表):
def f7_noHash(seq)
seen = set()
return [ x for x in seq if str( x ) not in seen and not seen.add( str( x ) )]
減少變體速度快 5 倍,但更復雜
>>> l = [5, 6, 6, 1, 1, 2, 2, 3, 4]
>>> reduce(lambda r, v: v in r[1] and r or (r[0].append(v) or r[1].add(v)) or r, l, ([], set()))[0]
[5, 6, 1, 2, 3, 4]
解釋:
default = (list(), set())
# use list to keep order
# use set to make lookup faster
def reducer(result, item):
if item not in result[1]:
result[0].append(item)
result[1].add(item)
return result
>>> reduce(reducer, l, default)[0]
[5, 6, 1, 2, 3, 4]
這是一個簡單的方法:
list1 = ["hello", " ", "w", "o", "r", "l", "d"]
sorted(set(list1 ), key=list1.index)
這給出了輸出:
["hello", " ", "w", "o", "r", "l", "d"]
熊貓用戶應該查看pandas.unique
。
>>> import pandas as pd
>>> lst = [1, 2, 1, 3, 3, 2, 4]
>>> pd.unique(lst)
array([1, 2, 3, 4])
該函數返回一個 NumPy 數組。 如果需要,您可以使用tolist
方法將其轉換為列表。
您可以引用一個列表推導,因為它是由符號“_[1]”構建的。
例如,以下函數通過引用其列表推導來唯一化一個元素列表而不改變它們的順序。
def unique(my_list):
return [x for x in my_list if x not in locals()['_[1]']]
演示:
l1 = [1, 2, 3, 4, 1, 2, 3, 4, 5]
l2 = [x for x in l1 if x not in locals()['_[1]']]
print l2
輸出:
[1, 2, 3, 4, 5]
借用在為列表定義 Haskell 的nub
函數時使用的遞歸思想,這將是一種遞歸方法:
def unique(lst):
return [] if lst==[] else [lst[0]] + unique(filter(lambda x: x!= lst[0], lst[1:]))
例如:
In [118]: unique([1,5,1,1,4,3,4])
Out[118]: [1, 5, 4, 3]
我嘗試使用它來增加數據大小並看到次線性時間復雜度(不是確定的,但建議這對於正常數據應該沒問題)。
In [122]: %timeit unique(np.random.randint(5, size=(1)))
10000 loops, best of 3: 25.3 us per loop
In [123]: %timeit unique(np.random.randint(5, size=(10)))
10000 loops, best of 3: 42.9 us per loop
In [124]: %timeit unique(np.random.randint(5, size=(100)))
10000 loops, best of 3: 132 us per loop
In [125]: %timeit unique(np.random.randint(5, size=(1000)))
1000 loops, best of 3: 1.05 ms per loop
In [126]: %timeit unique(np.random.randint(5, size=(10000)))
100 loops, best of 3: 11 ms per loop
我還認為有趣的是,這可以很容易地通過其他操作推廣到唯一性。 像這樣:
import operator
def unique(lst, cmp_op=operator.ne):
return [] if lst==[] else [lst[0]] + unique(filter(lambda x: cmp_op(x, lst[0]), lst[1:]), cmp_op)
例如,您可以傳入一個函數,該函數使用舍入到相同整數的概念,就好像它是“相等”一樣,以實現唯一性目的,如下所示:
def test_round(x,y):
return round(x) != round(y)
那么 unique(some_list, test_round) 將提供列表的唯一元素,其中唯一性不再意味着傳統的相等性(這是通過使用任何類型的基於集合或基於字典鍵的方法來解決這個問題)而是意味着采取對於元素可能舍入到的每個可能的整數 K,僅舍入到 K 的第一個元素,例如:
In [6]: unique([1.2, 5, 1.9, 1.1, 4.2, 3, 4.8], test_round)
Out[6]: [1.2, 5, 1.9, 4.2, 3]
1.這些解決方案很好……
為了在保留順序的同時刪除重復項,本頁其他地方提出的優秀解決方案:
seen = set()
[x for x in seq if not (x in seen or seen.add(x))]
和變體,例如:
seen = set()
[x for x in seq if x not in seen and not seen.add(x)]
確實很受歡迎,因為它們簡單、簡約,並且部署了正確的散列以獲得最佳效率。 關於這些的主要抱怨似乎是使用方法seen.add(x)
“返回”的不變None
作為邏輯表達式中的常量(因此多余/不必要的)值 - 只是為了它的副作用 - 是 hacky 和/或令人困惑。
2. …但是他們每次迭代都會浪費一次哈希查找。
令人驚訝的是,考慮到關於這個主題的大量討論和辯論,實際上對似乎被忽視的代碼進行了重大改進。 如圖所示,每次“測試和設置”迭代都需要兩次哈希查找:第一次測試成員x not in seen
,然后再次實際添加值seen.add(x)
。 由於第一個操作保證了第二個操作總是成功的,所以這里的重復工作是浪費的。 而且因為這里的整體技術非常有效,多余的哈希查找可能最終成為剩下的少量工作中最昂貴的部分。
3. 相反,讓set
做好它的工作!
請注意,上面的示例僅調用set.add
並預先知道這樣做將始終導致集合成員資格的增加。 set
本身永遠沒有機會拒絕重復; 我們的代碼片段基本上已經為自己篡奪了這個角色。 使用顯式的兩步測試和設置代碼正在剝奪set
自身排除這些重復項的核心能力。
4.改進后的代碼:
以下版本將每次迭代的哈希查找次數減少了一半——從兩次減少到只有一次。 這大大提高了已經很快速的方法的性能。
seen = set()
[x for x in seq if len(seen) < len(seen.add(x) or seen)]
至於令人不快的黑客攻擊,現在比以前發生了一些變異,它似乎確實可以繼續看到另一天。
使用_sorted_
一個numpy
數組的相對有效的方法:
b = np.array([1,3,3, 8, 12, 12,12])
numpy.hstack([b[0], [x[0] for x in zip(b[1:], b[:-1]) if x[0]!=x[1]]])
輸出:
array([ 1, 3, 8, 12])
你可以做一種丑陋的列表理解黑客。
[l[i] for i in range(len(l)) if l.index(l[i]) == i]
l = [1,2,2,3,3,...]
n = []
n.extend(ele for ele in l if ele not in set(n))
使用 O(1) 查找集合來確定是否在新列表中包含元素的生成器表達式。
一個簡單的遞歸解決方案:
def uniquefy_list(a):
return uniquefy_list(a[1:]) if a[0] in a[1:] else [a[0]]+uniquefy_list(a[1:]) if len(a)>1 else [a[0]]
消除序列中的重復值,但保留剩余項目的順序。 使用通用生成器功能。
# for hashable sequence
def remove_duplicates(items):
seen = set()
for item in items:
if item not in seen:
yield item
seen.add(item)
a = [1, 5, 2, 1, 9, 1, 5, 10]
list(remove_duplicates(a))
# [1, 5, 2, 9, 10]
# for unhashable sequence
def remove_duplicates(items, key=None):
seen = set()
for item in items:
val = item if key is None else key(item)
if val not in seen:
yield item
seen.add(val)
a = [ {'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 1, 'y': 2}, {'x': 2, 'y': 4}]
list(remove_duplicates(a, key=lambda d: (d['x'],d['y'])))
# [{'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 2, 'y': 4}]
一個班輪列表理解:
values_non_duplicated = [value for index, value in enumerate(values) if value not in values[ : index]]
x = [1, 2, 1, 3, 1, 4]
# brute force method
arr = []
for i in x:
if not i in arr:
arr.insert(x[i],i)
# recursive method
tmp = []
def remove_duplicates(j=0):
if j < len(x):
if not x[j] in tmp:
tmp.append(x[j])
i = j+1
remove_duplicates(i)
remove_duplicates()
如果您需要一個班輪,那么這可能會有所幫助:
reduce(lambda x, y: x + y if y[0] not in x else x, map(lambda x: [x],lst))
...應該可以,但如果我錯了,請糾正我
MizardX 的答案提供了多種方法的良好集合。
這是我在大聲思考時想到的:
mylist = [x for i,x in enumerate(mylist) if x not in mylist[i+1:]]
如果您經常使用pandas
,並且美學優先於性能,那么請考慮內置函數pandas.Series.drop_duplicates
:
import pandas as pd
import numpy as np
uniquifier = lambda alist: pd.Series(alist).drop_duplicates().tolist()
# from the chosen answer
def f7(seq):
seen = set()
seen_add = seen.add
return [ x for x in seq if not (x in seen or seen_add(x))]
alist = np.random.randint(low=0, high=1000, size=10000).tolist()
print uniquifier(alist) == f7(alist) # True
定時:
In [104]: %timeit f7(alist)
1000 loops, best of 3: 1.3 ms per loop
In [110]: %timeit uniquifier(alist)
100 loops, best of 3: 4.39 ms per loop
這將保持秩序並在 O(n) 時間內運行。 基本上這個想法是在發現重復的地方創建一個洞並將其沉入底部。 使用讀寫指針。 每當找到重復項時,只有讀取指針前進,而寫入指針停留在重復項上以覆蓋它。
def deduplicate(l):
count = {}
(read,write) = (0,0)
while read < len(l):
if l[read] in count:
read += 1
continue
count[l[read]] = True
l[write] = l[read]
read += 1
write += 1
return l[0:write]
不使用導入模塊或集合的解決方案:
text = "ask not what your country can do for you ask what you can do for your country"
sentence = text.split(" ")
noduplicates = [(sentence[i]) for i in range (0,len(sentence)) if sentence[i] not in sentence[:i]]
print(noduplicates)
給出輸出:
['ask', 'not', 'what', 'your', 'country', 'can', 'do', 'for', 'you']
這種方法是二次的,因為我們對列表中的每個元素進行了線性查找(為此,我們必須添加重新排列列表的成本,因為del
s)。
也就是說,如果我們從列表的末尾開始向原點前進,刪除其左側子列表中存在的每個術語,則可以就地操作
代碼中的這個想法很簡單
for i in range(len(l)-1,0,-1):
if l[i] in l[:i]: del l[i]
一個簡單的實現測試
In [91]: from random import randint, seed
In [92]: seed('20080808') ; l = [randint(1,6) for _ in range(12)] # Beijing Olympics
In [93]: for i in range(len(l)-1,0,-1):
...: print(l)
...: print(i, l[i], l[:i], end='')
...: if l[i] in l[:i]:
...: print( ': remove', l[i])
...: del l[i]
...: else:
...: print()
...: print(l)
[6, 5, 1, 4, 6, 1, 6, 2, 2, 4, 5, 2]
11 2 [6, 5, 1, 4, 6, 1, 6, 2, 2, 4, 5]: remove 2
[6, 5, 1, 4, 6, 1, 6, 2, 2, 4, 5]
10 5 [6, 5, 1, 4, 6, 1, 6, 2, 2, 4]: remove 5
[6, 5, 1, 4, 6, 1, 6, 2, 2, 4]
9 4 [6, 5, 1, 4, 6, 1, 6, 2, 2]: remove 4
[6, 5, 1, 4, 6, 1, 6, 2, 2]
8 2 [6, 5, 1, 4, 6, 1, 6, 2]: remove 2
[6, 5, 1, 4, 6, 1, 6, 2]
7 2 [6, 5, 1, 4, 6, 1, 6]
[6, 5, 1, 4, 6, 1, 6, 2]
6 6 [6, 5, 1, 4, 6, 1]: remove 6
[6, 5, 1, 4, 6, 1, 2]
5 1 [6, 5, 1, 4, 6]: remove 1
[6, 5, 1, 4, 6, 2]
4 6 [6, 5, 1, 4]: remove 6
[6, 5, 1, 4, 2]
3 4 [6, 5, 1]
[6, 5, 1, 4, 2]
2 1 [6, 5]
[6, 5, 1, 4, 2]
1 5 [6]
[6, 5, 1, 4, 2]
In [94]:
zmk 的方法使用非常快的列表理解,但自然保持順序。 為了應用於區分大小寫的字符串,它可以很容易地修改。 這也保留了原始案例。
def DelDupes(aseq) :
seen = set()
return [x for x in aseq if (x.lower() not in seen) and (not seen.add(x.lower()))]
密切相關的功能是:
def HasDupes(aseq) :
s = set()
return any(((x.lower() in s) or s.add(x.lower())) for x in aseq)
def GetDupes(aseq) :
s = set()
return set(x for x in aseq if ((x.lower() in s) or s.add(x.lower())))
感謝@wjandrea 的 dict.fromdict 方法理念:
def solve(arr):
return list(dict.fromkeys(arr[::-1]))[::-1]
這將反轉輸入和輸出以正確迭代
from collections import OrderedDict as od
k=[9,5,2,5,3,6,4,7,8,9,1,1,1,2]
print(list(od.fromkeys(k).keys()))
#output
[9, 5, 2, 3, 6, 4, 7, 8, 1]
如果你使用 pydantic,你可以使用unique_list :
在保持秩序的同時使列表獨一無二。 如果設置了另一個具有相同名稱的列表(例如,在子類中覆蓋了根驗證器),我們將更新列表
例子:
from pydantic.utils import unique_list
print(unique_list([1, 2, 3, 1, 2, 3, 5, 6, 7, 8]))
# [1, 2, 3, 5, 6, 7, 8]
def remove_duplicates_thenSort():
t = ['b', 'c', 'd','d','a','c','c']
t2 = []
for i,k in enumerate(t):
index = t.index(k)
if i == index:
t2.append(t[i])
return sorted(t2)
print(remove_duplicates_thenSort())
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.