ฉันจะคืนค่าหลายค่าจากฟังก์ชันได้อย่างไร [ปิด]


1067

วิธีที่เป็นที่ยอมรับในการส่งคืนค่าหลายค่าในภาษาที่สนับสนุนมักจะส่งเสียงซ้ำ

ตัวเลือก: การใช้ tuple

ลองพิจารณาตัวอย่างเล็ก ๆ น้อย ๆ นี้:

def f(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return (y0, y1, y2)

อย่างไรก็ตามสิ่งนี้จะได้รับปัญหาอย่างรวดเร็วเนื่องจากจำนวนของค่าที่ส่งคืนเพิ่มขึ้น ถ้าคุณต้องการคืนค่าสี่หรือห้าค่า แน่นอนว่าคุณสามารถติดตามพวกเขาได้ แต่มันก็ง่ายที่จะลืมคุณค่าที่เป็นอยู่ นอกจากนี้ยังค่อนข้างน่าเกลียดที่จะแกะกล่องพวกเขาทุกที่ที่คุณต้องการรับ

ตัวเลือก: การใช้พจนานุกรม

ขั้นตอนตรรกะถัดไปน่าจะเป็นการแนะนำ 'สัญกรณ์บันทึก' บางประเภท dictในหลามวิธีที่ชัดเจนที่จะทำเช่นนี้คือโดยวิธีการของ

พิจารณาสิ่งต่อไปนี้:

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0': y0, 'y1': y1 ,'y2': y2}

(เพื่อให้ชัดเจน, y0, y1 และ y2 นั้นมีความหมายเป็นตัวบ่งชี้เชิงนามธรรมอย่างที่ทราบแล้วในทางปฏิบัติคุณจะต้องใช้ตัวระบุที่มีความหมาย)

ตอนนี้เรามีกลไกที่เราสามารถฉายสมาชิกเฉพาะของวัตถุที่ส่งคืน ตัวอย่างเช่น,

result['y0']

ตัวเลือก: การใช้คลาส

อย่างไรก็ตามมีตัวเลือกอื่น เราสามารถส่งคืนโครงสร้างพิเศษแทน ฉันใส่กรอบนี้ในบริบทของ Python แต่ฉันแน่ใจว่ามันใช้ได้กับภาษาอื่นเช่นกัน แน่นอนถ้าคุณทำงานใน C นี่อาจเป็นทางเลือกเดียวของคุณ ไปที่นี่:

class ReturnValue:
  def __init__(self, y0, y1, y2):
     self.y0 = y0
     self.y1 = y1
     self.y2 = y2

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return ReturnValue(y0, y1, y2)

ในหลามก่อนหน้านี้สองอาจจะคล้ายกันมากในแง่ของการประปา - หลังจากทั้งหมด{ y0, y1, y2 }เพียงแค่จบลงด้วยการรายการในภายในของ__dict__ReturnValue

มีหนึ่งคุณสมบัติเพิ่มเติมที่จัดทำโดย Python แต่สำหรับวัตถุขนาดเล็ก__slots__คุณลักษณะ ชั้นสามารถแสดงเป็น:

class ReturnValue(object):
  __slots__ = ["y0", "y1", "y2"]
  def __init__(self, y0, y1, y2):
     self.y0 = y0
     self.y1 = y1
     self.y2 = y2

จากคู่มืออ้างอิง Python :

การ__slots__ประกาศใช้ลำดับของตัวแปรอินสแตนซ์และสำรองพื้นที่เพียงพอในแต่ละอินสแตนซ์เพื่อเก็บค่าสำหรับแต่ละตัวแปร ประหยัดพื้นที่เนื่องจาก__dict__ไม่ได้สร้างสำหรับแต่ละอินสแตนซ์

ตัวเลือก: การใช้ดาต้าคลาส (Python 3.7+)

การใช้ดาต้าคลาสใหม่ของ Python 3.7 ให้ส่งคืนคลาสด้วยวิธีพิเศษการพิมพ์และเครื่องมือที่มีประโยชน์อื่น ๆ โดยอัตโนมัติ:

@dataclass
class Returnvalue:
    y0: int
    y1: float
    y3: int

def total_cost(x):
    y0 = x + 1
    y1 = x * 3
    y2 = y0 ** y3
    return ReturnValue(y0, y1, y2)

ตัวเลือก: การใช้รายการ

ข้อเสนอแนะอีกข้อที่ฉันมองข้ามมาจาก Bill the Lizard:

def h(x):
  result = [x + 1]
  result.append(x * 3)
  result.append(y0 ** y3)
  return result

นี่เป็นวิธีที่ฉันชอบน้อยที่สุด ฉันคิดว่าฉันเสียเปรียบจากการสัมผัสกับ Haskell แต่ความคิดของรายการประเภทผสมมักจะรู้สึกไม่สบายสำหรับฉัน ในตัวอย่างนี้ลิสต์เป็นแบบไม่ผสม แต่อาจเป็นไปได้

รายการที่ใช้ในวิธีนี้จริง ๆ แล้วไม่ได้อะไรเลยเกี่ยวกับ tuple เท่าที่ฉันสามารถบอกได้ ความแตกต่างที่แท้จริงระหว่างรายการและสิ่งอันดับใน Python คือรายการนั้นไม่แน่นอนในขณะที่สิ่งอันดับจะไม่

ฉันมักจะดำเนินการตามอนุสัญญาจากการเขียนโปรแกรมการทำงาน: ใช้รายการสำหรับองค์ประกอบจำนวนใด ๆ ของประเภทเดียวกันและ tuples สำหรับองค์ประกอบจำนวนคงที่ของประเภทที่กำหนดไว้ล่วงหน้า

คำถาม

หลังจากการเริ่มนำที่มีความยาวมาเป็นคำถามที่หลีกเลี่ยงไม่ได้ วิธีใดที่คุณคิดว่าดีที่สุด?


10
ในตัวอย่างที่ยอดเยี่ยมของคุณคุณใช้ตัวแปรy3แต่ถ้าไม่มีการประกาศ y3 ทั่วโลกสิ่งนี้NameError: global name 'y3' is not definedอาจให้ผลเพียงแค่ใช้3?
hetepeperfan

11
คำถามยอดเยี่ยมมากมายพร้อมคำตอบที่ดีนั้นถูกปิดเพราะคำหลัก 'ความเห็น' เกิดขึ้น คุณสามารถโต้แย้งทั้งหมดของ SO ขึ้นอยู่กับความคิดเห็น แต่มันเป็นความคิดเห็นที่ได้รับแจ้งจากข้อเท็จจริงการอ้างอิงและความเชี่ยวชาญเฉพาะด้าน เพียงเพราะมีคนถาม "ซึ่งคุณคิดว่าดีที่สุด" ไม่ได้หมายความว่าพวกเขากำลังขอความคิดเห็นส่วนตัวที่แยกออกจากข้อเท็จจริงในโลกแห่งความเป็นจริงการอ้างอิงและความเชี่ยวชาญเฉพาะด้าน พวกเขาเกือบจะถามอย่างแน่นอนว่ามีความคิดเห็นประเภทที่มีพื้นฐานมาจากและจัดทำเอกสารข้อเท็จจริงข้อเท็จจริงการอ้างอิงและความเชี่ยวชาญเฉพาะที่บุคคลนั้นใช้ในการสร้างความคิดเห็น
NeilG

@hetepeperfan ไม่จำเป็นต้องเปลี่ยน 3 และไม่ได้กำหนด y3 ในระดับโลกคุณยังสามารถใช้ชื่อท้องถิ่นy3ที่จะทำงานเดียวกันได้เช่นกัน
Okie

คำตอบ:


637

tuples ที่มีชื่อถูกเพิ่มใน 2.6 เพื่อจุดประสงค์นี้ ดูos.statสำหรับตัวอย่าง builtin ที่คล้ายกัน

>>> import collections
>>> Point = collections.namedtuple('Point', ['x', 'y'])
>>> p = Point(1, y=2)
>>> p.x, p.y
1 2
>>> p[0], p[1]
1 2

ในเวอร์ชันล่าสุดของ Python 3 (3.6+ ฉันคิดว่า) typingห้องสมุดใหม่มีNamedTupleคลาสเพื่อให้การสร้างชื่อ tuples ง่ายขึ้นและมีประสิทธิภาพยิ่งขึ้น การรับช่วงจากtyping.NamedTupleช่วยให้คุณใช้เอกสารประกอบ, ค่าเริ่มต้นและคำอธิบายประกอบประเภท

ตัวอย่าง (จากเอกสาร):

class Employee(NamedTuple):  # inherit from typing.NamedTuple
    name: str
    id: int = 3  # default value

employee = Employee('Guido')
assert employee.id == 3

68
นี่เป็นเพียงคำตอบที่ถูกต้องเพราะมันเป็นโครงสร้างแบบบัญญัติเดียวที่ OP ไม่ได้พิจารณาและเพราะมันแก้ปัญหาการจัดการสิ่งอันดับความยาว ควรทำเครื่องหมายว่ายอมรับแล้ว
airstrike

7
เหตุผลการออกแบบสำหรับnamedtupleการมีหน่วยความจำขนาดเล็กสำหรับผลลัพธ์จำนวนมาก (รายการ tuples ยาวเช่นผลลัพธ์ของแบบสอบถาม DB) สำหรับแต่ละรายการ (ถ้าฟังก์ชั่นดังกล่าวไม่ได้ถูกเรียกบ่อย ๆ ) พจนานุกรมและคลาสก็ดีเช่นกัน แต่ชื่อ tuples เป็นคำตอบที่ดีกว่าในกรณีนี้เช่นกัน
Lutz Prechelt

8
@wom: อย่าทำอย่างนี้ Python ไม่พยายามรวบรวมnamedtupleคำจำกัดความ (การเรียกแต่ละครั้งสร้างใหม่) การสร้างnamedtupleคลาสนั้นค่อนข้างแพงทั้งในซีพียูและหน่วยความจำและคำจำกัดความของคลาสทั้งหมดนั้นเกี่ยวข้องกับการอ้างอิงแบบวนรอบ (เช่น CP CP คุณกำลังรอ cyclic GC เพื่อให้พวกเขาได้รับการปล่อยตัว) นอกจากนี้ยังทำให้เป็นไปไม่ได้pickleในชั้นเรียน (และเป็นไปไม่ได้ที่จะใช้อินสแตนซ์กับmultiprocessingในกรณีส่วนใหญ่) การสร้างคลาสในแต่ละครั้งที่ 3.6.4 x64 ของฉันใช้เวลาประมาณ 0.337 มิลลิวินาทีและใช้หน่วยความจำน้อยกว่า 1 กิโลไบต์ทำให้ประหยัดค่าใช้จ่าย
ShadowRanger

3
ผมจะทราบหลาม 3.7 การปรับปรุงความเร็วของการสร้างใหม่namedtupleการเรียน ; ค่าใช้จ่ายซีพียูลดลงประมาณ 4 เท่าแต่ก็ยังสูงกว่าค่าใช้จ่ายในการสร้างอินสแตนซ์ประมาณ 1,000 เท่าและค่าใช้จ่ายหน่วยความจำสำหรับแต่ละคลาสนั้นยังคงสูง (ฉันผิดในความคิดเห็นสุดท้ายของฉันเกี่ยวกับ สำหรับชั้นเรียน_sourceโดยทั่วไปแล้วจะมี 1.5 KB และ_sourceจะถูกลบออกใน 3.7 ดังนั้นจึงน่าจะใกล้เคียงกับการเรียกร้องดั้งเดิมที่น้อยกว่า 1 KB ต่อการสร้างคลาส)
ShadowRanger

4
@SergeStroobandt - นี่เป็นส่วนหนึ่งของไลบรารีมาตรฐานไม่ใช่ตัวใน คุณไม่ต้องกังวลว่าอาจจะไม่ได้ติดตั้งในระบบอื่นด้วย Python> = 2.6 หรือคุณแค่คัดค้านบรรทัดรหัสพิเศษ?
Justin

234

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

กลับพจนานุกรมด้วยปุ่ม"y0", "y1", "y2"และอื่น ๆ ไม่ได้มีประโยชน์ใด ๆ ในช่วง tuples กลับReturnValueอินสแตนซ์ที่มีคุณสมบัติ.y0, .y1, .y2ฯลฯ ไม่ได้ให้ประโยชน์ใด ๆ ในช่วง tuples อย่างใดอย่างหนึ่ง คุณต้องเริ่มตั้งชื่อสิ่งต่าง ๆ ถ้าคุณต้องการไปไหนมาไหนและคุณสามารถทำได้โดยใช้สิ่งอันดับ:

def get_image_data(filename):
    [snip]
    return size, (format, version, compression), (width,height)

size, type, dimensions = get_image_data(x)

IMHO เทคนิคที่ดีเท่านั้นเกิน tuples คือการกลับวัตถุจริงด้วยวิธีการที่เหมาะสมและคุณสมบัติเช่นคุณได้รับจากหรือre.match()open(file)


6
คำถาม - มีความแตกต่างระหว่างsize, type, dimensions = getImageData(x)และ(size, type, dimensions) = getImageData(x)? เช่นการตัดมือซ้ายของงานมอบหมาย tupled สร้างความแตกต่างหรือไม่?
Reb.Cabin

11
@ Reb.Cabin ไม่มีความแตกต่าง Tuple ถูกระบุด้วยเครื่องหมายจุลภาคและการใช้วงเล็บเป็นเพียงการจัดกลุ่มสิ่งต่าง ๆ เข้าด้วยกัน ตัวอย่างเช่น(1)int ในขณะที่(1,)หรือ1,เป็นสิ่งอันดับ
phil

19
เรื่อง "การส่งคืนพจนานุกรมด้วยคีย์ y0, y1, y2 และอื่น ๆ ไม่มีข้อได้เปรียบเหนือสิ่งอันดับ": พจนานุกรมมีข้อได้เปรียบในการที่คุณสามารถเพิ่มเขตข้อมูลในพจนานุกรมที่ส่งคืนโดยไม่ทำลายโค้ดที่มีอยู่
ostrokach

เรื่อง "การส่งคืนพจนานุกรมด้วยคีย์ y0, y1, y2 และอื่น ๆ ไม่ได้ให้ประโยชน์ใด ๆ กับสิ่งอันดับ": มันยังอ่านได้ง่ายขึ้นและมีข้อผิดพลาดน้อยลงเมื่อคุณเข้าถึงข้อมูลตามชื่อแทนที่จะเป็นตำแหน่ง
เดนิส Dollfus

204

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

def f():
    return True, False
x, y = f()
print(x)
print(y)

ให้:

True
False

24
คุณยังคงส่งคืนคอลเล็กชัน มันเป็นสิ่งอันดับ ฉันชอบวงเล็บเพื่อทำให้ชัดเจนยิ่งขึ้น ลองนี้ผลตอบแทนtype(f()) <class 'tuple'>
อิกอร์

20
@Igor: ไม่มีเหตุผลที่จะต้องให้ความtupleกระจ่าง มันไม่สำคัญว่าคุณจะส่งคืน a tupleนี่เป็นสำนวนสำหรับการส่งคืนค่าหลายช่วงเวลา เหตุผลเดียวกับที่คุณไม่ใช้ parens ด้วยการใช้สำนวน swap x, y = y, x, การเริ่มต้นหลายครั้งx, y = 0, 1, ฯลฯ แน่นอนมันทำให้tupleภายใต้ประทุน แต่ไม่มีเหตุผลที่จะทำให้ชัดเจนเนื่องจากtuples ไม่ได้เป็นจุดเลย งูหลามกวดวิชาแนะนำที่ได้รับมอบหมายหลายนานก่อนที่จะได้สัมผัสกับtuples
ShadowRanger

@ShadowRanger ลำดับของค่าใด ๆ ที่คั่นด้วยเครื่องหมายจุลภาคทางด้านขวาของ=คือ tuple ใน Python ที่มีหรือไม่มีวงเล็บล้อมรอบ ดังนั้นจึงไม่มีความชัดเจนหรือโดยนัยที่นี่ a, b, c เท่ากับ tuple มาก (a, b, c) นอกจากนี้ยังไม่มีการทำสิ่งอันดับภายใต้ประทุนเมื่อคุณคืนค่าดังกล่าวเพราะมันเป็นเพียงสิ่งอันดับธรรมดา ๆ OP กล่าวถึงสิ่งอันดับแล้วดังนั้นจึงไม่มีความแตกต่างระหว่างสิ่งที่เขาพูดถึงและสิ่งที่คำตอบนี้แสดง ไม่มี
Ken4scholars

2
นี่เป็นตัวเลือกแรกที่แนะนำในคำถาม
endolith

1
@endolith สองครั้งที่ผู้ชายถามคำถาม ("ฉันจะคืนค่าหลายค่าได้อย่างไร" และ " คุณจะคืนค่าหลายค่าได้อย่างไร") ตอบโดยคำตอบนี้ ข้อความของคำถามมีการเปลี่ยนแปลงบางครั้ง และเป็นคำถามที่อิงตามความคิดเห็น
โจเซฟแฮนเซน

74

ฉันลงคะแนนให้พจนานุกรม

ฉันพบว่าถ้าฉันสร้างฟังก์ชั่นที่คืนค่าอะไรมากกว่า 2-3 ตัวแปรฉันจะพับมันในพจนานุกรม มิฉะนั้นฉันมักจะลืมคำสั่งซื้อและเนื้อหาของสิ่งที่ฉันกลับมา

นอกจากนี้การแนะนำโครงสร้าง 'พิเศษ' จะทำให้โค้ดของคุณติดตามยากขึ้น (คนอื่นจะต้องค้นหารหัสเพื่อค้นหาว่ามันคืออะไร)

หากคุณกังวลเกี่ยวกับประเภทการค้นหาให้ใช้คีย์พจนานุกรมอธิบายเช่น 'รายการค่า x'

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }

