سؤال

أنا متأكد من أن هناك طريقة أبسط للقيام بذلك والتي لا تخطر على بالي.

أنا أتصل بمجموعة من الطرق التي تُرجع قائمة.قد تكون القائمة فارغة.إذا كانت القائمة غير فارغة، أريد إرجاع العنصر الأول؛وإلا، أريد العودة لا شيء.يعمل هذا الكود:

my_list = get_list()
if len(my_list) > 0: return my_list[0]
return None

يبدو لي أنه يجب أن يكون هناك مصطلح بسيط من سطر واحد للقيام بذلك، لكن طوال حياتي لا أستطيع التفكير في ذلك.هل هناك؟

يحرر:

السبب الذي يجعلني أبحث عن تعبير من سطر واحد هنا ليس أنني أحب التعليمات البرمجية المقتضبة بشكل لا يصدق، ولكن لأنني مضطر إلى كتابة الكثير من التعليمات البرمجية مثل هذا:

x = get_first_list()
if x:
    # do something with x[0]
    # inevitably forget the [0] part, and have a bug to fix
y = get_second_list()
if y:
    # do something with y[0]
    # inevitably forget the [0] part AGAIN, and have another bug to fix

من المؤكد أن ما أود القيام به يمكن تحقيقه من خلال وظيفة (وربما سيتم):

def first_item(list_or_none):
    if list_or_none: return list_or_none[0]

x = first_item(get_first_list())
if x:
    # do something with x
y = first_item(get_second_list())
if y:
    # do something with y

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

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

المحلول

بايثون 2.6+

next(iter(your_list), None)

لو your_list يمكن ان يكون None:

next(iter(your_list or []), None)

بايثون 2.4

def get_first(iterable, default=None):
    if iterable:
        for item in iterable:
            return item
    return default

مثال:

x = get_first(get_first_list())
if x:
    ...
y = get_first(get_second_list())
if y:
    ...

خيار آخر هو تضمين الوظيفة المذكورة أعلاه:

for x in get_first_list() or []:
    # process x
    break # process at most one item
for y in get_second_list() or []:
    # process y
    break

لتجنب break يمكنك الكتابة:

for x in yield_first(get_first_list()):
    x # process x
for y in yield_first(get_second_list()):
    y # process y

أين:

def yield_first(iterable):
    for item in iterable or []:
        yield item
        return

نصائح أخرى

أفضل طريقة هي هذه:

a = get_list()
return a[0] if a else None

يمكنك أيضًا القيام بذلك في سطر واحد، لكن يصعب على المبرمج قراءته:

return (get_list()[:1] or [None])[0]
(get_list() or [None])[0]

التي يجب أن تعمل.

راجع للشغل لم أستخدم المتغير list, ، لأن ذلك يحل محل المدمج list() وظيفة.

يحرر:كان لدي نسخة أبسط قليلا، ولكن خاطئة هنا في وقت سابق.

الطريقة الأكثر اصطلاحًا في لغة بايثون هي استخدام التالي () على المكرر نظرًا لأن القائمة هي متوقعة.تمامًا مثل ما وضعه @J.F.Sebastian في التعليق بتاريخ 13 كانون الأول (ديسمبر) 2011.

next(iter(the_list), None) هذا يُرجع لا شيء إذا the_list فارغ.يرى التالي () بايثون 2.6+

أو إذا كنت تعرف على وجه اليقين the_list ليس فارغا:

iter(the_list).next() يرى iterator.next() بايثون 2.2+

لقد أصبح حل OP قريبًا، هناك بعض الأشياء التي تجعله أكثر لغة بايثونية.

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

if list:

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

لذلك دعونا نغير ذلك إلى

some_list = get_list()
if some_list:

النقطة المهمة حقًا التي تفتقدها الكثير من الحلول هنا هي ذلك جميع وظائف/طرق بايثون لا تُرجع أي شيء افتراضيًا.جرب ما يلي أدناه.

def does_nothing():
    pass

foo = does_nothing()
print foo

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

some_list = get_list()
if some_list:
    return list[0]

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

def get_first_item(some_list): 
    if some_list:
        return list[0]

my_list = get_list()
first_item = get_first_item(my_list)

كما قلت، كان OP على وشك الانتهاء، وبضع لمسات فقط أعطته نكهة بايثون التي تبحث عنها.

إذا وجدت نفسك تحاول انتزاع أول شيء (أو لا شيء) من قائمة الفهم، فيمكنك التبديل إلى المولد للقيام بذلك مثل:

next((x for x in blah if cond), None)

طليعة:يعمل إذا لم يكن بلاه قابلاً للفهرسةإنها بنية غير مألوفة.إنه مفيد أثناء اختراق الأشياء وتصفيتها في ipython.

for item in get_list():
    return item

