เป็นไปได้ไหมที่จะส่งต่อประกาศฟังก์ชันใน Python


189

เป็นไปได้ไหมที่จะส่งต่อประกาศฟังก์ชันใน Python ฉันต้องการเรียงลำดับรายการโดยใช้cmpฟังก์ชั่นของตัวเองก่อนที่จะมีการประกาศ

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

ฉันจัดระเบียบโค้ดเพื่อกำหนดนิยามของcmp_configsวิธีหลังจากการภาวนา มันล้มเหลวด้วยข้อผิดพลาดนี้:

NameError: name 'cmp_configs' is not defined

มีวิธีใดที่จะ "ประกาศ" cmp_configsวิธีการก่อนที่มันจะใช้? มันจะทำให้โค้ดของฉันดูสะอาดขึ้นไหม

ฉันคิดว่าบางคนจะถูกล่อลวงให้บอกฉันว่าฉันควรจัดระเบียบรหัสของฉันใหม่เพื่อที่ฉันจะได้ไม่มีปัญหานี้ อย่างไรก็ตามมีบางกรณีที่อาจหลีกเลี่ยงไม่ได้เช่นเมื่อใช้การเรียกซ้ำบางรูปแบบ ถ้าคุณทำไม่ได้เช่นตัวอย่างนี้สมมติว่าผมมีกรณีที่มันเป็นความจริงที่จำเป็นเพื่อส่งต่อประกาศฟังก์ชั่น

พิจารณากรณีนี้ที่จำเป็นต้องใช้ฟังก์ชันการประกาศล่วงหน้าใน Python:

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

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

ที่ไหนend_conditionและend_resultมีการกำหนดไว้ก่อนหน้านี้

เป็นทางออกเดียวที่จะจัดระเบียบรหัสใหม่และมักจะใส่คำจำกัดความก่อนที่จะเรียก?

คำตอบ:


76

หากคุณไม่ต้องการกำหนดฟังก์ชั่นก่อนที่จะใช้งานและการกำหนดในภายหลังเป็นไปไม่ได้สิ่งที่เกี่ยวกับการกำหนดในโมดูลอื่น ๆ ?

ในทางเทคนิคคุณยังคงนิยามไว้ก่อน แต่มันก็สะอาด

คุณสามารถสร้างการสอบถามซ้ำดังนี้:

def foo():
    bar()

def bar():
    foo()

ฟังก์ชันของไพ ธ อนนั้นไม่ระบุชื่อเช่นเดียวกับค่าที่ไม่ระบุชื่อ แต่ก็สามารถผูกกับชื่อได้

ในรหัสข้างต้นfoo()ไม่เรียกฟังก์ชั่นที่มีชื่อ foo มันเรียกฟังก์ชั่นที่เกิดขึ้นจะถูกผูกไว้กับชื่อfooที่จุดที่มีการโทร มันเป็นไปได้ที่จะกำหนดที่fooอื่นbarแล้วจะเรียกฟังก์ชั่นใหม่

ปัญหาของคุณไม่สามารถแก้ไขได้เพราะมันเหมือนการขอให้รับตัวแปรที่ยังไม่ได้ประกาศ


47
กล่าวโดยย่อถ้าคุณมีถ้า __name__ == '__main__': main ()เป็นบรรทัดสุดท้ายในสคริปต์ของคุณทุกอย่างจะไม่เป็นไร!
Filipe Pina

3
@FilipePina ฉันไม่เข้าใจความคิดเห็นของคุณ - ทำไมคุณไม่ใส่บรรทัดสุดท้ายของรหัสง่ายๆmain()?
Sanjay Manohar

11
@SanjayManohar: เพื่อหลีกเลี่ยงการดำเนินการในวันที่import your_module
jfs

2
ฉันต้องการเพิ่ม - บางครั้งเป็นไปได้ที่จะหลีกเลี่ยงปัญหาเหล่านี้โดยใช้ lambdas เนื่องจากได้รับการประเมินในภายหลัง
โจ

2
Wrt "ไม่ระบุชื่อ" คุณหมายถึง "วัตถุชั้นหนึ่ง" จริงๆ
danielm

119

สิ่งที่คุณสามารถทำได้คือห่อคำภาวนาให้เป็นหน้าที่ของมันเอง

ดังนั้น

foo()

def foo():
    print "Hi!"

จะทำลาย แต่

def bar():
    foo()

def foo():
    print "Hi!"

bar()

จะทำงานอย่างถูกต้อง

กฎทั่วไปในPythonคือไม่ว่าการทำงานควรจะกำหนดไว้ที่สูงขึ้นในรหัส (ในPascal) แต่ที่มันควรจะกำหนดไว้ก่อนที่จะใช้งาน