5
หลังจากหลายปีของการเขียนโปรแกรมฉันมักจะพิจารณาโครงสร้างของข้อมูลและฟังก์ชั่นที่ต้องการ ฟังก์ชั่นก่อนคุณสามารถ refactor ตามที่จำเป็น
monkut

เราจะได้ค่าภายในพจนานุกรมโดยไม่เรียกใช้ฟังก์ชันหลายครั้งได้อย่างไร ตัวอย่างเช่นถ้าฉันต้องการใช้ y1 และ y3 ในฟังก์ชั่นที่แตกต่างกันอย่างไร
Matt

3
กำหนดผลลัพธ์ให้กับตัวแปรแยกกัน result = g(x); other_function(result)
monkut

1
@monkut ใช่ วิธีนี้ยังช่วยให้ส่งผ่านผลลัพธ์ไปยังฟังก์ชั่นต่าง ๆ ซึ่งรับผลต่างจากผลลัพธ์โดยไม่ต้องอ้างอิงเฉพาะส่วนของผลลัพธ์ทุกครั้ง
Gnudiff

38

ตัวเลือกอื่นจะใช้กำเนิด:

>>> def f(x):
        y0 = x + 1
        yield y0
        yield x * 3
        yield y0 ** 4


>>> a, b, c = f(5)
>>> a
6
>>> b
15
>>> c
1296

