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?

Community
  • 1
  • 1
Dave Johansen
  • 889
  • 1
  • 7
  • 23
  • 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. – Dave Johansen Apr 16 '10 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: http://catb.org/~esr/faqs/smart-questions.html#goal – tzot Apr 16 '10 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. – tzot Apr 19 '10 at 23:32
  • I apologize, because I meant an instance and not the class itself. – Dave Johansen Apr 20 '10 at 18:58

4 Answers4

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 ')
eyquem
  • 26,771
  • 7
  • 38
  • 46
Daniel Stutzbach
  • 74,198
  • 17
  • 88
  • 77
  • 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__') – Dave Johansen Apr 16 '10 at 17:37
  • 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. – Dave Johansen Apr 16 '10 at 19:22
  • 1
    Daniel, the first version of `is_new_style` needs improvement: it won't consider as new-style this class: `class NewStyle(object): __slots__= "attribute",`. – tzot Apr 19 '10 at 23:35
  • 1
    @ΤΖΩΤΖΙΟΥ, thanks for catching that. I've updated the code to handle that case. – Daniel Stutzbach Apr 20 '10 at 00:55
  • 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. – Filipe Correia Feb 08 '12 at 10:48
  • I definitely wouldn't trust the last one. Any class that overrides `__str__` will fail that test. – Taywee Jun 01 '17 at 21:38
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.

tzot
  • 92,761
  • 29
  • 141
  • 204
  • The problem with that is that everything (even old-style classes and basic types like int) will return true. – Dave Johansen Apr 16 '10 at 20:06
  • The problems with your comment are: old-style classes return False, and `int` (being a type, by definition) *is* a new-style class. – tzot Apr 16 '10 at 21:12
  • The issue is that int doesn't have a \__dict\__ attribute and therefore violates one of the "rules" of new-style classes. – Dave Johansen Apr 19 '10 at 17:57
  • @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. – tzot Apr 19 '10 at 23:25
  • 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. – Eric O. Lebigot Mar 14 '12 at 09:49
  • @EOL: thanks for the pointer; I corrected the code. I'll look into it why the previous version produced the incorrect result. – tzot Mar 14 '12 at 18:36
  • @tzot: Thanks! Downvote removed. It all boils down to the fact that the type of an object and its class are two different things, with old-style classes. Reference: http://stackoverflow.com/questions/9699591/instance-is-an-object-but-class-is-not-a-subclass-of-object-how-is-this-po/9699961#9699961 – Eric O. Lebigot Mar 15 '12 at 12:24
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?

0 _
  • 10,524
  • 11
  • 77
  • 109
rbp
  • 43,594
  • 3
  • 38
  • 31
  • That returns True for int as well – Daniel Stutzbach Apr 16 '10 at 16:59
  • 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. – Dave Johansen Apr 16 '10 at 17:04
  • That's correct, an int is an instance of a new-style class. That's the point of the unification. – Nicholas Riley Apr 16 '10 at 17:05
  • But an int doesn't have a \__dict\__ attribute, which I assumed all classes had. – Dave Johansen Apr 16 '10 at 17:08
  • @Dave Johansen: you might consider correcting your assumptions. – tzot Apr 16 '10 at 21:30
  • The issue is that the documentation (and a few of the responses here) states that the \__dict\__ attribute is part of new-style classes, so I don't understand what assumption should be changed. – Dave Johansen Apr 19 '10 at 17:58
  • @Dave: I answered with an example of a new-style class without a `__dict__` attribute in both the accepted and my answer. If the documentation states that a `__dict__` attribute is a sine-qua-non for a new-style class, then it needs to be fixed. Please give me the related URL in the documentation, I'll supply the patch. – tzot Apr 19 '10 at 23:38
  • The first paragraph in the section about classes at http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy states (or at least strongly implies) that the dictionary exists. Yes, it does state that there are other methods for accessing the variables, but it doesn't state that the \__dict\__ does not exist until you go down to the slots section of the same page. Basically, someone like me who's not familiar with the inner workings on new-style classes would not be able to gather all the necessary information since there's no link to slots in the Classes section. – Dave Johansen Apr 20 '10 at 19:09
-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.

Ants Aasma
  • 53,288
  • 15
  • 90
  • 97
  • `types.ClassType` is [Python 2-specific](https://stackoverflow.com/questions/529240/what-happened-to-types-classtype-in-python-3), rendering this answer obsolete. Moreover, there _is_ a [reliable means](https://stackoverflow.com/a/12237216/2809027) of distinguishing builtin from user-defined classes under CPython. – Cecil Curry Dec 07 '16 at 06:07