Tại sao [] nhanh hơn list ()?

724
Augusta 2015-05-14 03:16.

Gần đây tôi đã so sánh tốc độ xử lý của []list()và đã rất ngạc nhiên phát hiện ra rằng []chạy hơn ba lần nhanh hơn list(). Tôi đã chạy thử nghiệm tương tự với {}dict()và kết quả thực tế giống hệt nhau: []{}cả hai đều mất khoảng 0,128 giây / triệu chu kỳ, trong khi list()dict()mất khoảng 0,428 giây / triệu chu kỳ mỗi lần.

Tại sao thế này? Làm []{}(và có lẽ ()'', quá) ngay lập tức vượt qua trở lại một bản sao của một số cổ phiếu đen trống rỗng trong khi các đối tác một cách rõ ràng tên của họ ( list(), dict(), tuple(), str()) đầy đủ đi về việc tạo ra một đối tượng, dù có hoặc không thực sự có yếu tố?

Tôi không biết hai phương pháp này khác nhau như thế nào nhưng tôi rất muốn tìm hiểu. Tôi không thể tìm thấy câu trả lời trong tài liệu hoặc trên SO và việc tìm kiếm các dấu ngoặc trống hóa ra có vấn đề hơn tôi mong đợi.

Tôi nhận được kết quả thời gian của mình bằng cách gọi timeit.timeit("[]")timeit.timeit("list()"), và timeit.timeit("{}")timeit.timeit("dict()")để so sánh danh sách và từ điển, tương ứng. Tôi đang chạy Python 2.7.9.

Gần đây tôi phát hiện ra " Tại sao if True lại chậm hơn if 1? " Mà so sánh hiệu suất của if Trueđể if 1và dường như chạm vào một tương tự đen-versus-toàn cầu kịch bản; có lẽ nó cũng đáng được xem xét.

5 answers

770
Martijn Pieters 2015-05-14 03:21.

Bởi vì []{}cú pháp theo nghĩa đen . Python có thể tạo bytecode chỉ để tạo danh sách hoặc các đối tượng từ điển:

>>> import dis
>>> dis.dis(compile('[]', '', 'eval'))
  1           0 BUILD_LIST               0
              3 RETURN_VALUE        
>>> dis.dis(compile('{}', '', 'eval'))
  1           0 BUILD_MAP                0
              3 RETURN_VALUE        

list()dict()là những đối tượng riêng biệt. Tên của chúng cần được giải quyết, ngăn xếp phải được tham gia để đẩy các đối số, khung phải được lưu trữ để truy xuất sau này và phải thực hiện lệnh gọi. Tất cả đều mất nhiều thời gian hơn.

Đối với trường hợp trống, điều đó có nghĩa là bạn có ít nhất a LOAD_NAME(phải tìm kiếm thông qua không gian tên chung cũng như builtinsmô-đun ) theo sau là a CALL_FUNCTION, phải bảo toàn khung hiện tại:

>>> dis.dis(compile('list()', '', 'eval'))
  1           0 LOAD_NAME                0 (list)
              3 CALL_FUNCTION            0
              6 RETURN_VALUE        
>>> dis.dis(compile('dict()', '', 'eval'))
  1           0 LOAD_NAME                0 (dict)
              3 CALL_FUNCTION            0
              6 RETURN_VALUE        

Bạn có thể đặt thời gian tra cứu tên riêng với timeit:

>>> import timeit
>>> timeit.timeit('list', number=10**7)
0.30749011039733887
>>> timeit.timeit('dict', number=10**7)
0.4215109348297119

Sự khác biệt về thời gian có thể là do xung đột băm từ điển. Trừ thời gian đó cho số lần gọi các đối tượng đó và so sánh kết quả với thời gian sử dụng các ký tự:

>>> timeit.timeit('[]', number=10**7)
0.30478692054748535
>>> timeit.timeit('{}', number=10**7)
0.31482696533203125
>>> timeit.timeit('list()', number=10**7)
0.9991960525512695
>>> timeit.timeit('dict()', number=10**7)
1.0200958251953125