แม้ว่า IMHO tuples มักจะดีที่สุดยกเว้นในกรณีที่ค่าที่ส่งคืนเป็นตัวเลือกสำหรับการห่อหุ้มในคลาส


1
นี่น่าจะเป็นทางออกที่สะอาดที่สุดและมีไวยากรณ์ที่สะอาด มีข้อเสียใด ๆ ในเรื่องนี้หรือไม่? หากคุณไม่ได้ใช้ผลตอบแทนทั้งหมดจะมีอัตราผลตอบแทนที่ 'ไม่ได้ใช้' ที่รอทำร้ายคุณหรือไม่?
Jiminion

24
นี่อาจเป็น "สะอาด" แต่ดูเหมือนจะไม่ง่ายเลย วิธีการจะเป็นคนที่ไม่เคยเจอแบบนี้มาก่อนรู้ว่าการทำเช่นการเปิดออก tuple อัตโนมัติจะเรียกแต่ละyield?
coredumperror

1
@CoreDumpError กำเนิดเป็นเพียงว่า ... กำเนิด ไม่มีความแตกต่างภายนอกระหว่างdef f(x): …; yield b; yield a; yield rvs. (g for g in [b, a, r])และทั้งสองอย่างพร้อมที่จะแปลงเป็นรายการหรือสิ่งอันดับและเช่นนี้จะรองรับการเปิด tuple รูปแบบตัวสร้าง tuple เป็นไปตามวิธีการทำงานในขณะที่รูปแบบฟังก์ชั่นมีความจำเป็นและจะช่วยให้การควบคุมการไหลและการกำหนดตัวแปร
sleblanc