بصراحة، لا أعتقد أن هناك لغة أفضل:كلامك واضح ومقتضب - لا حاجة لأي شيء "أفضل".ربما، لكن هذه مسألة ذوق حقًا، يمكنك التغيير if len(list) > 0: مع if list: - سيتم تقييم القائمة الفارغة دائمًا إلى خطأ.

وفي ملاحظة ذات صلة، بايثون هي لا Perl (لا أقصد التورية!)، ليس عليك الحصول على أفضل كود ممكن.
في الواقع، أسوأ كود رأيته في بايثون، كان أيضًا رائعًا جدًا :-) وغير قابل للصيانة تمامًا.

بالمناسبة، معظم الحلول التي رأيتها هنا لا تأخذ في الاعتبار عندما يتم تقييم القائمة [0] إلى خطأ (على سبيل المثال.سلسلة فارغة، أو صفر) - في هذه الحالة، جميعها تُرجع لا شيء وليس العنصر الصحيح.

لغة بايثون لإرجاع العنصر الأول أو لا شيء؟

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

def get_first(l): 
    return l[0] if l else None

وإذا تم إرجاع القائمة من أ get_list وظيفة:

l = get_list()
return l[0] if l else None

تم توضيح طرق أخرى للقيام بذلك هنا، مع التوضيحات

for

عندما بدأت أحاول التفكير في طرق ذكية للقيام بذلك، هذا هو الشيء الثاني الذي فكرت فيه:

for item in get_list():
    return item

هذا يفترض أن الوظيفة تنتهي هنا، وتعود ضمنيًا None لو get_list إرجاع قائمة فارغة.الكود الصريح أدناه مكافئ تمامًا:

for item in get_list():
    return item
return None

if some_list

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

some_list = get_list()
if some_list:
    return some_list[0]

شريحة or [None] وحدد الفهرس الصفري

هذا أيضًا موجود في الإجابة الأكثر تصويتًا:

return (get_list()[:1] or [None])[0]

الشريحة غير ضرورية، وتقوم بإنشاء قائمة إضافية مكونة من عنصر واحد في الذاكرة.يجب أن يكون ما يلي أكثر أداءً.لشرح، or إرجاع العنصر الثاني إذا كان الأول False في سياق منطقي، لذلك إذا get_list بإرجاع قائمة فارغة، فإن التعبير الموجود بين القوسين سيرجع قائمة بـ "لا شيء"، والتي سيتم الوصول إليها بعد ذلك عن طريق 0 فِهرِس:

return (get_list() or [None])[0]

يستخدم العنصر التالي حقيقة ذلك ويعيد العنصر الثاني إذا كان الأول True في سياق منطقي، وبما أنه يشير إلى my_list مرتين، فهو ليس أفضل من التعبير الثلاثي (وليس من الناحية الفنية عبارة عن سطر واحد):

my_list = get_list() 
return (my_list and my_list[0]) or None

next

ثم لدينا الاستخدام الذكي التالي للدمج next و iter

return next(iter(get_list()), None)

لشرح، iter إرجاع مكرر مع a .next طريقة.(.__next__ في بايثون 3.) ثم المدمج next يدعو ذلك .next الطريقة، وإذا تم استنفاد المكرر، فسيتم إرجاع القيمة الافتراضية التي قدمناها، None.

تعبير ثلاثي زائد عن الحاجة (a if b else c) وتدور مرة أخرى

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

return None if not get_list() else get_list()[0]

العكس أفضل:

return get_list()[0] if get_list() else None

والأفضل من ذلك، استخدام متغير محلي لذلك get_list يتم استدعاؤه مرة واحدة فقط، وقد تمت مناقشة الحل Pythonic الموصى به أولاً:

l = get_list()
return l[0] if l else None

وفيما يتعلق بالتعابير، هناك وصفة itertools مُسَمًّى nth.

من وصفات itertools :

def nth(iterable, n, default=None):
    "Returns the nth item or a default value"
    return next(islice(iterable, n, None), default)

إذا كنت تريد سطرًا واحدًا، ففكر في تثبيت مكتبة تنفذ هذه الوصفة لك، على سبيل المثال. more_itertools:

import more_itertools as mit

mit.nth([3, 2, 1], 0)
# 3

mit.nth([], 0)                                             # default is `None`
# None

تتوفر أداة أخرى تقوم بإرجاع العنصر الأول فقط، والذي يسمى more_itertools.first.

mit.first([3, 2, 1])
# 3

mit.first([], default=None)
# None

يتم قياس أدوات التكرار هذه بشكل عام لأي شيء قابل للتكرار، وليس فقط للقوائم.

