4

I need something like this (pseudocode):

if hasattr(object, 'detail.infotext')

I mean I want to check if object has attribute details and if it has, then if details has a attribute named infotext

I could do it like this:

if hasattr(object, 'detail'):
    if hasattr(object.detail, 'infotext'):
        do something

But one-liners are so much easier to read.

Korem
  • 11,383
  • 7
  • 55
  • 72
Lord_JABA
  • 2,545
  • 7
  • 31
  • 58
  • 11
    `if hasattr(object, 'detail') and hasattr(object.detail, 'infotext'):`? Lazy evaluation means the second `hasattr` only gets called if the first is `True`. – jonrsharpe Jul 16 '14 at 11:23

7 Answers7

6

I know this isn't really what you want to do, but it's more pythonic anyway (if you know the names of the attributes you're looking for explicitly):

try:
   do something with object.detail.infotext
except AttributeError:
   do something else
Andrew Jaffe
  • 26,554
  • 4
  • 50
  • 59
4

Assuming you want to actually use your attribute values, operator.attrgetter (in Python 2.6+) might also be useful. You still need to catch an exception, apart from that it's quite straightforward:

from operator import attrgetter

try:
   print attrgetter("detail.infotext")(object)
except:
   <handle exception>
ofrommel
  • 2,129
  • 14
  • 19
0

Not tested but it should work:

def hasattrpath(obj, path):
    parts = path.split(".")
    for part in parts:
        if hasattr(obj, part):
            obj = getattr(obj, part)
        else:
            return False
    else:
        return True

But unless you have such kind of tests everywhere in your code base with deeply nested path expressions, jonrsharpe's comment is probably the most straightforward solution.

bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
0

I used to something like in my project;

attr = 'detail.infotext'.split('.')
if len(attr) == 1:
    return getattr(item, attr[0], "")
else:
    return getattr(getattr(item, attr[0], ""), attr[1], "")

or, just one liner statement;

    attr = "detail.infotext"
    if hasattr(item, attr.rsplit('.', 1)[0]) and hasattr(getattr(item, attr.rsplit('.', 1)[0]), attr.rsplit('.', 1)[1]):
        pass
cengizkrbck
  • 704
  • 6
  • 21
0

Old question, but here is another approach building off of this answer which had a nice solution for nested getattr...

import functools

def rhasattr(obj, path):
    try:
        functools.reduce(getattr, path.split("."), obj)
        return True
    except AttributeError:
        return False
totalhack
  • 2,298
  • 17
  • 23
0

I was looking for the same thing and wasn't happy with the above answers because they don't go to infinite depth. I created this helper function which I tend to use for this problem. There's probably places where it doesn't work or break, but so far so good.

import copy
def check_if_obj_contains_nested_attribute_path(obj, nested_attr):
    '''Determine if object has the provided nested path - returns True of False
    
    :param str nested_attr: the nested attribute path e.g. 'status.load_balancer.ingress'
    :param obj: The object that you want to test for the attribute on
    '''
    # divide attribute path string into dot parts
    parts = nested_attr.split('.')
    temp_obj = copy.deepcopy(obj)
    
    for part in parts:
        if hasattr(temp_obj, part):
            temp_obj = getattr(temp_obj, part)
        else:
            return False
    return True

So now I can just use it like:

result_bool = check_if_obj_contains_nested_attribute_path(obj, 'status.load_balancer.ingress')

I'm sure there's someone around who can probably improve this with unexpected type captures and more efficient code in less lines etc... but it does the job for the moment.

Roochiedoor
  • 887
  • 12
  • 19
0
  • The best way to get child-th value from an object:
from operator import attrgetter

def get_nested_val(main_obj, fields_str):
    result = None
    try:
        result = attrgetter(fields_str)(main_obj)
    except Exception as ex:
        pass
    finally:
        return result

Usage:

product = Product.objects.filter(id=101)[0]
get_nested_val(product, 'customer.document.email')
Out[1]: 'jack@gmail.com'
Ashish Sondagar
  • 917
  • 1
  • 9
  • 16