30

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

ฉันใช้พจนานุกรมเป็นค่าตอบแทนเฉพาะเมื่อวัตถุที่จัดกลุ่มไม่เหมือนกันเสมอไป คิดว่าส่วนหัวอีเมลที่เป็นตัวเลือก

สำหรับส่วนที่เหลือของกรณีที่วัตถุที่จัดกลุ่มมีความหมายโดยธรรมชาติภายในกลุ่มหรือวัตถุที่เต็มเปี่ยมด้วยวิธีการของตัวเองเป็นสิ่งที่จำเป็นฉันใช้ชั้นเรียน


29

ฉันชอบ:

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }

ดูเหมือนว่าทุกอย่างเป็นเพียงรหัสเสริมเพื่อทำสิ่งเดียวกัน


22
สิ่งอันดับจะง่ายกว่าในการแกะ: y0, y1, y2 = g () พร้อม dict ที่คุณต้องทำ: result = g () y0, y1, y2 = result.get ('y0'), result.get ('y1' ), result.get ('y2') ซึ่งน่าเกลียดนิดหน่อย แต่ละวิธีมี 'pluses' และ 'minuses'
Oli

27
>>> def func():
...    return [1,2,3]
...
>>> a,b,c = func()
>>> a
1
>>> b
2
>>> c
3

