python اكواد - كيف يمكنك إرجاع قيم متعددة في بايثون؟




لغة جاهزة (12)

عادةً ما تكون الطريقة المتبعة لإعادة القيم المتعددة في اللغات التي تدعمها عبارة عن tupling .

الخيار: استخدام الصفوف

فكر في هذا المثال التافه:

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

ومع ذلك ، فإن هذا يحصل بسرعة مع زيادة عدد القيم المرتجعة. ماذا لو كنت تريد إرجاع أربع أو خمس قيم؟ من المؤكد أنك يمكن أن تستمر في الإمساك بها ، ولكن من السهل نسيان أي قيمة لها. كما أنه من البشع فك حزمها أينما تريد استلامها.

الخيار: استخدام القاموس

يبدو أن الخطوة المنطقية التالية هي تقديم نوع من "تدوين السجل". في بيثون ، الطريقة الواضحة للقيام بذلك هي عن طريق الوسيلة.

خذ بعين الاعتبار ما يلي:

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

(تحرير- فقط لكي تكون واضحًا ، y0 ، y1 و y2 معني فقط كمعرِّفات مجردة. كما أشرنا ، من الناحية العملية ، ستستخدم مُعرِّفات ذات معنى)

الآن ، لدينا آلية يمكن بواسطتها أن نعلن عن عضو معين في الكائن المرتجع. فمثلا،

result['y0']

الخيار: استخدام الفصل

ومع ذلك، هناك خيار آخر. يمكننا بدلا من ذلك إعادة هيكل متخصص. لقد وضعت هذا في سياق بايثون ، لكنني متأكد من أنه ينطبق على لغات أخرى أيضًا. في الواقع ، إذا كنت تعمل في C ، فقد يكون هذا هو خيارك الوحيد. هنا يذهب:

class ReturnValue(object):
  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 .

هناك ميزة إضافية تقدمها بايثون لكائنات صغيرة ، السمة __slots__ . يمكن التعبير عن الفئة على النحو التالي:

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

من دليل بايثون المرجعي :

يأخذ الإقرار __slots__ سلسلة من متغيرات الحالة ويحتفظ فقط بمساحة كافية في كل حالة للاحتفاظ بقيمة لكل متغير. يتم حفظ الفضاء لأنه لم يتم إنشاء __dict__ لكل مثيل.

الخيار: استخدام القائمة

اقتراح آخر الذي أغفلته يأتي من Bill the Lizard:

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

هذا هو أقل وسيلة مفضلة على الرغم من. أفترض أني موبوء بالتعرض لـ هاسكل ، لكن فكرة القوائم المختلطة كانت دائمًا غير مريحة لي. في هذا المثال بالذات ، تكون القائمة من النوع غير المختلط ، لكن يمكن تصوره. إن القائمة المستخدمة بهذه الطريقة لا تكسب أي شيء فيما يتعلق بالصفوف بقدر ما أستطيع أن أقول. الفرق الحقيقي الوحيد بين القوائم و tuples في بايثون هو أن القوائم mutable ، في حين أن الصفوف لا تكون كذلك. أنا شخصيا أميل إلى حمل الاتفاقيات من البرمجة الوظيفية: استخدام قوائم لأي عدد من العناصر من نفس النوع ، ومجموعة من عدد محدد من عناصر الأنواع المحددة سلفا.

سؤال

بعد ديباجة طويلة ، يأتي السؤال الحتمي. ما هي الطريقة (في رأيك) الأفضل؟

لقد وجدت نفسي عادة في طريق السير في القاموس لأنها تنطوي على عمل أقل في الإعداد. من وجهة نظر الأنواع ومع ذلك ، قد يكون من الأفضل لك الذهاب إلى مسار الصف ، لأن ذلك قد يساعدك على تجنب الخلط بين ما يمثله القاموس. من ناحية أخرى ، هناك البعض في مجتمع بايثون الذي يشعر بأنه يجب تفضيل الواجهات الضمنية إلى واجهات صريحة ، وعند هذه النقطة يكون نوع الكائن غير ذي صلة حقًا ، حيث أنك تعتمد بشكل أساسي على الاتفاقية التي لها نفس السمة سوف يكون دائما نفس المعنى.

إذن ، كيف تعيد قيم متعددة في بايثون؟


Answers

تقدم مجموعات pththon ، dicts ، والكائنات للمبرمج على نحو سلس بين الشكلية والراحة لهياكل البيانات الصغيرة ("الأشياء"). بالنسبة لي ، فإن اختيار كيفية تمثيل شيء ما يمليه بشكل أساسي على كيفية استخدام البنية. في C ++ ، يكون من الاصطلاحات الشائعة استخدام struct لعناصر class البيانات فقط للكائنات ذات الأساليب ، حتى إذا كان بإمكانك وضع الطرق بشكل قانوني على struct ؛ عادتي مشابهة في بيثون ، مع dict و tuple بدلاً من struct .

بالنسبة لمجموعات الإحداثيات ، سوف أستخدم tuple أكثر من class نقطية أو dict (ونلاحظ أنه يمكنك استخدام مجموعة tuple كمفتاح القاموس ، لذا يجب أن تقوم بإنشاء صفائف متفرقة متعددة الأبعاد كبيرة).

إذا كنت سأقوم بالتكرار على قائمة من الأشياء ، فأفضل أن أفرز tuple s في التكرار:

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)

ناهيك عن الوسيلة

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

