Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python new-style classes and __subclasses__ function

Tags:

python

Can somebody explain to me why this works (in Python 2.5) :

class Foo(object):
    pass

class Bar(Foo):
    pass

print(Foo.__subclasses__())

but this doesn't :

class Foo():
    pass

class Bar(Foo):
    pass

print(Foo.__subclasses__())

The latter returns "AttributeError: class Foo has no attribute '__subclasses__'" but i'm not sure why. I know this is related to old-style vs. new-style classes but i'm not clear on why that would make this functionality unavailable.

Clarification: I'm looking to understand WHY __subclasses__() isn't available in old-style, i get that the method doesn't exist for old-style classes but I don't get what it is about new-style that makes these new functions possible.

like image 540
Fraser Graham Avatar asked May 20 '10 19:05

Fraser Graham


People also ask

What are new style classes in Python?

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.

What is __ new __ in Python?

In the base class object , the __new__ method is defined as a static method which requires to pass a parameter cls . cls represents the class that is needed to be instantiated, and the compiler automatically provides this parameter at the time of instantiation.

What is class and subclass in Python?

The class from which a class inherits is called the parent or superclass. A class which inherits from a superclass is called a subclass, also called heir class or child class. Superclasses are sometimes called ancestors as well. There exists a hierarchical relationship between classes.

Are all Python classes subclasses of object?

All the Python built-ins are subclasses of object and I come across many user-defined classes which are too.

What is __ module __ in Python?

The __module__ property is intended for retrieving the module where the function was defined, either to read the source code or sometimes to re-import it in a script.


2 Answers

class Foo(object):
    pass

The class above is a "new-style" class because it inherits from the object class. New-style classes provide a lot of extra framework that "old-style" classes do not have. One particular attribute of a new-style class is to be able to determine the subclasses of the class with the __subclasses__ method.

There is some good discussion about new-style classes and the __subclasses__ method which use to be completely undocumented. ( Here is an unofficial explanation from Tim Peters, though. )

"Each new-style class keeps a list of weak references to its immediate subclasses. This method returns a list of all those references still alive."

So to answer your question, the __subclasses__ functionality is not available because in your second example:

class Foo():
    pass

The old-style class Foo does not inherit from object (so it's not a new-style class) and there for does not inherit the __subclasses__ method.

Note, if you don't understand why an old-style class does not have the __subclasses__ method you could always fire up a python interpreter and do some inspection with dir

>>> class Foo(object):
...     pass
...
>>> dir(Foo.__class__)
['__abstractmethods__', '__base__', '__bases__', '__basicsize__', '__call__', '__class__', '__delattr__', '__dict__', '__dictoffset__', '__doc__', '__
eq__', '__flags__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__instancecheck__', '__itemsize__', '__le__', '__lt
__', '__module__', '__mro__', '__name__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__s
ubclasscheck__', '__subclasses__', '__subclasshook__', '__weakrefoffset__', 'mro']
>>> class Bar():
...     pass
...
>>> dir(Bar.__class__)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: class Bar has no attribute '__class__'
>>> dir(Bar)
['__doc__', '__module__']
>>> dir(Foo)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '
__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
like image 100
manifest Avatar answered Oct 22 '22 06:10

manifest


Adding to the answer above.

Python 3.0 has a separate implementation of classes. Subclassing is a feature in Python 3.0 and not Python 2.0.

So if you install python 3.x and do

class Foo():
    pass

class Bar(Foo):
    pass

print(Foo.__subclasses__())

It will not give any attribute error.

Now, the same functionality is extended to Python 2.0 classes, by inheriting "object" base class in each class definition.

So if you do in Python 2.x:

class Foo(object):
    pass

class Bar(Foo):
    pass

print(Foo.__subclasses__())

Since you are inheriting object base class in new class Foo, the 3.x style class is inherited and no attribute error comes.

But when you do in Python 2.x

class Foo():
    pass

class Bar(Foo):
    pass

print(Foo.__subclasses__())

it means, new style classes have not been inherited and since subclass is a part of new style classes, it will throw an attribute error.

So remember, if you want to extend any Python 3.x class functionality to Python 2.x, you need to inherit object class in your class definition.

Hope this helps.

like image 38
Priyesh Avatar answered Oct 22 '22 04:10

Priyesh