2017-06-15 22 views
5

Tôi biết rằng cách pythonic concatenating một danh sách các chuỗi là sử dụngconcatenating một chuỗi từ một danh sách các đối tượng

l =["a", "b", "c"] 
"".join(l) 

Nhưng làm thế nào tôi sẽ làm điều này nếu tôi có một danh sách các đối tượng có chứa một chuỗi (như là một thuộc tính), mà không reasigning chuỗi?

Tôi đoán tôi có thể triển khai __str__(self) . Nhưng đó là một cách giải quyết mà tôi không muốn sử dụng.

+0

bạn có thể cung cấp một ví dụ làm việc của bạn tình và kỳ vọng? – Tbaki

Trả lời

10

Tôi đoán nhiều nhất pythonic cách để làm điều này sẽ được sử dụng biểu phát/list hiểu. Nếu chuỗi ví dụ là một thuộc tính của đối tượng obj_instance.str_attr sau đó chỉ cần chạy:

"".join(x.str_attr for x in l) 

hoặc

"".join([x.str_attr for x in l]) 

sửa: xem thảo luận về việc thực hiện dưới đây (they claim that list comprehension - 2nd option is faster).

+1

Về mặt kỹ thuật, đó là biểu thức máy phát điện – pat

+0

, tôi đã chỉnh sửa một chút – Dimgold

+1

Tại sao lại có phần thưởng về hiệu suất này? Các đặc điểm giữa 2,7 và 3 là không thay đổi trong khía cạnh này. –

4

gì về cái gì đó như:

joined = "".join([object.string for object in lst_object]) 
+1

Bạn có thể bỏ qua các dấu ngoặc vuông để bạn nhận được một trình tạo ra mỗi chuỗi khi tham gia cần nó thay vì nó phải tạo toàn bộ danh sách trước tiên. – pat

+0

@pat một danh sách hiểu là nhanh hơn so với một máy phát điện (ít nhất là khi được sử dụng trong 'str.join') – vaultah

+0

@vaultah thú vị, tôi đã không bao giờ hẹn giờ nó – pat

1

danh sách hiểu biết có thể hữu ích. ví dụ, với một danh sách các từ điển,

# data 
data = [ 
    {'str': 'a', 'num': 1}, 
    {'str': 'b', 'num': 2}, 
] 
joined_string = ''.join([item['str'] for item in data]) 
1

Từ câu trả lời trước:

"".join([x.str_attr if hasattr(x,'str_attr_') else x for x in l ]) 

Nếu kiểu dữ liệu của bạn rất đơn giản.

''.join([somefunction(x) for x in l]) # 

Hãy xem mô-đun itertools. Sau đó, bạn có thể kiểm tra lọc trên các giá trị.

4

Sự khác biệt về hiệu năng giữa biểu hiện máy phát điện và danh sách hiểu rất dễ dàng để đo:

python --version && python -m timeit -s \ 
    "import argparse; l = [argparse.Namespace(a=str(i)) for i in range(1000000)]" \ 
    "''.join(obj.a for obj in l)" 
Python 2.7.12 
10 loops, best of 3: 87.2 msec per loop 

python --version && python -m timeit -s \ 
    "import argparse; l = [argparse.Namespace(a=str(i)) for i in range(1000000)]" \ 
    "''.join([obj.a for obj in l])" 
Python 2.7.12 
10 loops, best of 3: 77.1 msec per loop 

python3.4 --version && python3.4 -m timeit -s \ 
    "import argparse; l = [argparse.Namespace(a=str(i)) for i in range(1000000)]" \ 
    "''.join(obj.a for obj in l)" 
Python 3.4.5 
10 loops, best of 3: 77.4 msec per loop 

python3.4 --version && python3.4 -m timeit -s \ 
    "import argparse; l = [argparse.Namespace(a=str(i)) for i in range(1000000)]" \ 
    "''.join([obj.a for obj in l])" 
Python 3.4.5 
10 loops, best of 3: 66 msec per loop 

