بدائل لبيان التبديل في بيثون؟
-
09-06-2019 - |
سؤال
أريد أن أكتب دالة في بايثون تقوم بإرجاع قيم ثابتة مختلفة بناءً على قيمة فهرس الإدخال.
في اللغات الأخرى سأستخدم a switch
أو case
عبارة، ولكن لا يبدو أن بايثون تحتوي على switch
إفادة.ما هي حلول بايثون الموصى بها في هذا السيناريو؟
المحلول
يمكنك استخدام القاموس:
def f(x):
return {
'a': 1,
'b': 2,
}[x]
نصائح أخرى
إذا كنت تريد الإعدادات الافتراضية، فيمكنك استخدام القاموس get(key[, default])
طريقة:
def f(x):
return {
'a': 1,
'b': 2
}.get(x, 9) # 9 is default if x not found
لقد أحببت دائمًا القيام بذلك بهذه الطريقة
result = {
'a': lambda x: x * 5,
'b': lambda x: x + 7,
'c': lambda x: x - 2
}[value](x)
بالإضافة إلى طرق القاموس (التي تعجبني حقًا، راجع للشغل)، يمكنك أيضًا استخدام 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 المتداخلة، على الرغم من أن هذا هو أقرب إليه من الناحية الوظيفية.
وصفتي المفضلة في بايثون للتبديل/الحالة هي:
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;
}
يمكنك أيضًا تعيين متغيرات متعددة باستخدام الصفوف:
choices = {'a': (1, 2, 3), 'b': (4, 5, 6)}
(result1, result2, result3) = choices.get(key, ('default1', 'default2', 'default3'))
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.
هناك نمط تعلمته من كود 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_
الأساليب في الطبقة الأساسية.
يحرر:كيف يتم استخدام ذلك بالضبط
في حالة SMTP سوف تتلقى HELO
من السلك.الكود ذو الصلة (من 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'
).تم ترميز هذا في parts
مثل ['HELO', 'foo.bar.com']
.اسم البحث عن الطريقة الفعلية مأخوذ من parts[0]
.
(تسمى الطريقة الأصلية أيضًا state_COMMAND
, لأنه يستخدم نفس النمط لتنفيذ آلة الحالة، أي. getattr(self, 'state_' + self.mode)
)
المفضل لدي هو لطيف حقا وصفة.سوف يعجبك حقًا.إنها أقرب ما رأيته إلى بيانات حالة التبديل الفعلية، خاصة في الميزات.
class switch(object):
def __init__(self, value):
self.value = value
self.fall = False
def __iter__(self):
"""Return the match method once, then stop"""
yield self.match
raise StopIteration
def match(self, *args):
"""Indicate whether or not to enter a case suite"""
if self.fall or not args:
return True
elif self.value in args: # changed for v1.5, see below
self.fall = True
return True
else:
return False
هنا مثال:
# The following example is pretty much the exact use-case of a dictionary,
# but is included for its simplicity. Note that you can include statements
# in each suite.
v = 'ten'
for case in switch(v):
if case('one'):
print 1
break
if case('two'):
print 2
break
if case('ten'):
print 10
break
if case('eleven'):
print 11
break
if case(): # default, could also just omit condition or 'if True'
print "something else!"
# No need to break here, it'll stop anyway
# break is used here to look as much like the real thing as possible, but
# elif is generally just as good and more concise.
# Empty suites are considered syntax errors, so intentional fall-throughs
# should contain 'pass'
c = 'z'
for case in switch(c):
if case('a'): pass # only necessary if the rest of the suite is empty
if case('b'): pass
# ...
if case('y'): pass
if case('z'):
print "c is lowercase!"
break
if case('A'): pass
# ...
if case('Z'):
print "c is uppercase!"
break
if case(): # default
print "I dunno what c was!"
# As suggested by Pierre Quentel, you can even expand upon the
# functionality of the classic 'case' statement by matching multiple
# cases in a single shot. This greatly benefits operations such as the
# uppercase/lowercase example above:
import string
c = 'A'
for case in switch(c):
if case(*string.lowercase): # note the * for unpacking as arguments
print "c is lowercase!"
break
if case(*string.uppercase):
print "c is uppercase!"
break
if case('!', '?', '.'): # normal argument passing style also applies
print "c is a sentence terminator!"
break
if case(): # default
print "I dunno what c was!"
class Switch:
def __init__(self, value):
self.value = value
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
return False # Allows a traceback to occur
def __call__(self, *values):
return self.value in values
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
لنفترض أنك لا تريد إرجاع قيمة فقط، ولكنك تريد استخدام الأساليب التي تغير شيئًا ما في الكائن.باستخدام النهج المذكور هنا سيكون:
result = {
'a': obj.increment(x),
'b': obj.decrement(x)
}.get(value, obj.default(x))
ما يحدث هنا هو أن بايثون تقوم بتقييم جميع الأساليب في القاموس.لذلك، حتى لو كانت القيمة الخاصة بك هي "a"، فسيتم زيادة الكائن و انخفض بمقدار x.
حل:
func, args = {
'a' : (obj.increment, (x,)),
'b' : (obj.decrement, (x,)),
}.get(value, (obj.default, (x,)))
result = func(*args)
حتى تحصل على قائمة تحتوي على وظيفة ووسائطها.بهذه الطريقة، يتم إرجاع مؤشر الوظيفة وقائمة الوسائط فقط، لا تقييمها.تقوم "النتيجة" بعد ذلك بتقييم استدعاء الوظيفة التي تم إرجاعها.
سأقوم فقط بإسقاط سنتي هنا.السبب وراء عدم وجود بيان الحالة/التبديل في بايثون هو أن بايثون تتبع مبدأ "هناك طريقة واحدة صحيحة لفعل شيء ما".من الواضح أنه يمكنك التوصل إلى طرق مختلفة لإعادة إنشاء وظيفة التبديل/الحالة، ولكن طريقة بايثون لتحقيق ذلك هي بناء if/elif.أي
if something:
return "first thing"
elif somethingelse:
return "second thing"
elif yetanotherthing:
return "third thing"
else:
return "default thing"
لقد شعرت أن PEP 8 يستحق الإيماءة هنا.من الأشياء الجميلة في بايثون هي بساطتها وأناقتها.وهذا مستمد إلى حد كبير من المبادئ التي وضعناها في PEP 8، بما في ذلك "هناك طريقة واحدة صحيحة لفعل شيء ما"
التوسع في فكرة "الإملاء كمفتاح".إذا كنت تريد استخدام قيمة افتراضية لمفتاحك:
def f(x):
try:
return {
'a': 1,
'b': 2,
}[x]
except KeyError:
return 'default'
إذا كان لديك مجموعة معقدة من الحالات، فيمكنك التفكير في استخدام جدول البحث في قاموس الوظائف...
إذا لم تكن قد قمت بذلك من قبل، فمن الجيد أن تدخل إلى مصحح الأخطاء الخاص بك وتشاهد بالضبط كيف يبحث القاموس عن كل وظيفة.
ملحوظة:يفعل لا استخدم "()" داخل البحث عن الحالة/القاموس وإلا فسوف يستدعي كل وظيفة من وظائفك أثناء إنشاء القاموس/كتلة الحالة.تذكر هذا لأنك تريد فقط استدعاء كل وظيفة مرة واحدة باستخدام البحث عن نمط التجزئة.
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()
إذا كنت تبحث عن عبارة إضافية، مثل "switch"، فقد قمت ببناء وحدة بايثون تعمل على توسيع لغة بايثون.تسمى إسبي باسم "البنية المحسنة لـ Python" وهي متاحة لكل من Python 2.x وPython 3.x.
على سبيل المثال، في هذه الحالة، يمكن تنفيذ عبارة التبديل بواسطة الكود التالي:
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")
لذا espy ترجمتها في بايثون على النحو التالي:
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
لم أجد الإجابة البسيطة التي كنت أبحث عنها في أي مكان على بحث Google.لكنني اكتشفت ذلك على أي حال.انها حقا بسيطة جدا.قررت نشره، وربما منع حدوث عدد قليل من الخدوش على رأس شخص آخر.المفتاح هو ببساطة "في" والصفوف.فيما يلي سلوك بيان التبديل مع السقوط، بما في ذلك السقوط العشوائي.
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.
لقد وجدت أن بنية التبديل المشتركة:
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)
بدلًا من أن تكون عبارة، فإن إصدار بايثون هو تعبير يتم تقييمه إلى قيمة.
الحلول التي أستخدمها:
مزيج من حلين من الحلول المنشورة هنا، وهو سهل القراءة نسبيًا ويدعم الإعدادات الافتراضية.
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
.
# simple case alternative
some_value = 5.0
# this while loop block simulates a case block
# case
while True:
# case 1
if some_value > 5:
print ('Greater than five')
break
# case 2
if some_value == 5:
print ('Equal to five')
break
# else case 3
print ( 'Must be less than 5')
break
معظم الإجابات هنا قديمة جدًا، وخاصة الإجابات المقبولة، لذا يبدو أنها تستحق التحديث.
أولا، الرسمية الأسئلة الشائعة في بايثون يغطي هذا، ويوصي elif
سلسلة للحالات البسيطة و dict
للحالات الأكبر أو الأكثر تعقيدًا.كما يقترح مجموعة من visit_
الأساليب (أسلوب يستخدمه العديد من أطر عمل الخادم) في بعض الحالات:
def dispatch(self, value):
method_name = 'visit_' + str(value)
method = getattr(self, method_name)
method()
تذكر الأسئلة الشائعة أيضًا بيب 275, ، والذي تمت كتابته للحصول على قرار رسمي نهائي بشأن إضافة بيانات التبديل على النمط C.ولكن تم تأجيل PEP فعليًا إلى Python 3، وتم رفضه رسميًا فقط كاقتراح منفصل. بيب 3103.كانت الإجابة بالطبع لا، ولكن لدى الشخصين السياسيين السياسيين روابط لمعلومات إضافية إذا كنت مهتمًا بالأسباب أو التاريخ.
الشيء الوحيد الذي ظهر عدة مرات (ويمكن رؤيته في PEP 275، على الرغم من أنه تم قطعه كتوصية فعلية) هو أنه إذا كنت منزعجًا حقًا من وجود 8 أسطر من التعليمات البرمجية للتعامل مع 4 حالات، مقابل 8 أسطر من التعليمات البرمجية للتعامل مع 4 حالات.الأسطر الستة التي لديك في لغة C أو Bash، يمكنك دائمًا كتابة هذا:
if x == 1: print('first')
elif x == 2: print('second')
elif x == 3: print('third')
else: print('did not place')
هذا لا يشجعه PEP 8 تمامًا، ولكنه قابل للقراءة وليس أحادي المفردة.
على مدى أكثر من عقد من الزمان منذ رفض PEP 3103، تم اعتبار مسألة بيانات الحالة على النمط C، أو حتى الإصدار الأقوى قليلاً في Go، ميتة؛عندما يطرح أي شخص الأمر على أفكار python أو -dev، تتم إحالتهم إلى القرار القديم.
ومع ذلك، فإن فكرة المطابقة الكاملة لنمط ML تظهر كل بضع سنوات، خاصة وأن لغات مثل Swift وRust قد تبنتها.المشكلة هي أنه من الصعب الاستفادة كثيرًا من مطابقة الأنماط بدون أنواع البيانات الجبرية.في حين كان جويدو متعاطفًا مع الفكرة، لم يتوصل أحد إلى اقتراح يناسب بايثون جيدًا.(يمكنك أن تقرأ بلدي القش 2014 على سبيل المثال.) يمكن أن يتغير هذا مع dataclass
في 3.7 وبعض المقترحات المتفرقة لأكثر قوة enum
للتعامل مع أنواع المجموع، أو مع مقترحات مختلفة لأنواع مختلفة من الارتباطات المحلية للبيانات (مثل بيب 3150, ، أو مجموعة المقترحات التي تتم مناقشتها حاليًا حول الأفكار).ولكن حتى الآن، لم يحدث ذلك.
هناك أيضًا في بعض الأحيان مقترحات لمطابقة أنماط Perl 6، والتي تعد في الأساس مزيجًا من كل شيء elif
إلى regex لتبديل نوع الإرسال الفردي.
def f(x):
dictionary = {'a':1, 'b':2, 'c':3}
return dictionary.get(x,'Not Found')
##Returns the value for the letter x;returns 'Not Found' if x isn't a key in the dictionary
أحببت إجابة مارك بيس
منذ x
يجب استخدام المتغير مرتين، وقمت بتعديل وظائف لامدا إلى بلا معلمات.
لا بد لي من تشغيل مع 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'
يحرر: لقد لاحظت أنه يمكنني استخدامها None
اكتب مع القواميس.لذلك هذا من شأنه أن يحاكي switch ; case else
حل لتشغيل الوظائف:
result = {
'case1': foo1,
'case2': foo2,
'case3': foo3,
'default': default,
}.get(option)()
حيث foo1() و foo2() و foo3() و default() هي وظائف
def f(x):
return 1 if x == 'a' else\
2 if x in 'bcd' else\
0 #default
قصير وسهل القراءة، وله قيمة افتراضية ويدعم التعبيرات في كل من الشروط وقيم الإرجاع.
ومع ذلك، فهو أقل كفاءة من الحل باستخدام القاموس.على سبيل المثال، يتعين على بايثون فحص جميع الشروط قبل إرجاع القيمة الافتراضية.
أعتقد أن أفضل طريقة هي استخدم مصطلحات لغة بايثون لإبقاء الكود الخاص بك قابلاً للاختبار.كما هو موضح في الإجابات السابقة، أستخدم القواميس الاستفادة من هياكل ولغة بايثون واحتفظ برمز "الحالة" معزولًا بطرق مختلفة.يوجد أدناه فئة، ولكن يمكنك استخدام الوحدة النمطية والمجموعات العالمية والوظائف مباشرة.الفصل لديه أساليب ذلك يمكن اختبارها بالعزلة.اعتمادًا على احتياجاتك، يمكنك اللعب باستخدام الأساليب والسمات الثابتة أيضًا.
class ChoiceManager:
def __init__(self):
self.__choice_table = \
{
"CHOICE1" : self.my_func1,
"CHOICE2" : self.my_func2,
}
def my_func1(self, data):
pass
def my_func2(self, data):
pass
def process(self, case, data):
return self.__choice_table[case](data)
ChoiceManager().process("CHOICE1", my_data)
من الممكن استفد من هذه الطريقة باستخدام الفئات أيضًا كمفاتيح من "__choice_table".بهذه الطريقة يمكنك تجنب إساءة استخدام المثيل والحفاظ على كل نظيفة وقابلة للاختبار.
لنفترض أنه يتعين عليك معالجة الكثير من الرسائل أو الحزم من الشبكة أو من MQ الخاص بك.كل حزمة لها هيكلها الخاص ورمز الإدارة الخاص بها (بطريقة عامة).باستخدام الكود أعلاه من الممكن القيام بشيء مثل هذا:
class PacketManager:
def __init__(self):
self.__choice_table = \
{
ControlMessage : self.my_func1,
DiagnosticMessage : self.my_func2,
}
def my_func1(self, data):
# process the control message here
pass
def my_func2(self, data):
# process the diagnostic message here
pass
def process(self, pkt):
return self.__choice_table[pkt.__class__](pkt)
pkt = GetMyPacketFromNet()
PacketManager().process(pkt)
# isolated test or isolated usage example
def test_control_packet():
p = ControlMessage()
PacketManager().my_func1(p)
لذا لا ينتشر التعقيد في تدفق التعليمات البرمجية ولكن يتم تقديمه في بنية التعليمات البرمجية.
التوسع في إجابة جريج هيوجيل - يمكننا تغليف حل القاموس باستخدام الديكور:
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
ويمكن بعد ذلك استخدام هذا مع @case
-ديكور
@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-وحدة.ببساطة قم بالتثبيت باستخدام النقطة:
pip install NeoPySwitch
الحل الذي أميل إلى استخدامه والذي يستخدم القواميس أيضًا هو:
def decision_time( key, *args, **kwargs):
def action1()
"""This function is a closure - and has access to all the arguments"""
pass
def action2()
"""This function is a closure - and has access to all the arguments"""
pass
def action3()
"""This function is a closure - and has access to all the arguments"""
pass
return {1:action1, 2:action2, 3:action3}.get(key,default)()
يتميز هذا بأنه لا يحاول تقييم الوظائف في كل مرة، وعليك فقط التأكد من أن الوظيفة الخارجية تحصل على جميع المعلومات التي تحتاجها الوظائف الداخلية.
يمكنك استخدام الإملاء المرسل:
#!/usr/bin/env python
def case1():
print("This is case 1")
def case2():
print("This is case 2")
def case3():
print("This is case 3")
token_dict = {
"case1" : case1,
"case2" : case2,
"case3" : case3,
}
def main():
cases = ("case1", "case3", "case2", "case1")
for case in cases:
token_dict[case]()
if __name__ == '__main__':
main()
انتاج:
This is case 1
This is case 3
This is case 2
This is case 1
بسيطة، لم يتم اختبارها.يتم تقييم كل حالة بشكل مستقل:لا يوجد أي خطأ، ولكن يتم تقييم جميع الحالات (على الرغم من أن التعبير المطلوب تشغيله يتم تقييمه مرة واحدة فقط)، ما لم يكن هناك بيان فاصل.على سبيل المثال،
for case in [expression]:
if case == 1:
print(end='Was 1. ')
if case == 2:
print(end='Was 2. ')
break
if case in (1, 2):
print(end='Was 1 or 2. ')
print(end='Was something. ')
مطبوعات Was 1. Was 1 or 2. Was something.
(اللعنة!لماذا لا يمكنني الحصول على مسافة بيضاء زائدة في كتل التعليمات البرمجية المضمنة؟) لو expression
يقيم ل 1
, Was 2.
لو expression
يقيم ل 2
, ، أو Was something.
لو expression
يقيم لشيء آخر.
تعريف:
def switch1(value, options):
if value in options:
options[value]()
يسمح لك باستخدام صيغة واضحة إلى حد ما، مع تجميع الحالات في الخريطة:
def sample1(x):
local = 'betty'
switch1(x, {
'a': lambda: print("hello"),
'b': lambda: (
print("goodbye," + local),
print("!")),
})
لقد ظللت أحاول إعادة تعريف التبديل بطريقة تسمح لي بالتخلص من "lambda:"، لكنني استسلمت.تعديل التعريف:
def switch(value, *maps):
options = {}
for m in maps:
options.update(m)
if value in options:
options[value]()
elif None in options:
options[None]()
سمح لي بتعيين حالات متعددة لنفس الرمز، وتوفير خيار افتراضي:
def sample(x):
switch(x, {
_: lambda: print("other")
for _ in 'cdef'
}, {
'a': lambda: print("hello"),
'b': lambda: (
print("goodbye,"),
print("!")),
None: lambda: print("I dunno")
})
يجب أن تكون كل حالة منسوخة في قاموسها الخاص؛يقوم الدالة Switch() بدمج القواميس قبل البحث عن القيمة.لا يزال الأمر أقبح مما أريد، لكنه يتمتع بالكفاءة الأساسية المتمثلة في استخدام بحث مجزأ في التعبير، بدلاً من إجراء حلقة عبر جميع المفاتيح.
إذا كنت لا تقلق بشأن فقدان تمييز بناء الجملة داخل مجموعات الحالات، فيمكنك القيام بما يلي:
exec {
1: """
print ('one')
""",
2: """
print ('two')
""",
3: """
print ('three')
""",
}.get(value, """
print ('None')
""")
أين value
هي القيمة.في C، سيكون هذا:
switch (value) {
case 1:
printf("one");
break;
case 2:
printf("two");
break;
case 3:
printf("three");
break;
default:
printf("None");
break;
}
يمكننا أيضًا إنشاء وظيفة مساعدة للقيام بذلك:
def switch(value, cases, default):
exec cases.get(value, default)
لذلك يمكننا استخدامه مثل هذا للمثال مع واحد واثنين وثلاثة:
switch(value, {
1: """
print ('one')
""",
2: """
print ('two')
""",
3: """
print ('three')
""",
}, """
print ('None')
""")