Vì vậy, phải gọi đối tượng mất thêm một 1.00 - 0.31 - 0.30 == 0.39giây trên 10 triệu cuộc gọi.

Bạn có thể tránh chi phí tra cứu toàn cầu bằng cách đặt tên chung là tên địa phương (sử dụng cách timeitthiết lập, mọi thứ bạn liên kết với tên đều là tên địa phương):

>>> timeit.timeit('_list', '_list = list', number=10**7)
0.1866450309753418
>>> timeit.timeit('_dict', '_dict = dict', number=10**7)
0.19016098976135254
>>> timeit.timeit('_list()', '_list = list', number=10**7)
0.841480016708374
>>> timeit.timeit('_dict()', '_dict = dict', number=10**7)
0.7233691215515137

nhưng bạn không bao giờ có thể vượt qua CALL_FUNCTIONchi phí đó .

154
Dan D. 2015-05-14 03:22.

list()yêu cầu tra cứu toàn cục và một lệnh gọi hàm nhưng []biên dịch thành một lệnh duy nhất. Xem:

Python 2.7.3
>>> import dis
>>> dis.dis(lambda: list())
  1           0 LOAD_GLOBAL              0 (list)
              3 CALL_FUNCTION            0
              6 RETURN_VALUE        
>>> dis.dis(lambda: [])
  1           0 BUILD_LIST               0
              3 RETURN_VALUE        
74
Torxed 2015-05-14 03:21.

Bởi vì listlà một hàm để chuyển đổi nói một chuỗi thành một đối tượng danh sách, trong khi []được sử dụng để tạo một danh sách tắt. Hãy thử điều này (có thể có ý nghĩa hơn đối với bạn):

x = "wham bam"
a = list(x)
>>> a
["w", "h", "a", "m", ...]

Trong khi

y = ["wham bam"]
>>> y
["wham bam"]

Cung cấp cho bạn một danh sách thực tế chứa bất cứ thứ gì bạn đưa vào đó.

23
Dimitris Fasarakis Hilliard 2016-12-03 09:01.

Các câu trả lời ở đây là tuyệt vời, đến mức và bao hàm đầy đủ câu hỏi này. Tôi sẽ giảm một bước nữa từ mã byte cho những người quan tâm. Tôi đang sử dụng repo gần đây nhất của CPython; các phiên bản cũ hoạt động tương tự về mặt này nhưng có thể có những thay đổi nhỏ.

Dưới đây là bảng phân tích việc thực thi cho từng cái này, BUILD_LISTcho []CALL_FUNCTIONcho list().


Các BUILD_LISThướng dẫn:

Bạn chỉ nên xem kinh dị:

PyObject *list =  PyList_New(oparg);
if (list == NULL)
    goto error;
while (--oparg >= 0) {
    PyObject *item = POP();
    PyList_SET_ITEM(list, oparg, item);
}
PUSH(list);
DISPATCH();

Rất phức tạp, tôi biết. Đây là cách nó đơn giản:

  • Tạo một danh sách mới với PyList_New(điều này chủ yếu cấp phát bộ nhớ cho một đối tượng danh sách mới), opargbáo hiệu số lượng đối số trên ngăn xếp. Đi thẳng vào vấn đề.
  • Kiểm tra xem không có gì sai if (list==NULL).
  • Thêm bất kỳ đối số nào (trong trường hợp của chúng tôi, điều này không được thực thi) nằm trên ngăn xếp với PyList_SET_ITEM(macro).

Không có gì ngạc nhiên khi nó là nhanh chóng! Nó được tùy chỉnh để tạo danh sách mới, không có gì khác :-)

Các CALL_FUNCTIONhướng dẫn:

Đây là điều đầu tiên bạn nhìn thấy khi xem qua xử lý mã CALL_FUNCTION:

