Xóa một phần tử khỏi từ điển


1395

Có cách nào để xóa một mục khỏi từ điển trong Python không?

Ngoài ra, làm cách nào tôi có thể xóa một mục khỏi từ điển để trả về một bản sao (nghĩa là không sửa đổi bản gốc)?


13
Tại sao bạn cần một hàm trả về một từ điển, khi bạn chỉ có thể sửa đổi từ điển trực tiếp?
amillerrhodes

5
Các từ điển popphương pháp thay đổi từ điển tại chỗ . Do đó, nó thay đổi tham chiếu đến từ điển được truyền từ người gọi đến "hàm trợ giúp". Vì vậy, "hàm trợ giúp" không cần trả về bất cứ thứ gì, vì tham chiếu ban đầu đến từ điển trong trình gọi sẽ bị thay đổi. Đừng chỉ định trả lại dict.pop()cho bất cứ điều gì nếu bạn không cần nó. TRỨNG : do stuff with my_dict; my_dict.pop(my_key, None); do more stuff with my_dict # now doesn't have my_key. Sử dụng deepcopy(my_dict)nếu cần.
Đánh dấu Mikofski

1
Vì tiêu đề ban đầu không đồng ý với các chi tiết và đặc biệt loại trừ giải pháp rõ ràng d.pop(), tôi đã sửa tiêu đề để đặt câu hỏi được chỉ định trong chi tiết.
smci

1
Chúng ta nên thêm một lời cảnh báo nếu bạn thực sự muốn làm điều này, vì nếu bạn thực hiện N lần trên một từ điển với các phần tử E, bạn sẽ rò rỉ (/ sử dụng) bộ nhớ O (N * E) với tất cả các bản sao sâu. Nếu bạn chỉ muốn đọc chỉ (bản sao nông), hãy làm d.pop(key). Nhưng nếu bất cứ điều gì từng sửa đổi bản sao nông, bạn có một vấn đề nổi tiếng với răng cưa . Nó giúp nếu bạn cho chúng tôi bối cảnh rộng hơn. (Có điều gì khác từng sửa đổi các giá trị dict không? Bạn có đang cố gắng lặp đi lặp lại một danh sách không? Nếu không, thì sao?)
smci

5
"Tại sao bạn cần một chức năng trả về một từ điển, khi bạn chỉ có thể sửa đổi từ điển trực tiếp?" Có lẽ bởi vì bạn muốn viết các hàm thuần túy không sửa đổi các tham số của chúng?
Gene Callahan

Câu trả lời:


1728

Câu dellệnh loại bỏ một phần tử:

del d[key]

Tuy nhiên, điều này làm thay đổi từ điển hiện có nên nội dung của từ điển thay đổi cho bất kỳ ai khác có tham chiếu đến cùng thể hiện. Để trả về một từ điển mới , tạo một bản sao của từ điển:

def removekey(d, key):
    r = dict(d)
    del r[key]
    return r

Các dict()constructor tạo một bản sao nông . Để tạo một bản sao sâu, xem copymô-đun .


Lưu ý rằng tạo một bản sao cho mỗi dict del/ gán / vv. có nghĩa là bạn đang đi từ thời gian không đổi đến thời gian tuyến tính và cũng sử dụng không gian tuyến tính. Đối với các dicts nhỏ, đây không phải là một vấn đề. Nhưng nếu bạn dự định tạo ra nhiều bản sao của các bản sao lớn, bạn có thể muốn có một cấu trúc dữ liệu khác, như HAMT (như được mô tả trong câu trả lời này ).


15
đó là một điểm tuyệt vời về khả năng biến đổi của từ điển +1 - mặc dù tôi không thể nghĩ về thời điểm tôi muốn các bản sao của từ điển, tôi luôn luôn dựa vào bản sao của 'mọi người' giống nhau. điểm tuyệt vời.
tMC

30
@tMC Nếu bạn chỉnh sửa dictkhi bạn lặp qua nó, nó sẽ báo lỗi:RuntimeError: dictionary changed size during iteration
VertigoRay