หวังว่าจะช่วย


20
+1 คำตอบที่ตรงที่สุดด้วยแนวคิด keystone: Pascal = define สูงกว่า Python = กำหนดไว้ก่อนหน้า
Bob Stein

1
นี่คือคำตอบที่ถูกต้องมันยังอธิบายว่าทำไมการif __name__=="__main__":แก้ปัญหาทำงาน
00prometheus

2
หากฉันเข้าใจอย่างถูกต้องก็หมายความว่าตัวอย่างของ OP () และไข่ () นั้นใช้ได้ตามที่เขียน ถูกต้องไหม
krubo

1
@krubo ใช่มันเป็นเรื่องปกติตามที่เขียน
lxop

92

หากคุณเริ่มต้นสคริปต์ของคุณผ่านสิ่งต่อไปนี้:

if __name__=="__main__":
   main()

ดังนั้นคุณอาจไม่ต้องกังวลกับสิ่งต่าง ๆ เช่น "การประกาศล่วงหน้า" คุณเห็นแล้วผู้แปลจะโหลดฟังก์ชั่นทั้งหมดของคุณแล้วเริ่มฟังก์ชั่น main () ของคุณ แน่นอนให้แน่ใจว่าคุณมีการนำเข้าทั้งหมดถูกต้องด้วย ;-)

ลองคิดดูสิฉันไม่เคยได้ยินเรื่องเช่น "การประกาศล่วงหน้า" ในภาษาไพ ธ อน ... แต่จากนั้นอีกครั้งฉันอาจจะผิด ;-)


14
+1 คำตอบที่ใช้ได้จริงที่สุด: หากคุณวางรหัสนี้ไว้ที่ด้านล่างสุดของไฟล์ต้นฉบับนอกสุดคุณจะสามารถกำหนดได้ทุกลำดับ
Bob Stein

2
สุดยอดเคล็ดลับ; ช่วยฉันจริงๆ ตามที่ฉันชอบมากกว่าการเขียนโปรแกรม "บนลงล่าง" กับล่างขึ้นบน
GhostCat

10

หากการเรียกไปยัง cmp_configs อยู่ภายในนิยามฟังก์ชันของตัวเองคุณควรจะใช้ได้ ฉันจะยกตัวอย่าง

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.

โดยทั่วไปการวางโค้ดของคุณไว้ในฟังก์ชั่น (เช่น main ()) จะช่วยแก้ปัญหาของคุณได้ เพียงแค่เรียก main () ที่ท้ายไฟล์


10

ฉันขอโทษสำหรับการฟื้นฟูหัวข้อนี้ แต่มีกลยุทธ์ที่ไม่ได้กล่าวถึงที่นี่ซึ่งอาจมีผลบังคับใช้

การใช้การไตร่ตรองเป็นไปได้ที่จะทำสิ่งที่คล้ายเพื่อส่งต่อการประกาศ ตัวอย่างเช่นสมมติว่าคุณมีส่วนของรหัสที่มีลักษณะดังนี้:

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

ดังนั้นด้วยวิธีนี้เราได้กำหนดฟังก์ชันที่เราต้องการเรียกก่อนที่มันจะถูกกำหนดจริง ๆ แล้วเป็นการประกาศล่วงหน้า ในหลามคำสั่งglobals()[function_name]()เป็นเช่นเดียวกับfoo()ถ้าfunction_name = 'foo'ด้วยเหตุผลที่กล่าวข้างต้นตั้งแต่หลามต้องค้นหาแต่ละฟังก์ชั่นก่อนที่จะเรียกมันว่า ถ้าใครจะใช้timeitโมดูลเพื่อดูว่าทั้งสองงบเปรียบเทียบพวกเขามีต้นทุนการคำนวณที่แน่นอน

แน่นอนว่าตัวอย่างที่นี่ไม่มีประโยชน์มาก แต่ถ้าใครมีโครงสร้างที่ซับซ้อนซึ่งจำเป็นต้องใช้งานฟังก์ชั่น แต่จะต้องประกาศก่อน (หรือโครงสร้างมันทำให้รู้สึกเล็กน้อยที่จะมีมันในภายหลัง) เพียงแค่สามารถเก็บสตริงและ ลองเรียกใช้ฟังก์ชันในภายหลัง


9

ไม่มีสิ่งใดในไพ ธ อนเหมือนกับการประกาศล่วงหน้า คุณเพียงแค่ต้องทำให้แน่ใจว่าฟังก์ชั่นของคุณถูกประกาศก่อนที่จะต้องมีการ โปรดทราบว่าเนื้อความของฟังก์ชันไม่ถูกตีความจนกว่าจะมีการใช้งานฟังก์ชัน