PyObject **sp, *res;
sp = stack_pointer;
res = call_function(&sp, oparg, NULL);
stack_pointer = sp;
PUSH(res);
if (res == NULL) {
    goto error;
}
DISPATCH();

Trông khá vô hại, phải không? Chà, không, tiếc là không, call_functionkhông phải là một anh chàng thẳng thắn sẽ gọi hàm ngay lập tức, nó không thể. Thay vào đó, nó lấy đối tượng từ ngăn xếp, lấy tất cả các đối số của ngăn xếp và sau đó chuyển đổi dựa trên loại đối tượng; nó có phải là:

Chúng tôi đang gọi listkiểu, đối số được truyền vào call_functionPyList_Type. CPython bây giờ phải gọi một hàm chung để xử lý bất kỳ đối tượng có thể gọi nào được đặt tên _PyObject_FastCallKeywords, đồng thời có nhiều lệnh gọi hàm hơn.

Hàm này lại thực hiện một số kiểm tra đối với một số loại hàm nhất định (mà tôi không thể hiểu tại sao) và sau đó, sau khi tạo một dict cho kwargs nếu cần , tiếp tục gọi _PyObject_FastCallDict.

_PyObject_FastCallDictcuối cùng đưa chúng ta đến một nơi nào đó! Sau khi thực hiện nhiều lần kiểm tra hơn nữa,sẽ lấy tp_callvị trí từ cáitypetypechúng tôi đã vượt qua, tức là nó lấy type.tp_call. Sau đó, nó tiến hành tạo một bộ trong số các đối số được truyền vào _PyStack_AsTuplevà cuối cùng, một cuộc gọi cuối cùng cũng có thể được thực hiện !

tp_call, kết quả phù hợp type.__call__sẽ tiếp nhận và cuối cùng tạo đối tượng danh sách. Nó gọi các danh sách __new__tương ứng PyType_GenericNewvà cấp phát bộ nhớ cho nó với PyType_GenericAlloc: Đây thực sự là phần mà nó bắt kịp PyList_New, cuối cùng . Tất cả những thứ trước là cần thiết để xử lý các đối tượng theo kiểu chung.

Cuối cùng, type_callgọi list.__init__và khởi tạo danh sách với bất kỳ đối số có sẵn nào, sau đó chúng ta tiếp tục quay trở lại như cách chúng ta đã đến. :-)

Cuối cùng, sửa lại LOAD_NAME, đó là một người khác đóng góp ở đây.


Dễ dàng thấy rằng, khi xử lý đầu vào của chúng ta, Python thường phải nhảy qua các vòng để thực sự tìm ra Chàm thích hợp để thực hiện công việc. Nó không có nghĩa là gọi nó ngay lập tức vì nó năng động, ai đó có thể đeo mặt nạ list( và cậu bé thì nhiều người làm vậy ) và phải đi một con đường khác.

Đây là nơi list()mất nhiều thứ: Việc khám phá Python cần phải làm để tìm hiểu xem nó nên làm gì.

Mặt khác, cú pháp nghĩa đen có nghĩa chính xác; nó không thể thay đổi và luôn hoạt động theo cách được xác định trước.

Chú thích: Tất cả các tên hàm có thể thay đổi từ bản phát hành này sang bản phát hành khác. Vấn đề vẫn còn tồn tại và rất có thể sẽ tồn tại trong bất kỳ phiên bản nào trong tương lai, đó là chế độ tra cứu năng động làm chậm mọi thứ.

14
Aaron Hall 2017-11-28 04:20.

Tại sao []nhanh hơn list()?

Lý do lớn nhất là Python xử lý list()giống như một hàm do người dùng định nghĩa, có nghĩa là bạn có thể chặn nó bằng cách đặt biệt hiệu cho một thứ khác listvà làm điều gì đó khác (như sử dụng danh sách phân lớp của riêng bạn hoặc có thể là một deque).

Nó ngay lập tức tạo một phiên bản mới của danh sách nội trang với [].

