“İf x: return x” ifadelerinden kaçınmanın pitonik yolu


218

Belirli koşulları kontrol etmek için sırayla 4 diğer yöntemleri çağıran bir yöntem var ve bir Truthy bir şey döndüğünde hemen (aşağıdakileri kontrol değil) döner.

def check_all_conditions():
    x = check_size()
    if x:
        return x

    x = check_color()
    if x:
        return x

    x = check_tone()
    if x:
        return x

    x = check_flavor()
    if x:
        return x
    return None

Bu çok fazla bagaj kodu gibi görünüyor. Her 2 satırlık if ifadesi yerine, şöyle bir şey yapmayı tercih ederim:

x and return x

Ama bu geçersiz Python. Burada basit ve zarif bir çözümü özlüyor muyum? Bu arada, bu durumda, bu dört kontrol yöntemi pahalı olabilir, bu yüzden onları birden çok kez aramak istemiyorum.


7
Bu x'ler neler? Sadece Doğru / Yanlış mı, yoksa hiçbir bilgi içermeyen veri yapıları mıdır, Yok veya benzeri herhangi bir verinin yokluğunu göstermek için özel bir durum olarak mı kullanılır? Eğer ikinciyse, bunun yerine neredeyse kesinlikle istisnalar kullanmalısınız.
Nathaniel

13
@gerrit Yukarıda sunulan kod, Kod İncelemesinde konu dışı olan varsayımsal / sözde koddur. Gönderinin yazarı gerçek, gerçek çalışma kodunu gözden geçirmek istiyorsa , evet, Kod İncelemesinde yayın yapabilirler.
Phrancis

4
Neden x and return xdaha iyi olduğunu düşünüyorsun if x: return x? İkincisi çok daha okunabilir ve böylece bakımı yapılabilir. Karakter veya satır sayısı konusunda fazla endişelenmemelisiniz; okunabilirlik önemlidir. Zaten aynı sayıda boşluk olmayan karakterdir ve gerçekten ihtiyacınız varsa if x: return x, sadece bir satırda iyi çalışır.
marcelm

3
Lütfen gerçek değerleri önemsediğinizi mi yoksa gerçekten bir boole döndürmeniz mi gerektiğini açıklayın. Bu, hangi seçeneklerin mevcut olduğunu ve hangilerinin amacı daha net bir şekilde ilettiğini fark eder. Adlandırma yalnızca bir boole ihtiyacınız olduğunu gösterir. Ayrıca, bu işlevlere birden fazla çağrı yapılmasından kaçınmanın önemli olup olmadığı da fark yaratır. İşlevlerin herhangi bir veya farklı parametre setlerini alması da önemli olabilir. Bu açıklamalar olmadan, bu sorunun Açıkça Anlaşılmayan, Çok Geniş veya Görüş Bazlı olarak ele alındığını düşünüyorum.
jpmc26

7
@ jpmc26 OP açıkça doğru dönüş değerlerinden bahseder ve sonra kodu x(aksine bool(x)) geri döner , bu yüzden dururken OP'nin işlevlerinin herhangi bir şey döndürebileceğini varsaymanın güvenli olduğunu düşünüyorum ve ilk doğru olan her şeyi istiyor.
timgeb

Yanıtlar:


278

Bir döngü kullanabilirsiniz:

conditions = (check_size, check_color, check_tone, check_flavor)
for condition in conditions:
    result = condition()
    if result:
        return result

Bu, artık koşul sayısını değişken yapabileceğiniz ek bir avantaja sahiptir.

