Check if object attributes are non-empty python

Question:

I can check if python list or dictionary are empty or not like this

lis1, dict1 = [], {}
# similar thing can be done for dict1
if lis1:
    # Do stuff
else:
    print "List is empty"

If I try to do this with my class object, i.e checking if my object attributes are non-empty by typing if my_object: this always evaluate to True

>>> class my_class(object):
...   def __init__(self):
...     self.lis1 = []
...     self.dict1 = {}
... 
>>> obj1 = my_class()
>>> obj1
<__main__.my_class object at 0x10c793250>
>>> if obj1:
...   print "yes"
... 
yes

I can write a function specifically to check if my object attributes are non-empty and then call if obj1.is_attributes_empty():, but I am more interested in knowing how if evaluates the standard data-types like list and dict to True or False depending on the items they contain or are empty.

If I want to achieve this functionality with my class object, what methods do I need to override or make changes to?

Asked By: Anurag Sharma

||

Answers:

You need to implement the __nonzero__ method (or __bool__ for Python3)

https://docs.python.org/2/reference/datamodel.html#object.nonzero

class my_class(object):
    def __init__(self):
        self.lis1 = []
        self.dict1 = {}

    def __nonzero__(self):
        return bool(self.lis1 or self.dict1)

obj = my_class()
if obj:
    print "Available"
else:
    print "Not available"

Python also checks the __len__ method for truthiness, but that doesn’t seem to make sense for your example.

If you have a lot of attributes to check you may prefer to

return any((self.lis1, self.dict1, ...))
Answered By: John La Rooy

If your class defines (on Py2) __nonzero__, (on Py3) __bool__ or (on either) __len__, then that will be used to evaluate the “truthiness” of objects of that class (if only __len__ is defined, an instance is truthy when it returns non-zero, and falsy when it returns zero). So, for example, to make your class simply report if it’s attributes are non-empty in either Py2 or Py3, you’d add:

 def __bool__(self):
     return bool(self.lis1 or self.dict1)
 __nonzero__ = __bool__ # To make it work on Py2 too

Alternatively, if your class instances have meaningful lengths, you define:

 def __len__(self):
     return len(self.lis1) + len(self.dict1)  # For example; I doubt the length is meaningful in terms of both

and get boolean behavior by side-effect of supporting len(myobject).

Answered By: ShadowRanger

It is given in the documentation of Truth value testing for Python 2.x

instances of user-defined classes, if the class defines a __nonzero__() or __len__() method, when that method returns the integer zero or bool value False.

For Python 3.x –

instances of user-defined classes, if the class defines a __bool__() or __len__() method, when that method returns the integer zero or bool value False.

According to the definition of your class, if maybe meaningful to define __len__() method, which returns the sum of length of the list as well as the dict.Then this method would be called to determine whether to interpret the object as True or False in boolean context. Example –

class my_class(object):
    def __init__(self):
        self.lis1 = []
        self.dict1 = {}
    def __len__(self):
        print("In len")
        return len(self.lis1) + len(self.dict1)

Demo –

>>> class my_class(object):
...     def __init__(self):
...         self.lis1 = []
...         self.dict1 = {}
...     def __len__(self):
...         print("In len")
...         return len(self.lis1) + len(self.dict1)
...
>>> obj = my_class()
>>> if obj:
...     print("yes")
...
In len
>>> obj.lis1.append(1)
>>>
>>> if obj:
...     print("yes")
...
In len
yes
Answered By: Anand S Kumar

Combining the answers for using any() and __bool__(self), the following code will allow you to check for all of the attributes using list comprehension.

class my_class(object):
    def __init__(self):
        self.list1 = []
        self.dict1 = {}

    def __bool__(self):
        check = any([self.__dict__[attr] for attr in self.__dict__.keys()])

        return check

obj1 = my_class()

if obj1:
    print('yes')

This code snippet will print nothing as expected.

Answered By: qwertyuip9

As many answers and duplicate votes suggest, you need to override the __nonzero__ method. However, from your comment, you also want to avoid enumerating the attributes explicitly. This can be done with a trick like this:

class Example(object):
    def __init__(self):
        self._values = {}
        self.attr1 = []
        self.attr2 = {}

    def __setattr__(self, name, value):
        self.__dict__[name] = value
        if not name.startswith('_'):
            self._values[name] = value  # keep track of the assigned attributes

    def __nonzero__(self):
        return any(self._values.itervalues())

This handles all public attributes that are assigned or modified later on:

>>> ex = Example()
>>> bool(ex)
False
>>> ex.attr1.append('data')
>>> bool(ex)
True
>>> ex.attr1.pop()
>>> ex.attr3 = 42
bool(ex)
>>> False

Attribute deletion is not handled properly, for that you need to override __delattr__.

Answered By: bereal

The build in vars() function makes a useful one-liner for checking if an object has any non-empty attributes. Combine it with __nonzero__ and you get the following:

def __nonzero__(self):
    return any(vars(self).values())
Answered By: Felix Hoppe

In python you wouldn’t know which attributes to expect if you do not declare them. A standard way to declare attributes is supplied in the dataclass

see https://docs.python.org/3/library/dataclasses.html

from dataclasses import dataclass

@dataclass
class my_class:
   lis1:list = []
   dict1:dict = {}

   def isEmtpy(self)->bool:
      return len(self.lis1)+len(self.dict1)==0

To implement a more generic solution you might want to inspect the dataclass source code at https://github.com/python/cpython/blob/3.11/Lib/dataclasses.py especially the fields accessor

Answered By: Wolfgang Fahl
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.