Lời giải thích của tôi tìm cách cung cấp cho bạn trực giác về điều này.

Giải trình

[] thường được gọi là cú pháp theo nghĩa đen.

Trong ngữ pháp, đây được gọi là "hiển thị danh sách". Từ các tài liệu :

Hiển thị danh sách là một chuỗi các biểu thức có thể trống được đặt trong dấu ngoặc vuông:

list_display ::=  "[" [starred_list | comprehension] "]"

Hiển thị danh sách tạo ra một đối tượng danh sách mới, nội dung được chỉ định bởi danh sách các biểu thức hoặc một cách hiểu. Khi danh sách các biểu thức được phân tách bằng dấu phẩy được cung cấp, các phần tử của nó được đánh giá từ trái sang phải và được đặt vào đối tượng danh sách theo thứ tự đó. Khi một nội dung hiểu được cung cấp, danh sách được xây dựng từ các yếu tố tạo ra từ hiểu biết.

Nói tóm lại, điều này có nghĩa là một đối tượng kiểu nội trang listđược tạo ra.

Không có gì phá vỡ điều này - có nghĩa là Python có thể làm điều đó nhanh nhất có thể.

Mặt khác, list()có thể bị chặn tạo nội trang listbằng cách sử dụng hàm tạo danh sách nội trang.

Ví dụ: giả sử chúng tôi muốn danh sách của mình được tạo ồn ào:

class List(list):
    def __init__(self, iterable=None):
        if iterable is None:
            super().__init__()
        else:
            super().__init__(iterable)
        print('List initialized.')

Sau đó, chúng tôi có thể chặn tên listtrên phạm vi toàn cầu cấp mô-đun và sau đó khi chúng tôi tạo một list, chúng tôi thực sự tạo danh sách kiểu con của mình:

>>> list = List
>>> a_list = list()
List initialized.
>>> type(a_list)
<class '__main__.List'>

Tương tự, chúng tôi có thể xóa nó khỏi không gian tên chung

del list

và đặt nó vào không gian tên nội trang:

import builtins
builtins.list = List

Và bây giờ:

>>> list_0 = list()
List initialized.
>>> type(list_0)
<class '__main__.List'>

Và lưu ý rằng hiển thị danh sách tạo ra một danh sách vô điều kiện:

>>> list_1 = []
>>> type(list_1)
<class 'list'>

Chúng tôi có thể chỉ làm điều này tạm thời, vì vậy hãy hoàn tác các thay đổi của chúng tôi - trước tiên hãy xóa Listđối tượng mới khỏi nội trang:

>>> del builtins.list
>>> builtins.list
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'builtins' has no attribute 'list'
>>> list()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'list' is not defined

Ồ, không, chúng tôi đã mất dấu bản gốc.

Đừng lo lắng, chúng ta vẫn có thể nhận được list- đó là loại danh sách theo nghĩa đen:

>>> builtins.list = type([])
>>> list()
[]

Vì thế...

Tại sao []nhanh hơn list()?

Như chúng ta đã thấy - chúng ta có thể ghi đè list- nhưng chúng ta không thể chặn việc tạo ra kiểu chữ. Khi sử dụng listchúng ta phải tra cứu xem có gì không.

Sau đó, chúng ta phải gọi bất cứ thứ gì có thể gọi được mà chúng ta đã tra cứu. Từ ngữ pháp:

Một cuộc gọi gọi một đối tượng có thể gọi (ví dụ: một hàm) với một loạt các đối số có thể trống:

call                 ::=  primary "(" [argument_list [","] | comprehension] ")"

Chúng ta có thể thấy rằng nó làm điều tương tự cho bất kỳ tên nào, không chỉ danh sách:

>>> import dis
>>> dis.dis('list()')
  1           0 LOAD_NAME                0 (list)
              2 CALL_FUNCTION            0
              4 RETURN_VALUE
