E731 không gán biểu thức lambda, sử dụng def


193

Tôi nhận được cảnh báo pep8 này bất cứ khi nào tôi sử dụng biểu thức lambda. Là biểu thức lambda không được khuyến khích? Nếu không tại sao?


4
Để rõ ràng, câu hỏi đề cập đến một thông báo cho đăng ký tự động flake8( flake8.pycqa.org )
rakslice

Câu trả lời:


229

Đề xuất trong PEP-8 bạn đang sử dụng là:

Luôn sử dụng câu lệnh def thay vì câu lệnh gán liên kết trực tiếp biểu thức lambda với tên.

Đúng:

def f(x): return 2*x 

Không:

f = lambda x: 2*x 

Biểu mẫu đầu tiên có nghĩa là tên của đối tượng hàm kết quả cụ thể là 'f' thay vì tên chung '<lambda>'. Điều này hữu ích hơn cho tracebacks và biểu diễn chuỗi nói chung. Việc sử dụng câu lệnh gán sẽ loại bỏ lợi ích duy nhất mà biểu thức lambda có thể cung cấp cho câu lệnh def rõ ràng (nghĩa là nó có thể được nhúng bên trong một biểu thức lớn hơn)

Việc gán lambdas cho các tên về cơ bản chỉ là sao chép chức năng của def- và nói chung, tốt nhất là làm một cách duy nhất để tránh nhầm lẫn và tăng độ rõ ràng.

Trường hợp sử dụng hợp pháp cho lambda là nơi bạn muốn sử dụng hàm mà không gán nó, ví dụ:

sorted(players, key=lambda player: player.rank)

Nói chung, đối số chính chống lại việc này là các defcâu lệnh sẽ dẫn đến nhiều dòng mã hơn. Phản ứng chính của tôi cho điều đó sẽ là: có, và điều đó là tốt. Trừ khi bạn đang chơi golf, giảm thiểu số lượng dòng không phải là điều bạn nên làm: hãy giải thích rõ ràng.


5
Tôi không thấy nó tệ hơn thế nào. Truy nguyên vẫn sẽ bao gồm số dòng sai và tệp nguồn. Người ta có thể nói "f" trong khi người kia nói "lambda". Có lẽ lỗi lambda dễ quét hơn vì đó không phải là tên hàm đơn ký tự hoặc tên dài được đặt tên kém?
g33kz0r

4
@ g33kz0r Chà, chắc chắn, nếu bạn cho rằng phần còn lại của mã của bạn sẽ có chất lượng kém, các quy ước sau sẽ không giúp bạn kiếm được nhiều tiền. Nói chung, không, đó không phải là ngày tận thế, nhưng đó vẫn là một ý tưởng tồi.
Gareth Latty

39
Câu trả lời này không hữu ích lắm, vì khi chạy phương pháp được đề xuất sử dụng defqua trình kiểm tra PEP8, bạn sẽ nhận được E704 multiple statements on one line (def)và nếu bạn chia nó thành hai dòng bạn nhận được E301 expected 1 blank line, found 0: - /
Adam Spiers

4
Tôi đồng ý nó nên được chia. Quan điểm của tôi là a) nó không bị phân tách trong mã câu trả lời ở trên, gây ra E704 và b) nếu bạn tách nó, bạn cần một dòng trống xấu xí phía trên nó để tránh E602.
Adam Spiers

3
Tôi sử dụng lambdas khi tôi muốn nhấn mạnh một chức năng thuần túy (không có tác dụng phụ) và đôi khi tôi phải sử dụng cùng một chức năng ở hai nơi, tức là nhóm và sắp xếp cùng nhau. Vì vậy, tôi bỏ qua quy ước này.
manu

119

Đây là câu chuyện, tôi có một chức năng lambda đơn giản mà tôi đã sử dụng hai lần.

a = map(lambda x : x + offset, simple_list)
b = map(lambda x : x + offset, another_simple_list)

Đây chỉ là đại diện, tôi đã phải đối mặt với một vài phiên bản khác nhau của điều này.

Bây giờ, để giữ mọi thứ KHÔ, tôi bắt đầu sử dụng lại lambda thông thường này.

f = lambda x : x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)

Tại thời điểm này, trình kiểm tra chất lượng mã của tôi phàn nàn về lambda là một hàm được đặt tên nên tôi chuyển đổi nó thành một hàm.

def f(x):
    return x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)

Bây giờ trình kiểm tra phàn nàn rằng một hàm phải được giới hạn bởi một dòng trống trước và sau.

def f(x):
    return x + offset

a = map(f, simple_list)
b = map(f, another_simple_list)

