7

Consider you have two python files as defined below. Say one is a general package (class2), and the other one does specific overrides and serves as the executable (class1).

class1.py:

#!/usr/bin/python
class Test(object):
    pass

class Verificator():
    def check(self, myObject):
        if not isinstance( myObject, Test ):
            print "%s is no instance of %s" % (type(myObject),Test)
        else:
            print "OK!"

if __name__ == '__main__':
    from class2 import getTest

    v = Verificator()
    t = Test()
    v.check(t)
    s = getTest()
    v.check(s)

class2.py:

from class1 import Test
def getTest():
    return Test()

What happens is that the first check is OK, where the second fails. The reason is that t is __main__.Test whereas s is class1.Test and v.check() checks for __main__.Test, but at the end of the day it is the same class, right?

Is there a way to write v.check() such that it also accepts class1.Test objects, or any other way to solve this?

glglgl
  • 89,107
  • 13
  • 149
  • 217
Lars Hanke
  • 614
  • 5
  • 16

3 Answers3

8

If you plan to import class1.py from elsewhere, move the top-level code (if __name__ == '__main__': ... to a separate file altogether. That way both the main file and class2 work with the same class1.Test class.

Doing almost anything else opens a can of worms. While you can work around the immediate problem by switching isinstance to type(myObject).__name__ == ..., the fact remains that your Python process contains two Test classes where there should be only one. The otherwise indistinguishable classes know nothing of each other and fail each other's issubclass tests. This practically guarantees hard-to-diagnose bugs further down the line.

EDIT
Another option is to explicitly import the classes from class1 when executing as main, as in your answer. It would be advisable to go one step further and make sure that the classes aren't defined in double. For example, you can move the if __name__ == '__main__' block to the beginning of the file, and end it with sys.exit(0):

if __name__ == '__main__':
    import class1, class2
    ... use only the public API with module prefixes ...
    sys.exit(0)

# the rest of the module follows here
user4815162342
  • 141,790
  • 18
  • 296
  • 355
1

Thanks for the hints, which finally helped me to experiment in the right direction. The solution I just found in this toy model, is to fix the namespace issue using an import. In order to rule out the issue user4815162342 pointed out, I added another class to class1. The following code for class1.py appears to do, what I want:

#!/usr/bin/python
class Test(object):
    pass

class Toast(object):
    pass

class Verificator():
    def check(self, myObject):
        if not isinstance( myObject, Test ):
            print "NOPE: %s is no instance of %s" % (type(myObject),Test)
        else:
            print "OK: %s is instance of %s" % (type(myObject),Test)

if __name__ == '__main__':
    from class2 import getTest
    from class1 import Test, Toast

    v = Verificator()
    t = Test()
    v.check(t)
    t = getTest()
    v.check(t)
    t = Toast()
    v.check(t)
Lars Hanke
  • 614
  • 5
  • 16
-2

You could use the type() function (you'll find this invocation fairly common, in fact!):

#!/usr/bin/python
class Test(object):
    pass

class Verificator():
    def check(self, myObject):
        if not isinstance( type(myObject), type(Test) ):
            print "%s is no instance of %s" % (type(myObject),Test)
        else:
            print "OK!"

if __name__ == '__main__':
    from class2 import getTest

    v = Verificator()
    t = Test()
    v.check(t)
    s = getTest()
    v.check(s)

The perhaps worse solution:

if not isinstance( myObject.__class__, Test.__class__ ):  # Use __class__ here.

They're of course equivalent, but it's considered bad form to need to use double-underscore members unless you desperately need to! It's worth knowing of their existence, though, hence why I've included this one in my answer.

Note that this happens, to the best of my knowledge, because when you run python class1.py, class1.py would have no module. As such, python places everything into the __main__ module for you. This isn't the case when you import it from any other script, so seeing something as part of the __main__ module is actually the special case!

Ben Stott
  • 2,218
  • 17
  • 23
  • 1
    I'd use `type(myObject)` instead. It comes down to the same thing but is using the official API, instead of reaching into the hook attribute directly. – Martijn Pieters Mar 01 '13 at 14:36
  • 5
    Checking `isinstance(type(myObject), type(Test))` only tests that `myObject`'s type is created by `Test`'s metaclass. This will be true for any two new-style classes created by the default metaclass, and is almost certainly not what the OP was asking for. – user4815162342 Mar 01 '13 at 14:46
  • I triggered only on `__class__` there. Looking at it some more, I see that you only need test `isinstance(myObject, Test)`, no need to look up the type *at all*. – Martijn Pieters Mar 01 '13 at 14:48
  • 1
    This answer is wrong (just like @user4815162342 already wrote), it is a known anti-pattern, see: [Using type() to compare types](http://docs.quantifiedcode.com/python-anti-patterns/readability/do_not_compare_types_use_isinstance.html) – Murmel Nov 24 '15 at 21:51