heh, this is actually some dumb numpy thing, check this out:

>>> from numpy.ma.core import exp
>>> x = exp(1.0)
>>> a = x == x
>>> a
True
>>> a is True
False
>>> 

there’s your problem, the recipe fixes if you just say this:

class InexactFloat(TypeDecorator):
    impl = Float

    def compare_values(self, x, y):
        return bool(x == y)




On Nov 19, 2013, at 8:30 PM, Michael Bayer <mike...@zzzcomputing.com> wrote:

> 
> On Nov 19, 2013, at 8:10 PM, Seth P <spadow...@gmail.com> wrote:
> 
>> I have an issue where, I believe due to floating-point representation 
>> issues, reassigning the same value to a floating-point field causes 
>> SQLAlchemy to think the value has been modified, and therefore emits a 
>> gratuitous UPDATE. (This is particularly problematic when using the 
>> versioning mixin, 
>> http://docs.sqlalchemy.org/en/rel_0_8/orm/examples.html?highlight=versioned.)
>>  This doesn't happen all the time, e.g. in the example below the problem 
>> disappears if exp is imported from math rather than from numpy.ma.core. Am I 
>> doing something wrong? If not, is there a way to tell SQLAlchemy that a 
>> particular Float column (or even all Float columns) should not be considered 
>> modified if the new and old values differ by less than some threshold? Is 
>> there a way to provide a custom comparison function used for this testing? 
>> Apologies if I missed something about this in the docs.
> 
> well floating points are an inexact representation, so you are hitting that 
> issue.  If you used Numeric() instead, the floats would be coerced to Decimal 
> objects on the Python side, these don’t perform as well as floats but are an 
> exact, immutable representation - but you still can have variability in how 
> these come back from SQLite.
> 
> You can in fact change the comparison function used by the unit of work to 
> compare for a change, just use a TypeDecorator:
> 
> class InexactFloat(TypeDecorator):
>     impl = Float
> 
>    def compare_values(self, x, y):
>        return round(x) == round(y)  # or whatever
> 
> class A(Base):
>     __tablename__ = 'a'
>     id = Column(Integer, primary_key=True)
>     value = Column(InexactFloat)
> 
> 
> 
> 
> 
>> 
>> from sqlalchemy import Column, Integer, Float, create_engine
>> from sqlalchemy.ext.declarative import declarative_base
>> from sqlalchemy.orm import create_session
>> from numpy.ma.core import exp
>> 
>> sqlite = 'sqlite:///C:\\temp1.db'
>> engine = create_engine(sqlite, echo=True)
>> Base = declarative_base(bind=engine)
>> 
>> class A(Base):
>>     __tablename__ = 'a'
>>     id = Column(Integer, primary_key=True)
>>     value = Column(Float)
>> 
>> if __name__ == '__main__':
>>     x = exp(1.0)
>>     print "      x = %0.60f" % x
>>     Base.metadata.drop_all()
>>     Base.metadata.create_all()
>>     session = create_session(bind=engine, autocommit=False)
>>     a = A(id=1, value=x)
>>     session.add(a)
>>     session.commit()
>>     print "a.value = %0.60f" % a.value
>>     print "session.is_modified(a) = %s" % session.is_modified(a)  # False
>>     a.value = x
>>     print "a.value = %0.60f" % a.value
>>     print "session.is_modified(a) = %s" % session.is_modified(a)  # True
>>     session.commit()  # UPDATE
>>     session.close()
>> 
>> Thanks,
>> 
>> Seth
>> 
>> -- 
>> You received this message because you are subscribed to the Google Groups 
>> "sqlalchemy" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email to sqlalchemy+unsubscr...@googlegroups.com.
>> To post to this group, send email to sqlalchemy@googlegroups.com.
>> Visit this group at http://groups.google.com/group/sqlalchemy.
>> For more options, visit https://groups.google.com/groups/opt_out.
> 

Attachment: signature.asc
Description: Message signed with OpenPGP using GPGMail

Reply via email to