Bây giờ chúng ta có 6 dòng mã thay vì 2 dòng ban đầu mà không tăng khả năng đọc và không tăng pythonic. Tại thời điểm này, trình kiểm tra mã phàn nàn về chức năng không có tài liệu.

Theo tôi quy tắc này tốt hơn nên tránh và phá vỡ khi nó có ý nghĩa, sử dụng phán đoán của bạn.


13
a = [x + offset for x in simple_list]. Không cần sử dụng maplambdaở đây.
Georgy

8
@Georgy Tôi tin rằng vấn đề là di chuyển x + offsetphần đến một vị trí trừu tượng có thể được cập nhật mà không thay đổi nhiều hơn một dòng mã. Với việc hiểu danh sách như bạn đã đề cập, bạn vẫn sẽ cần hai dòng mã chứa x + offsetchúng bây giờ sẽ nằm trong danh sách hiểu. Để kéo chúng ra như tác giả muốn, bạn sẽ cần một defhoặc lambda.
Julian

1
@Julian Ngoài deflambdangười ta cũng có thể sử dụng funcools.partial : f = partial(operator.add, offset)và sau đó a = list(map(f, simple_list)).
Georgy

Điều gì về def f(x): return x + offset(nghĩa là một hàm đơn giản được xác định trên một dòng)? Ít nhất với flake8 tôi không nhận được khiếu nại về các dòng trống.
DocOc

1
@Julian Trong một số trường hợp, bạn có thể sử dụng cách hiểu lồng nhau:a, b = [[x + offset for x lst] for lst in (simple_list, another_simple_list)]
wjandrea

24

Lattyware hoàn toàn đúng: Về cơ bản PEP-8 muốn bạn tránh những thứ như

f = lambda x: 2 * x

và thay vào đó sử dụng

def f(x):
    return 2 * x

Tuy nhiên, như được đề cập trong một bugreport gần đây (tháng 8 năm 2014), các câu lệnh như sau đây hiện tuân thủ:

a.f = lambda x: 2 * x
a["f"] = lambda x: 2 * x

Vì trình kiểm tra PEP-8 của tôi chưa thực hiện chính xác điều này, tôi đã tắt E731 trong thời gian này.


8
Ngay cả khi sử dụng def, trình kiểm tra PEP8 vẫn phàn nàn E301 expected 1 blank line, found 0, do đó bạn phải thêm một dòng trống xấu xí trước nó.
Adam Spiers

1

Tôi cũng gặp phải một tình huống trong đó thậm chí không thể sử dụng hàm def (ined).

class SomeClass(object):
  # pep-8 does not allow this
  f = lambda x: x + 1  # NOQA

  def not_reachable(self, x):
    return x + 1

  @staticmethod
  def also_not_reachable(x):
    return x + 1

  @classmethod
  def also_not_reachable(cls, x):
    return x + 1

  some_mapping = {
      'object1': {'name': "Object 1", 'func': f},
      'object2': {'name': "Object 2", 'func': some_other_func},
  }

Trong trường hợp này, tôi thực sự muốn tạo một bản đồ thuộc về lớp. Một số đối tượng trong ánh xạ cần chức năng tương tự. Sẽ là phi logic khi đặt một hàm được đặt tên bên ngoài lớp. Tôi chưa tìm thấy cách nào để tham khảo một phương thức (staticmethod, classmethod hoặc normal) từ bên trong thân lớp. Một sốClass chưa tồn tại khi mã được chạy. Vì vậy, đề cập đến nó từ lớp học là không thể.


Bạn có thể tham khảo also_not_reachabletrong định nghĩa ánh xạ làSomeClass.also_not_reachable
yaccz

1
Tôi không biết điểm nào bạn đang cố gắng thực hiện ở đây. Mỗi tên hàm của bạn đều có thể truy cập được như ftrong cả 2.7 và 3.5 đối với tôi
Eric

Không, tất cả các chức năng, ngoại trừ chức năng lambda, không thể truy cập được từ bên trong cơ thể Lớp. Bạn sẽ nhận được một AttributionError: type object 'someClass' không có thuộc tính '...' nếu bạn cố truy cập một trong các hàm đó trong đối tượng some_mapping.
simP

3
@simP tất cả chúng đều có thể truy cập hoàn hảo. Những người có @staticmethod@classmethodkhông cần một đối tượng, chỉ SomeClass.also_not_reachable(mặc dù họ cần tên đặc biệt). Nếu bạn cần truy cập chúng từ các phương thức lớp, chỉ cần sử dụngself.also_not_reachable
ababak

@simP có lẽ bạn nên đổi tên *not_reachablephương thức của mình thành not_as_easily_reachable_from_class_definition_as_a_lambdaxD
Romain Vincent
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.