2010-07-29 42 views
6

Nếu tôi muốn thực hiện nhiều thay thế chuỗi, cách hiệu quả nhất để thực hiện điều này là gì?Thực hiện hiệu quả nhiều thay thế chuỗi trong Python

Một ví dụ về các loại tình huống tôi đã gặp trong chuyến đi của tôi là như sau:

>>> strings = ['a', 'list', 'of', 'strings'] 
>>> [s.replace('a', '')...replace('u', '') for s in strings if len(s) > 2] 
['a', 'lst', 'of', 'strngs'] 
+0

Tôi không chắc chắn về cách bạn dự định sử dụng bảng tra cứu: các khóa và giá trị là gì? Ngoài ra, ví dụ của bạn có một vài lỗi chính tả/mâu thuẫn. – zdav

+0

Lỗi chính tả ở đâu? Tôi đã sử dụng một dấu ba chấm để cắt ngắn ví dụ để dễ đọc. –

Trả lời

9

Ví dụ cụ thể mà bạn cung cấp cho (xóa nhân vật duy nhất) là hoàn hảo cho phương thức chuỗi translate, cũng như thay thế các ký tự đơn bằng các ký tự đơn. Nếu chuỗi đầu vào là một Unicode, sau đó, cũng như hai loại "thay thế" ở trên, việc thay thế các ký tự đơn có nhiều chuỗi ký tự cũng tốt bằng phương pháp translate (không phải nếu bạn cần làm việc trên các chuỗi byte)).

Nếu bạn cần thay thế các đế của nhiều ký tự, thì tôi cũng khuyên bạn nên sử dụng cụm từ thông dụng - mặc dù không theo cách trả lời của @ gnibbler; thay vào đó, tôi sẽ xây dựng regex từ r'onestring|another|yetanother|orthis' (tham gia các chất nền bạn muốn thay thế bằng các thanh dọc - hãy chắc chắn cũng re.escape chúng nếu chúng chứa các ký tự đặc biệt) và viết một hàm thay thế đơn giản dựa trên một dict.

Tôi sẽ không cung cấp nhiều mã vào lúc này vì tôi không biết hai đoạn nào áp dụng cho nhu cầu thực tế của bạn, nhưng (khi tôi về nhà và kiểm tra lại SO ;-) Tôi sẽ rất vui khi chỉnh sửa để thêm một ví dụ mã khi cần thiết tùy thuộc vào các chỉnh sửa của bạn cho câu hỏi của bạn (hữu ích hơn các bình luận cho câu trả lời này ;-).

Sửa: trong một chú thích OP nói rằng ông muốn có một câu trả lời "tổng quát hơn" (không làm rõ điều đó có nghĩa) sau đó trong một chỉnh sửa của Q của ông, ông nói ông muốn nghiên cứu "cân bằng" giữa các đoạn khác nhau tất cả trong đó sử dụng các ký tự một ký tự (và kiểm tra sự hiện diện của chúng, thay vì thay thế như được yêu cầu ban đầu - ngữ nghĩa hoàn toàn khác nhau, tất nhiên).

Với sự nhầm lẫn hoàn toàn và hoàn toàn này, tất cả những gì tôi có thể nói là "kiểm tra sự cân bằng" (hiệu suất) Tôi muốn sử dụng python -mtimeit -s'setup things here' 'statements to check' (đảm bảo rằng các câu lệnh kiểm tra không có tác dụng phụ để tránh bóp méo các phép đo thời gian, vì timeit vòng lặp ngầm để cung cấp các phép đo thời gian chính xác).

Câu trả lời chung (không có sự cân bằng và liên quan đến nhiều ký tự, vì vậy hoàn toàn trái với bản chỉnh sửa của Q nhưng phụ thuộc vào nhận xét của anh ấy - cả hai đều hoàn toàn mâu thuẫn với nó tất nhiên không thể đáp ứng cả hai): sử dụng

import re 

class Replacer(object): 

    def __init__(self, **replacements): 
    self.replacements = replacements 
    self.locator = re.compile('|'.join(re.escape(s) for s in replacements)) 

    def _doreplace(self, mo): 
    return self.replacements[mo.group()] 

    def replace(self, s): 
    return self.locator.sub(self._doreplace, s) 

