Parent class called before child - python

I am working on creating a child class that inherits from PySndfile that would contain extra functions related to the PySndfile class.
However, when initialising the child class with a keyword argument not intended for the PySndfile class, it appears that arguments are sent straight to the parent class, bypassing the child's init altogether (ie the print statment isn't called, the argument isn't popped from kwargs and the traceback doesn't refer to the child class at all.
Any help would be much appreciated.
ClassProblem.py:
from pysndfile import PySndfile
class AudioFile(PySndfile):
def __init__(self, filename, mode, rms="123"):
print 'AudioFile init called'
self.rms = rms
super(AudioFile, self).__cinit__(
self,
filename,
mode=mode,
format=None,
channels=None,
samplerate=None
)
aa = AudioFile(
"/path/to/audio/file.wav",
'r',
rms="456",
)
Error produced:
Traceback (most recent call last):
File "ClassProblem.py", line 18, in <module>
rms="456",
File "_pysndfile.pyx", line 564, in _pysndfile.PySndfile.__cinit__ (_pysndfile.cpp:3308)
TypeError: __cinit__() got an unexpected keyword argument 'rms'

It seems you'll have to override __new__, according to the cython documentation:
If you anticipate subclassing your extension type in Python, you may find it useful to give the cinit() method * and ** arguments so that it can accept and ignore extra arguments. Otherwise, any Python subclass which has an init() with a different signature will have to override new() [1] as well as init(), which the writer of a Python class wouldn’t expect to have to do.

PySndfile appears to be a Cython library. I don't know anything at all about Cython, but it appears from the traceback that it uses __cinit__ rather than __init__. You should probably override that method instead.

You are effectively passing **kwargs up to the PySndfile constructor but it doesn't know what to do with the rms argument which is specific to your class. I don't have PySndfile locally but after a quick look online, I see the pyx constructor looks like this:
def __cinit__(self, filename, mode='r', int format=0,
int channels=0, int samplerate=0):
This is not an *args, **kwargs function, able to take any arguments, rather it has a fixed signature. I would suggest updating your class with a similar constructor to which you can add the arguments specific to your class:
def __init__(self, filename, mode='r', rms=0, format=0,
channels=0, samplerate=0):
Then pass all the arguments to the super constructor (except rms, which it does not understand).

I found the answer by posting this question to the /r/learnpython sub-reddit
The user yes_or_gnome provides a great explanation as to why I needed to override the __new__ class method as well as the __init__ method and has provided example code.

Related

Why does super()() not do the same as super().__init__()?

I have been wondering this for a while now, and I hope this isn't a stupid question with an obvious answer I'm not realizing: Why can't I just call the __init__ method of super() like super()()? I have to call the method like this instead: super().__init__()
Here is an example that gets a TypeError: 'super' object is not callable error when I run it (specifically on line 6 that comes from line 3 in __init__):
class My_Int(int):
def __init__(self,value,extr_data=None):
super()(value)
self.extr_data=extr_data
x=My_Int(3)
Doesn't super() get the inherited class int making super()(value) the same as int(value)?
Furthermore, why can't I use the len function with super() when inheriting from the class list? Doesn't len() do the same as __len__()?
class My_List(list):
def some_method1(self):
print(len(super()))
def some_method2(self):
print(super().__len__())
x=My_List((1,2,3,4))
x.some_method2()
x.some_method1()
This example prints 4 and then an error as expected. Here is the output exactly:
4
Traceback (most recent call last):
File "/home/user/test.py", line 11, in <module>
x.some_method1()
File "/home/user/test.py", line 3, in some_method1
print(len(super()))
TypeError: object of type 'super' has no len()
Notice I called some_method2 before calling some_method1 (sorry for the confusion).
Am I missing something obvious here?
P.S. Thanks for all the help!
super() objects can't intercept most special method calls, because they bypass the instance and look up the method on the type directly, and they don't want to implement all the special methods when many of them won't apply for any given usage. This case gets weirder, super()() would try to lookup a __call__ method on the super type itself, and pass it the super instance.
They don't do this because it's ambiguous, and not particularly explicit. Does super()() mean invoke the super class's __init__? Its __call__? What if we're in a __new__ method, do you invoke __new__, __init__ or both? Does this mean all super uses must implicitly know which method they're called in (even more magical than knowing the class they were defined in and the self passed when constructed with zero arguments)?
Rather than deal with all this, and to avoid implementing all the special methods on super just so it can delegate them if they exist on the instance in question, they required you to explicitly specify the special method you intend to call.

Why does Python complains about the number of parameters in __init__?

