python اكواد - بدائل لبيان التبديل في بايثون؟




جاهزة لغة (25)

الحلول التي أستخدمها:

مجموعة من 2 من الحلول المنشورة هنا ، والتي يسهل قراءتها وتدعم الافتراضات.

result = {
  'a': lambda x: x * 5,
  'b': lambda x: x + 7,
  'c': lambda x: x - 2
}.get(whatToUse, lambda x: x - 22)(value)

أين

.get('c', lambda x: x - 22)(23)

يبحث عن "lambda x: x - 2" في الأمر ويستخدمها مع x=23

.get('xxx', lambda x: x - 22)(44)

لا يجدها في الأمر ويستخدم الافتراضي "lambda x: x - 22" مع x=44 .

أرغب في كتابة دالة في Python تقوم بإرجاع قيم ثابتة مختلفة استنادًا إلى قيمة فهرس الإدخال.

في اللغات الأخرى ، يمكنني استخدام عبارة switch أو case ، لكن لا يبدو أن Python تحتوي على عبارة switch . ما هي حلول بايثون الموصى بها في هذا السيناريو؟


التوسع في فكرة "dict as switch". إذا كنت تريد استخدام قيمة افتراضية لمفتاح التحويل:

def f(x):
    try:
        return {
            'a': 1,
            'b': 2,
        }[x]
    except KeyError:
        return 'default'

يمكنك استخدام القاموس:

def f(x):
    return {
        'a': 1,
        'b': 2,
    }[x]

Just mapping some a key to some code is not really and issue as most people have shown using the dict. The real trick is trying to emulate the whole drop through and break thing. I don't think I've ever written a case statement where I used that "feature". Here's a go at drop through.

def case(list): reduce(lambda b, f: (b | f[0], {False:(lambda:None),True:f[1]}[b | f[0]]())[0], list, False)

case([
    (False, lambda:print(5)),
    (True, lambda:print(4))
])

I was really imagining it as a single statement. I hope you'll pardon the silly formatting.

reduce(
    initializer=False,
    function=(lambda b, f:
        ( b | f[0]
        , { False: (lambda:None)
          , True : f[1]
          }[b | f[0]]()
        )[0]
    ),
    iterable=[
        (False, lambda:print(5)),
        (True, lambda:print(4))
    ]
)

I hope that's valid python. It should give you drop through. of course the boolean checks could be expressions and if you wanted them to be evaluated lazily you could wrap them all in a lambda. I wouldn't be to hard to make it accept after executing some of the items in the list either. Just make the tuple (bool, bool, function) where the second bool indicates whether or not to break or drop through.


أعجبتني إجابة مارك بيز

بما أنه يجب استخدام متغير x مرتين ، فقد قمت بتعديل وظائف lambda إلى بدون معلمات.

لا بد لي من العمل مع results[value](value)

In [2]: result = {
    ...:   'a': lambda x: 'A',
    ...:   'b': lambda x: 'B',
    ...:   'c': lambda x: 'C'
    ...: }
    ...: result['a']('a')
    ...: 
Out[2]: 'A'

In [3]: result = {
    ...:   'a': lambda : 'A',
    ...:   'b': lambda : 'B',
    ...:   'c': lambda : 'C',
    ...:   None: lambda : 'Nothing else matters'

    ...: }
    ...: result['a']()
    ...: 
Out[3]: 'A'

تحرير: لاحظت أنه يمكنني استخدام نوع بلا مع القواميس. لذلك هذا من شأنه أن يحاكي switch ; case else switch ; case else


وصفة بيثون المفضلة لدي للتبديل / الحالة هي:

choices = {'a': 1, 'b': 2}
result = choices.get(key, 'default')

قصيرة وبسيطة لسيناريوهات بسيطة.

قارن بين 11 سطر من كود C:

// C Language version of a simple 'switch/case'.
switch( key ) 
{
    case 'a' :
        result = 1;
        break;
    case 'b' :
        result = 2;
        break;
    default :
        result = -1;
}

يمكنك حتى تعيين متغيرات متعددة باستخدام tuples:

choices = {'a': (1, 2, 3), 'b': (4, 5, 6)}
(result1, result2, result3) = choices.get(key, ('default1', 'default2', 'default3'))

I would just use if/elif/else statements. I think that it's good enough to replace the switch statement.


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

result = {
  'a': obj.increment(x),
  'b': obj.decrement(x)
}.get(value, obj.default(x))

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

حل:

func, args = {
  'a' : (obj.increment, (x,)),
  'b' : (obj.decrement, (x,)),
}.get(value, (obj.default, (x,)))

result = func(*args)

حتى تحصل على قائمة تحتوي على وظيفة ووسائطها. بهذه الطريقة ، يتم فقط إرجاع مؤشر الدالة وقائمة الوسيطات ، ولا يتم تقييمهما. "النتيجة" ثم تقييم استدعاء الدالة التي تم إرجاعها.


لقد وجدت أن بنية تبديل مشتركة:

switch ...parameter...
case p1: v1; break;
case p2: v2; break;
default: v3;

يمكن التعبير عنها في بايثون على النحو التالي:

(lambda x: v1 if p1(x) else v2 if p2(x) else v3)

أو تنسيقها بطريقة أوضح:

(lambda x:
     v1 if p1(x) else
     v2 if p2(x) else
     v3)

بدلاً من أن تكون عبارة عن بيان ، فإن إصدار python هو تعبير ، يتم تقييمه إلى قيمة.


Expanding on Greg Hewgill's answer - We can encapsulate the dictionary-solution using a decorator:

def case(callable):
    """switch-case decorator"""
    class case_class(object):
        def __init__(self, *args, **kwargs):
            self.args = args
            self.kwargs = kwargs

        def do_call(self):
            return callable(*self.args, **self.kwargs)

return case_class

def switch(key, cases, default=None):
    """switch-statement"""
    ret = None
    try:
        ret = case[key].do_call()
    except KeyError:
        if default:
            ret = default.do_call()
    finally:
        return ret

This can then be used with the @case -decorator

@case
def case_1(arg1):
    print 'case_1: ', arg1

@case
def case_2(arg1, arg2):
    print 'case_2'
    return arg1, arg2

@case
def default_case(arg1, arg2, arg3):
    print 'default_case: ', arg1, arg2, arg3

ret = switch(somearg, {
    1: case_1('somestring'),
    2: case_2(13, 42)
}, default_case(123, 'astring', 3.14))

print ret

والخبر السار هو أن هذا قد تم بالفعل في NeoPySwitch -module. ببساطة تثبيت باستخدام pip:

pip install NeoPySwitch

هناك نمط تعلمته من كود Twisted Python.

class SMTP:
    def lookupMethod(self, command):
        return getattr(self, 'do_' + command.upper(), None)
    def do_HELO(self, rest):
        return 'Howdy ' + rest
    def do_QUIT(self, rest):
        return 'Bye'

SMTP().lookupMethod('HELO')('foo.bar.com') # => 'Howdy foo.bar.com'
SMTP().lookupMethod('QUIT')('') # => 'Bye'

يمكنك استخدامه في أي وقت تحتاج إلى إرساله على رمز مميز وتنفيذ جزء من التعليمات البرمجية الممتدة. في جهاز الدولة ، سيكون لديك وسائل state_ و ترسل على self.state . يمكن تمديد do_ عن طريق do_ من الفئة الأساسية وتحديد أساليب do_ الخاصة بك. في كثير من الأحيان لن يكون لديك حتى أساليب do_ في الصف الأساسي.

تحرير: كيف يتم ذلك بالضبط

في حالة SMTP ، ستستلم HELO من السلك. الشفرة ذات الصلة (من twisted/mail/smtp.py ، المعدلة twisted/mail/smtp.py ) تبدو كالتالي

class SMTP:
    # ...

    def do_UNKNOWN(self, rest):
        raise NotImplementedError, 'received unknown command'

    def state_COMMAND(self, line):
        line = line.strip()
        parts = line.split(None, 1)
        if parts:
            method = self.lookupMethod(parts[0]) or self.do_UNKNOWN
            if len(parts) == 2:
                return method(parts[1])
            else:
                return method('')
        else:
            raise SyntaxError, 'bad syntax'

