|
From: Jim A. <ji...@tr...> - 2001-03-28 06:17:11
|
I wasn't sure whether to post to 'jython-user': but I think this brings
up something relevant for 'dev'...
I have just found a problem with my Java/Jython code that was driving me
crazy! It turns out it was because of the use of
'System.identityHashCode(key)' instead of 'key.hashCode()' in
PyStringMap. When I change the 3 occurences to 'key.hashCode()' all my
problems disappear.
I have included some test code below that demonstrates the problem. Am I
doing something I shouldn't be ?
If not, then the question is: is this a bug? Or will I create one by
changing the code? In general I have been trying to extend the Jython
classes when necessary to add/change functionality.
But I'm not sure what to do here: I can't see a reason in this case to
use 'identityHashCode' instead of 'hashCode()' since 'key' is only a
'String'. (In other classes that use PyObjects I can sort of see why you
might need 'identityHashCode' (?) )
If this can be a patch to Jython, I will leave my code in until I get a
new version. If not, I may try to reimplement the 'PyStringMap' method
in an extending class (?) or figure out some other workaround.
Thanks (Finn: nice job on the Complex Number bug !)
__o
Jim Adrig _ \<,_
ji...@tr... ' `/ ' `
___________ `-' `-'
=============================================
Here is some backround info from Sun's web site (1.1 Update changes):
http://java.sun.com/docs/books/jls/first_edition/html/1.1Update.html
%< ------------------------------------------
D.2.2 New Hashing Method
The default implementation of Object.hashCode is to return keys that are
likely to be different for different objects. As described on page 64,
many
classes override both hashCode and equals to provide different notions
of equality. However, sometimes you need the original notion of
equality, in
which all objects are different, even for an object that usually is used
with a broader notion. You can use == to test if two objects are the
same, but you will
need to use System.identityHashCode to hash the objects, since it
preserves the default implementation of hashCode in which all objects
are
considered different.
%< ------------------------------------------
=============================================
And the test code:
=============================================
// Set up two strings that look the same but are different internally:
String key1 = "TESTKEY";
String key2 = "12345" + "TESTKEY" ;
key2 = key2.substring(5); // This just adjusts the pointers
to the internal byte[]
System.out.println("keys are '.equal' ?" + key1.equals(key2) );
System.out.println("keys are '==' ?" + (key1 == key2));
// The 'identityHashCode' attempts to hash differently if it finds any
differences:
System.out.println("key1 identityHashCode:" +
System.identityHashCode(key1));
System.out.println("key2 identityHashCode:" +
System.identityHashCode(key2));
// The 'String' class does it's own 'hashCode' based on the actual
characters:
System.out.println("key1 HashCode:" + key1.hashCode());
System.out.println("key2 HashCode:" + key2.hashCode());
// -= This is the bug it causes: =-
PythonInterpreter python = new PythonInterpreter();
python.exec( "class TestClass: pass" );
// Get the Class Object:
PyClass classObject = (PyClass)python.get( "TestClass" );
// So we can build new instances of it dynamically:
PyInstance classInstance = new PyInstance( classObject );
// Add attributes dynamically to support syntax like
"BorrowerList[1].AssetList[1].AssetAmount" :
classInstance.__setattr__( key1, new PyString("In my code this is
actually a PyList object..." ));
// Looking it up using the original key1 works:
PyObject findIt1 = classInstance.__findattr__(key1);
System.out.println("Using 'key1' this is OK :" + findIt1);
// But even though key2 is '.equal' it doesn't work:
PyObject findIt2 = classInstance.__findattr__(key2);
System.out.println("But with 'key2' it is not :" + findIt2);
|
|
From: <bc...@wo...> - 2001-03-28 08:08:56
|
[Jim Adrig] >I wasn't sure whether to post to 'jython-user': but I think this brings >up something relevant for 'dev'... It is not always easy to decide. Even when the orginal poster chose absolutely right, the followups may move the discussion into new territory. Anyway, it is a minor issue. >I have just found a problem with my Java/Jython code that was driving me >crazy! It turns out it was because of the use of >'System.identityHashCode(key)' instead of 'key.hashCode()' in >PyStringMap. When I change the 3 occurences to 'key.hashCode()' all my >problems disappear. > >I have included some test code below that demonstrates the problem. Am I >doing something I shouldn't be ? Yes, the name arguments to __findattr__ and __setattr__ must be interned strings. Almost the entire attribute lookup implementation assume this to be true for the speed improvement that it gives us. http://www.jython.org/docs/api/org/python/core/PyObject.html >If not, then the question is: is this a bug? Or will I create one by >changing the code? In general I have been trying to extend the Jython >classes when necessary to add/change functionality. > >But I'm not sure what to do here: I can't see a reason in this case to >use 'identityHashCode' instead of 'hashCode()' since 'key' is only a >'String'. (In other classes that use PyObjects I can sort of see why you >might need 'identityHashCode' (?) ) It goes deeper than just PyStringMap. A very common pattern is an implementation of __findattr__(..) like this: public PyObject __findattr__(String name) { if (name == "__dict__") return __dict__; if (name == "__name__") return new PyString(__name__); if (name == "__bases__") return __bases__; ... } >If this can be a patch to Jython, I will leave my code in until I get a >new version. If not, I may try to reimplement the 'PyStringMap' method >in an extending class (?) or figure out some other workaround. This is a correct way to set/get attributes: classInstance.__setattr__( key1.intern(), new PyString("In my code this is actually a PyList object..." )); // Looking it up using the original key1 works: PyObject findIt1 = classInstance.__findattr__(key1.intern()); System.out.println("Using 'key1' this is OK :" + findIt1); // Interned key2 also works PyObject findIt2 = classInstance.__findattr__(key2.intern()); System.out.println("And interned 'key2' also works :" + findIt2); >Thanks (Finn: nice job on the Complex Number bug !) You're welcome. regards, finn |
|
From: Jim A. <ji...@tr...> - 2001-03-29 00:24:18
|
Finn Bock wrote:
>
> This is a correct way to set/get attributes:
>
> classInstance.__setattr__( key1.intern(),
> new PyString("In my code this is actually a PyList object..." ));
> ...
I had missed the requirement to 'intern' Strings in the docs: yes this
works with 'identityHashCode()'.
Of course this makes my other changes from '==' -> '.equals' unnecessary
also.
Besides the API docs, there is not much info on the jython site about
this kind of use of Jython.
Would it be useful if I could try to put something together? Maybe a new
item in 'Working with Java' section ?
For example, I have some test code that compares this:
String s = "class Charge: pass \n" +
"c1 = Charge() \n" +
"c1.Code = 'AF' \n" +
"c1.Description = 'Appraisal Fee' \n" +
"c1.Amount = '100' \n" +
"c1.PaidTo = 'L' \n" +
"c3 = Charge() \n" +
"c3.Code = 'MSC' \n" +
"c3.Description = 'Misc Fee 1' \n" +
"c3.Amount = '200' \n" +
"c3.PaidTo = 'L' \n" +
"c4 = Charge() \n" +
"c4.Code = 'MSC' \n" +
"c4.Description = 'Misc Fee 2' \n" +
"c4.Amount = '300' \n" +
"c4.PaidTo = 'L' \n" +
...
"ChargeList = [c1, c2, c3, c4] \n" +
With this:
pi.exec( "class Charge: pass" );
PyClass Charge = (PyClass)pi.get("Charge"); // Get the Class itself
!
PyInstance c1 = new PyInstance( Charge ); // Build a new instance
of the class
c1.__setattr__( "Code".intern(), new PyString("AF") );
c1.__setattr__( "Description".intern(), new PyString("Appraisal
Fee") );
c1.__setattr__( "Amount".intern(), new PyInteger(
Integer.parseInt("100") ) );
PyInstance c2 = new PyInstance( Charge); // Another instance of
the class
c2.__setattr__( "Code".intern(), new PyString("MSC") );
c2.__setattr__( "Description".intern(), new PyString("Misc Fee 1")
);
c2.__setattr__( "Amount".intern(), new PyInteger(
Integer.parseInt("200") ) );
...
PyList ChargeList = new PyList();
ChargeList.append(c1);
ChargeList.append(c2);
ChargeList.append(c3);
pi.set("ChargeList", ChargeList );
Both allow Jython code such as this to execute:
Total = 0
for C in ChargeList:
print C.Code,C.Amount
Total += C.Amount
The advantage to the second (Java) approach is that it is easier to make
everything dynamic (for example based on XML or Database definitions).
In effect the 'words' in your database definitions because 'part of' the
scripting language from a 'non-programming user' perspective.
Let me know if you think this kind of approach is general enough to be
worth documenting on the site, and I can put something more detailed
together based on my current project.
--
__o
Jim Adrig _ \<,_
ji...@tr... ' `/ ' `
___________ `-' `-'
|
|
From: <bc...@wo...> - 2001-04-13 17:05:43
|
[Jim Adrig] >Besides the API docs, there is not much info on the jython site about >this kind of use of Jython. > >Would it be useful if I could try to put something together? Maybe a new >item in 'Working with Java' section ? > >For example, I have some test code that compares this: [Python vs. java API examples snipped] >Both allow Jython code such as this to execute: > > Total = 0 > for C in ChargeList: > print C.Code,C.Amount > Total += C.Amount > >The advantage to the second (Java) approach is that it is easier to make >everything dynamic (for example based on XML or Database definitions). >In effect the 'words' in your database definitions because 'part of' the >scripting language from a 'non-programming user' perspective. > >Let me know if you think this kind of approach is general enough to be >worth documenting on the site, and I can put something more detailed >together based on my current project. It is certainly worth documenting. If you can find the time to put together some examples, please send them to me. regards, finn |