i don't understand this error in Python. I read about "self" and "__init__" in this previous question , where says that Python does not pass transparently the instance to the constructor. So I tried a simple class definition and then declare a new instance.
#Basic class
class Testing:
atr1 = 33
def __init__():
pass
def sayHi():
print("Hello world")
When I try to declare a new instance of this class, Jupyter throws this error:
t1 = Testing()
TypeError Traceback (most recent call last)
<ipython-input-6-0019e8f92b90> in <module>
----> 1 t1 = Testing()
TypeError: __init__() takes 0 positional arguments but 1 was given
So for me, this error doesn't make sense, otherwise, Python would be actually passing the instance itself as an argument when initializes the new instance and because i'm not giving an explicit argument.
The solution is quite simple: just write "self" as an argument of init method, but i'm still confused about the error.
Hope somebody can explain me this weird error message u.u
The first parameter to an object method is a reference to the object itself. Traditionally its called self but really you could name it anything you want. In the end, a method is really just a function assigned to a class. That's what happened when you did
class Testing:
def __init__():
pass
The def caused python to compile a function and assign it to __init__. Because __init__ is in the Testing class namespace, it assigned it to the class. You could just as easily have done
class Testing:
pass
def whatever():
pass
Testing.__init__ = whatever
So, the idea of python just magically creating the self parameter on methods doesn't work. It would be a crazy rule for regular functions.
__init__ is an initializer, not a constructor. The object has been constructed to the point that it has a functioning self by the time __init__ has been called. Classes also have a __new__ that can be used to construct the object.

where does the 'self' object comes from in the method __init__?

