Python'da bir işlevi ileri bildirmek mümkün mü?


189

Python'da bir işlevi ileri bildirmek mümkün mü? Belirtilmeden cmpönce kendi işlevimi kullanarak bir listeyi sıralamak istiyorum .

print "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

Kodumu cmp_configsçağırmadan sonra yöntemin tanımını koymak için düzenledim . Bu hatayla başarısız olur:

NameError: name 'cmp_configs' is not defined

cmp_configsKullanılmadan önce yöntemi "ilan etmenin" bir yolu var mı ? Kodumu daha temiz gösterir mi?

Bazı insanların bana sadece kodumu yeniden düzenlemek gerektiğini söylemek için cazip olacağını varsayıyorum, bu yüzden bu sorun yok. Bununla birlikte, örneğin bazı özyineleme biçimlerini uygularken bunun muhtemelen kaçınılmaz olduğu durumlar vardır. Bu örneği beğenmediyseniz, bir işlev bildirmenin gerçekten gerekli olduğu bir vakam olduğunu varsayın .

Python'da bir fonksiyonun ileri bildirilmesinin gerekli olduğu bu durumu düşünün:

def spam():
    if end_condition():
        return end_result()
    else:
        return eggs()

def eggs():
    if end_condition():
        return end_result()
    else:
        return spam()

Nerede end_conditionve end_resultdaha önce tanımlanmış.

Kodu yeniden organize etmek ve her zaman çağrılardan önce tanımları koymak için tek çözüm mü?

Yanıtlar:


76

Bir işlevi kullanılmadan önce tanımlamak istemiyorsanız ve daha sonra tanımlamak mümkün değilse , başka bir modülde tanımlamaya ne dersiniz?

Teknik olarak hala ilk olarak tanımlıyorsunuz, ama temiz.

Aşağıdaki gibi bir özyineleme oluşturabilirsiniz:

def foo():
    bar()

def bar():
    foo()

Python'un işlevleri tıpkı değerler anonim gibi anonimdir, ancak bir isme bağlanabilirler.

Yukarıdaki kodda, foo()foo adında bir işlevi fooçağırmaz, çağrının yapıldığı noktadaki ada bağlı bir işlevi çağırır. fooBaşka bir yerde yeniden tanımlamak mümkündür ve barsonra yeni işlevi çağırır.

Sorununuz çözülemiyor çünkü bildirilmemiş bir değişken almayı istemek gibi.


47
Kısacası eğer betiğinizdeki son satır olarak __name__ == '__main__': main () varsa , her şey yoluna girecektir!
Filipe Pina

3
@FilipePina Yorumunuzu anlamadım - neden kodun son satırını basitçe koyamıyorsunuz main()?
Sanjay Manohar

11
@SanjayManohar: yürütmek önlemek içinimport your_module
jfs

2
Eklemek istiyorum - bazen bu sorunları daha sonra değerlendirildikleri için lambdas kullanarak atlatmak mümkündür.
Joe

2
Wrt "anonim", gerçekten "birinci sınıf nesneleri" demek.
danielm

119

Yapabileceğiniz şey, çağrıyı kendi işlevine sarmaktır.

Böylece

foo()

def foo():
    print "Hi!"

kırılacak, ama

def bar():
    foo()

def foo():
    print "Hi!"

bar()

düzgün çalışıyor olacak.

Genel kural Pythonolduğu değil işlevi (olduğu gibi kodunda yüksek tanımlı gerektiğini Pascal), ancak onun kullanımdan önce tanımlanmalıdır söyledi.

Umarım yardımcı olur.


20
En çok + 1 doğrudan cevap, kilit taşı kavramı ile: Pascal = daha yüksek tanımla, Python = daha önce tanımla.
Bob Stein

1
Bu doğru cevaptır, if __name__=="__main__":çözümün neden işe yaradığını da açıklar .
00prometheus

2
Doğru anlarsam, OP'nin spam () ve yumurta () örneğinin yazıldığı gibi iyi olduğu anlamına gelir. Bu doğru mu?
krubo