Sen kullanabilirsiniz map()+ filter()(kullanın Python 3 sürümleri future_builtinssürümleri tür ilk eşleşen değeri elde etmek Python 2'de):

try:
    # Python 2
    from future_builtins import map, filter
except ImportError:
    # Python 3
    pass

conditions = (check_size, check_color, check_tone, check_flavor)
return next(filter(None, map(lambda f: f(), conditions)), None)

ancak bu daha okunaklıysa tartışmalıdır.

Başka bir seçenek de bir jeneratör ifadesi kullanmaktır:

conditions = (check_size, check_color, check_tone, check_flavor)
checks = (condition() for condition in conditions)
return next((check for check in checks if check), None)

27
koşullar gerçekten sadece koşullar, yani booleans ise ilk teklifinizde anydöngü yerine yerleşik olanı da kullanabilirsiniz . return any(condition() for condition in conditions)

4
@Leonhard: anyiçinde neredeyse aynı uygulama var. Ama çok daha iyi görünüyor, lütfen bir cevap olarak gönderin)
Nick Volynkin

13
Okunabilirlik hemen hemen tüm diğer hususları ortadan kaldırır. Diyorsun ki, harita / filtre 'tartışmalı', oyumu tartışmasız çirkin olarak verdim. Teşekkürler, kesinlikle, ama eğer takımımdaki herhangi biri bu kod için bir harita / filtre koyarsa, onları başka bir takıma transfer edebilirim ya da yatak görevine atarım.
Kevin J. Rice

15
Bu okunamayan kod bloğu gerçekten "pythonian" mı? Özellikle de sıkıştırma conditionsve arguments? Bu IMHO, orijinal koddan çok daha kötü, beyin parlamacım tarafından ayrıştırılması yaklaşık 10 saniye sürüyor.
'

34
"Python'u tercih et" dediler. "Perl okunamıyor" dediler. Sonra bu oldu: return next((check for check in checks if check), None).
jja

393

Martijn'ın iyi cevabına alternatif olarak, zincirleyebilirsiniz or. Bu, ilk doğruluk değerini döndürür veya doğruluk değeri Noneyoksa:

def check_all_conditions():
    return check_size() or check_color() or check_tone() or check_flavor() or None

Demo:

>>> x = [] or 0 or {} or -1 or None
>>> x
-1
>>> x = [] or 0 or {} or '' or None
>>> x is None
True

9
Tabii, ancak birkaç seçenekten fazlası varsa bu hızlı okumak sıkıcı olacaktır. Ayrıca yaklaşımım değişken sayıda koşul kullanmanıza izin verir.
Martijn Pieters

14
@Martijn: \Her kontrolü kendi hattına koymak için kullanabilirsiniz .
Caridorc

12
@MartijnPieters Cevabımın sizinkinden daha iyi olduğunu ima etmedim, cevabınızı da
beğendim

38
@Caridorc: \Mantıksal çizgiyi genişletmek için kullanmayı sevmiyorum . Bunun yerine mümkünse parantez kullanın; böylece return (....)yeni satırlar gerektiği gibi eklenir. Yine de, bu uzun bir mantıksal çizgi olacak.
Martijn Pieters

47
Bence bu daha iyi bir çözüm. "Birkaç seçenekten fazlası varsa sıkıcı olacaktır [..] argümanı tartışmalıdır, çünkü tek bir işlev zaten fahiş sayıda kontrol yapmamalıdır. Gerekirse, kontroller birden fazla fonksiyona bölünmelidir.
BlueRaja - Danny Pflughoeft

88

Değiştirme

Diğer çeşitli cevapların gösterdiği gibi bunu yapmanın başka yolları da vardır. Hiçbiri orijinal kodunuz kadar net değil.


39
Buna karşı çıkarım, ama öneriniz dile getirilmesi meşru bir öneri. Şahsen, örneğin timgeb'in çözümü anında tıklarken, gözlerimi OP'yi okumaya çalışırken gergin buluyorum.
Reti43

3
Gerçekten bir fikir meselesi. Ben şahsen, sonradan yeni satırları kaldıracağım :, çünkü if x: return xoldukça iyi olduğunu düşünüyorum ve bu fonksiyonun daha kompakt görünmesini sağlıyor. Ama bu sadece ben olabilirim.
'

2
Sadece sen değil. orTimgeb gibi kullanmak uygun ve iyi anlaşılmış bir deyimdir. Birçok dilde bu vardır; belki de çağrıldığında orelsedaha da net, ama eski bile sadedir or(veya ||diğer dillerde) olduğu anlamına ilki eğer denemek için alternatif olarak anlaşılmalıdır "iş değildir."
Ray Toal

