bug with isinstance() ?

Discussion in 'Python' started by Mac, Jun 1, 2005.

  1. Mac

    Mac Guest

    Under certain circumstances isinstance() seems to return incorrect
    value for me. I'm using Python 2.3 (latest from Debian's unstable).
    Here's a sample program... the multi-module nature of the code is key.


    === test.py ===

    class Foo:
    pass

    def test():
    from test2 import make_me_a_foo
    foo = make_me_a_foo()
    if isinstance(foo, Foo):
    print "is a Foo"
    else:
    print "is NOT a Foo!"

    if __name__ == "__main__":
    test()


    === test2.py ===

    from test import Foo

    def make_me_a_foo():
    return Foo()


    --8<--

    When I run "python test.py", I get "is NOT a Foo!", when the object
    clearly IS a Foo! Am I missing something, or is this a bug?
    Mac, Jun 1, 2005
    #1
    1. Advertising

  2. Mac

    Terry Reedy Guest

    "Mac" <> wrote in message
    news:...
    > Under certain circumstances isinstance() seems to return incorrect
    > value for me. I'm using Python 2.3 (latest from Debian's unstable).
    > Here's a sample program... the multi-module nature of the code is key.


    The key, which my debug addition below should help you see, is a
    consequence of the mutually recursive definitions of the two modules, with
    one of them also being the main module. This is a recipe for confusing
    results that is best avoided. But I believe you could also get the same
    confusing result with one file that imported itself directly rather than
    indirectly via a second file.

    > === test.py ===
    >
    > class Foo:
    > pass


    > def test():
    > from test2 import make_me_a_foo
    > foo = make_me_a_foo()


    Try adding
    print id(foo.__class__), foo.__class__
    print id(Foo), Foo

    > if isinstance(foo, Foo):
    > print "is a Foo"
    > else:
    > print "is NOT a Foo!"
    >
    > if __name__ == "__main__":
    > test()


    > === test2.py ===
    >
    > from test import Foo
    >
    > def make_me_a_foo():
    > return Foo()


    > When I run "python test.py", I get "is NOT a Foo!", when the object
    > clearly IS a Foo! Am I missing something


    Yes, the ambiguity of Foo, which the print statements should reveal.
    Deriving Foo from object to make it newstyle should give similar behavior.

    > or is this a bug?


    Pretty sure not. It is hard to go wrong comparing ids for equality.

    Terry J. Reedy
    Terry Reedy, Jun 1, 2005
    #2
    1. Advertising

  3. Mac a écrit :
    > Under certain circumstances isinstance() seems to return incorrect
    > value for me. I'm using Python 2.3 (latest from Debian's unstable).
    > Here's a sample program... the multi-module nature of the code is key.
    >
    >
    > === test.py ===
    >
    > class Foo:
    > pass
    >
    > def test():
    > from test2 import make_me_a_foo
    > foo = make_me_a_foo()
    > if isinstance(foo, Foo):
    > print "is a Foo"
    > else:
    > print "is NOT a Foo!"
    >
    > if __name__ == "__main__":
    > test()
    >
    >
    > === test2.py ===
    >
    > from test import Foo
    >
    > def make_me_a_foo():
    > return Foo()
    >
    >
    > --8<--
    >
    > When I run "python test.py", I get "is NOT a Foo!", when the object
    > clearly IS a Foo! Am I missing something,


    Yes

    > or is this a bug?

    Nope

    try adding:
    print foo.__class__

    after the second line of your test() function.
    Bruno Desthuilliers, Jun 1, 2005
    #3
  4. Mac

    Mac Guest

    I see, interesting. OK, I understand that recursive importing can be
    problematic (having to "hide" the test2 import should have been a tip
    off; it's just that in my original app this relationship is not as
    clear), but what is the lesson I should take away from this? I mean, I
    was under the impression that "once a Foo, always a Foo", while from
    the above I'm starting to see that a single class definition can give
    rise to a multiple number of classes, and that the classes are
    parametrized by the module they come from (I guess that makes sense...
    else class names would have to be unique throughout all the source for
    a single program)... I guess the problem is I'm thinking of "classes"
    as these abstract concepts, sort of like Platonian "forms", whereas I
    should be thinking of classes as "class objects", object instances,
    each coming from some module's namespace... is this sort of the idea?
    Someone help me wrap my head around this, please. :)
    Mac, Jun 1, 2005
    #4
  5. Mac

    John Machin Guest

    Mac wrote:
    > Under certain circumstances isinstance() seems to return incorrect
    > value for me. I'm using Python 2.3 (latest from Debian's unstable).
    > Here's a sample program... the multi-module nature of the code is key.


    Yes, it has the multi-module nature. What it needs, though, is the
    Buddha nature :)

    >
    >
    > === test.py ===
    >
    > class Foo:
    > pass


    'print' and 'repr()' are your friends. Use them.
    Add this:
    print '*** Have just made class Foo:', repr(Foo)

    >
    > def test():
    > from test2 import make_me_a_foo
    > foo = make_me_a_foo()


    Add these lines:
    print 'foo is an instance of', foo.__class__
    print 'In test, Foo is', repr(Foo)

    > if isinstance(foo, Foo):
    > print "is a Foo"
    > else:
    > print "is NOT a Foo!"
    >
    > if __name__ == "__main__":
    > test()
    >
    >
    > === test2.py ===
    >
    > from test import Foo
    >
    > def make_me_a_foo():


    Add this:
    print "In test2, Foo is", repr(Foo)

    > return Foo()
    >
    >
    > --8<--
    >
    > When I run "python test.py", I get "is NOT a Foo!", when the object
    > clearly IS a Foo!


    Indeed foo is an instance of a class named Foo, but it is not the Foo
    you are looking for. You have created *TWO* Foo classes. A class is
    created when its source is executed. This has happened twice, once when
    you ran the test.py script, and again when test2.py imported test.

    Circular imports are big trouble (in any language). If you think you
    need them, you are wrong; refactor until they go away. Circularly
    importing all or some objects from your __main__ script is double trouble.

    HTH,

    John
    John Machin, Jun 1, 2005
    #5
  6. Mac

    Terry Reedy Guest

    "Mac" <> wrote in message
    news:...
    >I see, interesting. OK, I understand that recursive importing can be
    > problematic (having to "hide" the test2 import should have been a tip
    > off; it's just that in my original app this relationship is not as
    > clear), but what is the lesson I should take away from this?


    I suspect that the import hiding was needed to avoid infinite recursion but
    is not essential in itself to getting duplicate class Foo objects.

    > I mean, I
    > was under the impression that "once a Foo, always a Foo", while from
    > the above I'm starting to see that a single class definition can give
    > rise to a multiple number of classes,


    Unless you intend this, it is probably a programming error on your part.

    > and that the classes are
    > parametrized by the module they come from (I guess that makes sense...
    > else class names would have to be unique throughout all the source for
    > a single program)


    There is nothing special about classes here.

    >... I guess the problem is I'm thinking of "classes"
    > as these abstract concepts, sort of like Platonian "forms", whereas I
    > should be thinking of classes as "class objects",


    Definitely. In Python, 'everything' is an object. Understanding this is a
    key to understanding Python programming.

    > Someone help me wrap my head around this, please. :)


    Here is the source of your particular problem. Running 'python
    somefile.py' is more or less equivalent to a hidden single-line program:
    'import somefile.py as __main__'. The code in somefile is used to populate
    the main module, named '__main__'. If the code in somefile.py (or .pyc)
    leads, directly or indirectly, to execution of 'import somefile', the
    import function looks for an existing module named (bound to, in
    sys.modules, for CPython) 'somefile'. Not finding one, it create a new
    module, names it 'somefile', and populates it from somefile.py. Now there
    are duplicate modules and probably a program bug.

    This is not the only way to get two modules from one file. One can give
    import different access paths to a file such that it will not recognize
    that it has already imported the file. But this too is almost certainly an
    error.

    One way people avoid importing the main module file after startup is to
    limit it to top-level code with no class or function definitions that might
    need to be imported elsewhere. But when a module of definitions, intended
    for import, has an "if __name__ == '__main__': test()" section for testing
    purposes, then more care may be needed.

    Terry J. Reedy
    Terry Reedy, Jun 2, 2005
    #6
  7. Mac

    John Machin Guest

    Mac wrote:
    [snip]
    > I guess the problem is I'm thinking of "classes"
    > as these abstract concepts, sort of like Platonian "forms", whereas I
    > should be thinking of classes as "class objects", object instances,
    > each coming from some module's namespace... is this sort of the idea?
    > Someone help me wrap my head around this, please. :)
    >


    Yes, you're getting it; Python is a dynamic language. Even classes can
    be created on the fly.

    I have a kit for reading/writing boring old legacy mainframe-style files
    containg multiple record types with fixed-length fields. The record
    definitions are stored as data, not as code. A skeleton class's source
    is actually *local* to the class-making function. This means the class
    statement is executed each time the function is called. The function
    takes the current class instance like a shopping trolley and fills it up
    with more methods and attributes, before parking it in a dictionary
    keyed on the record name.

    Taxpayer = file_dict['TXPYR']
    taxpayer = Taxpayer()
    taxpayer.name = 'John Q Doe'
    taxpayer.date_of_birth = datetime.date(1970, 12, 31)
    taxpayer.reportable_income = 1234.56
    outfile.write(taxpayer.as_fixed_string())


    Cheers,
    John
    John Machin, Jun 2, 2005
    #7
  8. Mac

    Mac Guest

    OK, it's alllll coming together now, thx. Grrr... all this
    misconception, again, due to the evil, evil, EVIL influence of having
    worked a lot before with an inferior language (C/C++)... :)
    Mac, Jun 2, 2005
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Joona I Palaste

    Re: isInstance problem

    Joona I Palaste, Jul 1, 2003, in forum: Java
    Replies:
    4
    Views:
    1,019
    George W. Cherry
    Jul 2, 2003
  2. Ben Jessel

    Re: isInstance problem

    Ben Jessel, Jul 10, 2003, in forum: Java
    Replies:
    0
    Views:
    389
    Ben Jessel
    Jul 10, 2003
  3. Michal Vitecek

    isinstance() bug

    Michal Vitecek, Jan 28, 2004, in forum: Python
    Replies:
    6
    Views:
    435
    Sidharth Kuruvila
    Jan 28, 2004
  4. Michal Vitecek

    Re: isinstance() bug

    Michal Vitecek, Jan 28, 2004, in forum: Python
    Replies:
    7
    Views:
    334
    Rainer Deyke
    Jan 29, 2004
  5. dmitrey

    seems like a bug in isinstance()

    dmitrey, May 6, 2011, in forum: Python
    Replies:
    6
    Views:
    557
    Gregory Ewing
    May 7, 2011
Loading...

Share This Page