You probably don't want to do this, but I'll show you how anyway, using getattr
:
getattr(object, name[, default])
Return the value of the named attribute of object. name must be a string. If the string is the name of one of the object’s attributes, the result is the value of that attribute. For example, getattr(x, 'foobar')
is equivalent to x.foobar
. If the named attribute does not exist, default is returned if provided, otherwise AttributeError
is raised.
So:
def sortEmployees(some_list, age, key_attr):
return sorted(some_list, key=lambda employee: getattr(employee, key_attr))
However, if the only thing you're using this for is a sort key, attrgetter
in the stdlib wraps that up for you so you'd don't need to lambda
up your own function:
def sortEmployees(some_list, age, key_attr):
return sorted(some_list, key=operator.attrgetter(key_attr))
The reason you probably don't want to do this is that mixing up data and variable names is generally a bad idea, as explained by Ned Batchelder better than I could.
You end up with something that looks—to the human reader, and to your IDE, and to static checkers like linters and type checkers, and maybe even the optimizer—like dynamic code, even though what it actually does is purely static. You're getting all of the disadvantages of dynamic code without any of the benefits.
You don't even get shorter method calls:
sortEmployeesByName(some_list, name)
sortEmployees(some_list, name, "name")
However, the reason this is just "probably" rather than "definitely" is that there are cases where the same tradeoff goes the other way.
For example, if you had 15 of these attributes instead of 2, copying and pasting and editing the code 15 times would be a massive DRY violation. Or, imagine you were building the class or its instances dynamically, and the names weren't even known until runtime.
Of course you could write code that dynamically generates the methods at class or instance creation time, so they can then be used statically by client code. And this is a great pattern (used in various places in the stdlib). But for a dead simple case, it may be overcomplicating things badly. (A typical reader can figure out what a getattr
means more easily than figuring out a setattr
plus a descriptor __get__
call to manually bind a method, obviously.) And it still won't help many static tools understand your type's methods.
In many such cases, the way to fix that is to stop having separate named attributes and instead have a single attribute that's a dict holding all the not-quite-attribute things. But again, that's just "many", not "all", and the tradeoff can go the other way. For example, an ORM class, or something that acts like a Pandas DataFrame, you'd expect to be able to access the attributes as attributes.
So, that's why the feature is there: because sometimes you need it. I don't think you do need it in this case, but it's a judgment call.