I'm learning python and the construct method __init__ makes me confused.
class Test(object):
def __init__(self):
pass
As known, python implicitly pass a parameter representing the object itself at the first place. It is acceptable in common member functions. But what makes me confused is that the constructor requests an object itself.
It looks like the c++ code below. This code does not make sense! The parameter thiz does not even exit before the constructor!
class Test {
public:
Test(Test *thiz);
};
So, my question is, why python needs a "non-existent" self object in constructor __init__?
__init__ behaves like any other normal function in a class. It gets called by the interpreter at a special time if it is defined, but there is nothing special about it. You can call it any time yourself, just like a regular method.
Here are some facts to help you see the picture:
Function objects are non-data descriptors in python. That means that function objects have a __get__ method, but not __set__. And if course they have a __call__ method to actually run the function.
When you put a def statement in a class body, it creates a regular function, just like it would elsewhere. You can see this by looking at type(Test.__init__). To call Test.__init__, you would have to manually pass in a self parameter.
The magic happens when you call __init__ on an instance of Test. For example:
a = Test()
a.__init__()
This code actually calls __init__ twice (which we'll get into momentarily). The explicit second call does not pass in a parameter to the method. That's because a.__init__ has special meaning when the name __init__ is present in the class but not the instance. If __init__ is a descriptor, the interpreter will not return it directly, but rather will call __get__ on it and return the result. This is called binding.
a.__init__() is roughly equivalent to type(a).__init__.__get__(a, None)(). The call .__get__(a, None) returns a callable object that is a bound method. It is like a special type of partial function, in which the first positional parameter is set to a. You can see this by checking type(a.__init__).
The self parameter is passed to methods as the first positional parameter. As such, it does not have to be called self. The name is just a convention. def __init__(this): or def __init__(x) would work just as well.
Finally, let's discuss how a = Test() ends up calling __init__. Class objects are callable in python. They have a class themselves, called a metaclass (usually just type), which actually defines a __call__ method. When you do Test(), you are literally calling the class.
By default, the __call__ method of a class looks something (very roughly approximately) like this:
def __call__(cls, *args, **kwargs):
self = cls.__new__(cls, *args, **kwargs)
if isinstance(self, cls):
type(self).__init__(self, *args, **kwargs)
So a new instance is only created by __new__, not __init__. The latter is an initializer, not a constructor or allocator. In fact, as I mentioned before, you can call __init__ as often as you like on most classes. Notable exceptions include immutable built-in classes like tuple and int.
As an initializer, __init__ obviously needs access to the object it is initializing. Your example does nothing with self, but it is pretty standard to set instance attributes and do something things to prep the object. For cases like yours, an explicit __init__ is not necessary at all, since a missing function will be found in the base class hierarchy.

Python 2.7 warning: __init__ not compatible to __new__

I have a class defined in Python 2.7 like this:
from future.builtins import object
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
In PyCharm, this gives a warning in the __init__ line:
Signature is not compatible to __new__.
I don't understand what this warning is telling me. Can someone give an example where this warning would rightfully catch an error or can this warning be turned off?
There's a PyCharm thread for this, but it doesn't help me: https://intellij-support.jetbrains.com/hc/en-us/community/posts/115000254530-PyCharm-init-Signature-is-not-compatible-to-new-
I suffered from the same problem and found the solution in here.
As discussed in the above link, a direct solution is to remove your Pycharm config directory ("/home/username/.PyCharm2018.3" for me). However, it would remove all your other configs too. Finally, I just fix the problem by removing the rule from inspections. You can find the rule in the Pycharm Setting window (see the figure as follow).
This is a PyCharm bug; you can disable the warning via Xiong-Hui's post. Basically __new__ is a method that is called before __init__ with the same arguments when a class is constructed, so their signatures must be compatible (both functions must be able to be invoked with the same arguments) or the class cannot be instantiated. For example, here is a code snippet for which PyCharm correctly applies the warning:
class Test(object):
def __new__(cls, arg1):
return super().__new__(cls)
def __init__(self):
pass
Attempting to create the class with Test() will throw a TypeError since __new__ expects an argument, but creating the class with Test('something') will also throw a TypeError since __init__ doesn't expect any arguments, making it impossible to construct the class. Normally this is never an issue because the default implementation of __new__ in object accepts an arbitrary number of arguments, but if you define __new__ yourself you will need to be careful that the signatures remain compatible so the class can actually be constructed, which is the purpose of the warning.

Python functional programming reference to a constructor

I would like to have a function pointer ptr that can point to either:
a function,
the method of an object instance, or
the constructor of the object.
In the latter case, the execution of ptr() should instantiate the class.
def function(argument) :
print("Function called with argument: "+str(argument))
class C(object) :
def __init__(self,argument) :
print("C's __init__ method called with argument: "+str(argument))
def m(self,argument) :
print("C's method 'm' called with argument: "+str(argument))
## works
ptr = function
ptr('A')
## works
instance = C('asdf')
ptr = instance.m
ptr('A')
## fails
constructorPtr = C.__init__
constructorPtr('A')
This produces as output:
Function called with argument: A
C's __init__ method called with argument: asdf
C's method 'm' called with argument: A
Traceback (most recent call last): File "tmp.py", line 24, in <module>
constructorPtr('A')
TypeError: unbound method __init__() must be called with C instance as first argument (got str instance instead)
showing that the first two ptr() calls worked, but the last did not.
The reason this doesn't work is that the __init__ method isn't a constructor, it's an initializer.*
Notice that its first argument is self—that self has to be already constructed before its __init__ method gets called, otherwise, where would it come from.
In other words, it's a normal instance method, just like instance.m is, but you're trying to call it as an unbound method—exactly as if you'd tried to call C.m instead of instance.m.
Python does have a special method for constructors, __new__ (although Python calls this a "creator" to avoid confusion with languages with single-stage construction). This is a static method that takes the class to construct as its first argument and the constructor arguments as its other arguments. The default implementation that you've inherited from object just creates an instance of that class and passes the arguments along to its initializer.** So:
constructor = C.__new__
constructor(C, 'A')
Or, if you prefer:
from functools import partial
constructor = partial(C.__new__, C)
constructor('A')
However, it's incredibly rare that you'll ever want to call __new__ directly, except from a subclass's __new__. Classes themselves are callable, and act as their own constructors—effectively that means that they call the __new__ method with the appropriate arguments, but there are some subtleties (and, in every case where they differ, C() is probably what you want, not C.__new__(C)).
So:
constructor = C
constructor('A')
As user2357112 points out in a comment:
In general, if you want a ptr that does whatever_expression(foo) when you call ptr(foo), you should set ptr = whatever_expression
That's a great, simple rule of thumb, and Python has been carefully designed to make that rule of thumb work whenever possible.
Finally, as a side note, you can assign ptr to anything callable, not just the cases you described:
a function,
a bound method (your instance.m),
a constructor (that is, a class),
an unbound method (e.g., C.m—which you can call just fine, but you'll have to pass instance as the first argument),
a bound classmethod (e.g., both C.cm and instance.cm, if you defined cm as a #classmethod),
an unbound classmethod (harder to construct, and less useful),
a staticmethod (e.g., both C.sm and instance.sm, if you defined sm as a #staticmethod),
various kinds of implementation-specific "builtin" types that simulate functions, methods, and classes.
an instance of any type with a __call__ method,
And in fact, all of these are just special cases of the last one—the type type has a __call__ method, as do types.FunctionType and types.MethodType, and so on.
* If you're familiar with other languages like Smalltalk or Objective-C, you may be thrown off by the fact that Python doesn't look like it has two-stage construction. In ObjC terms, you rarely implement alloc, but you call it all the time: [[MyClass alloc] initWithArgument:a]. In Python, you can pretend that MyClass(a) means the same thing (although really it's more like [MyClass allocWithArgument:a], where allocWithArgument: automatically calls initWithArgument: for you).
** Actually, this isn't quite true; the default implementation just returns an instance of C, and Python automatically calls the __init__ method if isinstance(returnvalue, C).
I had a hard time finding the answer to this problem online, but I figured it out, so here is the solution.
Instead of pointing constructorPtr at C.__init__, you can just point it at C, like this.
constructorPtr = C
constructorPtr('A')
which produces as output:
C's __init__ method called with argument: A

Categories