15
Điều gì về popphương pháp trong thực tế không giống nhau? Nó không phải là nhiều pythonic? (là phương pháp của dict, không phải từ dành riêng đặc biệt)?
Serge

21
Câu trả lời này có một điểm yếu, nó có thể gây hiểu nhầm. Người đọc có thể hiểu nhầm rằng dict (d) có thể cung cấp cho họ một bản sao với 'd'. Nhưng đó là một bản sao không đầy đủ. Khi chỉ thực hiện các thao tác phím del, điều đó ổn. Nhưng khi bạn muốn làm một cái gì đó khác cho một dict lồng nhau, sửa đổi 'r' bằng cách sử dụng phương thức sao chép đó có thể gây ra thay đổi cho 'd' ban đầu. Để có được một bản sao xác thực, trước tiên bạn cần 'nhập bản sao', sau đó 'r = copy.deepcopy (d)'.
Zen

10
@Zen: Đủ công bằng, tôi đã thêm một lưu ý về bản sao nông và sâu.
Greg Hewgill

264

pop đột biến từ điển.

 >>> lol = {"hello": "gdbye"}
 >>> lol.pop("hello")
     'gdbye'
 >>> lol
     {}

Nếu bạn muốn giữ bản gốc, bạn có thể sao chép nó.


29
"Del" là ok, nhưng theo tôi thì "pop" có vẻ "Pythonic" hơn.
ivanleoncz

1
@ivanleoncz tại sao?
kevr

3
poptrả về giá trị đã được 'bật', cho phép bạn sử dụng giá trị này cho bất kỳ lý do nào khác. Nếu nó không phải là "Pythonic", chắc chắn tôi sẽ nói điều đó có vẻ tốt hơn :). Nó không phải là một dict, nhưng nó hoạt động theo cách tương tự cho cả hai: github.com/ivanlmj/python-prototypes/blob/master/3.4/...
ivanleoncz

2
@ivanleoncz Cũng tốt hơn vì một lý do nữa, popcó thể được cung cấp một giá trị mặc định sẽ được trả về khi thiếu khóa từ dict. Thật tốt khi bạn cần loại bỏ một vài phím nhưng một số trong số chúng có thể bị thiếu; delsẽ ném KeyErrortrong trường hợp như vậy.
itachi

80

Tôi nghĩ rằng giải pháp của bạn là cách tốt nhất để làm điều đó. Nhưng nếu bạn muốn một giải pháp khác, bạn có thể tạo một từ điển mới bằng cách sử dụng các khóa từ từ điển cũ mà không bao gồm khóa được chỉ định của bạn, như thế này:

>>> a
{0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> {i:a[i] for i in a if i!=0}
{1: 'one', 2: 'two', 3: 'three'}

2
Thật tuyệt Tôi thích phương pháp nhanh để lọc từ điển mà không cần xác định chức năng mới.
Joe J

6
Đối với những người không quen thuộc với sự hiểu biết, bạn cũng có thể làm một cái gì đó như thế này: {i:a[i] for i in a if i not in [0, 1, 2]}nếu bạn muốn loại bỏ một số yếu tố.
kmatheny 4/2/2015

9
Tốt hơn {k:v for k,v in a.items() if k != 0}tôi sẽ nghĩ.
rlbond

1
Giải pháp tốt nhất để xóa một mục bằng khóa và trả về kết quả của lệnh mới trong cùng một dòng. Ví dụ: nếu bạn cần sử dụng một lệnh đã được xây dựng mà không có một mục nào như **kwargs,some_function(**{k:v for k,v in some_dict.items() if k not 'some_key'})
Cole

Giải pháp tốt nhất tại đây. Một lớp lót và nó không làm thay đổi từ điển gốc.
Andrew Winterbotham

55

Các del tuyên bố là những gì bạn đang tìm kiếm. Nếu bạn có một từ điển có tên foo với một khóa gọi là 'bar', bạn có thể xóa 'bar' khỏi foo như thế này:

del foo['bar']

Lưu ý rằng điều này vĩnh viễn sửa đổi từ điển đang được vận hành trên. Nếu bạn muốn giữ từ điển gốc, bạn sẽ phải tạo một bản sao trước:

>>> foo = {'bar': 'baz'}
>>> fu = dict(foo)
>>> del foo['bar']
>>> print foo
{}
>>> print fu
{'bar': 'baz'}

Cuộc dictgọi làm cho một bản sao nông. Nếu bạn muốn có một bản sao sâu, sử dụngcopy.deepcopy .

Đây là một phương pháp bạn có thể sao chép và dán, để thuận tiện cho bạn:

def minus_key(key, dictionary):
    shallow_copy = dict(dictionary)
    del shallow_copy[key]
    return shallow_copy

1
@ pythonia29033, thực sự, không . Câu trả lời được chấp nhận hoạt động như mong đợi - nó trả về lệnh không có một khóa. Cách tiếp cận từ câu trả lời này làm thay đổi chính tả ban đầu;) Có một sự khác biệt đáng kể
maxkoryukov

