Home > other >  create objects in loop - creation fails - do not stop loop
create objects in loop - creation fails - do not stop loop

Time:10-04

i want to create objects in a loop. After the object is created i call methods on these objects. But the creation could fail and so the method call is not possible or even necessary. I want to log the failure, but do not want to stop the loop.

I came up with a stupid example:

import logging

class DivisionCreationError(Exception):
    pass

class Division(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b
    
        try:
            self.a / self.b
        except ZeroDivisionError:
            logging.warning('Not possible')
            raise DivisionCreationError
        else:
            self.result = self.a / self.b
        
    def get_result(self):
        return self.result
        
if __name__ == '__main__':
    
    numbers = [(1,2), (1,0), (1,3)]
    
    for i in numbers:
        try:
            created_object = Division(i[0], i[1])
        except DivisionCreationError:
            pass
        else:
            print(created_object.get_result())

This is just an example.

How can i log the failed creation of the objection and still keep the loop running. Is the proceeding in my example okay? I catch an exception and than throw a custom one, which i catch again.

I wouldn't mind if on failure i do not get an object at all and could do something like

for i in numbers:
    created_object = Division(i[0], i[1])
    if created_object:
        print(created_object.get_result())

But could not find a way to do that.

Thank you.

CodePudding user response:

I think, you can customize how your object should be built like this

import logging

class DivisionCreationError(Exception):
    pass

class DivisionMeta(type):
    def __call__(self, *args, **kwds):
        res = None
        try:
            res = super().__call__(*args, **kwds)
        except DivisionCreationError as err:
            pass
        return res
    


class Division(object, metaclass=DivisionMeta):
    def __init__(self, a, b):
        self.a = a
        self.b = b
    
        try:
            self.a / self.b
        except ZeroDivisionError:
            logging.warning('Not possible')
            raise DivisionCreationError
        else:
            self.result = self.a / self.b
        
    def get_result(self):
        return self.result
        
if __name__ == '__main__':
    
    numbers = [(1,2), (1,0), (1,3)]
    
    for i in numbers:
        created_object = Division(i[0], i[1])
        if created_object:
            print(created_object.get_result())

We are using metaclasses to change the way how my object should be built. So when you are initializing your object then we are calling __call__ method that will call __init__ method of division where we are raising DivisionCreationError. When we catch this error we got to know that object is not initialized correctly so we made that object None and then we returned it from our metaclass. This is how this solution is working.

CodePudding user response:

I suspect this is just an example, but anyway in this particular case I would do something like:

import logging

class DivisionCreationError(Exception):
    pass

class Division:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    @property
    def b(self):
        return self._b

    @b.setter
    def b(self, val):
        if val == 0:
            raise DivisionCreationError # IMHO a ValueError is more appropriate
        else:
            self._b = val

    @property 
    def result(self):
        return self.a / self.b

    def __repr__(self):
        return f'Division(a={self.a}, b={self.b})'
        
if __name__ == '__main__':
    numbers = [(1, 2), (1, 0), (1, 3)] 
    for nums in numbers:
        try:
            created_object = Division(*nums)
        except DivisionCreationError:
            logging.warning(f'Not possible: {nums}')
        else:
            print(created_object.result)
        finally: # this is just to show that in case of error no new object
            print(f'New object: {created_object}')
            created_object = None

output:

0.5
New object: Division(a=1, b=2)
WARNING:root:Not possible: (1, 0)
New object: None
0.3333333333333333
New object: Division(a=1, b=3)

Note that logging is done outside the class. Let the user of the class decide how they want to handle any error that may raise. Also note that it will raise error if attribute of already created object is changed to bad value.

CodePudding user response:

your code looks fine with me, just finish setting up the logger:

import logging
import os


# LOGLEVEL = os.environ.get('LOGLEVEL', 'WARNING').upper() # https://powerfulpython.com/blog/nifty-python-logging-trick/
# logging.basicConfig(level=os.environ.get("LOGLEVEL"))    # https://powerfulpython.com/blog/nifty-python-logging-trick/

logging.basicConfig(level=logging.INFO)

root_log = logging.getLogger("my-logger")
log = logging.getLogger("my-logger")
log.info("...starting log !!!")

class DivisionCreationError(Exception):
    pass

class Division(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b
    
        try:
            self.a / self.b
            log.info('data : ' str(self.a) '/' str(self.b))
        except ZeroDivisionError:
            log.info('data : ' str(self.a) '/' str(self.b))
            log.warning('Not possible')
            raise DivisionCreationError
        else:
            self.result = self.a / self.b
        
    def get_result(self):
        log.info('result : ' str(self.result))
        return self.result
        
if __name__ == '__main__':
    
    numbers = [(1,2), (1,0), (1,3)]
    
    for i in numbers:
        print(i)
        try:
            created_object = Division(i[0], i[1])
        except DivisionCreationError:
            pass
        else:
            print('--> ',created_object.get_result())


logging to stdout, result is output from IDE dont know how/why but keeps log away from prints:

INFO:my-logger:...starting log !!!
INFO:my-logger:data : 1/2
INFO:my-logger:result : 0.5
INFO:my-logger:data : 1/0
WARNING:my-logger:Not possible
INFO:my-logger:data : 1/3
INFO:my-logger:result : 0.3333333333333333
(1, 2)
-->  0.5
(1, 0)
(1, 3)
-->  0.3333333333333333

output from console:

INFO:my-logger:...starting log !!!
(1, 2)
INFO:my-logger:data : 1/2
INFO:my-logger:result : 0.5
-->  0.5
(1, 0)
INFO:my-logger:data : 1/0
WARNING:my-logger:Not possible
(1, 3)
INFO:my-logger:data : 1/3
INFO:my-logger:result : 0.3333333333333333
-->  0.3333333333333333
  • Related