1
@RayToal: Diğer dillerden deyimleri içe aktarmak kodu gizlemek için harika bir yoldur.
Jack Aidley

1
Bazen evet, kesinlikle! Ayrıca, zihnini açmanın ve daha önce hiç kimsenin denemediği yeni ve daha iyi kalıpları ve paradigmaları keşfetmesine yol açmanın bir yolu olabilir. Stil, yeni şeyler ödünç alıp paylaşan ve deneyen insanlar tarafından gelişir. Her iki yönde de çalışır. Her neyse, orPythonic olarak etiketlenmiş veya herhangi bir şekilde gizlenmiş etiketli kullanımını hiç duymadım , ama yine de olması gerektiği gibi bir görüş meselesi.
Ray Toal

83

Timgeb ile aynı cevapta, ancak daha güzel biçimlendirme için parantez kullanabilirsiniz:

def check_all_the_things():
    return (
        one()
        or two()
        or five()
        or three()
        or None
    )

8
Lütfen herkes bu cevabı birincilik seviyesine yükseltmeye yardım etsin. üstüne düşeni yap!
Gyom

74

Göre Kıvırcık hukuk , sen bölünme iki endişeleri tarafından bu kod daha okunabilir yapabilirsiniz:

  • Neleri kontrol ederim?
  • Bir şey doğru mu döndü?

iki fonksiyona ayrılır:

def all_conditions():
    yield check_size()
    yield check_color()
    yield check_tone()
    yield check_flavor()

def check_all_conditions():
    for condition in all_conditions():
        if condition:
            return condition
    return None

Bu önler:

  • karmaşık mantıksal yapılar
  • gerçekten uzun çizgiler
  • tekrarlama

... doğrusal, okunması kolay bir akışı korurken.

Ayrıca, özel durumunuza göre, daha da okunabilir hale getiren daha iyi işlev adları da bulabilirsiniz.


True / False sorusuna uyması için koşul / Yok olarak değiştirilmesine rağmen bunu beğendim.
Malcolm

2
Bu benim favorim! Farklı kontrol ve argümanlarla da başa çıkıyor. Bu özel örnek için oldukça fazla tasarlanmış ancak gelecekteki problemler için gerçekten yararlı bir araç!
rjh

4
return NoneGerekli olmadığını unutmayın , çünkü işlevler Nonevarsayılan olarak geri döner . Ancak, Noneaçıkça geri dönmekle ilgili yanlış bir şey yoktur ve bunu yapmayı tercih etmenizi seviyorum.
timgeb

1
Bu yaklaşımın yerel işlev tanımıyla daha iyi uygulanacağını düşünüyorum.
Jack Aidley

1
@timgeb "Açık, örtük olmaktan iyidir," Python'dan Zen .
jpmc26

42

Bu, Martijns'in ilk örneğinin bir çeşididir. Ayrıca, kısa devreye izin vermek için "callables koleksiyonu" stilini kullanır.

Bir döngü yerine yerleşik'i kullanabilirsiniz any.

conditions = (check_size, check_color, check_tone, check_flavor)
return any(condition() for condition in conditions) 

anyBir boole döndürdüğünü unutmayın , bu nedenle çekin tam dönüş değerine ihtiyacınız varsa bu çözüm çalışmaz. anybirbirinden ayırt edemez 14, 'red', 'sharp', 'spicy'dönüş değeri olarak, hepsi olarak iade edilecektir True.


Sen yapabileceği next(itertools.ifilter(None, (c() for c in conditions)))bir mantıksal değere çevrim olmadan gerçek değerini alır.
kojiro

1
Does anykısa devre aslında?
Mart'ta zwol

1
@zwol Evet bazı örnek işlevlerle deneyin veya bkz. docs.python.org/3/library/functions.html

1
Bu, 4 işlevi 'veya' ile zincirlemekten daha az okunabilir ve yalnızca koşul sayısı büyük veya dinamik olduğunda işe yarar.
rjh