Ví dụ:

r = Replacer(zap='zop', zip='zup') 
print r.replace('allazapollezipzapzippopzip') 

Nếu một số các chuỗi con được thay thế là các từ khóa Python, họ cần phải được thông qua trong một chút ít trực tiếp, ví dụ như, Sau đây:

r = Replacer(abc='xyz', def='yyt', ghi='zzq') 

sẽ thất bại vì def là một từ khóa, vì vậy bạn cần ví dụ .:

r = Replacer(abc='xyz', ghi='zzq', **{'def': 'yyt'}) 

hoặc tương tự.

Tôi thấy đây là cách sử dụng tốt cho một lớp (chứ không phải là lập trình thủ tục) vì RE xác định vị trí nền thay thế, dict thể hiện những gì cần thay thế, và phương pháp thực hiện thay thế, thực sự khóc "giữ tất cả lại với nhau", và một cá thể lớp chỉ là cách đúng đắn để thực hiện như vậy "giữ lại" trong Python. Một nhà máy đóng cửa cũng sẽ làm việc (kể từ khi phương pháp replace thực sự là phần duy nhất của trường hợp đó nhu cầu để được nhìn thấy "bên ngoài") nhưng trong một thể ít rõ ràng, khó khăn hơn để debug cách:

def make_replacer(**replacements): 
    locator = re.compile('|'.join(re.escape(s) for s in replacements)) 

    def _doreplace(mo): 
    return replacements[mo.group()] 

    def replace(s): 
    return locator.sub(_doreplace, s) 

    return replace 

r = make_replacer(zap='zop', zip='zup') 
print r('allazapollezipzapzippopzip') 

Lợi thế thực sự duy nhất có thể là hiệu suất tốt hơn rất khiêm tốn (cần được kiểm tra với timeit về "trường hợp điểm chuẩn" được coi là quan trọng và đại diện cho ứng dụng sử dụng nó) như truy cập vào "biến miễn phí" (replacements, locator, _doreplace) trường hợp này có thể nhanh hơn truy cập vào các tên đủ điều kiện (self.replacements vv) theo cách tiếp cận bình thường, dựa trên lớp (cho dù đây là trường hợp này sẽ phụ thuộc vào việc triển khai Python được sử dụng, từ đó cần phải kiểm tra với timeit về các điểm chuẩn quan trọng!).

+0

Cảm ơn câu trả lời chi tiết của bạn Alex. Tôi đã thực sự tìm kiếm một câu trả lời chung chung hơn. Xin lỗi vì không rõ ràng với câu hỏi. Tôi sẽ chỉnh sửa Q để phản ánh điều đó. –

0

Bạn có thể thấy rằng nó là nhanh hơn để tạo ra một regex và làm tất cả những thay thế cùng một lúc.

Cũng là một ý tưởng tốt để di chuyển mã thay ra một chức năng để bạn có thể memoize nếu bạn có khả năng để có bản sao trong danh sách

>>> import re 
>>> [re.sub('[aeiou]','',s) for s in strings if len(s) > 2] 
['a', 'lst', 'of', 'strngs'] 


>>> def replacer(s, memo={}): 
... if s not in memo: 
...  memo[s] = re.sub('[aeiou]','',s) 
... return memo[s] 
... 
>>> [replacer(s) for s in strings if len(s) > 2] 
['a', 'lst', 'of', 'strngs'] 
+0

Bạn có thể mở rộng điều này không? Liệu 're.sub ('[aeiou]', '', s)' có thay thế tất cả cùng một lúc không? Nếu nó kiểm tra char bằng char, tôi đã lo lắng về sự bất biến của chuỗi Python .. –

+0

@Tim, Có, '[aeiou]' thay thế tất cả các nguyên âm cùng một lúc. Tính không thay đổi không phải là vấn đề khi bạn tạo chuỗi mới. –

+0

@Tim Các brackes '[]' là một lớp nhân vật, nó phù hợp với bất kỳ một trong số chúng và thay thế char đó bằng chuỗi thay thế. Giải pháp này là tốt đẹp nếu: tất cả các thay thế đều giống nhau và nếu bạn chỉ khớp các ký tự đơn. – zdav

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