Identifying that a variable is a new-style class in Python?
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 foun开发者_Go百科d 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?
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 ')
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.
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?
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.
精彩评论