1
@rjh Mükemmel okunabilir; bu sadece bir liste hazır bilgisi ve bir kavramadır. Bunu tercih ederim çünkü gözlerim üçüncüsünden sonra parlıyorx = bar(); if x: return x;
Blacklight Shining

27

Sadece yazmayı düşündün mü if x: return xHepsini tek bir satıra ?

def check_all_conditions():
    x = check_size()
    if x: return x

    x = check_color()
    if x: return x

    x = check_tone()
    if x: return x

    x = check_flavor()
    if x: return x

    return None

Bu, sahip olduklarınızdan daha az tekrarlayıcı değil , ancak IMNSHO biraz daha düzgün okuyor.


24

Kimse anybu amaçla yapılmış yerleşikten bahsetmediğim için oldukça şaşırdım :

def check_all_conditions():
    return any([
        check_size(),
        check_color(),
        check_tone(),
        check_flavor()
    ])

Bu uygulama muhtemelen en açık olmasına rağmen, birincisi olsa bile tüm kontrolleri değerlendirdiğini unutmayın True.


İlk başarısız denetimde gerçekten durmanız gerekiyorsa, reducebir listeyi basit bir değere dönüştürmek için hangisinin kullanıldığını düşünün :

def check_all_conditions():
    checks = [check_size, check_color, check_tone, check_flavor]
    return reduce(lambda a, f: a or f(), checks, False)

reduce(function, iterable[, initializer]): Yinelenebilir öğeyi tek bir değere azaltmak için soldan sağa, yinelenebilir öğelere toplu olarak iki argümanın işlevini uygulayın. Sol argüman, x, biriken değer ve sağ argüman, y, yinelenebilir öğenin güncelleme değeridir. İsteğe bağlı başlatıcı varsa, hesaplamada yinelenebilir öğelerin önüne yerleştirilir

Senin durumunda:

  • lambda a, f: a or f()kontrol akümülatör ya o fonksiyonu aveya geçerli kontrol f()olduğu True. Eğer unutmayın aolduğunu True, f()değerlendirmeye alınmayacaktır.
  • checksçek fonksiyonları içerir ( flambdadan alınan öğe)
  • False başlangıç ​​değeridir, aksi takdirde kontrol olmaz ve sonuç her zaman olur True

anyve reducefonksiyonel programlama için temel araçlardır. Bunların yanı sıra mapharika olanı da eğitmenizi şiddetle tavsiye ediyorum !


9
anyyalnızca kontroller gerçekten bir boole değeri döndürürse çalışır Trueveya Falsesoru bunu belirtmezse. reduceÇek tarafından döndürülen gerçek değeri döndürmek için kullanmanız gerekir . Ayrıca, tüm kontrolleri anybir jeneratör kullanarak değerlendirmekten kaçınmak kolaydır , örn any(c() for c in (check_size, check_color, check_tone, check_flavor)). Olduğu gibi Leonhard cevabı
David Z

Açıklamasını ve kullanımını seviyorum reduce. @DavidZ gibi, çözümünüzün anybir jeneratör kullanması gerektiğine inanıyorum ve bunun geri dönüşle sınırlı olduğuna TrueveyaFalse .
timgeb

1
@DavidZ aslında any gerçek değerlerle çalışır:any([1, "abc", False]) == True veany(["", 0]) == False
ngasull

3
@ blint üzgünüm, net değildim. Sorunun amacı çekin sonucunu döndürmektir (ve sadece çekin başarılı olup olmadığını belirtmek için değil). Sadece bunun anyiçin işe yaradığını işaret ediyordum fiili boole değerleri onay işlevlerden döndürüldüğünde ise amaç.
David Z

19

Aynı kod yapısını istiyorsanız üçlü ifadeler kullanabilirsiniz!

def check_all_conditions():
    x = check_size()
    x = x if x else check_color()
    x = x if x else check_tone()
    x = x if x else check_flavor()

    return x if x else None

Bence bu güzel ve net görünüyor.

Demo:

Çalışan ekran görüntüsü