@ edouard ไม่มันไม่ส่งคืน tuple ไม่ใช่รายการ
Simon Hibbs

1
destructuring เป็นอาร์กิวเมนต์สำหรับรายการกลับมาในความคิดของฉัน
semiomant

21

โดยทั่วไป "โครงสร้างพิเศษ" ที่จริงแล้วเป็นสถานะปัจจุบันที่เหมาะสมของวัตถุด้วยวิธีการของตัวเอง

class Some3SpaceThing(object):
  def __init__(self,x):
    self.g(x)
  def g(self,x):
    self.y0 = x + 1
    self.y1 = x * 3
    self.y2 = y0 ** y3

r = Some3SpaceThing( x )
r.y0
r.y1
r.y2

ฉันชอบที่จะหาชื่อสำหรับโครงสร้างที่ไม่ระบุชื่อที่เป็นไปได้ ชื่อที่มีความหมายทำให้สิ่งต่าง ๆ ชัดเจนยิ่งขึ้น


20

Tuples, dicts, และวัตถุของ Python ช่วยให้โปรแกรมเมอร์ทำการแลกเปลี่ยนอย่างราบรื่นระหว่างพิธีการและความสะดวกสบายสำหรับโครงสร้างข้อมูลขนาดเล็ก ("สิ่งของ") สำหรับฉันการเลือกวิธีที่จะเป็นตัวแทนของสิ่งนั้นส่วนใหญ่จะกำหนดโดยวิธีที่ฉันจะใช้โครงสร้าง ใน C ++ เป็นวิธีการทั่วไปที่ใช้structสำหรับไอเท็มข้อมูลอย่างเดียวและclassสำหรับออบเจ็กต์ที่มีเมธอดถึงแม้ว่าคุณจะสามารถวางเมธอดบน a struct; นิสัยของฉันคือที่คล้ายกันในหลามด้วยdictและในสถานที่ของtuplestruct

