سؤال

في بايثون، يمكنك الحصول على دالة تقوم بإرجاع قيم متعددة.إليك مثال مفتعل:

def divide(x, y):
    quotient = x/y
    remainder = x % y
    return quotient, remainder  

(q, r) = divide(22, 7)

يبدو هذا مفيدًا جدًا، ولكن يبدو أنه من الممكن أيضًا إساءة استخدامه ("حسنًا.. الدالة X تحسب بالفعل ما نحتاجه كقيمة وسيطة.لنجعل X تُرجع هذه القيمة أيضًا").

متى يجب عليك رسم الخط وتحديد طريقة مختلفة؟

هل كانت مفيدة؟

المحلول

بالتأكيد (على سبيل المثال الذي قدمته).

Tuples هم مواطنون من الدرجة الأولى في بايثون

هناك وظيفة مدمجة divmod() هذا يفعل ذلك بالضبط.

q, r = divmod(x, y) # ((x - x%y)/y, x%y) Invariant: div*y + mod == x

هناك أمثلة أخرى: zip, enumerate, dict.items.

for i, e in enumerate([1, 3, 3]):
    print "index=%d, element=%s" % (i, e)

# reverse keys and values in a dictionary
d = dict((v, k) for k, v in adict.items()) # or 
d = dict(zip(adict.values(), adict.keys()))

راجع للشغل، الأقواس ليست ضرورية في معظم الأوقات.اقتباس من مرجع مكتبة بايثون:

يمكن بناء الصفوف بعدة طرق:

  • استخدام زوج من الأقواس للدلالة على الصف الفارغ:()
  • استخدام فاصلة زائدة لمجموعة مفردة:أ، أو (أ،)
  • فصل العناصر بفواصل:أ، ب، ج أو (أ، ب، ج)
  • باستخدام tuple() المدمج:tuple () أو tuple (قابل للتكرار)

يجب أن تخدم الوظائف غرضًا واحدًا

لذلك يجب عليهم إرجاع كائن واحد.في حالتك هذا الكائن عبارة عن صف.خذ بعين الاعتبار tuple كبنية بيانات مركبة مخصصة.هناك لغات حيث تقوم كل دالة تقريبًا بإرجاع قيم متعددة (القائمة في Lisp).

في بعض الأحيان يكفي العودة (x, y) بدلاً من Point(x, y).

الصفوف المسماة

مع تقديم الصفوف المُسمّاة في Python 2.6، يُفضل في كثير من الحالات إرجاع الصفوف المُسمّاة بدلاً من الصفوف البسيطة.

>>> import collections
>>> Point = collections.namedtuple('Point', 'x y')
>>> x, y = Point(0, 1)
>>> p = Point(x, y)
>>> x, y, p
(0, 1, Point(x=0, y=1))
>>> p.x, p.y, p[0], p[1]
(0, 1, 0, 1)
>>> for i in p:
...   print(i)
...
0
1

نصائح أخرى

أولاً، لاحظ أن بايثون تسمح بما يلي (لا حاجة للأقواس):

q, r = divide(22, 7)

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

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

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

بالنسبة لي، إذا كان هذا يجعل الكود أكثر نظافة، فهو بيثوني.قارن بين كتلتي التعليمات البرمجية هذه:

seconds = 1234
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)

seconds = 1234
minutes = seconds / 60
seconds = seconds % 60
hours = minutes / 60
minutes = minutes % 60

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

  1. أحيانًا يكون إرجاع قيم متعددة مفيدًا جدًا.خذ، على سبيل المثال، الطريقة التي تعالج حدثًا اختياريًا (تعيد بعض القيمة عند القيام بذلك) وترجع أيضًا النجاح أو الفشل.قد ينشأ هذا في نمط سلسلة المسؤولية.وفي حالات أخرى، قد ترغب في إرجاع أجزاء متعددة ومترابطة بشكل وثيق من البيانات --- كما في المثال الموضح.في هذا الإعداد، يكون إرجاع قيم متعددة بمثابة إرجاع مثيل واحد لفئة مجهولة تحتوي على عدة متغيرات للأعضاء.
  2. يتطلب تعامل بايثون مع وسيطات الطريقة القدرة على إرجاع قيم متعددة مباشرة.في لغة C++، على سبيل المثال، يمكن تمرير وسائط الطريقة حسب المرجع، بحيث يمكنك تعيين قيم الإخراج لها، بالإضافة إلى قيمة الإرجاع الرسمية.في بايثون، يتم تمرير الوسائط "حسب المرجع" (ولكن بمعنى Java، وليس C++).لا يمكنك تعيين قيم جديدة لوسائط الطريقة وجعلها تنعكس خارج نطاق الطريقة.على سبيل المثال:

    // C++
    void test(int& arg)
    {
        arg = 1;
    }
    
    int foo = 0;
    test(foo); // foo is now 1!
    

    مقارنة مع:

    # Python
    def test(arg):
        arg = 1
    
    foo = 0
    test(foo) # foo is still 0
    

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

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

إن إرجاع الصفوف أمر رائع.لاحظ أيضًا اسم اسمه الجديد الذي تمت إضافته في Python 2.6 والذي قد يجعل هذا أكثر قبولا بالنسبة لك:http://docs.python.org/dev/library/collections.html#collections.namedtuple

وقت إضافي:يحتوي Algol68 الخاص بـ RSRE على عامل التشغيل "/:=" الغريب.على سبيل المثال.

INT quotient:=355, remainder;
remainder := (quotient /:= 113);

حاصل على 3 والباقي 16.

ملحوظة:عادةً ما يتم تجاهل قيمة "(x/:=y)" حيث يتم تعيين حاصل القسمة "x" بواسطة المرجع، ولكن في حالة RSRE تكون القيمة التي تم إرجاعها هي الباقي.

راجع. الحساب الصحيح - Algol68

من الجيد إرجاع قيم متعددة باستخدام صف للوظائف البسيطة مثل divmod.إذا كان هذا يجعل الكود قابلاً للقراءة، فهو بايثوني.

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

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

def divide(x, y):
    return {'quotient': x/y, 'remainder':x%y }

answer = divide(22, 7)
print answer['quotient']
print answer['remainder']
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top