7
Terminalinizin üstündeki küçük ASCII balıkları nedir?

36
@LegoStormtroopr Balık kabuğunu kullanıyorum, bu yüzden beni mutlu etmek için bir ascii balık tankı ile süslüyorum. :)
Phinet

3
İyi balıklar için teşekkürler (ve bu arada renkler, bu hangi editör?)
mathreadler

4
Fishshell.com adresinden balık ve ascii için yapılandırma dosyası buradan pastebin.com/yYVYvVeK , editör de yüce metindir.
Phinet

9
x if x else <something> sadece azaltılabilir x or <something>

5

Benim için en iyi cevap @ phil-frost, ardından @ wayne-werner'dan geliyor.

İlginç bulduğum şey, hiç kimsenin bir işlevin birçok farklı veri türünü döndüreceği gerçeği hakkında bir şey söylemedi, bu da daha sonra başka bir iş yapmak için x'in kendisinin türünü kontrol etmeyi zorunlu hale getirecektir.

Bu yüzden @ PhilFrost'un yanıtını tek bir tür tutma fikriyle karıştırırdım:

def all_conditions(x):
    yield check_size(x)
    yield check_color(x)
    yield check_tone(x)
    yield check_flavor(x)

def assessed_x(x,func=all_conditions):
    for condition in func(x):
        if condition:
            return x
    return None

Bağımsız xdeğişken olarak iletildiğine dikkat edin , aynı zamanda all_conditionshepsinin bir denetleneceği kontrol işlevlerinin geçirilmiş bir jeneratörü olarak kullanılır xve Trueveya döndürür False. Kullanarak funcile all_conditionsvarsayılan değer olarak kullanabileceğinizassessed_x(x) veya üzeri bir başka kişiye jeneratör geçebilirfunc .

Bu şekilde, xbir kontrol geçer geçmez alırsınız , ancak her zaman aynı tür olacaktır.


4

İdeal olarak, ben yeniden yazmak istiyorum check_ getiri fonksiyonlarını Trueveya Falseyerine bir değerden. Çekleriniz daha sonra

if check_size(x):
    return x
#etc

xDeğişmez olduğunuzu varsayarsak , işleviniz yine de değiştirebilir (yeniden atayamasalar da) - ancak adı verilen bir işlev checkzaten gerçekten değiştirmemelidir.


3

Yukarıdaki Martijns ilk örneğinde, döngü içinde olup olmadığını önleyen küçük bir varyasyon:

Status = None
for c in [check_size, check_color, check_tone, check_flavor]:
  Status = Status or c();
return Status

Yapar? Hâlâ bir karşılaştırma yapıyorsunuz. Sürümünüzde ayrıca, tüm koşulları kontrol edeceksiniz ve bir doğruluk değerinin ilk örneğinde geri dönmeyeceksiniz, bu işlevlerin ne kadar pahalı olduğuna bağlı olarak, bu istenmeyebilir.
Reti43

4
@ Reti43: Status or c()atlar / kısa devre doğru c()ise çağrıları değerlendirir Status, bu nedenle bu cevaptaki kod OP kodundan daha fazla işlevi çağırmaz. stackoverflow.com/questions/2580136/…
Neil Slater

2
@NeilSlater Doğru. Gördüğüm tek dezavantaj, en iyi durumun şimdi O (n) 'de olmasıdır, çünkü listiterator, O (1)' de ilk işlev O (1) 'de doğru bir şey döndürürse, O (1) iken n kez vermelidir.
timgeb

1
Evet iyi puan. Ben sadece c () neredeyse boş bir döngü döngü daha değerlendirmek için biraz daha zaman alır umuyoruz. Lezzet kontrol etmek, en azından iyi bir akşam ise bütün akşam alabilir.
mathreadler

3

@ Timgeb'in hoşuma gitti. Bu arada ben ifade olduğunu eklemek istiyorum Noneiçinde returnifadenin koleksiyonu olarak gerekli değildir orayrılmış ifadelerden değerlendirilir ve ilk hiçbiri sıfır, hiçbiri boş, hiçbiri-Yok döndürülür ve daha sonra hiç yok ise Nonedöndürülür olup olmadığıNone olmadığı!

