Apakah mungkin untuk meneruskan-mendeklarasikan fungsi dengan Python?

189

Apakah mungkin untuk meneruskan-mendeklarasikan fungsi dengan Python? Saya ingin mengurutkan daftar menggunakan cmpfungsi saya sendiri sebelum dideklarasikan.

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

Saya telah mengatur kode saya untuk memasukkan definisi cmp_configsmetode setelah doa. Gagal dengan kesalahan ini:

NameError: name 'cmp_configs' is not defined

Apakah ada cara untuk "mendeklarasikan" cmp_configsmetode sebelum digunakan? Itu akan membuat kode saya terlihat lebih bersih?

Saya berasumsi bahwa beberapa orang akan tergoda untuk memberi tahu saya bahwa saya harus mengatur ulang kode saya sehingga saya tidak memiliki masalah ini. Namun, ada beberapa kasus di mana hal ini mungkin tidak dapat dihindari, misalnya ketika menerapkan beberapa bentuk rekursi. Jika Anda tidak menyukai contoh ini, asumsikan bahwa saya memiliki kasus di mana itu benar - benar perlu untuk maju menyatakan suatu fungsi.

Pertimbangkan kasus ini di mana penerusan maju sebuah fungsi akan diperlukan dalam Python:

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

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

Di mana end_conditiondan end_resulttelah ditentukan sebelumnya.

Apakah satu-satunya solusi untuk mengatur ulang kode dan selalu meletakkan definisi sebelum pemanggilan?

Nathan Fellman
sumber

Jawaban:

76

Jika Anda tidak ingin mendefinisikan suatu fungsi sebelum digunakan, dan mendefinisikannya sesudahnya adalah mustahil, bagaimana dengan mendefinisikannya dalam beberapa modul lain?

Secara teknis Anda masih mendefinisikannya terlebih dahulu, tetapi bersih.

Anda dapat membuat rekursi seperti berikut:

def foo():
    bar()

def bar():
    foo()

Fungsi Python adalah anonim sama seperti nilai-nilai anonim, namun mereka dapat terikat pada suatu nama.

Dalam kode di atas, foo()tidak memanggil fungsi dengan nama foo, itu memanggil fungsi yang kebetulan terikat pada nama foopada saat panggilan dilakukan. Dimungkinkan untuk mendefinisikan ulang di footempat lain, dan barkemudian akan memanggil fungsi baru.

Masalah Anda tidak dapat diselesaikan karena seperti meminta untuk mendapatkan variabel yang belum dideklarasikan.

RichN
sumber
47
singkatnya, jika Anda memiliki if __name__ == '__main__': main () sebagai baris terakhir dalam skrip Anda, semuanya akan baik-baik saja!
Filipe Pina
3
@FilipePina Saya belum mengerti komentar Anda - mengapa Anda tidak bisa memasukkan baris terakhir dari kode itu secara sederhana main()?
Sanjay Manohar
11
@SanjayManohar: untuk menghindari eksekusi padaimport your_module
jfs
2
Saya ingin menambahkan - kadang-kadang mungkin untuk menghindari masalah ini menggunakan lambdas karena mereka dievaluasi nanti.
Joe
2
Dengan "anonim", maksud Anda adalah "objek kelas satu".
danielm
119

Apa yang dapat Anda lakukan adalah membungkus doa menjadi fungsi tersendiri.

Yang seperti itu

foo()

def foo():
    print "Hi!"

akan pecah, tapi

def bar():
    foo()

def foo():
    print "Hi!"

bar()

akan bekerja dengan baik.

Aturan umum dalam Pythonadalah tidak bahwa fungsi harus didefinisikan lebih tinggi dalam kode (seperti dalam Pascal), tetapi itu harus didefinisikan sebelum penggunaannya.

Semoga itu bisa membantu.

Vanya
sumber
20
+1 jawaban paling langsung, dengan konsep keystone: Pascal = define lebih tinggi, Python = define sebelumnya.
Bob Stein
1
Ini adalah jawaban yang benar, itu juga menjelaskan mengapa if __name__=="__main__":solusinya bekerja.
00prometheus
2
Jika saya mengerti dengan benar, itu berarti bahwa contoh spam OP () dan telur () baik-baik saja seperti ditulis. Apakah itu benar?
krubo
1
@ Krubo ya, tidak apa-apa seperti yang tertulis
lxop
92

Jika Anda memulai skrip Anda melalui yang berikut ini:

if __name__=="__main__":
   main()

maka Anda mungkin tidak perlu khawatir tentang hal-hal seperti "pernyataan maju". Anda lihat, penerjemah akan memuat semua fungsi Anda dan kemudian mulai fungsi utama Anda (). Tentu saja, pastikan Anda memiliki semua impor yang benar juga ;-)

