Reputation: 5039
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?
Upvotes: 8
Views: 14236
Reputation: 15769
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
Upvotes: 0
Reputation: 31
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())
Upvotes: 3
Reputation: 34282
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__
.
Upvotes: 0
Reputation: 1632
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.
Upvotes: 0
Reputation: 155468
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)
.
Upvotes: 0
Reputation: 304255
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, ...))
Upvotes: 7
Reputation: 90929
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
Upvotes: 2