Concise way to getattr() and use it if not None in Python
I am finding myself doing the following a bit too often:
attr = getattr(obj, 'attr', None)
if attr is not None:
attr()
# Do something, either attr(), or func(attr), or whatever
else:
# Do something else
Is there a more pythonic way of writing that? Is this better? (At least no开发者_StackOverflow社区t in performance, IMO.)
try:
obj.attr() # or whatever
except AttributeError:
# Do something else
Since you are calling the attr
, you could just do:
def default_action():
# do something else
action = getattr(obj, 'attr', default_action)
action()
Similar to Joe's answer, but shorter:
getattr(obj, 'attr', lambda: None)()
There is another nice idiom:
if hasattr(obj, 'attr'):
...something...
else:
...something else...
From the two options you posted I prefer the first one. The notion of throwing exceptions when accessing members just do not seem the thing the exceptions were meant to be used for.
The try
/except
alternative, as you code it, might accidentally cause an AttributeError
caused by problems in anything that attr()
is calling, to be caught. If you want to code with try
/except
, which is reasonable and quite well performing if it's rare for the attribute to be missing, use:
try:
attr = obj.attr
except AttributeError:
dosome(thingelse)
else:
attr()
this will not cause "accidental catching" as above. As to which approach is preferable, I generally go with the exception-catching approach - "EAFP", "it's Easier to Ask Forgiveness than Permission" (a motto often attributed to Admiral Grace Hopper, the driving force behind COBOL and CODASYL) definitely applies well to Python;-).
Using try/except is generally considered more pythonic. It is also more efficient if obj does have the attribute, since it eliminates the test - try blocks have no overhead if the exception is not thrown. On the other hand it is less efficient if obj does not have the attribute since it will have the overhead of throwing and catching the exception.
Ah, it depends on the exact code. Your two tools:
- hasattr(obj, 'attr') return True if and only if obj.attr exists.
- getattr(obj, 'attr', other_value) returns obj.attr if it exists, else other_value
- try a = obj.attr/except failure()/else do_something(a) when performance beats readability.
Here are the most common cases:
the_name = getattr(user, 'name', '<Unknown User>')
user.name = getattr(user, 'name', '<Unknown User>')
if not hasattr(name, 'user'):
try_asking_again()
name = user.name if hasattr(user, 'name') else do_expensive_name_lookup(user)
To better understand the whole process, look at this snippet:
class Thing():
def __init__(self):
self.a = 'A'
def __getattr__(self, attr):
if attr == "b":
return "B"
else:
raise AttributeError("Thing instance has no attribute '" + attr + "'")
item = Thing()
print "hasattr(a) is " + str(hasattr(item, "a"))
print "a is " + item.a
print "hasattr(b) is " + str(hasattr(item, "b"))
print "b is " + item.b
out = "c is " + item.c if hasattr(item, "c") else "No C"
print out
print "and c is also " + getattr(item, "c", "Not Assigned")
print "c throws an Attribute exception " + item.c
which has this output:
hasattr(a) is True
a is A
hasattr(b) is True
b is B
No C
and c is also Not Assigned
Traceback (most recent call last):
File "attr_snippet.py", line 23, in <module>
print "c throws an Attribute exception " + item.c
File "attr_snippet.py", line 9, in __getattr__
raise AttributeError("Thing instance has no attribute '" + attr + "'")
AttributeError: Thing instance has no attribute 'c'
One liner:
object.attr() if hasattr(object, 'attr') else None
Explanation from the docs
hasattr(object, name: str)
The arguments are an object and a string. The result is True if the string is the name of one of the object’s attributes, False if not. (This is implemented by calling getattr(object, name) and seeing whether it raises an AttributeError or not.)
精彩评论