There is no "best" way, because you are never just checking to see if an attribute exists; it is always a part of some larger program. There are several correct ways and one notable incorrect way.
if 'property' in a.__dict__:a.property
Here is a demonstration which shows this technique failing:
class A(object):@propertydef prop(self):return 3a = A()print "'prop' in a.__dict__ =", 'prop' in a.__dict__print "hasattr(a, 'prop') =", hasattr(a, 'prop')print "a.prop =", a.prop
'prop' in a.__dict__ = Falsehasattr(a, 'prop') = Truea.prop = 3
Most of the time, you don't want to mess with
__dict__. It's a special attribute for doing special things, and checking to see if an attribute exists is fairly mundane.
A common idiom in Python is "easier to ask for forgiveness than permission", or EAFP for short. You will see lots of Python code that uses this idiom, and not just for checking attribute existence.
# Cached attributetry:big_object = self.big_object# or getattr(self, 'big_object')except AttributeError:# Creating the Big Object takes five days# and three hundred pounds of over-ripe melons.big_object = CreateBigObject()self.big_object = big_objectbig_object.do_something()
Note that this is exactly the same idiom for opening a file that may not exist.
try:f = open('some_file', 'r')except IOError as ex:if ex.errno != errno.ENOENT:raise# it doesn't existelse:# it does and it's open
Also, for converting strings to integers.
try:i = int(s)except ValueError:print "Not an integer! Please try again."sys.exit(1)
Even importing optional modules...
try:import readlineexcept ImportError:pass
hasattr method, of course, works too. This technique is called "look before you leap", or LBYL for short.
# Cached attributeif not hasattr(self, 'big_object'):big_object = CreateBigObject()self.big_object = CreateBigObject()big_object.do_something()
hasattr builtin actually behaves strangely in Python versions prior to 3.2 with regard to exceptions -- it will catch exceptions that it shouldn't -- but this is probably irrelevant, since such exceptions are unlikely. The
hasattr technique is also slower than
try/except, but you don't call it often enough to care and the difference isn't very big. Finally,
hasattr isn't atomic so it could throw
AttributeError if another thread deletes the attribute, but this is a far-fetched scenario and you'll need to be very careful around threads anyway. I don't consider any of these three differences to be worth worrying about.)
hasattr is much simpler than
try/except, as long as all you need to know is whether the attribute exists. The big issue for me is that the LBYL technique looks "strange", since as a Python programmer I'm more used to reading the EAFP technique. If you rewrite the above examples so that they use the
LBYL style, you get code that is either clumsy, outright incorrect, or too difficult to write.
# Seems rather fragile...if re.match('^(:?0|-?[1-9][0-9]*)$', s):i = int(s)else:print "Not an integer! Please try again."sys.exit(1)
And LBYL is sometimes outright incorrect:
if os.path.isfile('some_file'):# At this point, some other program could# delete some_file...f = open('some_file', 'r')
If you want to write a LBYL function for importing optional modules, be my guest... it sounds like the function would be a total monster.
If you just need a default value,
getattr is a shorter version of
x = getattr(self, 'x', default_value)
If the default value is expensive to construct, then you'll end up with something like this:
x = getattr(self, 'attr', None)if x is None:x = CreateDefaultValue()self.attr = x
None is a possible value,
sentinel = object()x = getattr(self, 'attr', sentinel)if x is sentinel:x = CreateDefaultValue()self.attr = x
hasattr builtins just use
try/except technique (except written in C). So they all behave the same way where it counts, and picking the right one is due to a matter of circumstances and style.
try/except EAFP code will always rub some programmers the wrong way, and the
hasattr/getattr LBYL code will irk other programmers. They're both correct, and there's often no truly compelling reason to pick one or the other. (Yet other programmers are disgusted that you would consider it normal for an attribute to be undefined, and some programmers are horrified that it's even possible to have an undefined attribute in Python.)
def get_size(obj, seen=None):# From# Recursively finds size of objectssize = sys.getsizeof(obj)if seen is None:seen = set()obj_id = id(obj)if obj_id in seen:return 0# Important mark as seen *before* entering recursion to gracefully handle# self-referential objectsseen.add(obj_id)if isinstance(obj, dict):size += sum([get_size(v, seen) for v in obj.values()])size += sum([get_size(k, seen) for k in obj.keys()])elif hasattr(obj, '__dict__'):size += get_size(obj.__dict__, seen)elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes, bytearray)):size += sum([get_size(i, seen) for i in obj])return size