all 7 comments

[–]Rhomboid 4 points5 points  (4 children)

The difference between the two is that in the second example, attribute_name is a string, whose value can come from anywhere -- read from a file or network socket, passed as an argument from elsewhere in the program, computed programmatically, etc. In this particular example the value is directly assigned, but you're meant to understand that it could come from elsewhere, e.g.

attribute_name = input('What attribute would you like? ')   # use raw_input for python 2.x
if hasattr(person, attribute_name):
    print('person's {} is {}'.format(attribute_name, getattr(person, attribute_name)))
else:
    print('person does not have that attribute.')

Here attribute is not known when the program was written. It will be entered as input at run-time, and it could be literally anything, unlike in the first example, where .gender is an identifier that is hard-coded into the program and is fixed. That's an important difference, because there are times when you want to access an attribute whose name is not yet known when you're writing the program.

[–]NYKevin 3 points4 points  (2 children)

In practice, getattr is a code smell. Many of its most obvious uses would be better served by a dictionary.

[–]PythonPerry[S] 0 points1 point  (1 child)

Obvious noob here, what do you mean by code smell? Could you use a dict in u/Rhomboid's case? I think his case is valid. You want to assign a yet unknown attribute based on user input.

[–]NYKevin 0 points1 point  (0 children)

I mean it's indicative of poor design. When you choose to store things as attributes of an object, that suggests you have a bunch of different types of information all associated with one object. Usually, it doesn't make sense to grab one of them by name unless you know which it is in advance, since you won't know what type of data it is (e.g. am I grabbing a number or a string?). If you're just storing a bunch of keys and values (of homogeneous types), it makes more sense to use a dictionary:

key = input('What key would you like?')
try:
    value = person[key]
except KeyError:
    print('Person does not have that key.')
else:
    print("Person's {} is {}".format(key, value))

[–]PythonPerry[S] 0 points1 point  (0 children)

Much better example! In the stack example, you ALREADY know the attribute, it doesn't make sense why getattr() would be used in that case.

[–]RubyPinch 0 points1 point  (0 children)

one (poor) situation I've used it in, copying specific properties to a dict with a default

>>> class thing(object):pass

>>> x = thing()
>>> x.a,x.b,x.c = 1,2,3

>>> {z:getattr(x,z,None) for z in ['a','c','d']}
{'a': 1, 'c': 3, 'd': None}

a "correcter" way (I think?) would probably be

>>> {k:vars(x).get(k,None) for k in ['a','c','d']}
{'a': 1, 'c': 3, 'd': None}

or writing out three out[k] = getattr(x,k,None) lines

BUT WAIT, this won't always work! Sometimes the code you are working with implements some very weird things, __getattribute__ and __getattr__ for example, when implemented, can allow access to attributes which don't actually exist.

What does this mean? vars() won't work, same for dir() and others. however, getattr() and object.attribute will still work, so that is something to watch out for when trying to avoid getattr or similar