Yani benim check_all_conditions()fonksiyonum şöyle görünüyor:

def check_all_conditions():
    return check_size() or check_color() or check_tone() or check_flavor()

Kullanımı timeitile number=10**7I önerileri bir dizi çalışma süresi baktı. Karşılaştırma uğruna sadece random.random()bir dize döndürmek veya Nonerastgele sayılara dayalı işlevi kullandım . İşte tüm kod:

import random
import timeit

def check_size():
    if random.random() < 0.25: return "BIG"

def check_color():
    if random.random() < 0.25: return "RED"

def check_tone():
    if random.random() < 0.25: return "SOFT"

def check_flavor():
    if random.random() < 0.25: return "SWEET"

def check_all_conditions_Bernard():
    x = check_size()
    if x:
        return x

    x = check_color()
    if x:
        return x

    x = check_tone()
    if x:
        return x

    x = check_flavor()
    if x:
        return x
    return None

def check_all_Martijn_Pieters():
    conditions = (check_size, check_color, check_tone, check_flavor)
    for condition in conditions:
        result = condition()
        if result:
            return result

def check_all_conditions_timgeb():
    return check_size() or check_color() or check_tone() or check_flavor() or None

def check_all_conditions_Reza():
    return check_size() or check_color() or check_tone() or check_flavor()

def check_all_conditions_Phinet():
    x = check_size()
    x = x if x else check_color()
    x = x if x else check_tone()
    x = x if x else check_flavor()

    return x if x else None

def all_conditions():
    yield check_size()
    yield check_color()
    yield check_tone()
    yield check_flavor()

def check_all_conditions_Phil_Frost():
    for condition in all_conditions():
        if condition:
            return condition

def main():
    num = 10000000
    random.seed(20)
    print("Bernard:", timeit.timeit('check_all_conditions_Bernard()', 'from __main__ import check_all_conditions_Bernard', number=num))
    random.seed(20)
    print("Martijn Pieters:", timeit.timeit('check_all_Martijn_Pieters()', 'from __main__ import check_all_Martijn_Pieters', number=num))
    random.seed(20)
    print("timgeb:", timeit.timeit('check_all_conditions_timgeb()', 'from __main__ import check_all_conditions_timgeb', number=num))
    random.seed(20)
    print("Reza:", timeit.timeit('check_all_conditions_Reza()', 'from __main__ import check_all_conditions_Reza', number=num))
    random.seed(20)
    print("Phinet:", timeit.timeit('check_all_conditions_Phinet()', 'from __main__ import check_all_conditions_Phinet', number=num))
    random.seed(20)
    print("Phil Frost:", timeit.timeit('check_all_conditions_Phil_Frost()', 'from __main__ import check_all_conditions_Phil_Frost', number=num))

if __name__ == '__main__':
    main()

İşte sonuçlar:

Bernard: 7.398444877040768
Martijn Pieters: 8.506569201346597
timgeb: 7.244275416364456
Reza: 6.982133448743038
Phinet: 7.925932800076634
Phil Frost: 11.924794811353031

2

Bu şekilde kutunun dışında biraz, ama sonuç basit, okunabilir ve güzel görünüyor düşünüyorum.

Temel fikir, raiseişlevlerden biri doğruluk olarak değerlendirildiğinde ve sonucu döndürdüğünde bir istisnadır. İşte böyle görünebilir:

def check_conditions():
    try:
        assertFalsey(
            check_size,
            check_color,
            check_tone,
            check_flavor)
    except TruthyException as e:
        return e.trigger
    else:
        return None

assertFalseyAranan işlev bağımsız değişkenlerinden biri doğruluk olarak değerlendirildiğinde bir özel durum oluşturan bir işleve ihtiyacınız olacaktır:

def assertFalsey(*funcs):
    for f in funcs:
        o = f()
        if o:
            raise TruthyException(o)

