Why do Python metaclasses prohibit passing paramters to the __init__ method? - python

This question is related to this question about PyTables metaclasses. I was trying to subclass the IsDescription metaclass in PyTables, to define the shape of the Column by a variable:
import tables
class MyClass(tables.IsDescription):
def __init__(self, param):
var1 = tables.Float64Col(shape=(param))
MyClass1 = MyClass(12)
This throws the error: TypeError: object.__new__() takes no parameters. Using self.var1 = ... gives the same error.
In this SO question the same error is reported, and the reason is attributed to the fact that IsDescription is a metaclass.
My question (which is not answered at the linked question, and I haven't been able to find anything by Googling) is: why do metaclasses prohibit this functionality? Is it specific to this metaclass, or generic for all metaclasses?

This is generic to all metaclasses.
In fact a metaclass is instanciated when a class is being created and the parameters passed are always the same, and determine by python.
Those are :
The name of the class to create
The list of inherited classes for this class. It's a tuple of class reference. By default, for new-style class it's object
The dictionary of all fields of the class to be created
You cannot pass you parameters here as this call is done automatically by the python interpreter. In python 2, a metaclass is defined as a __metaclass__ attribute in the class itself, whereas it's a argument in the definition in the class in Python 3.
The __new__ method of the metaclass is called just before the __init__ method of your class and so, takes the same arguments as your class. You can change the behavior of your class at initialization in the metaclass rather than in the constructor for instance.
If you want to define arguments to a metaclass, you can for example use some specific fields to be defined in the class to be defined. You can also write a function that will act like a metaclass to create a class for you, and you will be able to add parameters to that function. But I don't know PyTables and your exact requirements or possibilities.

Related

Object class and Type class in Python [duplicate]

I am learning about metaclass and I see that every class is a subclass of type class in python but sometimes I see people are using object class but object class is also a subclass of type class then what is the difference between them?
object is not a subclass of type: it is an instance of type.
object, the class, is the root of all class hierarchy in Python - however as everything in Python is an instance, it has to have a "class" that when properly instantiated with the proper parameters results in it.
As it is an obvious "chicken and egg" paradox, after all, the class type itself must inherit from object, that part of the class hierarchy is hand-wired in loop: it would be impossible to replicate the same relationships in pure Python code.
And finally: a class being an instance of a metaclass is not the same as inheriting, or being a subclass of that metaclass: inheritance hierarchy is one thing, the metaclass, which is used to construct each class itself, is another, ortogonal thing.
So, to recap: all classes in Python are themselves instances of a "metaclass" - and the default metaclass is type. All classes in Python also inherit from object - and that includes type. The class object itself must also be an instance of type, and that relationship is hardcoded in the Python runtime source-code (which is written in C in the case of cPython)

what is the difference between type class and object class in python

I am learning about metaclass and I see that every class is a subclass of type class in python but sometimes I see people are using object class but object class is also a subclass of type class then what is the difference between them?
object is not a subclass of type: it is an instance of type.
object, the class, is the root of all class hierarchy in Python - however as everything in Python is an instance, it has to have a "class" that when properly instantiated with the proper parameters results in it.
As it is an obvious "chicken and egg" paradox, after all, the class type itself must inherit from object, that part of the class hierarchy is hand-wired in loop: it would be impossible to replicate the same relationships in pure Python code.
And finally: a class being an instance of a metaclass is not the same as inheriting, or being a subclass of that metaclass: inheritance hierarchy is one thing, the metaclass, which is used to construct each class itself, is another, ortogonal thing.
So, to recap: all classes in Python are themselves instances of a "metaclass" - and the default metaclass is type. All classes in Python also inherit from object - and that includes type. The class object itself must also be an instance of type, and that relationship is hardcoded in the Python runtime source-code (which is written in C in the case of cPython)

Explicit passing of Self when calling super class's __init__ in python

This question is in relation to posts at What does 'super' do in Python? , How do I initialize the base (super) class? , and Python: How do I make a subclass from a superclass? which describes two ways to initialize a SuperClass from within a SubClass as
class SuperClass:
def __init__(self):
return
def superMethod(self):
return
## One version of Initiation
class SubClass(SuperClass):
def __init__(self):
SuperClass.__init__(self)
def subMethod(self):
return
or
class SuperClass:
def __init__(self):
return
def superMethod(self):
return
## Another version of Initiation
class SubClass(SuperClass):
def __init__(self):
super(SubClass, self).__init__()
def subMethod(self):
return
So I'm a little confused about needing to explicitly pass self as a parameter in
SuperClass.__init__(self)
and
super(SubClass, self).__init__().
(In fact if I call SuperClass.__init__() I get the error
TypeError: __init__() missing 1 required positional argument: 'self'
). But when calling constructors or any other class method (ie :
## Calling class constructor / initiation
c = SuperClass()
k = SubClass()
## Calling class methods
c.superMethod()
k.superMethod()
k.subMethod()
), The self parameter is passed implicitly .
My understanding of the self keyword is it is not unlike the this pointer in C++, whereas it provides a reference to the class instance. Is this correct?
If there would always be a current instance (in this case SubClass), then why does self need to be explicitly included in the call to SuperClass.__init__(self)?
Thanks
This is simply method binding, and has very little to do with super. When you can x.method(*args), Python checks the type of x for a method named method. If it finds one, it "binds" the function to x, so that when you call it, x will be passed as the first parameter, before the rest of the arguments.
When you call a (normal) method via its class, no such binding occurs. If the method expects its first argument to be an instance (e.g. self), you need to pass it in yourself.
The actual implementation of this binding behavior is pretty neat. Python objects are "descriptors" if they have a __get__ method (and/or __set__ or __delete__ methods, but those don't matter for methods). When you look up an attribute like a.b, Python checks the class of a to see if it has a attribute b that is a descriptor. If it does, it translates a.b into type(a).b.__get__(a, type(a)). If b is a function, it will have a __get__ method that implements the binding behavior I described above. Other kinds of descriptors can have different behaviors. For instance, the classmethod decorator replaces a method with a special descriptor that binds the function the class, rather than the instance.
Python's super creates special objects that handle attribute lookups differently than normal objects, but the details don't matter too much for this issue. The binding behavior of methods called through super is just like what I described in the first paragraph, so self gets passed automatically to the bound method when it is called. The only thing special about super is that it may bind a different function than you'd get lookup up the same method name on self (that's the whole point of using it).
The following example might elucidate things:
class Example:
def method(self):
pass
>>> print(Example.method)
<unbound method Example.method>
>>> print(Example().method)
<bound method Example.method of <__main__.Example instance at 0x01EDCDF0>>
When a method is bound, the instance is passed implicitly. When a method is unbound, the instance needs to be passed explicitly.
The other answers will definitely offer some more detail on the binding process, but I think it's worth showing the above snippet.
The answer is non-trivial and would probably warrant a good article. A very good explanation of how super() works is brilliantly given by Raymond Hettinger in a Pycon 2015 talk, available here and a related article.
I will attempt a short answer and if it is not sufficient I (and hopefully the community) will expand on it.
The answer has two key pieces:
Python's super() needs to have an object on which the method being overridden is called, so it is explicitly passed with self. This is not the only possible implementation and in fact, in Python 3, it is no longer required that you pass the self instance.
Python super() is not like Java, or other compiled languages, super. Python's implementation is designed to support the multiple collaborative inheritance paradigm, as explained in Hettinger's talk.
This has an interesting consequence in Python: the method resolution in super() depends not only on the parent class, but on the children classes as well (consequence of multiple inheritance). Note that Hettinger is using Python 3.
The official Python 2.7 documentation on super is also a good source of information (better understood after watching the talk, in my opinion).
Because in SuperClass.__init__(self), you're calling the method on the class, not the instance, so it cannot be passed implicitly. Similarly you cannot just call SubClass.subMethod(), but you can call SubClass.subMethod(k) and it'll be equivalent to k.subMethod(). Similarly if self refers to a SubClass then self.__init__() means SubClass.__init__(self), so if you want to call SuperClass.__init you have to call it directly.

Importing methods from Python class into another class instance

Let's say I've got python object A, that is an instance of class A. Every instance of class A has an attribute SubType.
I've also got classes SubType_B, SubType_C, and SubType_D, each of which has a method called ingest(). The ingest() method was previously using (self) to get all the parameters it needed, but that self is now the instance of class A.
What is the most elegant way to inherit / use the method ingest() from a SubType class (e.g. SubType_A), using the self from object A?
Previously, the ingest() method was defined in class A, but I would like to break that out into separate classes. Class A is instantiated, and based on the SubType parameter, that object would inherit the correct ingest() method from the corresponding SubType class.
In real simple terms, I'd like one object to grab a method from another object, as if it were its own.
UPDATE
Final solution was to make a factory function that returns instances of the SubType classes. This factory reads information in parameters passed to it, that determine which SubType class to instantiate. The SubType classes all extend Class A.
I had envisioned things backwards, thinking I started with the most general class, then incorporated attributes and methods from sub-classes. This factory-like-function does the requisite information gathering, then instantiates the appropriate sub-class.
Thanks to #HenryGomersall and #IgnacioVazquez-Abrams for ideas and clarification.

Why are parent constructors not called when instantiating a class? [duplicate]

This question already has an answer here:
Why not constructor of super class invoked when we declare the object of sub class?
(1 answer)
Closed 9 years ago.
class A:
def __init__(self):
print 'A'
class B(A):
def __init__(self):
print 'B'
b = B()
B
In C++, I would have expected to see A B output, but in Python I am getting only B. I know that I can do super(B, self).__init__() to achieve the same in Python, but as this is apparently not the default (or is it - I am new to the syntax as well), I am worried that the paradigms for instatinating objects are completely different.
So what are objects in Python, what is their relation with classes and what is the standard way to initialize all data in all parent classes in Python?
Python rarely does anything automatically. As you say, if you want to invoke the superclass __init__, then you need to do it yourself, usually by calling super:
class B(A):
def __init__(self):
print 'B'
super(B, self).__init__()
The point to note is that instance attributes, like everything else in Python, are dynamic. __init__ is not the constructor, that's __new__ which you rarely need to meddle with. The object is fully constructed by the time __init__ is called, but since instance attributes are dynamic they are usually added by that method, which is only special in that it's called first once the object is created.
You can of course create instance attributes in any other method, or even from outside the class itself by simply doing something like myBobj.foo = 'bar'.
So what are objects in Python
Well, objects in python are like dictionaries of members and methods. It's no more sophisticated than that. You don't have visibility handling (if you want to hide a member, just do not talk about it in the public documentation, only with a comment).
what is their relation with classes
A class defines the method/member skeleton that will instantiate that dict/object. So you got the constructor __init__(), which is only a handler used when you create that object.
what is the standard way to initialize all data in all parent classes in Python?
Either you do not redefine the constructor, and then all parent classes will have their constructor initiated (like the default C++ behavior) or you do redefine the constructor, and then have to make an explicit call to your parent class's constructor.
Remember the zen of python: "Explicit is better than implicit". It totally applies here.
You need to invoke the base constructor in your inherited class constructor:
class B(A):
def __init__(self):
A.__init__(self)
# super(B, self).__init__() you can use this line as well
print 'B'

Categories

Resources