Kalau dipikir-pikir, saya belum pernah mendengar hal seperti "forward declaration" dengan python ... tapi sekali lagi, saya mungkin salah ;-)

jldupont
sumber
14
+1 jawaban paling praktis: Jika Anda meletakkan kode ini di bagian bawah file sumber terluar, maka Anda bebas menentukan dalam urutan apa pun.
Bob Stein
2
Tip yang bagus; sangat membantu saya; karena saya lebih suka pemrograman "top-down" dibandingkan bottom-up.
GhostCat
10

Jika panggilan ke cmp_configs ada di dalam definisi fungsinya sendiri, Anda seharusnya baik-baik saja. Saya akan memberi contoh.

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.

Secara umum, menempatkan kode Anda di dalam fungsi (seperti main ()) akan menyelesaikan masalah Anda; panggil saja main () di akhir file.

BJ Homer
sumber
10

Saya minta maaf karena menghidupkan kembali utas ini, tetapi ada strategi yang tidak dibahas di sini yang mungkin berlaku.

Menggunakan refleksi dimungkinkan untuk melakukan sesuatu yang mirip dengan meneruskan deklarasi. Misalnya katakanlah Anda memiliki bagian kode yang terlihat seperti ini:

# 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]()

Jadi dengan cara ini kita telah menentukan fungsi apa yang ingin kita panggil sebelum benar-benar didefinisikan, secara efektif deklarasi maju. Dalam python pernyataannya globals()[function_name]()sama seperti foo()jika function_name = 'foo'untuk alasan yang dibahas di atas, karena python harus mencari setiap fungsi sebelum memanggilnya. Jika seseorang menggunakan timeitmodul untuk melihat bagaimana kedua pernyataan ini dibandingkan, mereka memiliki biaya komputasi yang sama persis.

Tentu saja contoh di sini sangat tidak berguna, tetapi jika seseorang memiliki struktur kompleks yang diperlukan untuk menjalankan suatu fungsi, tetapi harus dideklarasikan sebelumnya (atau secara struktural tidak masuk akal untuk memilikinya setelah itu), seseorang hanya dapat menyimpan string dan coba panggil fungsi tersebut nanti.

KGardevoir
sumber
9

Tidak ada yang namanya python seperti forward declaration. Anda hanya perlu memastikan bahwa fungsi Anda dideklarasikan sebelum dibutuhkan. Perhatikan bahwa tubuh suatu fungsi tidak ditafsirkan sampai fungsi dijalankan.

Perhatikan contoh berikut:

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.

Anda dapat berpikir bahwa tubuh suatu fungsi hanyalah skrip lain yang akan ditafsirkan begitu Anda memanggil fungsi tersebut.

Piotr Czapla
sumber
7

Tidak, saya tidak percaya ada cara untuk meneruskan-mendeklarasikan fungsi dengan Python.

Bayangkan Anda adalah juru bahasa Python. Ketika Anda sampai ke garis

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

Anda tahu apa itu cmp_configs atau tidak. Untuk melanjutkan, Anda harus tahu cmp_configs. Tidak masalah jika ada rekursi.

unutbu
sumber
9
nah ini adalah jika Anda hanya melakukan satu melewati kode. Beberapa kompiler (dan saya menyadari python di interpretasikan) melakukan dua lintasan, sehingga hal-hal ini dapat dipecahkan. deklarasi ke depan, atau paling tidak semacam penemuan tertutup, akan sangat bagus.
Mark Lakewood
7

Terkadang suatu algoritma paling mudah untuk dipahami dari atas ke bawah, dimulai dengan struktur keseluruhan dan menelusuri detailnya.

Anda dapat melakukannya tanpa meneruskan deklarasi:

def main():
  make_omelet()
  eat()

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

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

# ...

main()
funroll
sumber
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()

Keluaran:

Hello, world!
jmurphy61
sumber
3

Anda tidak bisa meneruskan-mendeklarasikan fungsi dengan Python. Jika Anda memiliki eksekusi logika sebelum Anda mendefinisikan fungsi, Anda mungkin punya masalah. Letakkan tindakan Anda di if __name__ == '__main__'akhir skrip Anda (dengan menjalankan fungsi yang Anda beri nama "utama" jika tidak sepele) dan kode Anda akan lebih modular dan Anda akan dapat menggunakannya sebagai modul jika Anda membutuhkannya. untuk.

Juga, ganti pemahaman daftar itu dengan generator express (yaitu, print "\n".join(str(bla) for bla in sorted(mylist, cmp=cmp_configs)))

Juga, jangan gunakan cmp, yang sudah usang. Gunakan keydan sediakan fungsi yang kurang dari itu.