Yukarıdakiler, değerlendirilecek fonksiyonlar için argümanlar sağlayacak şekilde değiştirilebilir.

Ve tabii ki TruthyExceptionkendine ihtiyacınız olacak . Bu istisna object, istisnayı tetikleyen şeyi sağlar :

class TruthyException(Exception):
    def __init__(self, obj, *args):
        super().__init__(*args)
        self.trigger = obj

Orijinal işlevi daha genel bir şeye dönüştürebilirsiniz, elbette:

def get_truthy_condition(*conditions):
    try:
        assertFalsey(*conditions)
    except TruthyException as e:
        return e.trigger
    else:
        return None

result = get_truthy_condition(check_size, check_color, check_tone, check_flavor)

Bu biraz daha yavaş olabilir çünkü her ikisini de if ifade bir istisna . Ancak, istisna yalnızca en fazla bir kez ele alındığından, kontrolü çalıştırmayı ve Truebinlerce kez bir değer almayı beklemediğiniz sürece performans isabeti küçük olmalıdır .


// , Şirin! Bu tür bir şey için istisna işlemeyi kullanmak "Pitonik" kabul edilir mi?
Nathan Basanese

@NathanBasanese Elbette kontrol akışı için istisnalar kullanılır. StopIterationoldukça iyi bir örnektir: yinelenebilir her tüketildiğinde bir istisna ortaya çıkar. Kaçınmak istediğiniz şey, art arda istisnaları tekrar tekrar yükseltmektir, bu da pahalı olacaktır. Ama bunu bir kez yapmak değil.
Rick, Monica

//, Ah, programcılar.stackexchange.com/ questions/112463/… gibi bir şeye atıfta bulunduğunuzu düşünüyorum . Bu soruya ve bu cevaba oy verdim. Bunun için Python 3 belgeleri: docs.python.org/3/library/stdtypes.html#iterator-types , sanırım.
Nathan Basanese

1
Genel amaçlı bir işlev ve bir istisna tanımlamak istiyorsunuz, sadece bir yerde başka bir işlevde birkaç kontrol yapmak mı istiyorsunuz? Bence bu biraz fazla.
Blacklight Parlayan

@BacklightShining Kabul ediyorum. Bunu asla kendim yapmam. OP tekrarlanan koddan kaçınmanın yollarını istedi, ama ne ile başladığını gayet iyi olduğunu düşünüyorum.
Rick, Monica

2

Pitonik yol, azaltma (daha önce bahsedildiği gibi) veya itertools (aşağıda gösterildiği gibi) kullanmaktır, ancak bana göre, oroperatörün kısa devresini kullanmanın daha net kod ürettiği görülmektedir.

from itertools import imap, dropwhile

def check_all_conditions():
    conditions = (check_size,\
        check_color,\
        check_tone,\
        check_flavor)
    results_gen = dropwhile(lambda x:not x, imap(lambda check:check(), conditions))
    try:
        return results_gen.next()
    except StopIteration:
        return None

0

Buraya atlayacağım ve asla tek bir Python satırı yazmadım ama if x = check_something(): return x geçerli ?

Öyleyse:

def check_all_conditions():

    if (x := check_size()): return x
    if (x := check_color()): return x
    if (x := check_tone()): return x
    if (x := check_flavor()): return x

    return None

1
Geçerli Python değil, hayır. Python, atama işlecini böyle kullanmanıza izin vermez. Ancak, son zamanlarda yeni bir özel atama ifadesi eklendi, böylece şimdi if ( x := check_size() ) :aynı etki için yazabilirsiniz .
Jack Aidley

0

Veya şunu kullanın max:

def check_all_conditions():
    return max(check_size(), check_color(), check_tone(), check_flavor()) or None

-2

Geçmişte beni bu cevaba götüren dikte ile anahtar / vaka ifadelerinin bazı ilginç uygulamalarını gördüm. Sağladığınız örneği kullanarak aşağıdakileri elde edersiniz. (Bu delilik using_complete_sentences_for_function_names, check_all_conditionsyeniden adlandırıldı status. Bkz. (1))

