views:

83

answers:

3

Hi!

I have a method that accepts a parameter that can be of several types, and has to do one thing or other depending on the type, but if I check the type of said parameter, I don't get the 'real' type, I always get <type 'instance'>, and that is messing up with my comparisons.

I have something like:

from classes import Class1
from classes import Class2
# Both classes are declared in the same file.
# I don't know if that can be a problem         #
# ... #
def foo(parameter)
    if (type(parameter) == type(Class1()):
    # ... #
    elif (type(parameter) == type(Class2()):
    # ... #

And as type(parameter) returns <type 'instance'> and type(Class1()) is <type 'instance'> as well, it turns out that even if the parameter is an instance of Class2, it is going into the first comparison...

By the way, str(parameter.__class__) properly shows classes.Class1 . I guess I could always use that, but I would like to understand what's going on... I have made tenths of comparisons like this and all them worked properly...

Thank you!! :)

+3  A: 

Old-style classes do that. Derive your classes from object in their definitions.

Ignacio Vazquez-Abrams
Wooo... That works! :)Now I am going to try to figure out "why"... :D
BorrajaX
It has to do with the fact that new-style classes have more metadata, including the stuff that `type()` touches.
Ignacio Vazquez-Abrams
Thx again, I was reading this: http://www.cafepy.com/article/python_types_and_objects/python_types_and_objects.htmlAnd I kind of (kind of) figured that something like that had to be reason
BorrajaX
+6  A: 

you should really use isinstance:

In [26]: def foo(param):
   ....:     print type(param)
   ....:     print isinstance(param, Class1)
   ....:

In [27]: foo(x)
<type 'instance'>
True

Type is better for built-in types.

Wayne Werner
Woo! Thank you for the really fast answer...I considered that, but then I read this:http://www.canonical.org/~kragen/isinstance/And I chickened out... :SAlthough I must say that, on the other hand, 'isinstance' my be suitable for my needs...
BorrajaX
`isinstance` is certainly usually preferable to direct `type()` checking, and definitely the form of type checking you're using where you actually instantiate a `Class1()` for each check is undesirable. However what that article is saying is that in many cases checking for types *at all* (whichever method you use for that) is a sign of bad coding practice. IMO the article is unhelpfully religious and there are still valid uses for type-checking, but if you are doing ‘tens’ of checks against your own defined types that does sound like a potential ‘not object-oriented enough’ code-smell.
bobince
You can clearly see in the Ops code why doing type-checking means that you're *not* doing OOP: Whatever code would come after `if isinstace( obj, Cls)` should be in a method of Cls. It might sound religious, but if you want to use OOP (not that you have to) then you have to get *at least that* right.
THC4k
Well... What I wrote was a simple example to make it as clear as possible, but the thing is that I can actually receive (as a parameter) an object of Class1, or a list of objects of Class1 (and then walk the list and recursively call the method so it would behave as if one object was received) or an object that is not a Class1, but has values that allow me to initialize a Class1 object and call the method again with that Class1 instance, etc. That's why I need to check the type
BorrajaX
@BorrajaX that's not better, instead this method and all similar should always take and return lists of Class1. Sometimes the lists have just one element, so they might look kind of pointless, but *the interface stays the same* and that's pretty much the point of OOP.
THC4k
But can I have two methods with the same name in the same class in Python? Since it doesn't have clearly defined types, my understanding is that I can't do as in Java:class foo{ void method(int a){} void method(String b){} }and the only way I found to get a similar behavior is to do the check the type "by hand" inside the method
BorrajaX
+4  A: 

The fact that type(x) returns the same type object for all instances x of legacy, aka old-style, classes, is one of many infuriating defects of those kinds of classes -- unfortunately they have to stay (and be the default for a class without base) in Python 2.* for reasons of backwards compatibility.

Nevertheless, don't use old-style classes unless you're forced to maintain a bunch of old, legacy code (without a good test suite to give you the confidence to try and switch kind o classes). When a class has no "natural" bases, subclass it from object rather than from nothing. Alternatively, your module, at the top, can set

__metaclass__ = type

which changes the default from the crufty, legacy old-style classes, to the shiny bright new-style ones -- while explicitly inheriting from object is usually preferred ("explicit is better than implicit"), the module-global setting of __metaclass__ may feel "less invasive" to existing old modules where you're switching from old to new classes, so it's offered as a possibility.

Alex Martelli
Thank you! Good answer too! I had already used the one Ignacio proposed. but well... in the end, if my understanding is correct, they are pretty similar, right?
BorrajaX
@Borrajax, the end result of explicitly inheriting from `object` vs not inheriting and having a module-global `__metaclass__=type` is exactly the same - very different styles, undistinguishable results.
Alex Martelli