ลองพิจารณาตัวอย่างต่อไปนี้:

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.

คุณสามารถคิดว่าเนื้อความของฟังก์ชันเป็นเพียงสคริปต์อื่นที่จะถูกตีความเมื่อคุณเรียกใช้ฟังก์ชัน


7

ไม่ฉันไม่เชื่อว่ามีวิธีใดที่จะส่งต่อประกาศฟังก์ชันใน Python

ลองนึกภาพคุณเป็นล่าม Python เมื่อคุณไปถึงที่หมาย

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

อย่างใดอย่างหนึ่งคุณรู้ว่า cmp_configs คืออะไรหรือคุณไม่ ในการดำเนินการต่อคุณต้องรู้ cmp_configs ไม่สำคัญว่าจะมีการสอบถามซ้ำหรือไม่


9
อย่างนี้ถ้าคุณเพียงทำผ่านรหัส คอมไพเลอร์บางคน (และฉันรู้ว่าหลามแปลความหมาย) ทำสองรอบเพื่อให้สิ่งเหล่านี้สามารถหาได้ การประกาศไปข้างหน้าหรืออย่างน้อยการค้นพบที่ จำกัด ขอบเขตบางอย่างจะดีมาก
Mark Lakewood

7

บางครั้งอัลกอริทึมง่ายที่สุดในการทำความเข้าใจจากบนลงล่างเริ่มต้นด้วยโครงสร้างโดยรวมและเจาะลึกลงไปในรายละเอียด

คุณสามารถทำได้โดยไม่ต้องมีการแจ้งล่วงหน้า:

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

เอาท์พุท:

Hello, world!

3

คุณไม่สามารถส่งต่อประกาศฟังก์ชันใน Python หากคุณมีการดำเนินการทางตรรกะก่อนที่คุณจะได้กำหนดฟังก์ชั่นแสดงว่าคุณอาจมีปัญหาอยู่ดี ใส่การกระทำของคุณในif __name__ == '__main__'ตอนท้ายของสคริปต์ของคุณ (โดยการดำเนินการฟังก์ชั่นที่คุณชื่อ "หลัก" ถ้ามันไม่สำคัญ) และรหัสของคุณจะเป็นแบบแยกส่วนและคุณจะสามารถใช้มันเป็นโมดูลหากคุณต้องการ ถึง.

นอกจากนี้ให้แทนที่รายการนั้นเข้าใจด้วยตัวสร้างด่วน (เช่นprint "\n".join(str(bla) for bla in sorted(mylist, cmp=cmp_configs)))

นอกจากนี้อย่าใช้cmpซึ่งเลิกใช้แล้ว ใช้keyและจัดให้มีฟังก์ชั่นน้อยกว่า


ฉันจะให้ฟังก์ชันที่น้อยกว่าได้อย่างไร
นาธาน Fellman

แทนที่จะเป็น cmp_configs คุณควรกำหนดฟังก์ชั่นที่รับอาร์กิวเมนต์สองตัวและส่งคืนค่า True ถ้าค่าแรกน้อยกว่าค่าที่สองและ False
Mike Graham

สำหรับพวกเราที่มาจากพื้นหลังแบบ C ไม่มีอะไรที่ไม่มีเหตุผลเกี่ยวกับการดำเนินการทางตรรกะก่อนที่จะมีการกำหนดฟังก์ชั่น คิดว่า: "คอมไพเลอร์แบบหลายพาส" บางครั้งอาจต้องใช้เวลาสักครู่ในการปรับตัวเข้ากับภาษาใหม่ :)
ลุคเอช

3

นำเข้าไฟล์ของตัวเอง สมมติว่าไฟล์ชื่อ test.py:

import test

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

1

"เพียงจัดระเบียบโค้ดของฉันใหม่เพื่อที่ฉันจะได้ไม่มีปัญหานี้" แก้ไข. ง่ายต่อการทำ ใช้ได้ผลเสมอ

คุณสามารถให้ฟังก์ชันก่อนการอ้างอิง

"อย่างไรก็ตามมีบางกรณีที่อาจหลีกเลี่ยงไม่ได้เช่นเมื่อใช้การเรียกซ้ำบางรูปแบบ"

ไม่เห็นว่าเป็นไปได้อย่างไรจากระยะไกล โปรดระบุตัวอย่างของสถานที่ที่คุณไม่สามารถกำหนดฟังก์ชั่นก่อนการใช้งานได้