SMTP().state_COMMAND('   HELO   foo.bar.com  ') # => Howdy foo.bar.com

سوف تتلقى ' HELO foo.bar.com ' (أو قد تحصل على 'QUIT' أو 'RCPT TO: foo' ). هذا هو tokenized إلى parts مثل ['HELO', 'foo.bar.com'] . يتم أخذ اسم البحث عن الطريقة الفعلية من parts[0] .

(تسمى الطريقة الأصلية أيضًا باسم state_COMMAND ، لأنها تستخدم نفس النمط لتطبيق جهاز الحالة ، أي getattr(self, 'state_' + self.mode) )


لطالما أحببت فعل ذلك بهذه الطريقة

result = {
  'a': lambda x: x * 5,
  'b': lambda x: x + 7,
  'c': lambda x: x - 2
}[value](x)

من هنا


إذا كنت تبحث عن بيان إضافي ، مثل "switch" ، فقد بنيت وحدة python التي تمد بايثون. يطلق عليه ESPY كـ "بنية محسنة لـ Python" وهو متوفر لكل من Python 2.x و Python 3.x.

على سبيل المثال ، في هذه الحالة ، يمكن تنفيذ عبارة switch بالشفرة التالية:

macro switch(arg1):
    while True:
        cont=False
        val=%arg1%
        socket case(arg2):
            if val==%arg2% or cont:
                cont=True
                socket
        socket else:
            socket
        break

التي يمكن استخدامها على هذا النحو:

a=3
switch(a):
    case(0):
        print("Zero")
    case(1):
        print("Smaller than 2"):
        break
    else:
        print ("greater than 1")

حتى إسبى ترجمها في بايثون على النحو التالي:

a=3
while True:
    cont=False
    if a==0 or cont:
        cont=True
        print ("Zero")
    if a==1 or cont:
        cont=True
        print ("Smaller than 2")
        break
    print ("greater than 1")
    break

I was quite confused after reading the answer, but this cleared it all up:

def numbers_to_strings(argument):
    switcher = {
        0: "zero",
        1: "one",
        2: "two",
    }
    return switcher.get(argument, "nothing")

This code is analogous to:

function(argument){
    switch(argument) {
        case 0:
            return "zero";
        case 1:
            return "one";
        case 2:
            return "two";
        default:
            return "nothing";
    }
}

Check the Source for more about dictionary mapping to functions.


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

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

ملاحظة: لا تستخدم "()" داخل بحث الحالة / القاموس أو ستقوم باستدعاء كل من وظائفك كما يتم إنشاء كتلة القاموس / القضية. تذكر هذا لأنك تريد فقط استدعاء كل وظيفة مرة واحدة باستخدام بحث نمط تجزئة.

def first_case():
    print "first"

def second_case():
    print "second"

def third_case():
    print "third"

mycase = {
'first': first_case, #do not use ()
'second': second_case, #do not use ()
'third': third_case #do not use ()
}
myfunc = mycase['first']
myfunc()

class Switch:
    def __init__(self, value): self._val = value
    def __enter__(self): return self
    def __exit__(self, type, value, traceback): return False # Allows traceback to occur
    def __call__(self, *mconds): return self._val in mconds

from datetime import datetime
with Switch(datetime.today().weekday()) as case:
    if case(0):
        # Basic usage of switch
        print("I hate mondays so much.")
        # Note there is no break needed here
    elif case(1,2):
        # This switch also supports multiple conditions (in one line)
        print("When is the weekend going to be here?")
    elif case(3,4): print("The weekend is near.")
    else:
        # Default would occur here
        print("Let's go have fun!") # Didn't use case for example purposes

بالإضافة إلى أساليب القاموس (التي أحبها حقاً ، BTW) ، يمكنك أيضًا استخدام if-elif-else للحصول على وظيفة التبديل / الحالة / الافتراضية:

if x == 'a':
    # Do the thing