بدافع الفضول، قمت بتشغيل التوقيتات على اثنين من الحلول.الحل الذي يستخدم عبارة return لإنهاء حلقة for قبل الأوان هو أكثر تكلفة قليلاً على جهازي مع Python 2.5.1، وأظن أن هذا له علاقة بإعداد التكرار.

import random
import timeit

def index_first_item(some_list):
    if some_list:
        return some_list[0]


def return_first_item(some_list):
    for item in some_list:
        return item


empty_lists = []
for i in range(10000):
    empty_lists.append([])

assert empty_lists[0] is not empty_lists[1]

full_lists = []
for i in range(10000):
    full_lists.append(list([random.random() for i in range(10)]))

mixed_lists = empty_lists[:50000] + full_lists[:50000]
random.shuffle(mixed_lists)

if __name__ == '__main__':
    ENV = 'import firstitem'
    test_data = ('empty_lists', 'full_lists', 'mixed_lists')
    funcs = ('index_first_item', 'return_first_item')
    for data in test_data:
        print "%s:" % data
        for func in funcs:
            t = timeit.Timer('firstitem.%s(firstitem.%s)' % (
                func, data), ENV)
            times = t.repeat()
            avg_time = sum(times) / len(times)
            print "  %s:" % func
            for time in times:
                print "    %f seconds" % time
            print "    %f seconds avg." % avg_time

هذه هي المواعيد التي حصلت عليها:

empty_lists:
  index_first_item:
    0.748353 seconds
    0.741086 seconds
    0.741191 seconds
    0.743543 seconds avg.
  return_first_item:
    0.785511 seconds
    0.822178 seconds
    0.782846 seconds
    0.796845 seconds avg.
full_lists:
  index_first_item:
    0.762618 seconds
    0.788040 seconds
    0.786849 seconds
    0.779169 seconds avg.
  return_first_item:
    0.802735 seconds
    0.878706 seconds
    0.808781 seconds
    0.830074 seconds avg.
mixed_lists:
  index_first_item:
    0.791129 seconds
    0.743526 seconds
    0.744441 seconds
    0.759699 seconds avg.
  return_first_item:
    0.784801 seconds
    0.785146 seconds
    0.840193 seconds
    0.803380 seconds avg.
try:
    return a[0]
except IndexError:
    return None
def head(iterable):
    try:
        return iter(iterable).next()
    except StopIteration:
        return None

print head(xrange(42, 1000)  # 42
print head([])               # None

بالمناسبة:سأعيد صياغة تدفق برنامجك العام إلى شيء مثل هذا:

lists = [
    ["first", "list"],
    ["second", "list"],
    ["third", "list"]
]

def do_something(element):
    if not element:
        return
    else:
        # do something
        pass

for li in lists:
    do_something(head(li))

(تجنب التكرار كلما أمكن ذلك)

وماذا عن هذا:

(my_list and my_list[0]) or None

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

يمكنك استخدام طريقة الاستخراج.بمعنى آخر، قم باستخراج هذا الرمز إلى طريقة يمكنك الاتصال بها بعد ذلك.

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

استخدام الخدعة و-أو:

a = get_list()
return a and a[0] or None

وماذا عن: next(iter(get_list()), None)؟قد لا يكون الأسرع هنا، ولكنه قياسي (يبدأ من Python 2.6) وموجز.

ربما ليس الحل الأسرع، لكن لم يذكر أحد هذا الخيار:

dict(enumerate(get_list())).get(0)

لو get_list() يمكن العودة None يمكنك استخدام:

dict(enumerate(get_list() or [])).get(0)

مزايا:

-خط واحد

-أنت فقط اتصل get_list() مرة واحدة

-سهل الفهم

كانت حالة الاستخدام الخاصة بي فقط لتعيين قيمة متغير محلي.

أنا شخصياً وجدت أن أسلوب المحاولة والاستثناء أنظف للقراءة

items = [10, 20]
try: first_item = items[0]
except IndexError: first_item = None
print first_item

من تقطيع القائمة.

items = [10, 20]
first_item = (items[:1] or [None, ])[0]
print first_item
if mylist != []:

       print(mylist[0])

   else:

       print(None)

اقترح العديد من الأشخاص القيام بشيء مثل هذا:

list = get_list()
return list and list[0] or None

يعمل هذا في كثير من الحالات، ولكنه لن ينجح إلا إذا كانت القائمة [0] لا تساوي 0 أو خطأ أو سلسلة فارغة.إذا كانت القائمة [0] تساوي 0 أو خطأ أو سلسلة فارغة، فسترجع الطريقة بلا شيء بشكل غير صحيح.

لقد قمت بإنشاء هذا الخطأ في الكود الخاص بي عدة مرات!

ليس الثعبان الاصطلاحي مكافئًا للمشغلين الثلاثيين على النمط C

cond and true_expr or false_expr

أي.

list = get_list()
return list and list[0] or None
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top