สำหรับชุดพิกัดฉันจะใช้จุดtupleมากกว่าclassหรือ a dict(และโปรดทราบว่าคุณสามารถใช้ a tupleเป็นคีย์พจนานุกรมดังนั้นdictสร้างอาร์เรย์หลายมิติที่กระจัดกระจาย)

ถ้าฉันจะทำซ้ำในรายการของสิ่งต่าง ๆ ฉันชอบที่จะเอาออกtupleในการทำซ้ำ:

for score,id,name in scoreAllTheThings():
    if score > goodScoreThreshold:
        print "%6.3f #%6d %s"%(score,id,name)

... เนื่องจากเวอร์ชันของวัตถุอ่านรกรุงรังมากขึ้น:

for entry in scoreAllTheThings():
    if entry.score > goodScoreThreshold:
        print "%6.3f #%6d %s"%(entry.score,entry.id,entry.name)

... dictให้อยู่คนเดียว

for entry in scoreAllTheThings():
    if entry['score'] > goodScoreThreshold:
        print "%6.3f #%6d %s"%(entry['score'],entry['id'],entry['name'])

หากสิ่งนี้มีการใช้กันอย่างแพร่หลายและคุณพบว่าตัวเองกำลังทำสิ่งที่คล้าย ๆ กันในหลาย ๆ ที่ในรหัสก็มักจะคุ้มค่าที่จะทำให้มันเป็นคลาสวัตถุด้วยวิธีการที่เหมาะสม