1
@krubo evet, yazıldığı gibi iyi
lxop

92

Senaryonuzu aşağıdakilerden birini kullanarak başlatırsanız:

if __name__=="__main__":
   main()

muhtemelen "ileri bildirim" gibi şeyler için endişelenmenize gerek yoktur. Gördüğünüz gibi, yorumlayıcı tüm işlevlerinizi yükleyecek ve daha sonra ana () işlevinizi başlatacaktır. Tabii ki, tüm ithalat da doğru olduğundan emin olun ;-)

Düşünmeye gel, python "ileri beyan" gibi bir şey duymadım ... ama sonra tekrar, yanlış olabilir ;-)


14
En pratik +1 cevap: Bu kodu en dıştaki kaynak dosyanın altına koyarsanız , herhangi bir sırada tanımlayabilirsiniz.
Bob Stein

2
Büyük ipucu; gerçekten bana yardım ediyor; daha çok "yukarıdan aşağıya" programlamayı aşağıdan yukarıya tercih ettiğim için.
GhostCat

10

Cmp_configs çağrısı kendi işlev tanımının içindeyse, iyi olmalısınız. Bir örnek vereceğim.

def a():
  b()  # b() hasn't been defined yet, but that's fine because at this point, we're not
       # actually calling it. We're just defining what should happen when a() is called.

a()  # This call fails, because b() hasn't been defined yet, 
     # and thus trying to run a() fails.

def b():
  print "hi"

a()  # This call succeeds because everything has been defined.

Genel olarak, kodunuzu işlevlerin (main ()) içine koymak sorununuzu çözer; dosyanın sonunda main () öğesini çağırmanız yeterlidir.


10

Bu konuyu canlandırdığım için özür dilerim, ancak burada tartışılmayan ve uygulanabilecek bir strateji vardı.

Yansımayı kullanarak beyanı iletmeye benzer bir şey yapmak mümkündür. Örneğin şöyle bir kod bölümünüz olduğunu varsayalım:

# We want to call a function called 'foo', but it hasn't been defined yet.
function_name = 'foo'
# Calling at this point would produce an error

# Here is the definition
def foo():
    bar()

# Note that at this point the function is defined
    # Time for some reflection...
globals()[function_name]()

Bu şekilde, aslında tanımlanmadan önce hangi işlevi çağırmak istediğimizi belirledik, etkili bir şekilde ileri bir beyan. Python'da ifadesi globals()[function_name]()aynıdır foo()eğer function_name = 'foo'piton onu çağırmadan önce her fonksiyonu arama gerektiğinden nedenlerle, yukarıda tartışılan. timeitBu iki ifadenin nasıl karşılaştırıldığını görmek için modül kullanılacaksa , tam olarak aynı hesaplama maliyetine sahiptirler.

Tabii ki buradaki örnek çok işe yaramaz, ancak eğer bir işlevi yerine getirmek için gerekli olan ancak daha önce bildirilmesi gereken karmaşık bir yapıya sahip olsaydı (veya yapısal olarak daha sonra sahip olmak çok az mantıklıysa), bir dize saklayabilir ve işlevi daha sonra çağırmayı deneyin.


9

Python'da ileri bildirim gibi bir şey yoktur. Sadece fonksiyonunuzun gerekmeden önce bildirildiğinden emin olmalısınız. Bir işlevin gövdesinin, işlev yürütülene kadar yorumlanmadığını unutmayın.

Aşağıdaki örneği düşünün:

def a():
   b() # won't be resolved until a is invoked.

def b(): 
   print "hello"

a() # here b is already defined so this line won't fail.

Bir işlev gövdesinin, işlevi çağırdığınızda yorumlanacak başka bir komut dosyası olduğunu düşünebilirsiniz.


7

Hayır, Python'da bir işlevi ileri sürmenin bir yolu olduğuna inanmıyorum.

Python yorumlayıcısı olduğunuzu düşünün. Çizgiye ulaştığınızda

