ChatGPT解决这个技术问题 Extra ChatGPT

What is the difference between old style and new style classes in Python?

What is the difference between old style and new style classes in Python? When should I use one or the other?


1
10 revs, 7 users 53%

From New-style and classic classes:

Up to Python 2.1, old-style classes were the only flavour available to the user. The concept of (old-style) class is unrelated to the concept of type: if x is an instance of an old-style class, then x.__class__ designates the class of x, but type(x) is always . This reflects the fact that all old-style instances, independently of their class, are implemented with a single built-in type, called instance. New-style classes were introduced in Python 2.2 to unify the concepts of class and type. A new-style class is simply a user-defined type, no more, no less. If x is an instance of a new-style class, then type(x) is typically the same as x.__class__ (although this is not guaranteed – a new-style class instance is permitted to override the value returned for x.__class__). The major motivation for introducing new-style classes is to provide a unified object model with a full meta-model. It also has a number of immediate benefits, like the ability to subclass most built-in types, or the introduction of "descriptors", which enable computed properties. For compatibility reasons, classes are still old-style by default. New-style classes are created by specifying another new-style class (i.e. a type) as a parent class, or the "top-level type" object if no other parent is needed. The behaviour of new-style classes differs from that of old-style classes in a number of important details in addition to what type returns. Some of these changes are fundamental to the new object model, like the way special methods are invoked. Others are "fixes" that could not be implemented before for compatibility concerns, like the method resolution order in case of multiple inheritance. Python 3 only has new-style classes. No matter if you subclass from object or not, classes are new-style in Python 3.


None of these differences sound like compelling reasons to use new-style classes, yet everyone says you should always use the new-style. If I'm using duck typing like I should, I never need to use type(x). If I'm not subclassing a built in type, then there doesn't seem to be any advantage that I can see of the new-style classes. There is a disadvantage, which is the extra typing of (object).
Certain features like super() don't work on old-style classes. Not to mention, as that article says, there are fundamental fixes, like MRO, and special methods, which is more than a good reason to use it.
@User: Old-style classes behave the same in 2.7 as they did in 2.1—and, because few people even remember the quirks, and the documentation no longer discusses most of them, they're even worse. The documentation quote above directly says this: there are "fixes" that could not be implemented on old-style classes. Unless you want to run into quirks that nobody else has dealt with since Python 2.1, and that the documentation no longer even explains, don't use old-style classes.
Here's an example of a quirk you may stumble upon if you use old-style classes in 2.7: bugs.python.org/issue21785
For anyone wondering, a good reason to explicitly inherit from object in Python 3 is that it makes support multiple versions of Python easier.
P
Peter Mortensen

Declaration-wise:

New-style classes inherit from object, or from another new-style class.

class NewStyleClass(object):
    pass

class AnotherNewStyleClass(NewStyleClass):
    pass

Old-style classes don't.

class OldStyleClass():
    pass

Python 3 Note:

Python 3 doesn't support old style classes, so either form noted above results in a new-style class.


