2011-01-30 60 views
10

tôi thấy mình thường muốn viết danh sách Python comprehensions như thế này:biểu Bí danh trong danh sách comprehensions Python

nearbyPoints = [(n, delta(n,x)) for n in allPoints if delta(n,x)<=radius] 

Đó hy vọng cung cấp cho một số bối cảnh là tại sao tôi muốn làm điều này, nhưng có cũng là trường hợp nơi nhiều giá trị cần phải được tính toán/so mỗi yếu tố:

newlist = [(x,f(x),g(f(x))) for x in bigList if f(x)<p and g(f(x))<q] 

Vì vậy, tôi có hai câu hỏi:

  1. tất cả các chức năng đó sẽ được đánh giá nhiều lần hay là kết quả được lưu trong bộ nhớ cache? Ngôn ngữ có chỉ định hoặc là ngôn ngữ cụ thể cho việc triển khai không? Tôi đang sử dụng 2,6 bây giờ, nhưng sẽ 3.x là khác nhau?
  2. có cách nào khác để viết không? Đôi khi f và g là biểu thức dài và trùng lặp là lỗi dễ gây ra và trông lộn xộn. Tôi thực sự muốn để có thể viết này:
newList = [(x,a=f(x),b=g(a)) for x in bigList if a<p and b<q] 

nhưng điều đó không làm việc. Có lý do chính đáng để không hỗ trợ cú pháp này không? Có thể nó được thực hiện thông qua một cái gì đó như this? Hoặc tôi chỉ phải sử dụng nhiều danh sách hoặc một vòng lặp?

Trả lời

8

Liên quan đến # 1, có, chúng sẽ được đánh giá nhiều lần.

Liên quan đến # 2, cách để làm điều đó là để tính toán và bộ lọc trong comprehensions riêng biệt:

phiên bản thu nhỏ:

[(x,fx,gx) for (x,fx,gx) in ((x,fx,g(fx)) for (x,fx) in ((x,f(x)) for x in bigList) if fx < p) if gx<q] 

dài hơn phiên bản mở rộng để làm cho nó dễ dàng hơn để làm theo:

[(x,f,g) for (x,f,g) in 
    ((x,f,g(f)) for (x,f) in 
    ((x,f(x)) for x in bigList) 
    if f < p) 
if g<q] 

Điều này sẽ gọi fg càng ít lần càng tốt (giá trị cho mỗi f(x) không phải là < p sẽ không bao giờ gọi gf sẽ chỉ được gọi một lần cho mỗi giá trị trong số bigList).

Nếu bạn thích, bạn cũng có thể nhận được mã gọn gàng bằng cách sử dụng biến trung gian:

a = ((x,f(x)) for x in bigList) 
b = ((x,fx,g(fx)) for (x,fx) in a if fx<p) 
results = [ c for c in b if c[2] < q ] # faster than writing out full tuples 

ab máy phát điện sử dụng biểu thức để họ không phải thực sự nhanh chóng danh sách, và chỉ đơn giản là đánh giá khi cần thiết .

+0

1 Mặc dù tôi cảm thấy ghê tởm về các bộ lập chỉ mục. Ngoài ra, có những tình huống trong đó một chức năng hoặc một chức năng máy phát điện là lựa chọn tốt nhất. – Apalala

2
  1. Nếu bạn gọi hàm hai lần trong một biểu thức (kể cả trong danh sách), nó sẽ thực sự được gọi hai lần. Python không có cách nào để biết liệu hàm của bạn có phải là hàm thuần hay hàm thủ tục hay không. Nó gọi nó khi bạn nói với nó, trong trường hợp này, hai lần.

  2. Không có cách nào để gán cho một biến trong một danh sách hiểu, bởi vì trong Python, gán là một câu lệnh, không phải là một biểu thức.

Có vẻ như bạn nên sử dụng vòng lặp đầy đủ, không phải là hiểu danh sách.

+1

Tôi không muốn viết một tuyên bố bên trong sự hiểu biết, sẽ chỉ tốt đẹp để có một chút đường cú pháp để tránh phải loại nó ra một lần nữa. Có lẽ một cái gì đó như "a: = f (x)" sẽ tốt hơn. Nhưng khi bạn chỉ ra, vì chức năng được đánh giá lần thứ hai, điều này sẽ không thực sự giúp ích gì nhiều. – krashalot

3

Khi việc hiểu danh sách trở nên phức tạp hơn, chúng cũng bắt đầu trở nên thực sự khó đọc. Trong những trường hợp như vậy, tốt hơn nên chuyển nội bộ của chúng thành các chức năng của máy phát điện và cung cấp cho chúng một tên có ý nghĩa (hy vọng).

# First example 
def getNearbyPoints(x, radius, points): 
    """Yields points where 'delta(x, point) <= radius'""" 
    for p in points: 
     distance = delta(p, x) 
     if distance <= radius: 
      yield p, distance 

nearbyPoints = list(getNearbyPoints(x, radius, allPoints)) 


# Second example 
def xfg(data, p, q): 
    """Yield 3-tuples of x, f(x), g(f(x))""" 
    for x in data: 
     f = f(x) 
     if f < p: 
      g = g(f) 
      if g < q: 
       yield x, f, g 

newList = list(xfg(bigList, p, q)) 
8

Tôi có hack để tạo bí danh bên trong danh sách/đọc hiểu dict. Bạn có thể sử dụng thủ thuật for alias_name in [alias_value]. Ví dụ, bạn có chức năng này đắt:

def expensive_function(x): 
    print("called the very expensive function, that will be $2") 
    return x*x + x 

Và một số dữ liệu:

data = [4, 7, 3, 7, 2, 3, 4, 7, 3, 1, 1 ,1] 

Và sau đó bạn muốn áp dụng các chức năng đắt tiền trên mỗi phần tử, và cũng có thể lọc dựa trên nó. Những gì bạn làm là:

result = [ 
    (x, expensive) 
    for x in data 
    for expensive in [expensive_function(x)] #alias 
    if expensive > 3 
] 

print(result) 

Thứ hai sẽ chỉ lặp qua danh sách kích thước 1, có hiệu quả làm cho nó trở thành bí danh. Đầu ra sẽ cho thấy hàm đắt tiền được gọi là 12 lần, chính xác một lần cho mỗi phần tử dữ liệu. Tuy nhiên, kết quả của hàm được sử dụng (nhiều nhất) hai lần, một lần cho bộ lọc và một lần có thể một lần cho đầu ra.

Vui lòng luôn đảm bảo bố cục việc hiểu như vậy bằng nhiều dòng như tôi đã làm và thêm #alias vào dòng nơi bí danh. Nếu bạn sử dụng một bí danh, hiểu được khá phức tạp, và bạn nên giúp người đọc trong tương lai của mã của bạn để có được những gì bạn đang làm. Đây không phải là perl, bạn biết;).

Để hoàn chỉnh, kết quả:

called the very expensive function, that will be $2 
called the very expensive function, that will be $2 
called the very expensive function, that will be $2 
called the very expensive function, that will be $2 
called the very expensive function, that will be $2 
called the very expensive function, that will be $2 
called the very expensive function, that will be $2 
called the very expensive function, that will be $2 
called the very expensive function, that will be $2 
called the very expensive function, that will be $2 
called the very expensive function, that will be $2 
called the very expensive function, that will be $2 
[(4, 20), (7, 56), (3, 12), (7, 56), (2, 6), (3, 12), (4, 20), (7, 56), (3, 12)] 

Mã số: http://ideone.com/7mUQUt

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