if hasattr(obj, 'attribute'):
# do somthing
เทียบกับ
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
ควรเลือกแบบไหนและเพราะเหตุใด
if hasattr(obj, 'attribute'):
# do somthing
เทียบกับ
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
ควรเลือกแบบไหนและเพราะเหตุใด
คำตอบ:
hasattr
ภายในและรวดเร็วทำงานเช่นเดียวกับtry/except
บล็อก: เป็นเครื่องมือที่เฉพาะเจาะจงมากที่สุดได้รับการปรับให้เหมาะสมและเป็นงานเดียวดังนั้นจึงควรเป็นที่ต้องการสำหรับทางเลือกที่ใช้งานทั่วไป
hasattr
จะจับข้อยกเว้นทั้งหมดใน Python 2.x. ดูคำตอบของฉันสำหรับตัวอย่างและวิธีแก้ปัญหาเล็กน้อย
try
สามารถสื่อได้ว่าการดำเนินการควรได้ผล แม้ว่าtry
เจตนาจะไม่เป็นเช่นนั้นเสมอไป แต่ก็เป็นเรื่องธรรมดาดังนั้นจึงอาจถือว่าอ่านได้ง่าย
ม้านั่งใดที่แสดงให้เห็นถึงความแตกต่างในประสิทธิภาพ?
ถึงเวลาก็คือเพื่อนของคุณ
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'hasattr(c, "nonexistent")'
1000000 loops, best of 3: 1.87 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'hasattr(c, "a")'
1000000 loops, best of 3: 0.446 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'try:
c.a
except:
pass'
1000000 loops, best of 3: 0.247 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'try:
c.nonexistent
except:
pass'
100000 loops, best of 3: 3.13 usec per loop
$
|positive|negative
hasattr| 0.446 | 1.87
try | 0.247 | 3.13
try
hasattr()
หากไม่try
เป็นเช่นนั้นจะช้ากว่าประมาณ 1.5 เท่าhasattr()
(และทั้งสองอย่างช้ากว่าแอตทริบิวต์อย่างมาก) อาจเป็นเพราะบนเส้นทางแห่งความสุขtry
แทบจะไม่ทำอะไรเลย (Python จ่ายค่าใช้จ่ายสำหรับข้อยกเว้นอยู่แล้วไม่ว่าคุณจะใช้หรือไม่ก็ตาม) แต่hasattr()
ต้องการการค้นหาชื่อและการเรียกใช้ฟังก์ชัน บนเส้นทางที่ไม่มีความสุขทั้งคู่ต้องจัดการข้อยกเว้นและ a goto
แต่hasattr()
ทำใน C แทนที่จะเป็น Python bytecode
มีทางเลือกที่สามและมักจะดีกว่า:
attr = getattr(obj, 'attribute', None)
if attr is not None:
print attr
ข้อดี:
getattr
ไม่มีพฤติกรรมการกลืนข้อยกเว้นที่ไม่ดีที่Martin Geiser ชี้ให้เห็น - ใน Pythons เก่า ๆhasattr
จะกลืนกKeyboardInterrupt
.
เหตุผลปกติที่คุณกำลังตรวจสอบว่าวัตถุนั้นมีแอตทริบิวต์หรือไม่ก็เพื่อที่คุณจะสามารถใช้แอตทริบิวต์ได้และสิ่งนี้จะนำไปสู่มันโดยธรรมชาติ
แอตทริบิวต์ถูกอ่านออกโดยอะตอมและปลอดภัยจากเธรดอื่นที่เปลี่ยนวัตถุ (แม้ว่านี่จะเป็นข้อกังวลหลักคุณอาจต้องพิจารณาล็อกวัตถุก่อนเข้าถึง)
มันสั้นกว่าและมักจะสั้นกว่าtry/finally
hasattr
except AttributeError
บล็อกกว้าง ๆสามารถจับสิ่งอื่นAttributeErrors
นอกเหนือจากที่คุณคาดหวังซึ่งอาจนำไปสู่พฤติกรรมที่สับสน
การเข้าถึงแอตทริบิวต์ช้ากว่าการเข้าถึงตัวแปรภายใน (โดยเฉพาะอย่างยิ่งถ้าไม่ใช่แอตทริบิวต์อินสแตนซ์ธรรมดา) (พูดตามตรงการเพิ่มประสิทธิภาพไมโครใน Python มักเป็นธุระของคนโง่)
สิ่งหนึ่งที่ต้องระวังคือหากคุณสนใจเกี่ยวกับกรณีที่obj.attribute
ตั้งค่าเป็นไม่มีคุณจะต้องใช้ค่ายามอื่น
ฉันมักจะใช้ hasattr
มันเป็นตัวเลือกที่ถูกต้องสำหรับกรณีส่วนใหญ่
กรณีที่เป็นปัญหาคือเมื่อคลาสแทนที่__getattr__
: hasattr
จะจับข้อยกเว้นทั้งหมดแทนที่จะจับAttributeError
เหมือนที่คุณคาดไว้ กล่าวอีกนัยหนึ่งโค้ดด้านล่างจะพิมพ์b: False
แม้ว่าจะเหมาะสมกว่าหากเห็นValueError
ข้อยกเว้น:
class X(object):
def __getattr__(self, attr):
if attr == 'a':
return 123
if attr == 'b':
raise ValueError('important error from your database')
raise AttributeError
x = X()
print 'a:', hasattr(x, 'a')
print 'b:', hasattr(x, 'b')
print 'c:', hasattr(x, 'c')
ข้อผิดพลาดที่สำคัญจึงหายไป สิ่งนี้ได้รับการแก้ไขแล้วใน Python 3.2 ( issue9666 ) ซึ่งhasattr
ตอนนี้จับได้เท่านั้นAttributeError
ตอนนี้จับเท่านั้น
วิธีแก้ปัญหาง่ายๆคือการเขียนฟังก์ชันยูทิลิตี้ดังนี้:
_notset = object()
def safehasattr(thing, attr):
return getattr(thing, attr, _notset) is not _notset
เรามาgetattr
จัดการกับสถานการณ์และจะสามารถเพิ่มข้อยกเว้นที่เหมาะสมได้
safehasattr
ใช้getattr
เพื่อคัดลอกค่าไปยังตัวแปรท้องถิ่นหากคุณจะใช้ซึ่งคุณมักจะเป็น
hasattr
ได้รับการปรับปรุงแบบนั้น
hasattr
และไปตรวจสอบ เรามีบั๊ก bzr ตลก ๆ ที่ hasattr เพิ่งกลืน ^ C
ฉันจะบอกว่ามันขึ้นอยู่กับว่าฟังก์ชันของคุณอาจรับวัตถุที่ไม่มีแอตทริบิวต์ตามการออกแบบหรือไม่หรือไม่เช่นถ้าคุณมีผู้เรียกใช้ฟังก์ชันสองคนคนหนึ่งให้วัตถุที่มีแอตทริบิวต์และอีกคนหนึ่งให้วัตถุที่ไม่มี
หากในกรณีเดียวที่คุณจะได้รับวัตถุที่ไม่มีแอตทริบิวต์นั้นเกิดจากข้อผิดพลาดบางอย่างฉันขอแนะนำให้ใช้กลไกข้อยกเว้นแม้ว่ามันอาจจะช้ากว่าก็ตามเพราะฉันเชื่อว่ามันเป็นการออกแบบที่สะอาดกว่า
บรรทัดล่าง: ฉันคิดว่ามันเป็นปัญหาการออกแบบและความสามารถในการอ่านมากกว่าปัญหาด้านประสิทธิภาพ
หากไม่มีแอตทริบิวต์ไม่ใช่เงื่อนไขข้อผิดพลาดตัวแปรการจัดการข้อยกเว้นจะมีปัญหา: มันจะจับ AttributeErrors ที่อาจเกิดขึ้นภายในเมื่อเข้าถึง obj.attribute (เช่นเนื่องจากแอตทริบิวต์เป็นคุณสมบัติเพื่อให้การเข้าถึงเรียกใช้รหัส)
หัวข้อนี้ครอบคลุมในการพูดคุย EuroPython 2016 การเขียน Python ที่เร็วขึ้นโดย Sebastian Witowski นี่คือการจำลองสไลด์ของเขาพร้อมสรุปประสิทธิภาพ นอกจากนี้เขายังใช้คำศัพท์ก่อนที่คุณจะก้าวกระโดดในการสนทนานี้ซึ่งควรค่าแก่การกล่าวถึงที่นี่เพื่อแท็กคำหลักนั้น
หากแอตทริบิวต์หายไปจริงการขอการให้อภัยจะช้ากว่าการขอสิทธิ์ ตามหลักทั่วไปคุณสามารถใช้วิธีขออนุญาตได้หากทราบว่ามีโอกาสมากที่แอตทริบิวต์จะหายไปหรือมีปัญหาอื่น ๆ ที่คุณสามารถคาดเดาได้ มิฉะนั้นหากคุณคาดหวังว่าโค้ดจะส่งผลให้โค้ดส่วนใหญ่สามารถอ่านได้
# CASE 1 -- Attribute Exists
class Foo(object):
hello = 'world'
foo = Foo()
if hasatter(foo, 'hello'):
foo.hello
## 149ns ##
try:
foo.hello
except AttributeError:
pass
## 43.1 ns ##
## 3.5 times faster
# CASE 2 -- Attribute Absent
class Bar(object):
pass
bar = Bar()
if hasattr(bar, 'hello'):
bar.hello
## 428 ns ##
try:
bar.hello
except AttributeError :
pass
## 536 ns ##
## 25% slower
hasattr
ถ้ามันเป็นเพียงหนึ่งในคุณลักษณะที่คุณกำลังทดสอบผมว่าการใช้งาน อย่างไรก็ตามหากคุณกำลังเข้าถึงแอตทริบิวต์หลายรายการซึ่งอาจมีหรือไม่มีอยู่การใช้try
บล็อกอาจช่วยให้คุณประหยัดเวลาในการพิมพ์
ฉันขอแนะนำตัวเลือก 2 ตัวเลือกที่ 1 มีเงื่อนไขการแย่งชิงหากเธรดอื่นกำลังเพิ่มหรือลบแอตทริบิวต์
นอกจากนี้ python ยังมีสำนวน EAFP ('ง่ายกว่าที่จะขอการให้อภัยมากกว่าการอนุญาต') ดีกว่า LBYL ('ดูก่อนกระโดด')
จากมุมมองในทางปฏิบัติในภาษาส่วนใหญ่ที่ใช้เงื่อนไขจะเร็วกว่าการจัดการข้อยกเว้น
หากคุณต้องการจัดการกับกรณีของแอตทริบิวต์ที่ไม่มีอยู่นอกฟังก์ชันปัจจุบันข้อยกเว้นเป็นวิธีที่ดีกว่า ตัวบ่งชี้ที่คุณอาจต้องการใช้ข้อยกเว้นแทนเงื่อนไขคือเงื่อนไขเป็นเพียงการตั้งค่าสถานะและยกเลิกการดำเนินการปัจจุบันและบางสิ่งบางอย่างตรวจสอบแฟล็กนี้และดำเนินการตามนั้น
ดังที่ Rax Olgud ชี้ให้เห็นว่าการสื่อสารกับผู้อื่นเป็นคุณลักษณะที่สำคัญอย่างหนึ่งของรหัสและสิ่งที่คุณต้องการจะพูดโดยพูดว่า "นี่เป็นสถานการณ์พิเศษ" แทนที่จะเป็น "นี่คือสิ่งที่ฉันคาดว่าจะเกิดขึ้น" อาจสำคัญกว่า .
ครั้งแรก.
สั้นจะดีกว่า ข้อยกเว้นควรมีความพิเศษ
for
คำสั่งและhasattr
ใช้อย่างใดอย่างหนึ่งด้วย อย่างไรก็ตาม "สั้นกว่าดีกว่า" (และ "ง่ายกว่าดีกว่า"!) ใช้ดังนั้น hasattr ที่ง่ายกว่าสั้นกว่าและเฉพาะเจาะจงมากขึ้นจึงเป็นที่นิยมมากกว่า
อย่างน้อยเมื่อมันขึ้นอยู่กับสิ่งที่เกิดขึ้นในโปรแกรมการละเว้นส่วนที่เป็นมนุษย์ของความสามารถในการอ่าน ฯลฯ (ซึ่งจริงๆแล้วเวลาส่วนใหญ่ไม่สำคัญมากกว่าประสิทธิภาพ (อย่างน้อยก็ในกรณีนี้ - ด้วยช่วงประสิทธิภาพนั้น) ตามที่ Roee Adler และคนอื่น ๆ ชี้ให้เห็น)
อย่างไรก็ตามเมื่อมองจากมุมมองนั้นมันก็กลายเป็นเรื่องของการเลือกระหว่าง
try: getattr(obj, attr)
except: ...
และ
try: obj.attr
except: ...
เนื่องจากhasattr
ใช้เพียงกรณีแรกในการพิจารณาผลลัพธ์ อาหารสมอง ;-)