E731 ไม่กำหนดแลมบ์ดานิพจน์ใช้ def


193

ฉันได้รับคำเตือน pep8 นี้ทุกครั้งที่ฉันใช้แลมบ์ดานิพจน์ ไม่แนะนำให้ใช้แลมบ์ดานิพจน์? ถ้าไม่ใช่เพราะอะไร


4
เพื่อความชัดเจนคำถามหมายถึงข้อความสำหรับการเช็คอินอัตโนมัติflake8( flake8.pycqa.org )
rakslice

คำตอบ:


231

คำแนะนำในPEP-8 ที่คุณพบคือ:

ใช้คำสั่ง def เสมอแทนคำสั่งการมอบหมายที่ผูกแลมบ์ดานิพจน์กับชื่อโดยตรง

ใช่:

def f(x): return 2*x 

No:

f = lambda x: 2*x 

รูปแบบแรกหมายความว่าชื่อของวัตถุฟังก์ชั่นที่เกิดขึ้นเป็นพิเศษ 'f' แทนสามัญ '<lambda>' สิ่งนี้มีประโยชน์มากกว่าสำหรับการย้อนกลับและการแทนค่าสตริงโดยทั่วไป การใช้คำสั่งที่ได้รับมอบหมายช่วยลดผลประโยชน์เพียงอย่างเดียวที่แลมบ์ดาแสดงออกสามารถนำเสนอเหนือข้อความสั่งการที่ชัดเจน (เช่นสามารถฝังอยู่ภายในนิพจน์ที่ใหญ่กว่า)

การกำหนด lambdas ให้กับชื่อนั้นเป็นเพียงแค่การทำซ้ำการทำงานของdef- และโดยทั่วไปแล้วมันเป็นการดีที่สุดที่จะทำบางสิ่งด้วยวิธีเดียวเพื่อหลีกเลี่ยงความสับสนและเพิ่มความชัดเจน

กรณีการใช้งานที่ถูกต้องสำหรับแลมบ์ดาเป็นที่ที่คุณต้องการใช้ฟังก์ชั่นโดยไม่ต้องกำหนดเช่น:

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

โดยทั่วไปแล้วอาร์กิวเมนต์หลักในการทำเช่นนี้คือdefข้อความสั่งนั้นจะส่งผลให้มีบรรทัดของโค้ดมากขึ้น คำตอบหลักของฉันคือ: ใช่และนั่นก็ดี การเว้นจำนวนบรรทัดให้น้อยที่สุดไม่ใช่สิ่งที่คุณควรทำ: ไปเพื่อความชัดเจนในระยะสั้น


5
ฉันไม่เห็นว่ามันแย่ลง การติดตามกลับยังคงรวมหมายเลขบรรทัดที่ผิดพลาดและไฟล์ต้นฉบับ หนึ่งอาจพูดว่า "f" ในขณะที่คนอื่นพูดว่า "แลมบ์ดา" บางทีข้อผิดพลาดของแลมบ์ดานั้นง่ายกว่าในการสแกนเพราะไม่ใช่ชื่อฟังก์ชั่นอักขระเดี่ยวหรือชื่อยาวไม่ดี?
g33kz0r

4
@ g33kz0r แน่นอนถ้าคุณคิดว่าส่วนที่เหลือของรหัสของคุณจะมีคุณภาพไม่ดีการทำตามอนุสัญญาจะไม่ทำให้คุณได้รับประโยชน์มากนัก โดยทั่วไปไม่มันไม่ใช่จุดจบของโลก แต่ก็ยังเป็นแนวคิดที่ไม่ดี
Gareth Latty

39
คำตอบนี้ไม่เป็นประโยชน์มากเพราะเมื่อใช้วิธีการที่แนะนำในการใช้งานdefผ่านตัวตรวจสอบ PEP8 คุณจะได้รับE704 multiple statements on one line (def)และถ้าคุณแบ่งออกเป็นสองบรรทัดคุณจะได้รับE301 expected 1 blank line, found 0: - /
Adam Spires

4
ฉันเห็นด้วยว่าควรจะแยก ประเด็นของฉันคือก) มันไม่ได้แยกในรหัสของคำตอบข้างต้นทำให้ E704 และ b) ถ้าคุณแยกมันคุณต้องมีบรรทัดว่างที่น่าเกลียดข้างบนเพื่อหลีกเลี่ยง E301
Adam Spires

3
ฉันใช้แลมบ์ดาเมื่อฉันต้องการเน้นฟังก์ชั่นที่บริสุทธิ์ (ไม่มีผลข้างเคียง) และบางครั้งฉันต้องใช้ฟังก์ชั่นเดียวกันในสองแห่งคือจัดกลุ่มและเรียงกัน ฉันเลยเพิกเฉยต่ออนุสัญญานี้
มนู

119

นี่คือเรื่องราวฉันมีฟังก์ชั่นแลมบ์ดาที่ฉันใช้สองครั้ง

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

นี่เป็นเพียงการนำเสนอฉันได้เผชิญหน้ากับรุ่นนี้สองสามรุ่น

ตอนนี้เพื่อรักษาสิ่งที่แห้งฉันเริ่มที่จะนำแลมบ์ดากลับมาใช้ใหม่อีกครั้ง

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

ณ จุดนี้ตัวตรวจสอบคุณภาพรหัสของฉันบ่นเกี่ยวกับแลมบ์ดาว่าเป็นฟังก์ชั่นที่ตั้งชื่อดังนั้นฉันจึงแปลงมันเป็นฟังก์ชั่น

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

ตอนนี้ตัวตรวจสอบบ่นว่าฟังก์ชั่นจะต้องถูกล้อมรอบด้วยหนึ่งบรรทัดว่างก่อนและหลัง

