คำตอบจำนวนมากมีข้อมูลทั้งหมดเพียงเล็กน้อยดังนั้นฉันจึงต้องการนำมารวมกับรูปแบบที่ฉันชอบ
ค่าเริ่มต้นคือmutable
ประเภท
หากค่าเริ่มต้นเป็นวัตถุที่ไม่แน่นอนคุณจะโชคดี: คุณสามารถใช้ประโยชน์จากข้อเท็จจริงที่ว่าอาร์กิวเมนต์เริ่มต้นของ Python ได้รับการประเมินหนึ่งครั้งเมื่อกำหนดฟังก์ชัน (บางส่วนเพิ่มเติมเกี่ยวกับสิ่งนี้ในตอนท้ายของคำตอบในส่วนสุดท้าย)
ซึ่งหมายความว่าคุณสามารถเปรียบเทียบค่าเริ่มต้นที่เปลี่ยนแปลงได้อย่างง่ายดายโดยใช้is
เพื่อดูว่ามีการส่งผ่านเป็นอาร์กิวเมนต์หรือปล่อยให้เป็นค่าเริ่มต้นดังตัวอย่างต่อไปนี้เป็นฟังก์ชันหรือวิธีการ:
def f(value={}):
if value is f.__defaults__[0]:
print('default')
else:
print('passed in the call')
และ
class A:
def f(self, value={}):
if value is self.f.__defaults__[0]:
print('default')
else:
print('passed in the call')
อาร์กิวเมนต์เริ่มต้นที่ไม่เปลี่ยนรูป
ตอนนี้มันดูหรูหราน้อยลงเล็กน้อยหากค่าเริ่มต้นของคุณคาดว่าจะเป็นไฟล์ immutable
ค่า (และจำไว้ว่าแม้แต่สตริงก็ไม่เปลี่ยนรูป!) เพราะคุณไม่สามารถใช้ประโยชน์จากกลลวงได้เหมือนเดิม แต่ยังมีบางอย่างที่คุณสามารถทำได้ ประเภท; โดยพื้นฐานแล้วคุณใส่ค่าเริ่มต้น "ปลอม" ที่ไม่แน่นอนในลายเซ็นของฟังก์ชันและค่าเริ่มต้น "จริง" ที่ต้องการในเนื้อหาของฟังก์ชัน
def f(value={}):
"""
my function
:param value: value for my function; default is 1
"""
if value is f.__defaults__[0]:
print('default')
value = 1
else:
print('passed in the call')
print(value)
รู้สึกตลกเป็นอย่างยิ่งถ้าคุณเป็นค่าเริ่มต้นที่แท้จริงNone
แต่None
ไม่สามารถเปลี่ยนรูปได้ดังนั้น ... คุณยังคงต้องใช้ตัวแปรเริ่มต้นอย่างชัดเจนเป็นพารามิเตอร์เริ่มต้นของฟังก์ชันและเปลี่ยนเป็นไม่มีในโค้ด
การใช้Default
คลาสสำหรับค่าเริ่มต้นที่ไม่เปลี่ยนรูป
หรือคล้ายกับคำแนะนำ @cz หากเอกสาร python ไม่เพียงพอ :-) คุณสามารถเพิ่มวัตถุในระหว่างนั้นเพื่อทำให้ API ชัดเจนยิ่งขึ้น (โดยไม่ต้องอ่านเอกสาร) อินสแตนซ์คลาส used_proxy_ เริ่มต้นไม่แน่นอนและจะมีค่าเริ่มต้นจริงที่คุณต้องการใช้
class Default:
def __repr__(self):
return "Default Value: {} ({})".format(self.value, type(self.value))
def __init__(self, value):
self.value = value
def f(default=Default(1)):
if default is f.__defaults__[0]:
print('default')
print(default)
default = default.value
else:
print('passed in the call')
print("argument is: {}".format(default))
ตอนนี้:
>>> f()
default
Default Value: 1 (<class 'int'>)
argument is: 1
>>> f(2)
passed in the call
argument is: 2
ข้างต้นใช้งานได้ดีเช่นDefault(None)
กัน
รูปแบบอื่น ๆ
เห็นได้ชัดว่ารูปแบบข้างต้นดูน่าเกลียดกว่าที่ควรเพราะทั้งหมดprint
นี้มีไว้เพื่อแสดงวิธีการทำงานเท่านั้น มิฉะนั้นฉันจะพบว่ามันสั้นและทำซ้ำได้มากพอ
คุณสามารถเขียนมัณฑนากรเพื่อเพิ่ม__call__
รูปแบบที่แนะนำโดย @dmg ได้อย่างคล่องตัวมากขึ้น แต่สิ่งนี้จะยังคงต้องใช้เทคนิคแปลก ๆ ในการกำหนดฟังก์ชันเอง - คุณจะต้องแยกออกvalue
และvalue_default
หากโค้ดของคุณต้องการแยกความแตกต่างดังนั้น ฉันไม่เห็นประโยชน์มากนักและฉันจะไม่เขียนตัวอย่าง :-)
ประเภทที่เปลี่ยนแปลงได้เป็นค่าเริ่มต้นใน Python
ข้อมูลเพิ่มเติมเกี่ยวกับ# 1 python gotcha! ทารุณกรรมเพื่อความสุขของคุณเองข้างต้น คุณสามารถดูว่าเกิดอะไรขึ้นเนื่องจากการประเมินตามคำจำกัดความโดยทำดังนี้
def testme(default=[]):
print(id(default))
คุณสามารถเรียกใช้testme()
กี่ครั้งก็ได้ตามที่คุณต้องการคุณจะเห็นการอ้างอิงถึงอินสแตนซ์เริ่มต้นเดียวกันเสมอ (ดังนั้นโดยพื้นฐานแล้วค่าเริ่มต้นของคุณจะไม่เปลี่ยนรูป :-))
โปรดจำไว้ว่าในหลามมีเพียง 3 ไม่แน่นอนในตัวชนิด : set
, list
, dict
; อย่างอื่น - แม้แต่สตริง! - ไม่เปลี่ยนรูป