views:

148

answers:

5

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?

A: 

Yes, everything is an object, which means everything is based on a class.

Matthew Flaschen
A: 

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

type(o) == types.TypeType

Does that solve your problem?

rbp
That returns True for int as well
Daniel Stutzbach
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.TypeTypeBut, then it also evaluates to true for basic types like 'int', so it's not exactly what I was looking for.
Dave Johansen
That's correct, an int is an instance of a new-style class. That's the point of the unification.
Nicholas Riley
But an int doesn't have a \__dict\__ attribute, which I assumed all classes had.
Dave Johansen
@Dave Johansen: you might consider correcting your assumptions.
ΤΖΩΤΖΙΟΥ
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
@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.
ΤΖΩΤΖΙΟΥ
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
+3  A: 

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 ')
Daniel Stutzbach
That did the trick. Thanks.
Dave Johansen
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
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
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.
Daniel Stutzbach
A: 

I believe this suffices:

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

def is_new_style_class_instance(instance):
    return isinstance(instance, 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.

ΤΖΩΤΖΙΟΥ
The problem with that is that everything (even old-style classes and basic types like int) will return true.
Dave Johansen
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 Johansen
@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.
ΤΖΩΤΖΙΟΥ
You are correct, it was definitely a misconception of mine.
Dave Johansen
A: 

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