Liam Rahav
Liam Rahav

Reputation: 285

Python: Best way to check for same type between two variables

I'm looking to check if two variables are of the same type in python 3.x. What is the most ideal way to do this?

Take the following example:

class A():
    def __init__(self, x):
        self.x = x

class B(A):
    def __init__(self, x):
        x += 5
        super(B, self).__init__(x)

Ideally, I'd like to return True if two variables of type A and B are compared against one another. Here are some potential solutions that don't work:

>>> a = A(5)
>>> b = B(5)
>>>
>>> type(a) is type(b)
False
>>> isinstance(a, type(b))
False
>>> isinstance(b, type(a))
True

The last one isn't ideal because, as seen in the middle example, if the type being checked against is a subclass of the variable's type, False is returned.

The only solution I've tried that can cover all bases here is:

>>> isinstance(a, type(b)) or isinstance(b, type(a))
True

Is there a better way?

Upvotes: 5

Views: 4780

Answers (2)

Mad Physicist
Mad Physicist

Reputation: 114230

Given the stated goal of assessing compatibility between descendants of A, I think you may be over-complicating the issue. When it comes to rich comparison, at least, Python already does this check for you. According to the docs:

If the operands are of different types, and right operand’s type is a direct or indirect subclass of the left operand’s type, the reflected method of the right operand has priority, otherwise the left operand’s method has priority. Virtual subclassing is not considered.

This means that all you have to do is implement operators in A. If any of the descendants need to add functionality, they should do so. Here is an example:

class A():
    def __init__(self, x):
        self.x = x

    def __eq__(self, other):
        if not isinstance(other, __class__):
            return NotImplemented
        return self.x == other.x


class B(A):
    def __init__(self, x, y):
        super(B, self).__init__(x + 5)
        self.y = y

    def __eq__(self, other):
        if isinstance(other, __class__):
             return super().__eq__(other) and self.y == other.y
        return super().__eq__(other)  # Or alternatively, NotImplemented

Upvotes: 0

Andrej Kesely
Andrej Kesely

Reputation: 195418

This program goes through all __bases__ of provided objects and check for common intersection between them (sans object):

class A:
    def __init__(self, x):
        self.x = x

class B(A):
    def __init__(self, x):
        x += 5
        super(B, self).__init__(x)

class C(B):
    def __init__(self, x):
        self.x = x

class D:
    def __init__(self, x):
        self.x = x

class E(C, B):
    def __init__(self, x):
        self.x = x

a = A(5)
b = B(5)
c = C(5)
d = D(5)
e = E(5)

def check(*objs):
    def _all_bases(o):
        for b in o.__bases__:
            if b is not object:
                yield b
            yield from _all_bases(b)
    s = [(i.__class__, *_all_bases(i.__class__)) for i in objs]
    return len(set(*s[:1]).intersection(*s[1:])) > 0

print(check(a, b)) # True
print(check(a, c)) # True
print(check(a, d)) # False
print(check(a, e)) # True
print(check(b, c)) # True
print(check(b, d)) # False
print(check(b, e)) # True
print(check(e, d)) # False
print(check(a, b, c)) # True
print(check(a, b, c, e)) # True
print(check(a, b, c, d)) # False
print(check('string1', 'string2')) # True

Upvotes: 2

Related Questions