1
@ arussell84, tại sao >>>thường được sử dụng trong các ví dụ python? Vâng, python-doc chứa rất nhiều thứ như vậy. Nhưng mã như vậy không thuận tiện cho copypaste . Tôi bối rối ...
maxkoryukov

@maxkoryukov yup nó làm! nhưng hàm đó và câu trả lời này hoàn toàn giống nhau, ngoại trừ câu trả lời đó nằm trong hàm. và bạn không được mã hóa trong python trong một thời gian, >>>bắt chước ký hiệu nghe từ python ở chế độ cli
pythonia29033

2
@ pythonia29033 về >>>. Vâng, đó là phong cách REPL, nhưng hãy nói chuyện thẳng thắn: người duy nhất đã viết mẫu này và 1000 người đã đọc nó. Tôi nghĩ, sẽ thật tuyệt khi viết các ví dụ theo cách cho phép sao chép và chạy dễ dàng. Tôi không muốn loại bỏ dấu ngoặc góc này bằng tay. Hoặc sao chép từng dòng .. Vì vậy, tôi không hiểu: tại sao các góc này vẫn còn đó))) Tôi có thể không biết gì không?
maxkoryukov

3
Tôi đã thêm một chức năng có thể sao chép / dán, để thuận tiện cho bạn.
arussell84

48

Có rất nhiều câu trả lời hay, nhưng tôi muốn nhấn mạnh một điều.

Bạn có thể sử dụng cả dict.pop()phương thức và delcâu lệnh chung hơn để xóa các mục khỏi từ điển. Cả hai đều biến đổi từ điển gốc, vì vậy bạn cần tạo một bản sao (xem chi tiết bên dưới).

Và cả hai sẽ nâng lên KeyErrornếu khóa bạn cung cấp cho họ không có trong từ điển:

key_to_remove = "c"
d = {"a": 1, "b": 2}
del d[key_to_remove]  # Raises `KeyError: 'c'`

key_to_remove = "c"
d = {"a": 1, "b": 2}
d.pop(key_to_remove)  # Raises `KeyError: 'c'`

Bạn phải chăm sóc điều này:

bằng cách nắm bắt ngoại lệ:

key_to_remove = "c"
d = {"a": 1, "b": 2}
try:
    del d[key_to_remove]
except KeyError as ex:
    print("No such key: '%s'" % ex.message)

key_to_remove = "c"
d = {"a": 1, "b": 2}
try:
    d.pop(key_to_remove)
except KeyError as ex:
    print("No such key: '%s'" % ex.message)

bằng cách thực hiện kiểm tra:

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    del d[key_to_remove]

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    d.pop(key_to_remove)

nhưng pop()cũng có một cách ngắn gọn hơn nhiều - cung cấp giá trị trả về mặc định:

key_to_remove = "c"
d = {"a": 1, "b": 2}
d.pop(key_to_remove, None)  # No `KeyError` here

Trừ khi bạn sử dụng pop()để lấy giá trị của khóa bị xóa, bạn có thể cung cấp mọi thứ, không cần thiết None. Mặc dù có thể là việc sử dụng delvới inkiểm tra nhanh hơn một chút do pop()là một chức năng với các biến chứng của chính nó gây ra chi phí. Thông thường không phải vậy, vì vậy pop()với giá trị mặc định là đủ tốt.