python3.5 --version && python3.5 -m timeit -s \ 
    "import argparse; l = [argparse.Namespace(a=str(i)) for i in range(1000000)]" \ 
    "''.join(obj.a for obj in l)" 
Python 3.5.2 
10 loops, best of 3: 82.8 msec per loop 

python3.5 --version && python3.5 -m timeit -s \ 
    "import argparse; l = [argparse.Namespace(a=str(i)) for i in range(1000000)]" \ 
    "''.join([obj.a for obj in l])" 
Python 3.5.2 
10 loops, best of 3: 64.9 msec per loop 

python3.6 --version && python3.6 -m timeit -s \ 
    "import argparse; l = [argparse.Namespace(a=str(i)) for i in range(1000000)]" \ 
    "''.join(obj.a for obj in l)" 
Python 3.6.0 
10 loops, best of 3: 84.6 msec per loop 

python3.6 --version && python3.6 -m timeit -s \ 
    "import argparse; l = [argparse.Namespace(a=str(i)) for i in range(1000000)]" \ 
    "''.join([obj.a for obj in l])" 
Python 3.6.0 
10 loops, best of 3: 64.7 msec per loop 

Nó chỉ ra rằng danh sách hiểu là luôn nhanh hơn phát biểu:

  • 2,7: ~ 12% nhanh hơn
  • 3.4: ~ 15% nhanh hơn
  • 3.5: ~ 22% nhanh hơn
  • 3.6: ~ 24% nhanh hơn

Nhưng lưu ý rằng tiêu thụ bộ nhớ cho danh sách hiểu là 2x.

Cập nhật

Dockerfile bạn có thể chạy trên phần cứng của bạn để có được kết quả của bạn, giống như docker build -t test-so . && docker run --rm test-so.

FROM saaj/snake-tank 

RUN echo '[tox] \n\ 
envlist = py27,py33,py34,py35,py36 \n\ 
skipsdist = True \n\ 
[testenv] \n\ 
commands = \n\ 
    python --version \n\ 
    python -m timeit -s \\\n\ 
    "import argparse; l = [argparse.Namespace(a=str(i)) for i in range(1000000)]" \\\n\ 
    "str().join(obj.a for obj in l)" \n\ 
    python -m timeit -s \\\n\ 
    "import argparse; l = [argparse.Namespace(a=str(i)) for i in range(1000000)]" \\\n\ 
    "str().join([obj.a for obj in l])"' > tox.ini 
CMD tox 
+1

Một điều đáng tiếc là biểu thức máy phát điện trở nên chậm hơn trong 3,5 – bli

+0

@bli Vâng, trong các biểu thức trình tạo số tuyệt đối chạy trong cùng một thời gian trên tất cả các phiên bản này. Có vẻ như việc hiểu danh sách được tối ưu hóa trong 3.5. – saaj

+0

Không phải các biện pháp của bạn cho thấy rằng biểu thức máy phát điện chậm hơn trong 3,5 (82,8 msec mỗi vòng lặp) và 3,6 (84,6 msec mỗi vòng) so với 3,4 (77,4 msec mỗi vòng lặp)? – bli

1

Một khả năng khác là sử dụng chức năng lập trình:

class StrObj: 
    def __init__(self, str): 
     self.str = str 

a = StrObj('a') 
b = StrObj('b') 
c = StrObj('c') 

l = [a,b,c] 

"".join(map(lambda x: x.str, l)) 

này sẽ làm việc với bất kỳ cách nào chuỗi có thể được kết nối với các đối tượng (direktly như một thuộc tính hoặc theo một cách phức tạp hơn). Chỉ có lambda phải được điều chỉnh.

2

Bạn có thể chuyển đổi tất cả các chuỗi của bạn thuộc tính liệt kê các chuỗi:

string_list = [myobj.str for myobj in l]

Đoạn mã trên tạo ra danh sách các chuỗi sử dụng máy phát điện. Sau đó u sẽ sử dụng một cách tiêu chuẩn để nối chuỗi:

"".join(string_list)

1

Một tự giải thích một liner

"".join(str(d.attr) for d in l) 
Các vấn đề liên quan