14

I'm using Python 2.x and I'm wondering if there's a way to tell if a variable is a new-style class? I know that if it's an old-style class that I can do the following to find out.

import types

class oldclass:
  pass

def test():
  o = oldclass()
  if type(o) is types.InstanceType:
    print 'Is old-style'
  else:
    print 'Is NOT old-style'

But I haven't been able to find anything that works for new-style classes. I found this question, but the proposed solutions don't seem to work as expected, because simple values as are identified as classes.

import inspect

def newclass(object):
  pass

def test():
  n = newclass()
  if inspect.isclass(n):
    print 'Is class'
  else:
    print 'Is NOT class'
  if inspect.isclass(type(n)):
    print 'Is class'
  else:
    print 'Is NOT class'
  if inspect.isclass(type(1)):
    print 'Is class'
  else:
    print 'Is NOT class'
  if isinstance(n, object):
    print 'Is class'
  else:
    print 'Is NOT class'
  if isinstance(1, object):
    print 'Is class'
  else:
    print 'Is NOT class'

So is there anyway to do something like this? Or is everything in Python just a class and there's no way to get around that?

4
  • 1
    I was working on a class that serialized its children recursively and I wanted to be able to tell if the child was a class and handle it by also serializing its children recursively. Commented Apr 16, 2010 at 17:07
  • your previous comment seems like it should be the actual question, and the original question looks like a (possibly erroneous) step you took towards the answer to your problem. Check this: catb.org/~esr/faqs/smart-questions.html#goal Commented Apr 16, 2010 at 21:35
  • You might want to give us an example of a class having another class as a child; also, make sure you're not talking about instances. It is much more common for an instance to have children instances that you want to serialize. Commented Apr 19, 2010 at 23:32
  • I apologize, because I meant an instance and not the class itself. Commented Apr 20, 2010 at 18:58

4 Answers 4

8

I think what you are asking is: "Can I test if a class was defined in Python code as a new-style class?". Technically simple types such as int are new-style classes, but it is still possible to distinguish classes written in Python from the built-in types.

Here's something that works, although it's a bit of a hack:

def is_new_style(cls):
    return hasattr(cls, '__class__') \
           and \
           ('__dict__' in dir(cls) or hasattr(cls, '__slots__'))


class new_style(object):
    pass

class old_style():
    pass

print is_new_style(int)
print is_new_style(new_style)
print is_new_style(old_style)

Output from Python 2.6:

False
True
False

Here's a different way to do it:

def is_new_style(cls):
    return str(cls).startswith('<class ')
Sign up to request clarification or add additional context in comments.

6 Comments

Also, based on the response from ~unutbu, the can be used to identify old-style classes: def is_old_style(cls): return not hasattr(cls, 'init')
Sorry for the misinformation, but the above is_old_style(cls) doesn't work if the old-style class has an explicitly defined _init_ method, but type(cls) is types.InstanceType does work.
Daniel, the first version of is_new_style needs improvement: it won't consider as new-style this class: class NewStyle(object): __slots__= "attribute",.
@ΤΖΩΤΖΙΟΥ, thanks for catching that. I've updated the code to handle that case.
Both the ways look hacky, but I couldn't find a better way to do this either. Between the two, I'm sold to the simplicity of the second one.
|
2

I believe this suffices:

def is_new_style_class(klass):
    return issubclass(klass, object)

def is_new_style_class_instance(instance):
    return issubclass(instance.__class__, object)

Typically, you only need the is_new_style_class function for your purposes. Everything not a class will throw a TypeError, so you might want to update it to:

def is_new_style_class(klass):
    try:
        return issubclass(klass, object)
    except TypeError:
        return False

Examples:

>>> class New(object): pass
... 
>>> is_new_style_class(New)
True
>>> class Old: pass
... 
>>> is_new_style_class(Old)
False
>>> is_new_style_class(1)
False
>>> is_new_style_class(int)
True

int, being a type, is by definition a new-style class (see Unifying types and classes in Python 2.2 ), or —if you prefer— new-style classes are by definition types.

7 Comments

The problem with that is that everything (even old-style classes and basic types like int) will return true.
The problems with your comment are: old-style classes return False, and int (being a type, by definition) is a new-style class.
The issue is that int doesn't have a _dict_ attribute and therefore violates one of the "rules" of new-style classes.
@Dave: but is it a rule or a misconception of yours? This is a new-style class: class NewStyle(object): __slots__= "attribute", and its instances do not have a __dict__ attribute.
The instance test is not correct: being an object does not mean having a new-style class. Example: the code for the standard HTMLParser module shows that HTMLParser.HTMLParser is an old-style class; however, is_new_style_class_instance(HTMLParser()) incorrectly returns True.
|
1

It's not that "everything is a class": what you're bumping into is that "everything is an object" (that is, every (new-style) thing descends from "object").

But new-style classes are a "type" themselves (actually, the were introduced to bring classes and types together). So you can try checking for

import types

type(o) == types.TypeType

Does that solve your problem?

8 Comments

That returns True for int as well
No, that expression for both old-style and new-style classes evaluates to false. However, this does evaluate to true for new-style classes but not old-style classes: type(n.__class__) == types.TypeType But, then it also evaluates to true for basic types like 'int', so it's not exactly what I was looking for.
That's correct, an int is an instance of a new-style class. That's the point of the unification.
But an int doesn't have a _dict_ attribute, which I assumed all classes had.
@Dave Johansen: you might consider correcting your assumptions.
|
-1

Checking for old-style classes is really easy. Just check type(cls) is types.ClassType. Checking for new-style classes is also easy, isinstance(cls, type). Note that the built-in types are also new-style classes.

There seems to be no trivial way to distinguish built-ins from classes written in Python. New-style classes with __slots__ also don't have __dict__, just like int or str. Checking if str(cls) matches the expected pattern fails if the classes metaclass overrides the __str__ method. Some other ways that also don't work:

  • cls.__module__ == '__builtin__' (you can reassign __module__ on classes)
  • not any(value is cls for value in vars(__builtins__).values()) (you can add stuff to the __builtin__ module).

The fact that unification of builtin and userdefined types is so good that distinguishing them is non-trivial problem should imply to you the underlying point. You really shouldn't have to distinguish between them. It doesn't matter what the object is if it implements the expected protocol.

1 Comment

types.ClassType is Python 2-specific, rendering this answer obsolete. Moreover, there is a reliable means of distinguishing builtin from user-defined classes under CPython.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.