>>> dis.dis('doesnotexist()')
  1           0 LOAD_NAME                0 (doesnotexist)
              2 CALL_FUNCTION            0
              4 RETURN_VALUE

[]không có lệnh gọi hàm nào ở cấp mã byte Python:

>>> dis.dis('[]')
  1           0 BUILD_LIST               0
              2 RETURN_VALUE

Nó chỉ đơn giản là đi thẳng vào việc xây dựng danh sách mà không cần bất kỳ tra cứu hoặc cuộc gọi nào ở cấp bytecode.

Phần kết luận

Chúng tôi đã chứng minh rằng listcó thể bị chặn bằng mã người dùng bằng cách sử dụng các quy tắc xác định phạm vi và list()tìm kiếm một mã có thể gọi và sau đó gọi nó.

Trong khi đó []là hiển thị danh sách, hoặc theo nghĩa đen và do đó tránh được việc tra cứu tên và gọi hàm.

Related questions

MORE COOL STUFF

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett đã bất chấp những lời khuyên hẹn hò điển hình khi cô gặp chồng mình.

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Michael Sheen là một diễn viên phi lợi nhuận nhưng chính xác thì điều đó có nghĩa là gì?

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Ngôi sao của Hallmark Colin Egglesfield chia sẻ về những cuộc gặp gỡ với người hâm mộ ly kỳ tại RomaDrama Live! cộng với chương trình INSPIRE của anh ấy tại đại hội.

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Bạn sẽ phải phủi sạch đầu đĩa Blu-ray hoặc DVD để xem tại sao Northern Exposure trở thành một trong những chương trình nổi tiếng nhất của thập niên 90.

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

8 công dụng tuyệt vời của Baking Soda và Giấm

8 công dụng tuyệt vời của Baking Soda và Giấm

Bạn biết đấy, hai sản phẩm này là nguồn điện để làm sạch, riêng chúng. Nhưng cùng với nhau, chúng có một loạt công dụng hoàn toàn khác.

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Thủy điện rất cần thiết cho lưới điện của Hoa Kỳ, nhưng nó chỉ tạo ra năng lượng khi có nước di chuyển. Bao nhiêu nhà máy thủy điện có thể gặp nguy hiểm khi các hồ và sông cạn kiệt?

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Tóc tỉa từ các tiệm và các khoản quyên góp cá nhân có thể được tái sử dụng như những tấm thảm thấm dầu và giúp bảo vệ môi trường.

Một thương hiệu kế thừa khác có được cuộc sống mới khi những người chủ mới của Black Opal có được Hội chợ thời trang

Một thương hiệu kế thừa khác có được cuộc sống mới khi những người chủ mới của Black Opal có được Hội chợ thời trang

Desiree Rogers, trái, và Cheryl Mayberry McKissack Chuyển sang, Fenty Beauty và Pat McGrath, có một đế chế làm đẹp mới do phụ nữ làm chủ đang trỗi dậy. Sau thương vụ mua lại mỹ phẩm Black Opal vào tháng 9, đội ngũ quyền lực của Giám đốc điều hành công ty Desiree Rogers và Chủ tịch Cheryl Mayberry McKissack đã thông báo mua lại thương hiệu làm đẹp tiên phong Fashion Fair từ người sáng lập Johnson Publishing Company (JPC).

Ngoài việc trở nên vô nghĩa, các bên bộc lộ giới tính giờ đây đã trở nên chết người

Ngoài việc trở nên vô nghĩa, các bên bộc lộ giới tính giờ đây đã trở nên chết người

Đối với những người có quá nhiều thời gian, tiệc tiết lộ giới tính là một cách thú vị để không cần áp đặt những định kiến ​​hạn chế lên thai nhi trước một đối tượng bạn bè giả vờ quan tâm một cách lịch sự. Tuy nhiên, việc chỉ dựa vào những chiếc bánh nướng có màu nhân tạo để biểu thị một tiếng hoo-ha hoặc một đứa trẻ đi tè đã trở nên xa xỉ trong các bậc cha mẹ trên Instagram.

