1

I want to get either one of two attributes from a Python object. If neither of those attributes is present, I want to get a default value.

For instance, I want to get either the number of apples or of oranges in a FruitBasket, and if neither is present, I want to set n_fruits to 0 (because I am not interested in other fruits).

fruit_basket = FruitBasket(bananas=5)
try:
    n_fruits = getattr(fruit_basket, "apples")
except AttributeError as err:
    n_fruits = getattr(fruit_basket, "oranges")
except AttributeError as err:
    n_fruits = 0

This code breaks if there are neither apples or oranges:

AttributeError("'FruitBasket' object has no attribute 'oranges'")

The class FruitBasket can hold only one type of fruit. There is no risk that both apples and oranges are present. Even if that was the case, I still would store in n_fruits the first type of fruit (apples, in this case).

I want an elegant Pythonic way to solve this task: I have in mind many possible solutions, but all of them are ugly and convoluted!

By the way, FruitBasket is just an example. I am actually trying to get the centroids from a scikit-learn clusterer. KMeans and MeanShift algorithms store the centroids in the 'clusters_centers_' attribute, and GaussianMixture stores it in the 'means_' attribute (for my purposes, they are the same). If neither of them is present (in case of other clustering algorithms), I want to set centroids to an empty list.

2 Answers 2

2

you code breaks because you need to do add an additional try-except when there is an exception:

fruit_basket = FruitBasket(bananas=5)
try:
    n_fruits = getattr(fruit_basket, "apples")
except AttributeError as err:
    try:
        n_fruits = getattr(fruit_basket, "oranges")
    except AttributeError as err:
        n_fruits = 0

A better way would be to just set a default value to the getattr:

fruit_basket = FruitBasket(bananas=5)
n_fruits = getattr(fruit_basket, "apples", None)
if n_fruits is None:
    n_fruits = getattr(fruit_basket, "oranges", 0)
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you! I tried your "better way" and it works perfectly. It is also very readable, and it seems Pythonic to me.
Your welcome! because its python i'm sure there a lot more ways to solve this but it just looked very simple to me to use the default value of getattr (:
1

I accepted the answer by @RoseGod, and I am using it in my code. Still, I found another solution that may be useful to somebody.

My solution may be preferable if there are more than two possible attributes: in that case, manually chaining the getattr may be error-prone. In that case, I would do something like this:

fruit_basket = FruitBasket(bananas=5)
acceptable_fruits = ["apples", "oranges", "kiwis", "pineapples"]

fruits_in_basket = set(dir(fruit_basket)).intersection(acceptable_fruits)
fruits_in_basket = list(fruits_in_basket)
if fruits_in_basket:
    fruit_selected = fruits_in_basket[0]
    n_fruits = getattr(fruit_basket, fruit_selected)
else:
    fruit_selected = None
    n_fruits = 0

dir(fruit_basket) is a list containing all attributes and methods names in fruit_basket. I then get only the attributes present in acceptable_fruits with the intersection method. If the intersection is not empty, I extract the first attribute (after converting the set to a list), otherwise I use the default values.

2 Comments

Actually this solution is better if there a more than two possible attributes. I just want to point out one error in this code is that set data structure is referred as Unordered Collections of Unique Elements and that doesn't support operations like indexing or slicing etc. so fruits_in_basket[0] will cause a TypeError you should first convert fruits_in_basket to a list and than index it. just change fruits_in_basket = list(set(dir(fruit_basket)).intersection(acceptable_fruits))
You are perfectly right @RoseGod! Thanks for pointing out the bug in the code!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.