ใน Python มีการจำกัดจำนวนการเรียกซ้ำสูงสุด (จำนวนการเรียกซ้ำสูงสุด) ในการเรียกใช้ฟังก์ชันแบบเรียกซ้ำที่มีการโทรจำนวนมาก จำเป็นต้องเปลี่ยนขีดจำกัด ใช้ฟังก์ชันในโมดูล sys ของไลบรารีมาตรฐาน
จำนวนการเรียกซ้ำยังถูกจำกัดด้วยขนาดสแต็ก ในบางสภาพแวดล้อม สามารถใช้โมดูลทรัพยากรของไลบรารีมาตรฐานเพื่อเปลี่ยนขนาดสแต็กสูงสุดได้ (ทำงานบน Ubuntu แต่ใช้งานไม่ได้บน Windows หรือ Mac)
ข้อมูลต่อไปนี้มีให้ที่นี่
- รับขีดจำกัดสูงสุดของจำนวนการเรียกซ้ำปัจจุบัน:
sys.getrecursionlimit()
- เปลี่ยนขีดจำกัดสูงสุดของจำนวนการเรียกซ้ำ:
sys.setrecursionlimit()
- เปลี่ยนขนาดสูงสุดของสแต็ก:
resource.setrlimit()
โค้ดตัวอย่างกำลังทำงานบน Ubuntu
รับขีดจำกัดการเรียกซ้ำปัจจุบัน: sys.getrecursionlimit()
สามารถรับขีดจำกัดการเรียกซ้ำปัจจุบันได้ด้วย sys.getrecursionlimit()
import sys
import resource
print(sys.getrecursionlimit())
# 1000
ในตัวอย่าง จำนวนการเรียกซ้ำสูงสุดคือ 1,000 ซึ่งอาจแตกต่างกันไปตามสภาพแวดล้อมของคุณ โปรดทราบว่าทรัพยากรที่เรากำลังนำเข้าที่นี่จะถูกนำมาใช้ในภายหลัง แต่ไม่ใช่ใน Windows
ตัวอย่างเช่น เราจะใช้ฟังก์ชันเรียกซ้ำอย่างง่ายต่อไปนี้ หากระบุจำนวนเต็มบวก n เป็นอาร์กิวเมนต์ จำนวนการเรียกจะเป็น n ครั้ง
def recu_test(n):
if n == 1:
print('Finish')
return
recu_test(n - 1)
ข้อผิดพลาด (RecursionError) จะปรากฏขึ้นหากคุณพยายามเรียกซ้ำมากกว่าขีดจำกัดบน
recu_test(950)
# Finish
# recu_test(1500)
# RecursionError: maximum recursion depth exceeded in comparison
โปรดทราบว่าค่าที่ได้รับจาก sys.getrecursionlimit() ไม่ใช่จำนวนสูงสุดของการเรียกซ้ำ แต่เป็นความลึกสแต็กสูงสุดของตัวแปล Python ดังนั้นแม้ว่าจำนวนการเรียกซ้ำจะน้อยกว่าค่านี้เล็กน้อย ข้อผิดพลาด (RecursionError) จะ ถูกยกขึ้น
再帰限界は、再帰の限界ではなく、หลามอินタープリタのスタックの最大深度です。
python – Max recursion is not exactly what sys.getrecursionlimit() claims. How come? – Stack Overflow
# recu_test(995)
# RecursionError: maximum recursion depth exceeded while calling a Python object
เปลี่ยนขีด จำกัด การเรียกซ้ำ: sys.setrecursionlimit()
ขีดจำกัดสูงสุดของจำนวนการเรียกซ้ำสามารถเปลี่ยนแปลงได้โดย sys.setrecursionlimit() ขีดจำกัดบนถูกระบุเป็นอาร์กิวเมนต์
ช่วยให้สามารถเรียกซ้ำได้ลึกขึ้น
sys.setrecursionlimit(2000)
print(sys.getrecursionlimit())
# 2000
recu_test(1500)
# Finish
หากขีดจำกัดบนที่ระบุน้อยเกินไปหรือใหญ่เกินไป จะเกิดข้อผิดพลาดขึ้น ข้อจำกัดนี้ (ขีดจำกัดบนและล่างของขีดจำกัดเอง) จะแตกต่างกันไปตามสภาพแวดล้อม
มูลค่าสูงสุดของขีดจำกัดขึ้นอยู่กับแพลตฟอร์ม หากคุณต้องการการเรียกซ้ำในเชิงลึก คุณสามารถระบุค่าที่มากขึ้นภายในช่วงที่แพลตฟอร์มสนับสนุน แต่โปรดทราบว่าค่านี้จะทำให้เกิดการหยุดทำงานหากมีขนาดใหญ่เกินไป
If the new limit is too low at the current recursion depth, a RecursionError exception is raised.
sys.setrecursionlimit() — System-specific parameters and functions — Python 3.10.0 Documentation
sys.setrecursionlimit(4)
print(sys.getrecursionlimit())
# 4
# sys.setrecursionlimit(3)
# RecursionError: cannot set the recursion limit to 3 at the recursion depth 1: the limit is too low
sys.setrecursionlimit(10 ** 9)
print(sys.getrecursionlimit())
# 1000000000
# sys.setrecursionlimit(10 ** 10)
# OverflowError: signed integer is greater than maximum
จำนวนการเรียกซ้ำสูงสุดยังถูกจำกัดด้วยขนาดสแต็ก ตามที่อธิบายต่อไป
เปลี่ยนขนาดสูงสุดของสแต็ก: resource.setrlimit()
แม้ว่าจะมีการตั้งค่าจำนวนมากใน sys.setrecursionlimit() แต่ก็อาจไม่สามารถดำเนินการได้หากจำนวนการเรียกซ้ำมีมาก ความผิดพลาดในการแบ่งส่วนเกิดขึ้นดังนี้
sys.setrecursionlimit(10 ** 9)
print(sys.getrecursionlimit())
# 1000000000
recu_test(10 ** 4)
# Finish
# recu_test(10 ** 5)
# Segmentation fault
ใน Python สามารถใช้โมดูลทรัพยากรในไลบรารีมาตรฐานเพื่อเปลี่ยนขนาดสแต็กสูงสุดได้ อย่างไรก็ตาม โมดูลทรัพยากรเป็นโมดูลเฉพาะของ Unix และไม่สามารถใช้บน Windows ได้
- Unix Specific Services — Python 3.10.0 Documentation
- resource — Resource usage information — Python 3.10.0 Documentation
ด้วย resource.getrlimit() คุณสามารถรับขีดจำกัดของทรัพยากรที่ระบุในอาร์กิวเมนต์เป็น tuple ของ (soft limit, hard limit) ที่นี่ เราระบุทรัพยากร RLIMIT_STACK เป็นทรัพยากร ซึ่งแสดงถึงขนาดสูงสุดของ call stack ของกระบวนการปัจจุบัน
- resource.getrlimit() — Resource usage information — Python 3.10.0 Documentation
- resource.RLIMIT_STACK — Resource usage information — Python 3.10.0 Documentation
print(resource.getrlimit(resource.RLIMIT_STACK))
# (8388608, -1)
ในตัวอย่าง ขีดจำกัดซอฟต์คือ 8388608 (8388608 B = 8192 KB = 8 MB) และขีดจำกัดฮาร์ดคือ -1 (ไม่จำกัด)
คุณสามารถเปลี่ยนขีดจำกัดของทรัพยากรด้วย resource.setrlimit() ที่นี่ ขีดจำกัดซอฟต์ยังถูกตั้งค่าเป็น -1 (ไม่จำกัด) คุณยังสามารถใช้ทรัพยากรคงที่ RLIM_INFINIT เพื่อแสดงขีดจำกัดไม่จำกัด
การเรียกซ้ำแบบลึกซึ่งไม่สามารถทำได้เนื่องจากความผิดพลาดในการแบ่งส่วนก่อนการเปลี่ยนแปลงขนาดสแต็ก สามารถทำได้แล้ว
resource.setrlimit(resource.RLIMIT_STACK, (-1, -1))
print(resource.getrlimit(resource.RLIMIT_STACK))
# (-1, -1)
recu_test(10 ** 5)
# Finish
ในที่นี้ ขีดจำกัดซอฟต์ตั้งค่าเป็น -1 (ไม่จำกัด) สำหรับการทดสอบง่ายๆ แต่ในความเป็นจริง จะปลอดภัยกว่าหากจำกัดไว้เป็นค่าที่เหมาะสม
นอกจากนี้ เมื่อฉันพยายามตั้งค่าขีดจำกัดซอฟต์ไม่จำกัดบน mac ของฉันด้วย เกิดข้อผิดพลาดดังต่อไปนี้ValueError: not allowed to raise maximum limit
การรันสคริปต์ด้วย sudo ไม่ได้ช่วยอะไร อาจถูกจำกัดโดยระบบ
กระบวนการที่มี UID ที่มีประสิทธิภาพของ superuser สามารถร้องขอขีดจำกัดที่สมเหตุสมผล รวมถึงไม่จำกัด
อย่างไรก็ตาม คำขอที่เกินขีดจำกัดที่กำหนดโดยระบบจะยังคงส่งผลให้เกิด ValueError
resource.setrlimit() — Resource usage information — Python 3.10.0 Documentation
Windows ไม่มีโมดูลทรัพยากร และ mac ไม่สามารถเปลี่ยนขนาดสแต็กสูงสุดได้เนื่องจากข้อจำกัดของระบบ หากเราสามารถเพิ่มขนาดสแต็กได้ด้วยวิธีใดวิธีหนึ่ง เราก็ควรจะสามารถแก้ไขข้อผิดพลาดในการแบ่งส่วนได้ แต่เราไม่สามารถยืนยันได้