Đối với câu hỏi chính, bạn sẽ phải tạo một bản sao của từ điển của mình, để lưu từ điển gốc và có một từ mới mà không cần xóa khóa.

Một số người khác ở đây đề nghị tạo một bản sao đầy đủ (sâu) copy.deepcopy(), có thể là một bản sao quá mức, một bản sao "bình thường" (nông), sử dụng copy.copy()hoặcdict.copy() , có thể là đủ. Từ điển giữ một tham chiếu đến đối tượng làm giá trị cho khóa. Vì vậy, khi bạn xóa khóa khỏi từ điển, tham chiếu này sẽ bị xóa, không phải đối tượng được tham chiếu. Bản thân đối tượng có thể được xóa tự động sau đó bởi trình thu gom rác, nếu không có tài liệu tham khảo nào khác cho nó trong bộ nhớ. Tạo một bản sao sâu đòi hỏi nhiều tính toán hơn so với bản sao nông, do đó nó làm giảm hiệu suất mã bằng cách tạo bản sao, lãng phí bộ nhớ và cung cấp nhiều công việc hơn cho GC, đôi khi bản sao nông là đủ.

Tuy nhiên, nếu bạn có các đối tượng có thể thay đổi làm giá trị từ điển và có kế hoạch sửa đổi chúng sau này trong từ điển được trả về mà không có khóa, bạn phải tạo một bản sao sâu.

Với bản sao nông:

def get_dict_wo_key(dictionary, key):
    """Returns a **shallow** copy of the dictionary without a key."""
    _dict = dictionary.copy()
    _dict.pop(key, None)
    return _dict


d = {"a": [1, 2, 3], "b": 2, "c": 3}
key_to_remove = "c"

new_d = get_dict_wo_key(d, key_to_remove)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3], "b": 2}
new_d["a"].append(100)
print(d)  # {"a": [1, 2, 3, 100], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2}
new_d["b"] = 2222
print(d)  # {"a": [1, 2, 3, 100], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2222}

Với bản sao sâu:

from copy import deepcopy


def get_dict_wo_key(dictionary, key):
    """Returns a **deep** copy of the dictionary without a key."""
    _dict = deepcopy(dictionary)
    _dict.pop(key, None)
    return _dict


d = {"a": [1, 2, 3], "b": 2, "c": 3}
key_to_remove = "c"

new_d = get_dict_wo_key(d, key_to_remove)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3], "b": 2}
new_d["a"].append(100)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2}
new_d["b"] = 2222
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2222}

21

Làm thế nào tôi có thể xóa một mục từ từ điển để trả về một bản sao (nghĩa là không sửa đổi bản gốc)?

A dictlà cấu trúc dữ liệu sai để sử dụng cho việc này.

Chắc chắn, sao chép dict và popping từ bản sao hoạt động, và do đó, việc xây dựng một dict mới với sự hiểu biết, nhưng tất cả việc sao chép đó đều mất thời gian, bạn đã thay thế một hoạt động thời gian liên tục bằng một hoạt động thời gian tuyến tính. Và tất cả những bản sao còn sống cùng một lúc chiếm không gian tuyến tính trên mỗi bản sao.

Các cấu trúc dữ liệu khác, như các mảng băm được ánh xạ , được thiết kế cho chính xác loại trường hợp sử dụng này: thêm hoặc xóa một phần tử trả về một bản sao trong thời gian logarit, chia sẻ hầu hết lưu trữ của nó với bản gốc . 1

Tất nhiên có một số nhược điểm. Hiệu suất là logarit chứ không phải là hằng số (mặc dù với một cơ sở lớn, thường là 32-128). Và, trong khi bạn có thể làm cho API không biến đổi giống hệt nhau dict, API "đột biến" rõ ràng là khác nhau. Và, hầu hết, không có pin HAMT đi kèm với Python. 2