ท้ายที่สุดถ้าฉันจะแลกเปลี่ยนข้อมูลกับส่วนประกอบของระบบที่ไม่ใช่งูใหญ่ฉันจะเก็บไว้ในdictเพราะส่วนใหญ่เหมาะกับอนุกรม JSON


19

+1 ตามคำแนะนำของ S.Lott เกี่ยวกับคลาสคอนเทนเนอร์ที่ระบุชื่อ

สำหรับ Python 2.6 ขึ้นไปtuple ที่มีชื่อจะให้วิธีที่มีประโยชน์ในการสร้างคลาสคอนเทนเนอร์เหล่านี้ได้อย่างง่ายดายและผลลัพธ์นั้น "เบาและไม่ต้องใช้หน่วยความจำมากกว่า tuples ปกติ"


4

ในภาษาอย่าง Python ฉันมักจะใช้พจนานุกรมเนื่องจากมีค่าใช้จ่ายน้อยกว่าการสร้างคลาสใหม่

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


4

ฉันจะใช้ dict เพื่อส่งผ่านและคืนค่าจากฟังก์ชัน:

ใช้แบบฟอร์มตัวแปรที่กำหนดไว้ในแบบฟอร์ม

form = {
    'level': 0,
    'points': 0,
    'game': {
        'name': ''
    }
}