def f(x):
    return x + offset

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

ที่นี่เรามีรหัส 6 บรรทัดแทนที่จะเป็น 2 บรรทัดต้นฉบับโดยไม่เพิ่มความสามารถในการอ่านและไม่มีการเพิ่มขึ้นของการเป็นแบบ pythonic ณ จุดนี้ผู้ตรวจสอบรหัสบ่นเกี่ยวกับฟังก์ชั่นที่ไม่มีเอกสาร

ในความเห็นของฉันกฎนี้ควรหลีกเลี่ยงและเสียเมื่อมันเหมาะสมใช้วิจารณญาณของคุณ


13
a = [x + offset for x in simple_list]. ไม่จำเป็นต้องใช้mapและlambdaที่นี่
Georgy

8
@Georgy ฉันเชื่อว่าประเด็นคือการย้ายx + offsetส่วนไปยังตำแหน่งที่เป็นนามธรรมที่สามารถปรับปรุงได้โดยไม่ต้องเปลี่ยนรหัสมากกว่าหนึ่งบรรทัด ด้วยความเข้าใจในรายการตามที่คุณกล่าวถึงคุณจะยังคงต้องการโค้ดสองบรรทัดที่อยู่x + offsetในรายการตอนนี้ เพื่อที่จะดึงผู้ที่ออกมาเป็นผู้เขียนต้องการคุณจะต้องมีหรือdef lambda
Julian

1
นอกเหนือจาก @Julian defและlambdaหนึ่งยังสามารถใช้functools.partial : แล้วf = partial(operator.add, offset) a = list(map(f, simple_list))
Georgy

สิ่งที่เกี่ยวกับdef f(x): return x + offset(เช่นฟังก์ชั่นง่าย ๆ ที่กำหนดไว้ในบรรทัดเดียว)? อย่างน้อยกับ flake8 ฉันไม่ได้รับการร้องเรียนเกี่ยวกับบรรทัดว่าง
DocOc

1
@Julian ในบางกรณีคุณสามารถใช้ความเข้าใจแบบซ้อน:a, b = [[x + offset for x lst] for lst in (simple_list, another_simple_list)]
wjandrea

24

Lattyware ถูกต้องแน่นอน: โดยทั่วไปแล้วPEP-8ต้องการให้คุณหลีกเลี่ยงสิ่งต่างๆ

f = lambda x: 2 * x

และแทนที่จะใช้

def f(x):
    return 2 * x

อย่างไรก็ตามในขณะที่การแก้ไขในเร็ว ๆ นี้รายงานข้อบกพร่อง (สิงหาคม 2014) งบเช่นต่อไปนี้อยู่ในขณะนี้สอดคล้อง:

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

เนื่องจากตัวตรวจสอบ PEP-8 ของฉันยังไม่สามารถใช้งานได้อย่างถูกต้องฉันจึงปิด E731 ในขณะนั้น


8
แม้เมื่อใช้defตัวตรวจสอบ PEP8 ก็บ่นE301 expected 1 blank line, found 0ดังนั้นคุณต้องเพิ่มบรรทัดว่างที่น่าเกลียดไว้ข้างหน้า
Adam Spires

1

ฉันยังพบสถานการณ์ที่เป็นไปไม่ได้ที่จะใช้ฟังก์ชั่น 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},
  }

ในกรณีนี้ฉันต้องการทำแผนที่ที่เป็นของชั้นเรียนจริงๆ วัตถุบางอย่างในการทำแผนที่จำเป็นต้องมีฟังก์ชั่นเดียวกัน มันจะไม่มีเหตุผลที่จะใส่ฟังก์ชั่นที่มีชื่อนอกชั้นเรียน ฉันไม่พบวิธีการอ้างถึงวิธี (staticmethod, classmethod หรือ normal) จากภายในร่างกายของชั้นเรียน SomeClass ยังไม่มีเมื่อเรียกใช้รหัส ดังนั้นการอ้างอิงจากชั้นเรียนจึงเป็นไปไม่ได้เช่นกัน


คุณสามารถอ้างถึงalso_not_reachableในคำจำกัดความการทำแผนที่เป็นSomeClass.also_not_reachable
yaccz

1
ฉันไม่รู้ว่าคุณกำลังพยายามทำอะไรที่นี่ ชื่อฟังก์ชั่นของคุณทุกคนสามารถเข้าถึงได้เช่นเดียวกับfทั้ง 2.7 และ 3.5 สำหรับฉัน
Eric

ไม่ฟังก์ชันทั้งหมดยกเว้นฟังก์ชั่นแลมบ์ดาไม่สามารถเข้าถึงได้จากภายในตัวเครื่องคลาส คุณจะได้รับ AttributeError: type object 'SomeClass' ไม่มี attribute '... ' ถ้าคุณพยายามเข้าถึงหนึ่งในฟังก์ชั่นเหล่านี้ในอ็อบเจกต์ some_mapping
simP

3
@simP พวกเขาทั้งหมดสามารถเข้าถึงได้อย่างสมบูรณ์แบบ รายการที่มี@staticmethodและ@classmethodไม่ต้องการวัตถุเพียงSomeClass.also_not_reachable(แม้ว่าพวกเขาต้องการชื่อเฉพาะ) หากคุณต้องการเข้าถึงจากวิธีการเรียนเพียงใช้self.also_not_reachable
ababak

@simP คุณอาจเปลี่ยนชื่อ*not_reachableวิธีการเป็นnot_as_easily_reachable_from_class_definition_as_a_lambdaxD
Romain Vincent
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.