Các pyrsistent thư viện là một thực hiện khá vững chắc của HAMT dựa trên dict-thay thế (và các loại khác nhau) cho Python. Nó thậm chí còn có API evolver tiện lợi để chuyển mã đột biến hiện tại sang mã liên tục một cách trơn tru nhất có thể. Nhưng nếu bạn muốn rõ ràng về việc trả lại các bản sao thay vì đột biến, bạn chỉ cần sử dụng nó như thế này:

>>> from pyrsistent import m
>>> d1 = m(a=1, b=2)
>>> d2 = d1.set('c', 3)
>>> d3 = d1.remove('a')
>>> d1
pmap({'a': 1, 'b': 2})
>>> d2
pmap({'c': 3, 'a': 1, 'b': 2})
>>> d3
pmap({'b': 2})

Đó d3 = d1.remove('a')chính xác là những gì câu hỏi đang yêu cầu.

Nếu bạn có các cấu trúc dữ liệu có thể thay đổi như dictlistđược nhúng vào pmap, bạn vẫn sẽ gặp phải các vấn đề bí danh, bạn chỉ có thể khắc phục điều đó bằng cách không thay đổi trong suốt quá trình, nhúngpmap s và pvectors.


1. HAMT cũng đã trở nên phổ biến trong các ngôn ngữ như Scala, Clojure, Haskell vì chúng chơi rất độc đáo với lập trình không khóa và bộ nhớ giao dịch phần mềm, nhưng cả hai ngôn ngữ này đều không liên quan đến Python.

2. Trong thực tế, có một HAMT trong stdlib, được sử dụng trong việc thực hiện contextvars. PEP rút trước đó giải thích lý do. Nhưng đây là một chi tiết triển khai ẩn của thư viện, không phải là một loại bộ sưu tập công cộng.


19
d = {1: 2, '2': 3, 5: 7}
del d[5]
print 'd = ', d

Kết quả: d = {1: 2, '2': 3}


14

Chỉ cần gọi del d ['key'].

Tuy nhiên, trong sản xuất, việc kiểm tra xem 'khóa' có tồn tại trong d hay không.

if 'key' in d:
    del d['key']

7
Hmm, không, trong sản xuất, tốt hơn là tuân theo hệ tư tưởng EAFP . Chỉ cần loại bỏ chìa khóa trong try-exceptkhối. Ít nhất, đây sẽ là một hoạt động nguyên tử;)
maxkoryukov

1
Và nếu bạn muốn ngắn gọn - sử dụng d.pop('key', None), đó là oneliner. Nhưng câu hỏi thực tế là về việc lấy từ điển mà không cần một khóa, và không phải sửa đổi từ điển. Vì vậy, hiểu - là một lựa chọn tốt ở đây;)
maxkoryukov

7

Không, không có cách nào khác ngoài

def dictMinus(dct, val):
   copy = dct.copy()
   del copy[val]
   return copy

Tuy nhiên, thường tạo ra các bản sao của từ điển chỉ thay đổi một chút có lẽ không phải là một ý tưởng tốt bởi vì nó sẽ dẫn đến nhu cầu bộ nhớ tương đối lớn. Thông thường tốt hơn là đăng nhập từ điển cũ (nếu cần thiết) và sau đó sửa đổi nó.


7
# mutate/remove with a default
ret_val = body.pop('key', 5)
# no mutation with a default
ret_val = body.get('key', 5)

5
>>> def delete_key(dict, key):
...     del dict[key]
...     return dict
... 
>>> test_dict = {'one': 1, 'two' : 2}
>>> print delete_key(test_dict, 'two')
{'one': 1}
>>>

Điều này không thực hiện bất kỳ xử lý lỗi nào, nó giả sử khóa nằm trong lệnh, bạn có thể muốn kiểm tra trước và raisenếu không


10
Phương pháp của bạn khác biệt del test_dict[key]như thế nào?
Nhà vật lý điên

5

Đây là một phương pháp thiết kế cấp cao nhất:

def eraseElement(d,k):
    if isinstance(d, dict):
        if k in d:
            d.pop(k)
            print(d)
        else:
            print("Cannot find matching key")
    else:
        print("Not able to delete")


exp = {'A':34, 'B':55, 'C':87}
eraseElement(exp, 'C')

