why inheriting from object type - python

In one of my past questions, a answerer suggests me that it is better to inherit from object when the class you want to create is like from scratch, which is no need to inherit from other class.
For example, like what I always do:
class my_class:
"a class inherits from nothing"
def __init__(self):
pass
For what he or she suggested:
class suggested_class(object):
"a class inherits from object type"
def __init__(self):
pass
I am confused with the benefits or disadvantage from both approaches.
Question 1:
So what is your idea, inherit from object type or nothing?

Inheriting from nothing creates an old-style class, which has different behaviour to new-style classes. I don't remember the specifics just now (see here for an explanation), but as a general rule, there's no reason to favour old-style classes, so you should always inherit from object (if nothing else).

Related

How to typehint mixins if the target class for the mixin inherits from a metaclass?

Consider the following class and mixin:
class Target(ClassThatUsesAMetaclass):
def foo(self):
pass
class Mixin:
def __init__(self):
self.foo() # type error: type checker doesn't know Mixin will have
# access to foo once in use.
class Combined(Mixin, Target):
def __init__(self):
Target.__init__(self)
Mixin.__init__(self)
I'm trying to avoid the type checker error in the above scenario. One option is this:
from typing import Protocol
class Fooable(Protocol):
def foo(self): ...
class Mixin(Fooable):
def __init__(self):
self.foo()
Would've worked great, except that Target inherits from a class that uses a metaclass, so Combined can't inherit from both Target and Mixin.
So now I'm trying an alternative, annotating self in Mixin:
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .this import Mixin, Target
Mixin_T = type('Mixin_T', (Mixin, Target), {})
class Mixin:
def __init__(self: Mixin_T):
self.foo() # No longer an error
class Combined(Mixin, Target):
def __init__(self):
Target.__init__(self)
Mixin.__init__(self) # Now this is an error: "Type[Mixin]" is not
# assignable to parameter "self"
# "Mixin" is incompatible with "Mixin_T"
So how am I supposed to win this aside from using # type: ignore?
I found a very simple solution:
if TYPE_CHECKING:
from .this import Target
Mixin_T = Target
else:
Mixin_T = object
class Mixin(Mixin_T):
...
Now all of Target's methods are recognized within Mixin by the type checker, and there's no need to override the type of self into something imcompatible with Mixin. This might be a little awkward if the mixin is destined to all kinds of Target classes, but for my uses this is perfectly acceptable, since my case is a group of mixins extending a very specific target class.
Other than that, there is to little code and some msconceptions above that make this question not answrable at all, apart from providing some clarifications.
To start, are you sure you are "inheriting from a metaclass"?? It does not make sense to inherit a metaclass unless to create another metaclass. Your snippets show you inhriting froma supposed metaclass (with no code given), to create Target and them attempting to use Target as a parent to a normal class (a non-meta class). That makes no sense.
You might just have confused the terms and the hidden InheritFromMetaclass class actually just uses the metaclass, and do not "inherit" from it. Then your problem does not have to do with metaclasses at all.
So, the real visible problem in the snippet is that the static checkr does not "see" a self.foo method in the Mixin class - and guess what? There is no self.foo method in Mixin - the checker is just throwing a cold truth in your face: while Python does allow one to reference methods and attributes that are not available in a class, knowing that it will be used along other classes that do have those attributes, that is no good design and error prone. The kind of bad design static type checking exists to weed-off.
So, what you need is to have a base of Mixin that is an abstract class and have Foo as an abstract method. (Or have Mixin itself be that abstract class).
If - due to usage of other metaclass you can't have Mixin inheit from abc.ABC due to metaclass conflict, you have to either: create a combined metaclass from the metaclass acutually used by InheritsFromMetaclass with ABCMeta , nd use that as the metaclass for Mixin - or just create a stub foo method in Mixin as is (which could raise a NotImplementedError - thus having the same behavior of an abstract method, but without really having to inherit from it.
The important part to have in and is that an methods and attributes you access in code inside a class body have to exist in that class, without depending on attributes that will exist in a subclass of it.
If that does not solve your problem, you need to provide more data - including a reproducible complete example involving your actual metaclass. (and it mgt be solved just by combining the metaclasses as mentioned above)

