2013-08-04 37 views
7

Các chuỗi trong Python là không thay đổi và hỗ trợ giao diện bộ đệm. Vì vậy, nó sẽ là hiệu quả để trả về không phải là chuỗi mới, nhưng các phần của chuỗi cũ khi sử dụng phương thức slices hoặc split(). Nhưng, cho đến nay như tôi biết, đối tượng chuỗi mới được xây dựng mỗi lần. Tại sao nó như vậy? Lý do duy nhất tôi thấy là nó có thể làm cho một bộ sưu tập rác một chút khó khăn hơn.Chuỗi bất biến của Python và các lát của chúng

Đúng: theo định kỳ thông thường, phí trên bộ nhớ là tuyến tính và không đáng chú ý: sao chép nhanh, tôi tin là phân bổ. Nhưng có quá nhiều việc được thực hiện trong python để nói rằng nó không đáng để nỗ lực!

EDIT:

Dường như việc sử dụng cách này sẽ làm cho việc quản lý bộ nhớ trở nên phức tạp hơn. Trường hợp, trong đó 1/5 của chuỗi tùy ý chỉ được sử dụng, và chúng ta không thể deallocate toàn bộ chuỗi, là một ví dụ simle. Chúng tôi có thể cải thiện cấp phát bộ nhớ, do đó, nó sẽ có thể deallocate dây một phần, nhưng nó sẽ có lẽ chủ yếu là disprovement. Tất cả các chức năng tiêu chuẩn có thể được mô phỏng bằng bộ đệm hoặc bộ nhớ, nếu việc sử dụng bộ nhớ cực kỳ quan trọng. Có, mã sẽ không được như vậy ngắn gọn, nhưng chúng ta phải từ bỏ một cái gì đó để có được một cái gì đó.

+1

Bạn sẽ trả về các phần của chuỗi như thế nào? Bạn có nghĩa là con trỏ đến nó? điều gì sẽ xảy ra với chuỗi con nếu chuỗi gốc bị xóa? –

+0

Điều gì sẽ trở thành bộ đệm, nếu đối tượng gốc bị xóa? Tôi nghĩ, bộ sưu tập rác là đủ thông minh và không xóa đối tượng gốc. – gukoff

+0

