If the string name is a constant, say 'foo', getattr(obj, 'foo') is exactly the same thing as obj.foo.
So, the main use case for the built-in function getattr is when you don't have the attribute name as a constant, but rather as a variable. A second important use case is when you pass it three arguments, rather than just two: in that case, if the attribute is absent from the object, getattrreturns the third, "default", argument, rather than raising an exception.
__getattr__ is a special method, defined in a class, that gets invoked when some attribute of an instance of that class is requested, and other normal ways to supply that attribute (via the instance's __dict__, slots, properties, and so on) all failed. You can define it, for example, when you want to delegate otherwise-undefined attribute lookups to other objects.
So your second example is wrong because the builtin getattr can never be called with a single argument.
Use of __getattr__
You can also tell a class how to deal with attributes which it doesn't explicitly manage and do that via __getattr__ method.
Python will call this method whenever you request an attribute that hasn't already been defined. So you can define what to do with it.
Caveats and use of __getattribute__
If you need to catch every attribute regardless whether it exists or not, use __getattribute__ instead.
Difference__getattr__ only gets called for attributes that don't actually exist. If you set the attribute directly, referencing that attribute will retrieve it without calling __getattr__.
__getattribute__ is called all the times.
A key difference between __getattr__ and __getattribute__ is that __getattr__ is only invoked if the attribute wasn't found the usual ways. It's good for implementing a fallback for missing attributes, and is probably the one of two you want.