ฉันมีสถานการณ์เช่นนี้ ฉันพยายามที่จะส่งผ่านประเภทในมัณฑนากรฟังก์ชั่นและประเภทที่กำหนดไว้ต่อไปโมดูล ฉันไม่สามารถย้ายประเภทที่กระทำผิดได้เพราะนั่นจะทำลายห่วงโซ่การรับมรดก
โจ

ฉันแก้ไขมันโดยส่งแลมบ์ดาไปที่มัณฑนากรของฉันแทนประเภทจริง แต่ฉันไม่ทราบวิธีการแก้ไขเป็นอย่างอื่น (ที่ไม่ต้องการให้ฉันจัดเรียงการสืบทอดของฉัน)
Joe

0

รอสักครู่ เมื่อโมดูลของคุณไปถึงคำสั่งการพิมพ์ในตัวอย่างของคุณก่อนที่จะcmp_configsถูกกำหนดสิ่งที่คุณคาดหวังให้ทำคืออะไร?

หากการโพสต์คำถามโดยใช้การพิมพ์กำลังพยายามแสดงสิ่งนี้:

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

ดังนั้นจึงไม่มีข้อกำหนดที่จะกำหนด cmp_configsก่อนที่จะดำเนินการคำสั่งนี้เพียงแค่กำหนดไว้ในภายหลังในรหัสและทุกอย่างจะดี

ตอนนี้ถ้าคุณกำลังพยายามอ้างอิงcmp_configsเป็นค่าเริ่มต้นของการโต้เถียงกับแลมบ์ดานี่ก็เป็นอีกเรื่อง

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

ตอนนี้คุณต้องcmp_configsกำหนดตัวแปรก่อนที่จะถึงบรรทัดนี้

[แก้ไข - ส่วนต่อไปนี้ปรากฎว่าไม่ถูกต้องเนื่องจากค่าอาร์กิวเมนต์เริ่มต้นจะได้รับมอบหมายเมื่อรวบรวมฟังก์ชั่นและค่าดังกล่าวจะถูกนำมาใช้แม้ว่าคุณจะเปลี่ยนค่าของ cmp_configs ในภายหลัง]

โชคดีที่ Python ให้การรองรับแบบที่เป็นอยู่ไม่สนใจสิ่งที่คุณกำหนดcmp_configsดังนั้นคุณสามารถนำหน้าด้วยคำสั่งนี้:

cmp_configs = None

และคอมไพเลอร์จะมีความสุข เพียงให้แน่ใจว่าได้ประกาศของจริงcmp_configsก่อนที่คุณจะเรียกfnก่อนที่คุณเคยวิงวอน


-1

วิธีหนึ่งคือการสร้างฟังก์ชั่นการจัดการ กำหนดตัวจัดการต้นและวางตัวจัดการด้านล่างวิธีการทั้งหมดที่คุณต้องการโทร

จากนั้นเมื่อคุณเรียกใช้เมธอดตัวจัดการเพื่อเรียกใช้ฟังก์ชั่นของคุณมันจะพร้อมใช้งานเสมอ

ผู้ดำเนินการอาจโต้แย้ง nameOfMethodToCallตัวจัดการอาจจะใช้การโต้เถียงจากนั้นใช้คำสั่ง if เพื่อเรียกวิธีการที่ถูกต้อง

นี่จะช่วยแก้ปัญหาของคุณ

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)

ดูเหมือนว่าจะไม่เน้นเสียงมากนัก Python ควรจัดการทุกสิ่งนี้ด้วยตัวเอง
Nathan Fellman

อ่านคำตอบที่ยอมรับอีกครั้ง Python ไม่จำเป็นต้องกำหนดฟังก์ชั่นจนกว่าคุณจะเรียกใช้มันไม่ใช่แค่ใช้มันในการกำหนด
tacaswell

-3

ใช่เราสามารถตรวจสอบสิ่งนี้ได้

อินพุต

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

เอาท์พุต

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.

ดังที่บีเจโฮเมอร์กล่าวถึงความคิดเห็นข้างต้นกฎทั่วไปใน Python ไม่ใช่ฟังก์ชั่นที่ควรกำหนดไว้สูงกว่าในรหัส (เช่นเดียวกับใน Pascal) แต่ควรกำหนดไว้ก่อนการใช้งาน

หวังว่าจะช่วย


2
ไม่ถูกprint_lyrics()เรียก (ใช้) ในบรรทัดที่ 1 ก่อนที่จะถูกกำหนด? ฉันคัดลอกรหัสชิ้นนี้แล้วพยายามเรียกใช้และทำให้ฉันเกิดข้อผิดพลาดNameError: name 'print_lyrics' is not definedในบรรทัดที่ 1 คุณอธิบายได้ไหม
บัก Buggy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.