def test(form):
    form['game']['name'] = 'My game!'
    form['level'] = 2

    return form

>>> print(test(form))
{u'game': {u'name': u'My game!'}, u'points': 0, u'level': 2}

นี่เป็นวิธีที่มีประสิทธิภาพที่สุดสำหรับฉันและหน่วยการประมวลผล

คุณต้องผ่านตัวชี้เพียงตัวเดียวและส่งกลับตัวชี้เพียงตัวเดียว

คุณไม่จำเป็นต้องเปลี่ยนฟังก์ชั่น '(พันของพวกเขา) ขัดแย้งเมื่อใดก็ตามที่คุณทำการเปลี่ยนแปลงในรหัสของคุณ


Dicts ไม่แน่นอน หากคุณส่ง dict ไปยังฟังก์ชันและฟังก์ชันนั้นแก้ไข dict การเปลี่ยนแปลงจะปรากฏนอกขอบเขตของฟังก์ชันนั้น การมีฟังก์ชันส่งคืน dict ในตอนท้ายอาจหมายความว่าฟังก์ชันนั้นไม่มีผลข้างเคียงดังนั้นจึงไม่ควรส่งคืนค่าทำให้ชัดเจนว่าtestจะแก้ไขค่าโดยตรง เปรียบเทียบสิ่งนี้กับdict.updateซึ่งไม่ส่งคืนค่า
sleblanc

@sleblanc "การมีฟังก์ชั่นคืน dict ในตอนท้ายอาจหมายความว่าฟังก์ชั่นไม่มีผลข้างเคียง" มันไม่ได้หมายความว่าเพราะอย่างที่คุณบอกว่า dict นั้นไม่แน่นอน อย่างไรก็ตามการกลับมาformไม่ส่งผลกระทบต่อความสามารถในการอ่านและประสิทธิภาพ ในกรณีที่คุณอาจต้องฟอร์แมตใหม่การformส่งคืน [แบบฟอร์ม] จะทำให้แน่ใจว่าการformส่งคืนครั้งสุดท้ายจะส่งคืนเนื่องจากคุณจะไม่ติดตามการเปลี่ยนแปลงแบบฟอร์มทุกที่
Elis Byberi

3

"ดีที่สุด" เป็นการตัดสินใจแบบอัตนัยบางส่วน ใช้สิ่งอันดับสำหรับชุดผลตอบแทนขนาดเล็กในกรณีทั่วไปที่ยอมรับได้ไม่เปลี่ยนรูป tuple นั้นนิยมใช้ในรายการเสมอเมื่อความไม่แน่นอนนั้นไม่จำเป็นต้องมี

สำหรับค่าส่งคืนที่ซับซ้อนมากขึ้นหรือสำหรับกรณีที่เป็นทางการมีค่า (เช่นรหัสมูลค่าสูง) ชื่อ tuple จะดีกว่า สำหรับกรณีที่ซับซ้อนที่สุดวัตถุมักจะดีที่สุด อย่างไรก็ตามมันเป็นสถานการณ์ที่สำคัญจริงๆ ถ้ามันสมเหตุสมผลที่จะคืนค่าวัตถุเพราะนั่นคือสิ่งที่คุณมีในตอนท้ายของฟังก์ชั่น (เช่นรูปแบบโรงงาน) จากนั้นส่งคืนวัตถุ

ในฐานะที่เป็นคนฉลาดกล่าวว่า:

การเพิ่มประสิทธิภาพก่อนวัยอันควรเป็นรากของความชั่วร้ายทั้งหมด (หรืออย่างน้อยที่สุดของมัน) ในการเขียนโปรแกรม


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