Kirstjen Nielsen đang cố gắng đổi mới bản thân với tư cách là một người phụ nữ 'nói ra sự thật cho sức mạnh'

Kirstjen Nielsen đang cố gắng đổi mới bản thân với tư cách là một người phụ nữ 'nói ra sự thật cho sức mạnh'

Hôm thứ Tư, cựu Bộ trưởng An ninh Nội địa Kirstjen Nielsen vì một lý do nào đó đã được đưa ra một diễn đàn để phát biểu tại Hội nghị thượng đỉnh Những người phụ nữ quyền lực nhất của Fortune. Và có thể đoán trước được, người phụ nữ giám sát các nỗ lực (đang diễn ra) của chính quyền Trump nhằm chia cắt các gia đình ở biên giới đã sử dụng thời gian của mình như một cơ hội để tự bảo vệ mình và tự nhận mình là người — và hãy hít thở sâu ở đây — “đã nói sự thật với quyền lực.

Toyota Racing Development sẽ cho ra mắt Supra 3000GT Concept, Massive Wing vào tháng tới

Toyota Racing Development sẽ cho ra mắt Supra 3000GT Concept, Massive Wing vào tháng tới

Vì thế hệ mới nhất của chiếc xe điều chỉnh được yêu thích trên thế giới hiện đã có mặt tại đây, nên chỉ có điều kiện là Toyota Racing Development sẽ đưa phiên bản Supra thế hệ thứ năm sửa đổi của riêng mình đến SEMA vào tháng tới. Toyota đã cho chúng tôi một gợi ý về những gì sẽ xảy ra trong tuần này: một chiếc Concept GR Supra 3000GT hiện đại.

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Rothschild's luggage got lost, but luckily she has an incredible closet to shop: Sister Paris Hilton's!

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa. Từ Hollywood đến New York và mọi nơi ở giữa, hãy xem các ngôi sao yêu thích của bạn đang làm gì!

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

Các nhà điều tra đang xem xét liệu nhóm và nghi phạm có biết nhau trước vụ tấn công hay không

Thanh thiếu niên, Gia đình Florida Hội đồng quản trị trường học về Luật 'Không nói đồng tính': 'Buộc chúng tôi tự kiểm duyệt'

Thanh thiếu niên, Gia đình Florida Hội đồng quản trị trường học về Luật 'Không nói đồng tính': 'Buộc chúng tôi tự kiểm duyệt'

Vụ kiện, nêu tên một số học khu, lập luận rằng dự luật "Không nói đồng tính" được ban hành gần đây của Florida "có hiệu quả im lặng và xóa bỏ học sinh và gia đình LGBTQ +"

Đường băng hạ cánh

Đường băng hạ cánh

Cuối hè đầu thu là mùa hoài niệm. Những chiếc đèn đường chiếu ánh sáng của chúng qua những con đường đẫm mưa, và những chiếc lá dưới chân - màu đỏ cam tắt trong bóng chạng vạng - là lời nhắc nhở về những ngày đã qua.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Vào năm 2021, tôi khuyến khích bạn suy nghĩ lại mọi thứ bạn biết về khách hàng mà bạn phục vụ và những câu chuyện bạn kể cho họ. Lùi lại.

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Vào ngày sinh nhật thứ 9 của Felix The Cat, tôi nhớ về một trong những mất mát lớn nhất trong cuộc đời trưởng thành của tôi - Sophie của tôi vào năm 2013. Tôi đã viết bài luận này và chia sẻ nó trên nền tảng này một thời gian ngắn vào năm 2013.

Khi bạn không thể trở thành người mà Internet muốn bạn trở thành

Khi bạn không thể trở thành người mà Internet muốn bạn trở thành

Tôi ghét từ "tàu đắm". Mọi người cảm thấy thoải mái trong la bàn đạo đức của riêng mình, và khi làm như vậy, họ thấy mình vượt qua sự phán xét.

Language