print "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

ya cmp_configs'nin ne olduğunu biliyorsunuz ya da bilmiyorsunuz. Devam etmek için cmp_configs bilgisini bilmelisiniz. Özyineleme olup olmadığı önemli değil.


9
iyi bu sadece tek bir kod üzerinden geçmek yapıyor. Bazı derleyiciler (ve ben yorumlanmış python fark) iki geçiş yapmak, böylece bu şeyler çözülebilir. ileri bildirimi, ya da en azından bir çeşit kapsamlı keşif, gerçekten iyi olurdu.
Mark Lakewood

7

Bazen bir algoritma, genel yapıdan başlayıp ayrıntılara inerek yukarıdan aşağıya anlaşılması en kolay yöntemdir.

Bunu ileri bildirimler olmadan yapabilirsiniz:

def main():
  make_omelet()
  eat()

def make_omelet():
  break_eggs()
  whisk()
  fry()

def break_eggs():
  for egg in carton:
    break(egg)

# ...

main()

4
# declare a fake function (prototype) with no body
def foo(): pass

def bar():
    # use the prototype however you see fit
    print(foo(), "world!")

# define the actual function (overwriting the prototype)
def foo():
    return "Hello,"

bar()

Çıktı:

Hello, world!

3

Python'da bir işlevi ileri süremezsiniz. İşlevleri tanımlamadan önce yürütme mantığınız varsa, büyük olasılıkla zaten bir sorununuz vardır. Eyleminizi if __name__ == '__main__'komut dosyanızın sonuna yerleştirin (önemsiz değilse "ana" adını verdiğiniz bir işlevi yürüterek) ve kodunuz daha modüler olacak ve ihtiyacınız olduğunda bunu bir modül olarak kullanabileceksiniz. için.

Ayrıca, bu liste anlaşılmasını bir jeneratör ekspresiyle değiştirin (ör. print "\n".join(str(bla) for bla in sorted(mylist, cmp=cmp_configs)))

Ayrıca, kullanımdan cmpkaldırılmış olan kullanmayın . Küçüktür keyişlevini kullanın ve sağlayın.


Küçüktür işlevini nasıl sağlayabilirim?
Nathan Fellman

Cmp_configs yerine, iki bağımsız değişken alan ve birincisi ikinciden küçükse True, aksi halde False döndüren bir işlev tanımlarsınız.
Mike Graham

C benzeri bir arka plandan gelenlerimiz için, fonksiyonlar tanımlanmadan önce mantık yürütme hakkında mantıksız bir şey yoktur. Düşünün: "çok geçişli derleyici". Bazen yeni dillere uyum sağlamak biraz zaman alabilir :)
Luke H

3

Dosyanın kendisini içe aktarın. Dosyanın test.py olduğunu varsayarsak:

import test

if __name__=='__main__':
    test.func()
else:
    def func():
        print('Func worked')

1

"sadece kodumu yeniden organize et, böylece bu problemi yaşamıyorum." Doğru. Yapması kolay. Her zaman çalışır.

İşlevi her zaman referanstan önce sağlayabilirsiniz.

"Bununla birlikte, örneğin bazı özyineleme biçimlerini uygularken bunun muhtemelen kaçınılmaz olduğu durumlar vardır"

Bunun uzaktan nasıl mümkün olduğunu göremiyorum. Lütfen işlevi kullanmadan önce işlevi tanımlayamayacağınız bir yer örneği sağlayın.


Böyle bir durumum var. Bir işlev dekoratörü türleri geçirmeye çalışıyorum ve türleri modülü daha da tanımlanır. Sorun yaratan türleri yukarı taşıyamam çünkü kalıtım zincirini kıracaktı.
Joe

Ben gerçek türü yerine benim dekoratör bir lambda geçirerek düzeltti; ama başka türlü nasıl düzeltebileceğimi bilemem (miraslarımı yeniden düzenlememi gerektirmez)
Joe

0

