Python decimal.Decimal id is not the same

I am having a problem with JSON encoding in Python, in particular with decimal.Decimal. I use this to output JSON for a Google App Engine application.

To get around the exception from the default json module in Python saying that it cannot handle decimals. Decimal objects, I use this subclass of code:

 class DecimalEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, decimal.Decimal): return float(o) return super(DecimalEncoder, self).default(o) 

In other applications, this really works. In this case, this is not so. After many disappointments, I found out that this gives strange results:

 print id(decimal.Decimal) print id(type(o)) 

One would expect the identifier to be identical, because it makes sense that the same class object remains only once in memory. Since the identifier is different, isinstance() does not work.

Could it be that decimal.Decimal has already been imported somewhere else, for example, into App Engine and / or Webapp2 packages?

The following module reproduces the error on my system (OSx 10.10, Python 2.7.6, GAE SDK 1.9.20). Just create a GAE application and put it in main.py:

 import webapp2, decimal, json, MySQLdb, sys class DecimalEncoder(json.JSONEncoder): def default(self, o): print id(decimal.Decimal) print id(type(o)) if isinstance(o, decimal.Decimal): return float(o) return super(DecimalEncoder, self).default(o) class MainHandler(webapp2.RequestHandler): def get(self): db = MySQLdb.connect(unix_socket='/var/mysql/mysql.sock', host='localhost', user='root', db='ssss', charset='utf8') cursor = db.cursor(MySQLdb.cursors.DictCursor) cursor.execute("SELECT id, price FROM product WHERE id = 1") record = cursor.fetchone() self.response.headers['Content-Type'] = 'application/json' self.response.write(json.dumps( record, cls=DecimalEncoder, indent=4, separators=(',', ': ') )) app = webapp2.WSGIApplication([ ('/', MainHandler) ], debug=True) 

Database Table:

 CREATE TABLE `product` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `price` decimal(10,2) unsigned DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; INSERT INTO product VALUES(0, 5.00); 
+1
source share
1 answer

It looks like the decimal.Decimal class decimal.Decimal fixed somewhere in the Google App Engine SDK (or the module is reloaded), and this is done between the MySQL conversion library that imports decimal , and you import it.

Fortunately, we can get around this by updating the MySQL conversion table:

 from MySQLdb.constants import FIELD_TYPE from MySQLdb.converters import conversions import decimal conversions[FIELD_TYPE.DECIMAL] = conversions[FIELD_TYPE.NEWDECIMAL] = decimal.Decimal 

What is it; the above code resets the MySQL class, and type checking your JSON encoder succeeds.

An alternative would be to look up the MySQLdb class with:

 class DecimalEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, MySQLdb.converters.conversions[MySQLdb.constants.FIELD_TYPE.DECIMAL]): return float(o) return super(DecimalEncoder, self).default(o) 
+6
source

Source: https://habr.com/ru/post/1261675/


All Articles