Tôi đang chuyển từ điển và khóa tôi muốn vào chức năng của mình, xác thực nếu đó là từ điển và nếu khóa đó ổn, và nếu cả hai tồn tại, hãy xóa giá trị khỏi từ điển và in ra phần còn lại.

Đầu ra: {'B': 55, 'A': 34}

Mong rằng sẽ giúp!


3

Đoạn mã dưới đây sẽ giúp bạn chắc chắn, tôi đã thêm nhận xét trong mỗi dòng sẽ giúp bạn hiểu mã.

def execute():
   dic = {'a':1,'b':2}
   dic2 = remove_key_from_dict(dic, 'b')  
   print(dict2)           # {'a': 1}
   print(dict)            # {'a':1,'b':2}

def remove_key_from_dict(dictionary_to_use, key_to_delete):
   copy_of_dict = dict(dictionary_to_use)     # creating clone/copy of the dictionary
   if key_to_delete in copy_of_dict :         # checking given key is present in the dictionary
       del copy_of_dict [key_to_delete]       # deleting the key from the dictionary 
   return copy_of_dict                        # returning the final dictionary

hoặc bạn cũng có thể sử dụng dict.pop ()

d = {"a": 1, "b": 2}

res = d.pop("c")  # No `KeyError` here
print (res)       # this line will not execute

hoặc cách tiếp cận tốt hơn là

res = d.pop("c", "key not found")
print (res)   # key not found
print (d)     # {"a": 1, "b": 2}

res = d.pop("b", "key not found")
print (res)   # 2
print (d)     # {"a": 1}

2

Đây là một biến thể khác sử dụng hiểu danh sách:

original_d = {'a': None, 'b': 'Some'}
d = dict((k,v) for k, v in original_d.iteritems() if v)
# result should be {'b': 'Some'}

Cách tiếp cận dựa trên câu trả lời từ bài đăng này: Cách hiệu quả để loại bỏ các khóa có chuỗi trống từ một lệnh chính tả


1
Nếu bạn sẽ trả lời một câu hỏi lâu năm đã có câu trả lời đơn giản, phù hợp, được chấp nhận, ít nhất hãy đảm bảo câu trả lời của bạn là đúng. Điều này không làm những gì OP yêu cầu.
user2357112 hỗ trợ Monica

Tôi thường không kiểm tra ngày đối với các câu hỏi mà tôi nghĩ có thể có thông tin có giá trị được thêm vào chúng. Thêm vào đó, mỗi một trong những ý kiến về câu hỏi tôi có liên quan đến: "Thông thường đây là chính xác những gì ai đó muốn và có lẽ là những gì các nhu cầu của OP, nhưng nó không phải là những gì OP hỏi cho" stackoverflow.com/questions/12118695/... tôi biết đó không phải là câu trả lời trực tiếp cho câu hỏi; thay vì mở rộng các tùy chọn.
BigBlueHat

3
Câu trả lời này, mặc dù không đầy đủ, cho chúng tôi biết rằng chúng tôi có thể xóa các mục nếu có điều kiện. chỉ thay đổi if vđể if k is not 'a'trả lời op. Nhưng tôi không nghĩ đó là một cách hiệu quả, điều này loại bỏ phần tử trong O (n) thay vì O (log n) như pop hoặc del.
holgac

0
    species = {'HI': {'1': (1215.671, 0.41600000000000004),
  '10': (919.351, 0.0012),
  '1025': (1025.722, 0.0791),
  '11': (918.129, 0.0009199999999999999),
  '12': (917.181, 0.000723),
  '1215': (1215.671, 0.41600000000000004),
  '13': (916.429, 0.0005769999999999999),
  '14': (915.824, 0.000468),
  '15': (915.329, 0.00038500000000000003),
 'CII': {'1036': (1036.3367, 0.11900000000000001), '1334': (1334.532, 0.129)}}

Đoạn mã sau sẽ tạo một bản sao của dict speciesvà xóa các mục không có trongtrans_HI

trans_HI=['1025','1215']
for transition in species['HI'].copy().keys():
    if transition not in trans_HI:
        species['HI'].pop(transition)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.