elif x == 'b':
    # Do the other thing
if x in 'bc':
    # Fall-through by not using elif, but now the default case includes case 'a'!
elif x in 'xyz':
    # Do yet another thing
else:
    # Do the default

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


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


class switch(object):
    value = None
    def __new__(class_, value):
        class_.value = value
        return True

def case(*args):
    return any((arg == switch.value for arg in args))

الاستعمال:

while switch(n):
    if case(0):
        print "You typed zero."
        break
    if case(1, 4, 9):
        print "n is a perfect square."
        break
    if case(2):
        print "n is an even number."
    if case(2, 3, 5, 7):
        print "n is a prime number."
        break
    if case(6, 8):
        print "n is an even number."
        break
    print "Only single-digit numbers are allowed."
    break

الاختبارات:

n = 2
#Result:
#n is an even number.
#n is a prime number.
n = 11
#Result:
#Only single-digit numbers are allowed.

If you don't worry losing syntax highlight inside the case suites, you can do the following:

exec {
    1: """
print ('one')
""", 
    2: """
print ('two')
""", 
    3: """
print ('three')
""",
}.get(value, """
print ('None')
""")

Where value is the value. In C, this would be:

switch (value) {
    case 1:
        printf("one");
        break;
    case 2:
        printf("two");
        break;
    case 3:
        printf("three");
        break;
    default:
        printf("None");
        break;
}

We can also create a helper function to do this:

def switch(value, cases, default):
    exec cases.get(value, default)

So we can use it like this for the example with one, two and three:

switch(value, {
    1: """
print ('one')
    """, 
    2: """
print ('two')
    """, 
    3: """
print ('three')
    """,
}, """
print ('None')
""")


إذا كنت تريد الإعدادات الافتراضية ، فيمكنك استخدام القاموس get(key[, default]) :

def f(x):
    return {
        'a': 1,
        'b': 2
    }.get(x, 9)    # 9 is default if x not found

لم أجد الإجابة البسيطة التي كنت أبحث عنها في أي مكان على بحث Google. لكنني برزت على أي حال. انها حقا بسيطة للغاية. قررت لنشرها ، وربما منع بعض خدوش أقل على رأس شخص آخر. المفتاح هو ببساطة "في" و tuples. هنا سلوك بيان التبديل مع خريف ، بما في ذلك RANDOM-Fall-through.

l = ['Dog', 'Cat', 'Bird', 'Bigfoot',
     'Dragonfly', 'Snake', 'Bat', 'Loch Ness Monster']

for x in l:
    if x in ('Dog', 'Cat'):
        x += " has four legs"
    elif x in ('Bat', 'Bird', 'Dragonfly'):
        x += " has wings."
    elif x in ('Snake',):
        x += " has a forked tongue."
    else:
        x += " is a big mystery by default."
    print(x)

print()

for x in range(10):
    if x in (0, 1):
        x = "Values 0 and 1 caught here."
    elif x in (2,):
        x = "Value 2 caught here."
    elif x in (3, 7, 8):
        x = "Values 3, 7, 8 caught here."
    elif x in (4, 6):
        x = "Values 4 and 6 caught here"
    else:
        x = "Values 5 and 9 caught in default."
    print(x)

يوفر:

Dog has four legs
Cat has four legs
Bird has wings.
Bigfoot is a big mystery by default.
Dragonfly has wings.
Snake has a forked tongue.
Bat has wings.
Loch Ness Monster is a big mystery by default.

Values 0 and 1 caught here.
Values 0 and 1 caught here.
Value 2 caught here.
Values 3, 7, 8 caught here.
Values 4 and 6 caught here
Values 5 and 9 caught in default.
Values 4 and 6 caught here
Values 3, 7, 8 caught here.
Values 3, 7, 8 caught here.
Values 5 and 9 caught in default.

def f(x):
     return 1 if x == 'a' else\
            2 if x in 'bcd' else\
            0 #default

قصيرة وسهلة القراءة ، لديها قيمة افتراضية وتدعم التعبيرات في كل من الشروط والقيم.

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


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

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





python