if a new-style class inherits from another new-style class, then by extension, it inherits from object.
Is this an incorrect example of old style python class? class AnotherOldStyleClass: pass
@abc I believe that class A: pass and class A(): pass are strictly equivalent. The first means "A doesn't inherit of any parent class" and the second means "A inherits of no parent class" . That's quite similar to not is and is not
Just as a side note, for 3.X, the inheritance of "object" is automatically assumed (meaning that we've got no way to not inherit "object" in 3.X). For the backward compatibility reason, it is not bad to keep "(object)" there though.
If we're going to get technical about inherited classes, this answer should note that you an create another old-style class by inheriting from an old style class. (As written, this answer leaves the user questioning whether you can inherit from an old-style class. You can.)
P
Peter Mortensen

Important behavior changes between old and new style classes

super added

MRO changed (explained below)

descriptors added

new style class objects cannot be raised unless derived from Exception (example below)

__slots__ added

MRO (Method Resolution Order) changed

It was mentioned in other answers, but here goes a concrete example of the difference between classic MRO and C3 MRO (used in new style classes).

The question is the order in which attributes (which include methods and member variables) are searched for in multiple inheritance.

Classic classes do a depth-first search from left to right. Stop on the first match. They do not have the __mro__ attribute.

class C: i = 0
class C1(C): pass
class C2(C): i = 2
class C12(C1, C2): pass
class C21(C2, C1): pass

assert C12().i == 0
assert C21().i == 2

try:
    C12.__mro__
except AttributeError:
    pass
else:
    assert False

New-style classes MRO is more complicated to synthesize in a single English sentence. It is explained in detail here. One of its properties is that a base class is only searched for once all its derived classes have been. They have the __mro__ attribute which shows the search order.

class C(object): i = 0
class C1(C): pass
class C2(C): i = 2
class C12(C1, C2): pass
class C21(C2, C1): pass

assert C12().i == 2
assert C21().i == 2

assert C12.__mro__ == (C12, C1, C2, C, object)
assert C21.__mro__ == (C21, C2, C1, C, object)

New style class objects cannot be raised unless derived from Exception

Around Python 2.5 many classes could be raised, and around Python 2.6 this was removed. On Python 2.7.3:

# OK, old:
class Old: pass
try:
    raise Old()
except Old:
    pass
else:
    assert False

# TypeError, new not derived from `Exception`.
class New(object): pass
try:
    raise New()
except TypeError:
    pass
else:
    assert False

# OK, derived from `Exception`.
class New(Exception): pass
try:
    raise New()
except New:
    pass
else:
    assert False

# `'str'` is a new style object, so you can't raise it:
try:
    raise 'str'
except TypeError:
    pass
else:
    assert False

Nice clear summary, thanks. When you say "difficult to explain in English" I think you are describing a postorder depth-first search as opposed to the old-style class which uses a preorder depth-first search. (preorder means we search ourself before our first child and postorder means we search ourself after our last child).
P
Peter Mortensen

Old style classes are still marginally faster for attribute lookup. This is not usually important, but it may be useful in performance-sensitive Python 2.x code:

In [3]: class A:
   ...:     def __init__(self):
   ...:         self.a = 'hi there'
   ...:

In [4]: class B(object):
   ...:     def __init__(self):
   ...:         self.a = 'hi there'
   ...:

In [6]: aobj = A()
In [7]: bobj = B()

In [8]: %timeit aobj.a
10000000 loops, best of 3: 78.7 ns per loop

In [10]: %timeit bobj.a
10000000 loops, best of 3: 86.9 ns per loop

Interesting that you noticed in practice, I just read that this is because new-style classes, once they have found the attribute in the instance dict, have to do an additional lookup to work out whether it is a description, ie, it has a get method that needs to be invoked to get the value to be returned. Old style classes simple return the found object with no addition computations (but then do not support descriptors). You can read more in this excellent post by Guido python-history.blogspot.co.uk/2010/06/…, specifically the section on slots
doesn’t seem to be true with CPython 2.7.2: %timeit aobj.a 10000000 loops, best of 3: 66.1 ns per loop %timeit bobj.a 10000000 loops, best of 3: 53.9 ns per loop
Still faster for aobj in CPython 2.7.2 on x86-64 Linux for me.
It's probably a bad idea to rely on pure Python code for performance sensitive applications. Nobody says: "I need fast code so I'll use old-style Python classes." Numpy doesn't count as pure Python.
also in IPython 2.7.6, this isn't true. ''''477 ns vs. 456 ns per loop''''
P
Peter Mortensen

Guido has written The Inside Story on New-Style Classes, a really great article about new-style and old-style class in Python.

Python 3 has only new-style class. Even if you write an 'old-style class', it is implicitly derived from object.

New-style classes have some advanced features lacking in old-style classes, such as super, the new C3 mro, some magical methods, etc.


P
Peter Mortensen

Here's a very practical, true/false difference. The only difference between the two versions of the following code is that in the second version Person inherits from object. Other than that, the two versions are identical, but with different results:

Old-style classes class Person(): _names_cache = {} def __init__(self,name): self.name = name def __new__(cls,name): return cls._names_cache.setdefault(name,object.__new__(cls,name)) ahmed1 = Person("Ahmed") ahmed2 = Person("Ahmed") print ahmed1 is ahmed2 print ahmed1 print ahmed2 >>> False <__main__.Person instance at 0xb74acf8c> <__main__.Person instance at 0xb74ac6cc> >>> New-style classes class Person(object): _names_cache = {} def __init__(self,name): self.name = name def __new__(cls,name): return cls._names_cache.setdefault(name,object.__new__(cls,name)) ahmed1 = Person("Ahmed") ahmed2 = Person("Ahmed") print ahmed2 is ahmed1 print ahmed1 print ahmed2 >>> True <__main__.Person object at 0xb74ac66c> <__main__.Person object at 0xb74ac66c> >>>


what does '_names_cache' do? Could you share a reference?
_names_cache is a dictionary that caches (stores for future retrieval) every name you pass to Person.__new__. The setdefault method (defined in any dictionary) takes two arguments : a key and a value. If the key is in the dict, it will return its value. If it's not in the dict, it will set it first to the value passed as second argument and then return it.
The usage is wrong. The idea is to not construct a new object if it already exists, but in your case __new__() is always called, and it always constructs a new object, and then throws it. In this case a if is preferable over .setdefault().
But, I didn't get why is the difference in the output i.e. in old style class the two instances were different thus returned False, but in new style class, both the instances are same. How ? What is the change in new style class, that made the two instances same, which was not in old style class ?
@PabitraPati: It's kind of a cheap demonstration here. __new__ isn't actually a thing for old-style classes, it doesn't get used in instance construction (it's just a random name that looks special, like defining __spam__). So constructing the old-style class only invokes __init__, while the new-style construction invokes __new__ (coalescing to singleton instance by name) to construct, and __init__ to initialize it.
F
Francesco Montesano

New-style classes inherit from object and must be written as such in Python 2.2 onwards (i.e. class Classname(object): instead of class Classname:). The core change is to unify types and classes, and the nice side-effect of this is that it allows you to inherit from built-in types.

Read descrintro for more details.


C
Community

New style classes may use super(Foo, self) where Foo is a class and self is the instance.

super(type[, object-or-type]) Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class. The search order is same as that used by getattr() except that the type itself is skipped.

And in Python 3.x you can simply use super() inside a class without any parameters.