Is subclassing from object the same as defining type as metaclass?

This is an old-style class:
class OldStyle:
pass
This is a new-style class:
class NewStyle(object):
pass
This is also a new-style class:
class NewStyle2:
__metaclass__ = type
Is there any difference whatsoever between NewStyle and NewStyle2?
I have the impression that the only effect of inheriting from object is actually to define the type metaclass, but I cannot find any confirmation of that, other than that I do not see any difference.
Pretty much yes, there's no difference between NewStyle and NewStyle2. Both are of type type while OldStyle of type classobj.
If you subclass from object, the __class__ of object (meaning type) is going to be used; if you supply a __metaclass__ that is going to get picked up.
If nothing is supplied as __metaclass__ and you don't inherit from object, Py_ClassType is assigned as the metaclass for you.
In all cases, metaclass.__new__ is going to get called. For Py_ClassType.__new__ it follows the semantics defined (I've never examined them, really) and for type.__new__ it makes sure to pack object in the bases of your class.
Of course, a similar effect is achieved by:
cls = type("NewStyle3", (), {})
where a call is immediately made to type; it's just a bigger hassle :-)

What' the meaning of the brackets in the class?

In python, when I read others' code, I meet this situation where a class is defined and after it there is a pair of brackets.
class AStarFoodSearchAgent(SearchAgent):
def __init__():
#....
I don't know what is the meaning of '(SearchAgent)',because what I usually meet and use doesn't seem that.
It indicates that AStarFoodSearchAgent is a subclass of SearchAgent. It's part of a concept called inheritance.
What is inheritance?
Here's an example. You might have a Car class, and a RaceCar class. When implementing the RaceCar class, you may find that it has a lot of behavior that is very similar, or exactly the same, as a Car. In that case, you'd make RaceCar a subclass ofCar`.
class Car(object):
#Car is a subclass of Python's base objeect. The reasons for this, and the reasons why you
#see some classes without (object) or any other class between brackets is beyond the scope
#of this answer.
def get_number_of_wheels(self):
return 4
def get_engine(self):
return CarEngine(fuel=30)
class RaceCar(Car):
#Racecar is a subclass of Car
def get_engine(self):
return RaceCarEngine(fuel=50)
my_car = Car() #create a new Car instance
desired_car = RaceCar() #create a new RaceCar instance.
my_car.get_engine() #returns a CarEngine instance
desired_car.get_engine() #returns a RaceCarEngine instance
my_car.get_number_of_wheels() #returns 4.
desired_car.get_number_of_wheels() # also returns 4! WHAT?!?!?!
We didn't define get_number_of_wheels on RaceCar, and still, it exists, and returns 4 when called. That's because RaceCar has inherited get_number_of_wheels from Car. Inheritance is a very nice way to reuse functionality from other classes, and override or add only the functionality that needs to be different.
Your Example
In your example, AStarFoodSearchAgent is a subclass of SearchAgent. This means that it inherits some functionality from SearchAgemt. For instance, SearchAgent might implement a method called get_neighbouring_locations(), that returns all the locations reachable from the agent's current location. It's not necessary to reimplement this, just to make an A* agent.
What's also nice about this, is that you can use this when you expect a certain type of object, but you don't care about the implementation. For instance, a find_food function may expect a SearchAgent object, but it wouldn't care about how it searches. You might have an AStarFoodSearchAgent and a DijkstraFoodSearchAgent. As long as both of them inherit from SearchAgent, find_food can use ìsinstanceto check that the searcher it expects behaves like aSearchAgent. Thefind_food`function might look like this:
def find_food(searcher):
if not isinstance(searcher, SearchAgent):
raise ValueError("searcher must be a SearchAgent instance.")
food = searcher.find_food()
if not food:
raise Exception("No, food. We'll starve!")
if food.type == "sprouts":
raise Exception("Sprouts, Yuk!)
return food
Old/Classic Style Classes
Upto Python 2.1, old-style classes were the only type that existed. Unless they were a subclass of some other class, they wouldn't have any parenthesis after the class name.
class OldStyleCar:
...
New style classes always inherit from something. If you don't want to inherit from any other class, you inherit from object.
class NewStyleCar(object):
...
New style classes unify python types and classes. For instance, the type of 1, which you can obtain by calling type(1) is int, but the type of OldStyleClass() is instance, with new style classes, type(NewStyleCar) is Car.
SearchAgent is the superclass of the class AStarFoodSearchAgent. This basically means that an AStarFoodSearchAgent is a special kind of SearchAgent.
It means that class AStarFoodSearchAgent extends SearchAgent.
Check section 9.5 here
https://docs.python.org/2/tutorial/classes.html
This is inheritance in python, just like in any other OO language
https://docs.python.org/2/tutorial/classes.html#inheritance
It means that SearchAgent is a base class of AStarFoodSearchAgent. In other word, AStarFoodSearchAgent inherits from SearchAgent class.
See Inheritance - Python tutorial.

Python: how to define __new__ for MutableSequence derived class?

I'm trying to inherit from collections.MutableSequence:
class myList(MutableSequence):
def __new__(cls,*kwargs):
obj=MutableSequence.__new__(cls,*kwargs)
return obj
I get the error:
>>>a=myList()
>>>TypeError: Can't instantiate abstract class ContactMapList with abstract methods __delitem__, __getitem__, __len__, __setitem__, insert
I did not get any errors when I was deriving my class directly from the built-in list class in the same fashion:
class myList(list):
def __new__(cls,*kwargs):
obj=list.__new__(cls,*kwargs)
return obj
I've tried to search for ways to define a constructor for myList(MutableSequence) but with not luck :(
An abstract base class is a contract. It's a list of promises that any class deriving from it must keep.
The promises each of the collections classes make are listed in the docs. For example, anything inheriting from MutableSequence will have __getitem__, __setitem__, __delitem__ and some more.
You can't instantiate a contract!
When you make a class that inherits from an abstract base class, you don't inherit the methods. (Well... see note below.) You inherit a bunch of "abstract methods" which are just placeholders. To fulfill the promise you made by inheriting from the ABC, you have to give definitions for these methods. They can do whatever you like, but they must be there.
Actually, the methods you inherit can be real methods and you can delegate to them with super(). This lets you specify default behaviours. But that's not often used.
You can't instantiate an abstract class; you can instantiate a non-abstract class. That answers your question.
The unanswered question is: why are you defining __new__ in the first place?

Why do Python classes inherit object?

Why does the following class declaration inherit from object?
class MyClass(object):
...
Is there any reason for a class declaration to inherit from object?
In Python 3, apart from compatibility between Python 2 and 3, no reason. In Python 2, many reasons.
Python 2.x story:
In Python 2.x (from 2.2 onwards) there's two styles of classes depending on the presence or absence of object as a base-class:
"classic" style classes: they don't have object as a base class:
>>> class ClassicSpam: # no base class
... pass
>>> ClassicSpam.__bases__
()
"new" style classes: they have, directly or indirectly (e.g inherit from a built-in type), object as a base class:
>>> class NewSpam(object): # directly inherit from object
... pass
>>> NewSpam.__bases__
(<type 'object'>,)
>>> class IntSpam(int): # indirectly inherit from object...
... pass
>>> IntSpam.__bases__
(<type 'int'>,)
>>> IntSpam.__bases__[0].__bases__ # ... because int inherits from object
(<type 'object'>,)
Without a doubt, when writing a class you'll always want to go for new-style classes. The perks of doing so are numerous, to list some of them:
Support for descriptors. Specifically, the following constructs are made possible with descriptors:
classmethod: A method that receives the class as an implicit argument instead of the instance.
staticmethod: A method that does not receive the implicit argument self as a first argument.
properties with property: Create functions for managing the getting, setting and deleting of an attribute.
__slots__: Saves memory consumptions of a class and also results in faster attribute access. Of course, it does impose limitations.
The __new__ static method: lets you customize how new class instances are created.
Method resolution order (MRO): in what order the base classes of a class will be searched when trying to resolve which method to call.
Related to MRO, super calls. Also see, super() considered super.
If you don't inherit from object, forget these. A more exhaustive description of the previous bullet points along with other perks of "new" style classes can be found here.
One of the downsides of new-style classes is that the class itself is more memory demanding. Unless you're creating many class objects, though, I doubt this would be an issue and it's a negative sinking in a sea of positives.
Python 3.x story:
In Python 3, things are simplified. Only new-style classes exist (referred to plainly as classes) so, the only difference in adding object is requiring you to type in 8 more characters. This:
class ClassicSpam:
pass
is completely equivalent (apart from their name :-) to this:
class NewSpam(object):
pass
and to this:
class Spam():
pass
All have object in their __bases__.
>>> [object in cls.__bases__ for cls in {Spam, NewSpam, ClassicSpam}]
[True, True, True]
So, what should you do?
In Python 2: always inherit from object explicitly. Get the perks.
In Python 3: inherit from object if you are writing code that tries to be Python agnostic, that is, it needs to work both in Python 2 and in Python 3. Otherwise don't, it really makes no difference since Python inserts it for you behind the scenes.
Python 3
class MyClass(object): = New-style class
class MyClass: = New-style class (implicitly inherits from object)
Python 2
class MyClass(object): = New-style class
class MyClass: = OLD-STYLE CLASS
Explanation:
When defining base classes in Python 3.x, you’re allowed to drop the object from the definition. However, this can open the door for a seriously hard to track problem…
Python introduced new-style classes back in Python 2.2, and by now old-style classes are really quite old. Discussion of old-style classes is buried in the 2.x docs, and non-existent in the 3.x docs.
The problem is, the syntax for old-style classes in Python 2.x is the same as the alternative syntax for new-style classes in Python 3.x. Python 2.x is still very widely used (e.g. GAE, Web2Py), and any code (or coder) unwittingly bringing 3.x-style class definitions into 2.x code is going to end up with some seriously outdated base objects. And because old-style classes aren’t on anyone’s radar, they likely won’t know what hit them.
So just spell it out the long way and save some 2.x developer the tears.
Yes, this is a 'new style' object. It was a feature introduced in python2.2.
New style objects have a different object model to classic objects, and some things won't work properly with old style objects, for instance, super(), #property and descriptors. See this article for a good description of what a new style class is.
SO link for a description of the differences: What is the difference between old style and new style classes in Python?
History from Learn Python the Hard Way:
Python's original rendition of a class was broken in many serious
ways. By the time this fault was recognized it was already too late,
and they had to support it. In order to fix the problem, they needed
some "new class" style so that the "old classes" would keep working
but you can use the new more correct version.
They decided that they would use a word "object", lowercased, to be
the "class" that you inherit from to make a class. It is confusing,
but a class inherits from the class named "object" to make a class but
it's not an object really its a class, but don't forget to inherit
from object.
Also just to let you know what the difference between new-style classes and old-style classes is, it's that new-style classes always inherit from object class or from another class that inherited from object:
class NewStyle(object):
pass
Another example is:
class AnotherExampleOfNewStyle(NewStyle):
pass
While an old-style base class looks like this:
class OldStyle():
pass
And an old-style child class looks like this:
class OldStyleSubclass(OldStyle):
pass
You can see that an Old Style base class doesn't inherit from any other class, however, Old Style classes can, of course, inherit from one another. Inheriting from object guarantees that certain functionality is available in every Python class. New style classes were introduced in Python 2.2
Yes, it's historical. Without it, it creates an old-style class.
If you use type() on an old-style object, you just get "instance". On a new-style object you get its class.
The syntax of the class creation statement:
class <ClassName>(superclass):
#code follows
In the absence of any other superclasses that you specifically want to inherit from, the superclass should always be object, which is the root of all classes in Python.
object is technically the root of "new-style" classes in Python. But the new-style classes today are as good as being the only style of classes.
But, if you don't explicitly use the word object when creating classes, then as others mentioned, Python 3.x implicitly inherits from the object superclass. But I guess explicit is always better than implicit (hell)
Reference

Categories