Şimdi bir dakika. Modülünüz örneğinizdeki print deyimine ulaştığında, daha önce cmp_configstanımlanmışsa, tam olarak ne yapmasını beklersiniz?

Print kullanarak bir soru göndermeniz gerçekten böyle bir şeyi temsil etmeye çalışıyorsa:

fn = lambda mylist:"\n".join([str(bla)
                         for bla in sorted(mylist, cmp = cmp_configs)])

cmp_configsbu ifadeyi çalıştırmadan önce tanımlamaya gerek yoktur , sadece daha sonra kodda tanımlayın ve hepsi iyi olacaktır.

Şimdi cmp_configslambda'ya bir argümanın varsayılan değeri olarak başvurmaya çalışıyorsanız , bu farklı bir hikaye:

fn = lambda mylist,cmp_configs=cmp_configs : \
    "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

Şimdi cmp_configsbu satıra ulaşmadan önce tanımlanmış bir değişkene ihtiyacınız var .

[EDIT - işlev derlendiğinde varsayılan bağımsız değişken değeri atanacağından ve cmp_configs değerini daha sonra değiştirseniz bile bu değer kullanılacağından, bir sonraki bölümün doğru olmadığı ortaya çıkar.]

Neyse ki, Python olduğu gibi tip-accomodating olmak, ne tanımladığınız umurunda değil cmp_configs, bu yüzden sadece bu ifadeyle önsöz yapabilirsiniz:

cmp_configs = None

Ve derleyici mutlu olacak. Sadece çağırmadan cmp_configsönce gerçek ilan ettiğinizden emin olun fn.


-1

Bunun bir yolu bir işleyici işlevi oluşturmaktır. İşleyiciyi erken tanımlayın ve işleyiciyi çağırmanız gereken tüm yöntemlerin altına koyun.

Daha sonra işlevlerinizi çağırmak için işleyici yöntemini çağırdığınızda, bunlar her zaman kullanılabilir olacaktır.

İşleyici bir argüman alabilir nameOfMethodToCall. Daha sonra doğru yöntemi çağırmak için bir grup if ifadesi kullanır.

Bu, sorununuzu çözecektir.

def foo():
    print("foo")
    #take input
    nextAction=input('What would you like to do next?:')
    return nextAction

def bar():
    print("bar")
    nextAction=input('What would you like to do next?:')
    return nextAction

def handler(action):
    if(action=="foo"):
        nextAction = foo()
    elif(action=="bar"):
        nextAction = bar()
    else:
        print("You entered invalid input, defaulting to bar")
        nextAction = "bar"
    return nextAction

nextAction=input('What would you like to do next?:')

while 1:
    nextAction = handler(nextAction)

bu çok unpythonic gibi görünüyor. Python bu tür şeyleri tek başına ele almalıdır.
Nathan Fellman

kabul edilen cevabı tekrar okuyun. Eğer kadar Python tanımlanması işlevini gerekmez diyoruz sadece tanımında kullanmayın, bunu.
tacaswell

-3

Evet, bunu kontrol edebiliriz.

Giriş

print_lyrics() 
def print_lyrics():

    print("I'm a lumberjack, and I'm okay.")
    print("I sleep all night and I work all day.")

def repeat_lyrics():
    print_lyrics()
    print_lyrics()
repeat_lyrics()

Çıktı

I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.
I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.
I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.

BJ Homer'in yukarıdaki yorumlardan bahsettiği gibi, Python'daki genel bir kural, fonksiyonun kodda (Pascal'da olduğu gibi) daha yüksek tanımlanması değil, kullanılmadan önce tanımlanması gerektiğidir.

Umarım yardımcı olur.


2
Is not print_lyrics()tanımlanmadan önce hat 1'de (kullanılan) denir? Bu kod parçasını kopyaladım ve çalıştırmayı denedim ve bana NameError: name 'print_lyrics' is not definedSatır 1'deki hatayı veriyor . Bunu açıklayabilir misiniz?
Bugs Buggy
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.