def status(k = 'a', s = {'a':'b','b':'c','c':'d','d':None}) :
  select = lambda next, test : test if test else next
  d = {'a': lambda : select(s['a'], check_size()  ),
       'b': lambda : select(s['b'], check_color() ),
       'c': lambda : select(s['c'], check_tone()  ),
       'd': lambda : select(s['d'], check_flavor())}
  while k in d : k = d[k]()
  return k

Seçim işlevi, her birini check_FUNCTIONiki kez arama gereğini ortadan kaldırır;check_FUNCTION() if check_FUNCTION() else next başka bir işlev katmanı ekleyerek . Bu, uzun süre çalışan işlevler için kullanışlıdır. Dikteki lambdas değerlerinin while döngüsüne kadar yürütülmesini geciktirir.

Bonus olarak, yürütme sırasını değiştirebilir ve hatta değiştirerek kve sörneğin bazı testleri atlayabilirsiniz.k='c',s={'c':'b','b':None} sayısını test sayısını azaltarak ve orijinal işleme sırasını tersine çevirerek .

timeitArkadaşlar bakmak ama daha kod Hoşluklar ilgilenen görünüyor fazladan bir katman veya yığınına ikisini ve dict için maliyet ekleyerek maliyetini üzerinde pazarlık olabilir.

Alternatif olarak, daha basit bir uygulama aşağıdakiler olabilir:

def status(k=check_size) :
  select = lambda next, test : test if test else next
  d = {check_size  : lambda : select(check_color,  check_size()  ),
       check_color : lambda : select(check_tone,   check_color() ),
       check_tone  : lambda : select(check_flavor, check_tone()  ),
       check_flavor: lambda : select(None,         check_flavor())}
  while k in d : k = d[k]()
  return k
  1. Bu, pep8 açısından değil, bir cümle yerine açık bir tanımlayıcı kelime kullanma anlamındadır. OP'nin bazı kodlama kurallarını izlemesi, mevcut bir kod tabanından birini çalıştırması veya kod tabanlarındaki kısa terimleri önemsememesi olabilir.

1
Bazen insanlar bir kelimenin ne zaman isimlendirilmesiyle gerçekten çıldırırlar. OP kodunu örnek olarak kullanarak, check_no/some/even/prime/every_third/fancy_conditionssadece bu bir işlev olarak adlandırılan işlevlere sahip olması olası değildir, bu yüzden neden statusçağırmıyorsunuz veya ısrar ediyorsa check_status. Kullanmak _all_gereksizdir, evrenlerin bütünlüğünü sağlamaz. Adlandırma, mümkün olduğunca ad aralığını kullanan tutarlı bir anahtar kelime kümesi kullanmalıdır. Uzun cümleler en iyi öğretiler olarak hizmet eder. Birinin bir şeyi özünde tanımlamak için nadiren 8-10 karakterden fazlasına ihtiyacı vardır.
Carel

1
Uzun işlev adlarının hayranıyım, çünkü daha üst düzey işlevlerin kendi kendini belgelemesini istiyorum. Ama check_all_conditionskötü bir isim, çünkü doğru olup olmadığını tüm koşulları kontrol etmiyor . Gibi bir şey kullanırdım matches_any_condition.
John Hazen

Bu ilginç bir dokunuş. Daha sonra yazım hatası yapacağım harf sayısını en aza indirmeye çalışıyorum :) Görünüşe göre, gerçekten yararlı bir ipucu sağlamaya çalıştığımda, çözümümün bir yığınını dollopedmiştim. Bu düzenlenmeli mi?
Carel

2
Bu, özellikle bu sorudaki diğer çözümleri göz önünde bulundurarak çok acayip görünüyor. OP'nin yapmaya çalıştığı şey hiç de karmaşık değil; çözüm yarı uykuda anlayacak kadar basit olmalıdır. Ve burada neler olduğu hakkında hiçbir fikrim yok.
Blacklight Parlayan

Esnekliği hedefliyordum. Daha az 'hacky' bir varyantı içerecek şekilde değiştirilmiş cevap
Carel
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.