إذا كان الشيء مستخدَماً على نطاق واسع ، وتجد نفسك تقوم بعمليات مشابهة غير تافهة عليه في أماكن متعددة في الكود ، فمن المفيد عادةً جعله كائنًا صنفًا بالطرق المناسبة.

وأخيرًا ، إذا كنت سأقوم بتبادل البيانات مع مكونات نظام غير بيثون ، فغالبًا ما أحتفظ بها في أمر لأن هذا هو الأنسب لعملية تسلسل JSON.


أفضّل استخدام الصفوف عندما تشعر مجموعة "طبيعية" ؛ الإحداثيات هي مثال نموذجي ، حيث يمكن للكائنات المنفصلة أن تقف من تلقاء نفسها ، على سبيل المثال في حسابات القياس أحادية المحور فقط.

أستخدم القواميس كقيمة إرجاع فقط عندما لا تكون الكائنات المجمعة هي نفسها دائمًا. فكر في عناوين البريد الإلكتروني الاختيارية.

بالنسبة لباقي الحالات ، حيث يكون للكائنات المجمعة معنى أصيل داخل المجموعة أو هناك حاجة إلى كائن مكتمل بالكامل مع أساليبها الخاصة ، أستخدم فصل دراسي.


+1 على اقتراح S.Lott لفئة حاوية مسمى.

بالنسبة إلى python 2.6 وما فوق ، توفر مجموعة tuple طريقة مفيدة لإنشاء فئات الحاوية هذه بسهولة ، وتكون النتائج "خفيفة الوزن ولا تتطلب ذاكرة أكثر من الصفوف العادية".


أصوت للقاموس.

أجد أنه إذا قمت بعمل وظيفة تقوم بإرجاع أي شيء أكثر من متغيرين سوف أقوم بطيهما في القاموس. وإلا فإنني أميل إلى نسيان ترتيب ومحتوى ما أعيده.

أيضا ، إدخال بنية "خاصة" يجعل من الصعب متابعة التعليمات البرمجية الخاصة بك. (سيضطر شخص آخر للبحث عبر الرمز لمعرفة ما هو عليه)

إذا كان لديك قلق بشأن البحث عن النوع ، فاستخدم مفاتيح القاموس الوصفي ، على سبيل المثال ، "قائمة قيم x".

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

أنا أفضل

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

يبدو أن كل شيء آخر هو مجرد رمز إضافي للقيام بنفس الشيء.


في اللغات مثل Python ، عادةً ما أستخدم قاموسًا لأنه يتضمن مقدارًا أقل من إنشاء فئة جديدة.

ومع ذلك ، إذا وجدت نفسي أعيد باستمرار نفس مجموعة المتغيرات ، فإن ذلك ربما ينطوي على فئة جديدة سأحددها.


بالنسبة للمشروعات الصغيرة ، أجد أنه من الأسهل العمل مع الصفوف. عندما يصبح من الصعب للغاية إدارة (وليس قبل ذلك) أبدأ بتجميع الأشياء في بنيات منطقية ، ومع ذلك أعتقد أن استخدامك المقترح للقواميس وعناصر ReturnValue خاطئ (أو مفرط في التبسيط).

لا يوفر إرجاع قاموس بمفاتيح y0 و y1 و y2 وغيرها أي ميزة على الصفوف. عودة مثيل ReturnValue مع الخصائص .y0 .y1 .y2 إلخ لا تقدم أي ميزة على tuples أيضاً. تحتاج إلى البدء في تسمية الأشياء إذا كنت ترغب في الوصول إلى أي مكان ، ويمكنك القيام بذلك باستخدام الصفوف على أي حال:

def getImageData(filename):
  [snip]
  return size, (format, version, compression), (width,height)
size, type, dimensions = getImageData(x)

IMHO ، الأسلوب الجيد الوحيد وراء الصفوف هو إعادة الكائنات الحقيقية بالطرق والخصائص المناسبة ، مثل تحصل من re.match() أو open(file) .


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

يمكنني استخدام أداة لتمرير وإرجاع القيم من إحدى الوظائف:

استخدم نموذج متغير كما هو موضح في form .

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}

هذه هي الطريقة الأكثر فعالية بالنسبة لي وللوحدة المعالجة.

يجب عليك تمرير مؤشر واحد فقط وإرجاع مؤشر واحد فقط.

ليس عليك تغيير الدالات (آلاف منها) كلما قمت بإجراء تغيير في التعليمات البرمجية الخاصة بك.


تشير الكثير من الإجابات إلى أنك تحتاج إلى إرجاع مجموعة من بعض الأنواع ، مثل قاموس أو قائمة. يمكنك ترك بناء الجملة الإضافية وكتابة قيم الإرجاع ، مفصولة بفواصل. ملاحظة: هذا يُرجع من الناحية الفنية فئة.

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

يعطي:

True
False

خيار آخر هو استخدام المولدات:

>>> 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 تكون عادة أفضل ، إلا في الحالات التي تكون فيها القيم التي يتم إرجاعها عبارة عن مرشحين للتغليف في الصف.


أنا عادة استخدام:

import subprocess

p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
    print line,
retval = p.wait()

أنت حر في القيام بما تريده مع بيانات stdout في الأنبوب. في الواقع ، يمكنك ببساطة حذف هذه المعلمات ( stdout= و stderr= ) وأنها سوف تتصرف مثل os.system() .





python coding-style return return-value