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