Tôi tin rằng câu hỏi của bạn là bản sao của [Nếu chuỗi không thay đổi trong .NET, thì tại sao chuỗi con lại sử dụng thời gian O (n)?] (Http://stackoverflow.com/questions/6742923/if-strings-are-immutable -in-net-then-why-does-substring-take-on-time). Cùng một đối số hợp lệ cho python. – Bakuriu

Trả lời

2

Các biểu diễn chuỗi bên dưới là null bị chấm dứt, mặc dù nó theo dõi độ dài, do đó bạn không thể có đối tượng chuỗi tham chiếu chuỗi con đó không phải là hậu tố. Điều này đã hạn chế tính hữu ích của đề xuất của bạn vì nó sẽ thêm rất nhiều biến chứng để xử lý khác nhau với các suffices và không suffices (và từ bỏ các null-terminating strings mang lại các hậu quả khác).

Cho phép tham chiếu đến các chuỗi con của chuỗi có nghĩa là làm phức tạp rất nhiều thu thập rác và xử lý chuỗi. Đối với mỗi chuỗi, bạn phải theo dõi số lượng đối tượng tham chiếu đến từng ký tự hoặc cho mỗi phạm vi chỉ mục. Điều này có nghĩa là làm phức tạp nhiều đối tượng chuỗi và bất kỳ hoạt động nào liên quan đến chúng, nghĩa là một, có thể lớn, chậm lại.

Thêm thực tế bắt đầu bằng chuỗi python3 có 3 biểu diễn nội bộ khác nhau và mọi thứ sẽ quá lộn xộn để duy trì, và đề xuất của bạn có thể không cung cấp đủ lợi ích để được chấp nhận.


Một vấn đề khác với loại "tối ưu hóa" là khi bạn muốn deallocate "chuỗi lớn":

a = "Some string" * 10 ** 7 
b = a[10000] 
del a 

Sau hoạt động này, bạn có chuỗi b có thể ngăn chặn a, một chuỗi lớn , để được deallocated. Chắc chắn bạn có thể làm bản sao của các chuỗi nhỏ, nhưng nếu b = a[:10000] (hoặc một số lớn) thì sao? 10000 ký tự trông giống như một chuỗi lớn mà nên sử dụng tối ưu hóa để tránh sao chép, nhưng nó đang ngăn chặn để thực hiện megabyte dữ liệu. Người thu gom rác sẽ phải kiểm tra xem liệu có đáng để giải quyết một đối tượng chuỗi lớn và sao chép hay không, và tất cả các hoạt động này phải càng nhanh càng tốt, nếu không bạn sẽ giảm thời gian biểu diễn.

99% số lần chuỗi được sử dụng trong chương trình là "nhỏ" (tối đa 10k ký tự), do đó sao chép thực sự nhanh, trong khi tối ưu hóa bạn đề xuất bắt đầu có hiệu lực với các chuỗi thực sự lớn (ví dụ: 100k từ các văn bản lớn) và chậm hơn nhiều với các chuỗi thực sự nhỏ, đó là trường hợp phổ biến, tức là trường hợp cần được tối ưu hóa.


Nếu bạn nghĩ rằng quan trọng thì bạn được tự do đề xuất PEP, thể hiện triển khai và thay đổi kết quả tốc độ/bộ nhớ của đề xuất của bạn. Nếu nó thực sự là giá trị nỗ lực nó có thể được bao gồm trong một phiên bản tương lai của python.

+1

Tôi không nghĩ rằng đề xuất của OP là một ý tưởng hay, nhưng vì những lý do khác nhau ("không bắn tin nhắn"). Đoạn đầu tiên là thú vị, và chắc chắn là một vấn đề với tacking này lên CPython như-là, nhưng nó không phải là một vấn đề cơ bản và nó sẽ không được lần đầu tiên đại diện chuỗi thay đổi rất nhiều. Đoạn thứ hai giả định một thực hiện ngu ngốc. Bạn có thể chỉ có chuỗi con tham chiếu đến đối tượng chuỗi thích hợp và lưu trữ một khởi đầu và chiều dài/kết thúc (và biểu diễn này là hoàn toàn không biết gì về ruột của đối tượng chuỗi, vì vậy đoạn thứ ba biến mất trong một luồng logic). – delnan

+0

Cụm từ về kiểu dáng biểu diễn kết thúc null là khá hợp lý. Nhưng điều thú vị là phong cách này có hữu ích không? Đối với các chức năng như strcpy, có thể, nhưng chúng có thể được thay đổi với các chức năng như strncpy ... – gukoff

+0

@ Harold Tôi không nghĩ rằng nó cho 'strcpy', như chiều dài có sẵn nó đơn giản và nhanh hơn để chỉ' memcpy' - tương tự như vậy cho các chức năng tiêu chuẩn khác. Nó có lẽ là để tránh một bản sao khi biến nó thành một chuỗi C cho mã của bên thứ ba/khách hàng (xem 'PyBytes_AsString' và tương tự). – delnan

3

Đó là cách hoạt động của các lát. Các lát luôn luôn thực hiện một bản sao nông, cho phép bạn làm những việc như

>>> x = [1,2,3] 
>>> y = x[:] 

Bây giờ nó sẽ có thể làm cho một ngoại lệ cho chuỗi, nhưng nó thực sự đáng giá? Eric Lippert blogged about his decision not to do that for .NET; Tôi đoán đối số của ông là hợp lệ cho Python là tốt.

Xem thêm this question.

+0

Vâng, điều này rất hữu ích, ví dụ như khi lặp lại đối tượng có thể thay đổi được. Nhưng ở đây, cho dây? Nó sẽ không vi phạm sự nhất quán. – gukoff

+0

@ Harold: Đúng, nhưng có lẽ nó không đáng để nỗ lực. Tôi đã chỉnh sửa câu trả lời của mình. –

+0

Có vẻ như Eric đã cố gắng thực hiện tối ưu hóa nghiêm túc, ngay cả đối với các kết nối. Vì vậy, lớp StringBuilder thực sự nivelate sự cần thiết trong tối ưu hóa phức tạp như vậy trong C#. – gukoff

2

Nếu bạn đang lo lắng về bộ nhớ (trong trường hợp của chuỗi thực sự lớn), sử dụng một buffer():

>>> a = "12345" 
>>> b = buffer(a, 2, 2) 
>>> b 
<read-only buffer for 0xb734d120, size 2, offset 2 at 0xb734d4a0> 
>>> print b 
34 
>>> print b[:] 
34 

Biết về điều này cho phép bạn lựa chọn thay thế cho các phương pháp chuỗi như split().

Nếu bạn muốn split() một chuỗi, nhưng giữ đối tượng chuỗi gốc (như bạn có thể cần đến nó), bạn có thể làm:

def split_buf(s, needle): 
    start = None 
    add = len(needle) 
    res = [] 
    while True: 
     index = s.find(needle, start) 
     if index < 0: 
      break 
     res.append(buffer(s, start, index-start)) 
     start = index + add 
    return res 

hay, sử dụng .index():

def split_buf(s, needle): 
    start = None 
    add = len(needle) 
    res = [] 
    try: 
     while True: 
      index = s.index(needle, start) 
      res.append(buffer(s, start, index-start)) 
      start = index + add 
    except ValueError: 
     pass 
    return res 
+1

Vâng, tôi biết về bộ đệm. Nhưng nếu tôi muốn sử dụng phương thức split() trên chuỗi tùy ý? – gukoff

+0

@Harold Bạn có thể "mô phỏng" nó đáp ứng nhu cầu của bạn, xem chỉnh sửa của tôi. OTOH, nếu bạn phân chia một chuỗi và không cần nó nữa, bạn có thể thả bản gốc, giải phóng bộ nhớ và có cùng dấu vết bộ nhớ như trước đây. – glglgl

Các vấn đề liên quan