Mike Graham
sumber
Bagaimana cara saya memberikan fungsi yang kurang dari itu?
Nathan Fellman
Alih-alih cmp_configs, Anda akan mendefinisikan fungsi yang mengambil dua argumen dan mengembalikan True jika yang pertama kurang dari yang kedua dan False sebaliknya.
Mike Graham
Bagi kita yang berasal dari latar belakang seperti C, tidak ada yang tidak masuk akal tentang eksekusi logika sebelum fungsi didefinisikan. Pikirkan: "multi-pass compiler". Terkadang butuh beberapa saat untuk beradaptasi dengan bahasa-bahasa baru :)
Luke H
3

Impor file itu sendiri. Dengan asumsi file tersebut disebut test.py:

import test

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

"Atur ulang kode saya sehingga saya tidak memiliki masalah ini." Benar. Mudah untuk dilakukan. Selalu berhasil.

Anda selalu dapat menyediakan fungsi sebelum referensi itu.

"Namun, ada beberapa kasus di mana hal ini mungkin tidak dapat dihindari, misalnya ketika menerapkan beberapa bentuk rekursi"

Tidak dapat melihat bagaimana hal itu mungkin terjadi. Harap berikan contoh tempat di mana Anda tidak dapat menentukan fungsi sebelum digunakan.

S.Lott
sumber
Saya memiliki situasi seperti itu. Saya mencoba untuk melewatkan tipe dalam dekorator fungsi, dan tipe didefinisikan lebih jauh ke bawah modul. Saya tidak dapat memindahkan jenis yang menyinggung ke atas, karena itu akan memutus rantai warisan.
Joe
Saya memperbaikinya dengan menyerahkan lambda ke dekorator saya, bukan tipe yang sebenarnya; tapi saya tidak akan tahu bagaimana cara memperbaikinya jika tidak (yang tidak mengharuskan saya untuk mengatur ulang warisan saya)
Joe
0

Tunggu sebentar. Ketika modul Anda mencapai pernyataan cetak dalam contoh Anda, sebelum cmp_configstelah ditentukan, apa sebenarnya yang Anda harapkan untuk dilakukan?

Jika posting Anda dari pertanyaan menggunakan cetak benar-benar mencoba untuk mewakili sesuatu seperti ini:

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

maka tidak ada persyaratan untuk didefinisikan cmp_configssebelum menjalankan pernyataan ini, cukup tentukan nanti dalam kode dan semua akan baik-baik saja.

Sekarang jika Anda mencoba untuk referensi cmp_configssebagai nilai default dari argumen ke lambda, maka ini adalah cerita yang berbeda:

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

Sekarang Anda membutuhkan cmp_configsvariabel yang ditentukan sebelum Anda mencapai baris ini.

[EDIT - bagian selanjutnya ini ternyata tidak benar, karena nilai argumen default akan ditetapkan ketika fungsi dikompilasi, dan nilai itu akan digunakan bahkan jika Anda mengubah nilai cmp_configs nanti.]

Untungnya, Python menjadi tipe-akomodatif apa adanya, tidak peduli apa yang Anda definisikan cmp_configs, sehingga Anda bisa saja mengawali dengan pernyataan ini:

cmp_configs = None

Dan kompiler akan senang. Pastikan untuk mendeklarasikan yang asli cmp_configssebelum Anda memohon fn.

PaulMcG
sumber
-1

Salah satu caranya adalah dengan membuat fungsi handler. Tentukan pawang sejak dini, dan letakkan pawang di bawah semua metode yang perlu Anda panggil.

Kemudian ketika Anda memanggil metode handler untuk memanggil fungsi Anda, mereka akan selalu tersedia.

Pawang bisa mengambil argumen nameOfMethodToCall. Kemudian gunakan banyak pernyataan if untuk memanggil metode yang tepat.

Ini akan menyelesaikan masalah Anda.

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)
obesechicken13
sumber
yang tampaknya sangat unpythonic. Python harus menangani hal semacam ini dengan sendirinya.
Nathan Fellman
baca kembali jawaban yang diterima. Python tidak perlu fungsi yang akan didefinisikan sampai Anda memanggilnya , tidak hanya menggunakannya dalam definisi.
tacaswell
-3

Ya, kami dapat memeriksa ini.

Memasukkan

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()

Keluaran

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.

Seperti BJ Homer sebutkan di atas komentar di atas, Aturan umum dalam Python bukanlah fungsi yang harus didefinisikan lebih tinggi dalam kode (seperti dalam Pascal), tetapi harus didefinisikan sebelum penggunaannya.

Semoga itu bisa membantu.

Satish Reddy Venkannagari
sumber
2
Tidak print_lyrics()dipanggil (digunakan) pada baris 1 sebelum didefinisikan? Saya menyalin kode ini dan mencoba menjalankannya, dan itu memberi saya kesalahan NameError: name 'print_lyrics' is not definedpada Baris 1. Bisakah Anda menjelaskan ini?
Bugs Buggy