How can I fix the error on this Python code? - python

I have this superclass:
import wx
class Plugin(wx.Panel):
def __init__(self, parent, *args, **kwargs):
wx.Panel.__init__(self, parent, *args, **kwargs)
self.colorOver = ((89,89,89))
self.colorLeave = ((110,110,110))
self.SetBackgroundColour(self.colorLeave)
self.SetForegroundColour(self.colorLeave)
self.name = "plugin"
wx.StaticText(self, -1, self.getName(), style=wx.ALIGN_LEFT)
self.Bind(wx.EVT_ENTER_WINDOW, self.onMouseOver)
self.Bind(wx.EVT_LEAVE_WINDOW, self.onMouseLeave)
def onMouseOver(self, event):
self.SetBackgroundColour(self.colorOver)
self.Refresh()
def onMouseLeave(self, event):
self.SetBackgroundColour(self.colorLeave)
self.Refresh()
def OnClose(self, event):
self.Close()
app.Destroy()
def getName(self):
return self.name
and this subclass:
import plugin
import wx
class noisePlugin(plugin.Plugin):
self.name = "noise"
and it gives me this error compiling the subclass:
Traceback (most recent call last):
File "C:\Users\André Ferreira\Desktop\Tese\Código Python\SoundLog\Plugins\noisePlugin.py", line 4, in <module>
class noisePlugin(plugin.Plugin):
File "C:\Users\André Ferreira\Desktop\Tese\Código Python\SoundLog\Plugins\noisePlugin.py", line 5, in noisePlugin
self.name = "noise"
NameError: name 'self' is not defined
What can I do to fix this error?
I want getName() method to return the name of the instanciated class!
Thanks in advance :)

Make the subclass
class noisePlugin(plugin.Plugin):
def __init__(self, *a, **k):
plugin.Plugin.__init__(self, *a, **k)
self.name = "noise"
Whenever you want to use self.something you have to be within a method, not at class level outside of methods!

What makes you think this works?
class noisePlugin(plugin.Plugin):
self.name = "noise"
Why didn't you copy the
class Plugin(wx.Panel):
def __init__(self, parent, *args, **kwargs):
That comes before self.name=?

For the pattern you seem to be trying for (where the name is associated more with the class than with the instance), this is often a better idiom to follow:
class A(object):
name = 'parent'
def __init__(self, ...):
... etc
class B(A):
name = 'child'
def __init__(self, ...):
A.__init__(self, ...)
... etc
Even though the name attribute is stored on the class rather than the instance, you can access it using self.name in all the instances. Generally if you find yourself assigning a static (unchanging) attribute that is the same in all instances of a given class you should just use a static class attribute like this.
On a slightly different topic, were you aware that all wxPython widgets already have a name attribute, which can be assigned using the name keyword argument at initialization time, and accessed using either GetName() or (in recent versions of wxPython) the property Name? If you don't assign it, it will default to some fairly generic class-specific value (like "text" or "textctrl" for wx.TextCtrl). Depending on what you're trying to do, maybe you can just use it instead of your own name and getName(). wxPython itself does not make any use of this value, as it's intended for you the programmer to use as you see fit.

Related

In multiple inheritance in Python, init of parent class A and B is done at the same time?

I have a question about the instantiation process of a child class with multiple inheritance from parent class A without arg and parent class B with kwargs respectively.
In the code below, I don't know why ParentB's set_kwargs()method is executed while ParentA is inited when a Child instance is created.
(Expecially, why does the results show Child receive {}? How can I avoid this results?)
Any help would be really appreciated.
Thanks!
class GrandParent:
def __init__(self):
print(f"{self.__class__.__name__} initialized")
class ParentA(GrandParent):
def __init__(self):
super().__init__()
class ParentB(GrandParent):
def __init__(self, **kwargs):
super().__init__()
self.set_kwargs(**kwargs)
def set_kwargs(self, **kwargs):
print(f"{self.__class__.__name__} receive {kwargs}")
self.content = kwargs.get('content')
class Child(ParentA, ParentB):
def __init__(self, **kwargs):
ParentA.__init__(self)
ParentB.__init__(self, **kwargs)
c = Child(content = 3)
results:
Child initialized
Child receive {}
Child initialized
Child receive {'content': 3}
For most cases of multiple inheritance, you will want the superclass methods to be called in sequence by the Python runtime itself.
To do that, just place a call to the target method in the return of super().
In your case, the most derived class' init should read like this:
class Child(ParentA, ParentB):
def __init__(self, **kwargs):
super().__init__(self, **kwargs)
And all three superclasses __init__ methods will be correctly run. Note that for that to take place, they have to be built to be able to work cooperatively in a class hierarchy like this - for which two things are needed: one is that each method in any of the superclasses place itself a class to super().method()- and this is ok in your code. The other is that if parameters are to be passed to these methods, which not all classes will know, the method in each superclass should extract only the parameters it does know about, and pass the remaining parameters in its own super() call.
So the correct form is actually:
class GrandParent:
def __init__(self):
print(f"{self.__class__.__name__} initialized")
class ParentA(GrandParent):
def __init__(self, **kwargs):
super().__init__(**kwargs)
class ParentB(GrandParent):
def __init__(self, **kwargs):
content = kwargs.pop('content')
super().__init__(**kwargs)
self.set_kwargs(content)
def set_kwargs(self, content):
print(f"{self.__class__.__name__} receive {content}")
self.content = content
class Child(ParentA, ParentB):
def __init__(self, **kwargs):
super.__init__(**kwargs)
c = Child(content = 3)
The class which will be called next when you place a super() call is calculated by Python when you create a class with multiple parents - so, even though both "ParentA" and "ParentB" inherit directly from grandparent, when the super() call chain bubbles up from "Child", Python will "know" that from within "ParentA" the next superclass is "ClassB" and call its __init__ instead.
The algorithm for finding the "method resolution order" is quite complicated, and it just "works as it should" for most, if not all, usecases. It's exact description can be found here: https://www.python.org/download/releases/2.3/mro/ (really - you don't have to understand it all - there are so many corner cases it handles - just get the "feeling" of it.)

Python OOP: Are all self."something" attributes in __init__ available to other class methods?

Simple, silly question.
But say I had
class Stuff:
def __init__(self, name):
self.name = name:
def get_name(self):
print(name)
new_name = Stuff(name = "Richard")
new_name.get_name()
Would this work? Would get_name be able to access the name attribute and print it out?
I can't get this code to work...
There are a few things that you need to change but this works:
class Stuff:
def __init__(self, name):
self.name = name
def get_name(self):
print(self.name)
new_name = Stuff(name = "Richard")
new_name.get_name()
Besides a few syntax errors (class needs to be lowercase and some missing :) the main thing you were missing was accessing name by means of the self identifier. Since name is defined on the class you need to access it via self.

Should super always be at the top of an __init__ method, or can it be at the bottom?

In most Python examples, when super is used to call a parent class's constructors, it appears at the top.
Is it bad form to have it at the bottom of an init method?
In the examples below, super is at the bottom of A's constructor, but at the top of B's constructor.
class A:
def __init__(self):
# Do some stuff
b = result_of_complex_operation()
super(A, self).__init__(b)
class B:
def __init__(self):
super(A, self).__init__(b)
# Do some stuff
This totally depends on the use case. Consider this.
class Foo():
def __init__(self):
print(self.name)
#property
def name(self):
return self.__class__.__name__
class Bar(Foo):
def __init__(self, name):
self.name = name
super().__init__()
#property
def name(self):
return self.__name
#name.setter
def name(self, name):
self.__name = name
If you'd invoke super() before setting self.name within Bar.__init__ you'd get an AttributeError because the required name has not yet been set.
Is it bad form to have it at the bottom of an init method?
You're asking the wrong question. Regardless of whether it's bad from or not, there are valid use cases for moving the superclass initialization to the bottom of a sub-class's constructor. Where to put the call to the superclass's constructor entirely depends on the implementation of the superclass's constructor.
For example, suppose you have a superclass. When constructing the superclass, you want to give an attribute a certain value depending on an attribute of the subclasses:
class Superclass:
def __init__(self):
if self.subclass_attr:
self.attr = 1
else:
self.attr = 2
As you can see from above, we expect the subclasses to have the attribute subclass_attr. So what does this mean? We can't initialize Supperclass until we've given the subclasses the subclass_attr attribute.
Thus, we have to defer calling the superclass's constructor until we initialize subclass_attr. In other words, the call to super will have to be put at the bottom of a subclasses constructor:
class Subclass(Superclass):
def __init__(self):
self.subclass_attr = True
super(Superclass, self).__init__()
In the end, the choice of where to put super should not be based upon some style, but on what's necessary.

How remove a variable that appears in __init__?

If i have this:
class One(object):
def __init__(self, name):
self.name = name
I want to use One but altering the name name and relace it by other
The solution I supposed is inheriting:
class Two(One):
def __init__(self, other):
super(Two, self).__init__(other)
The idea is : How to delete or change the variable names that appears in __init__ ?
There is no relation at all between the name of the parameter passed to __init__ and the name of the instance variable that might eventuality be initialized by that argument. This is only a matter of convention than both are called the same.
Both code fragments below will perform exactly the same:
class One(object):
def __init__(self, name):
self.name = name
class One(object):
def __init__(self, xyz):
self.name = xyz
As about renaming an instance variable, you might do something like that, but this is (very) bad style and has (great) chances to break something in (the base class and/or in any client code that expects a proper One instance):
class Two(One):
def __init__(self, other):
super(Two, self).__init__(other)
self.other = self.name # <- no, seriously,
del self.name # <- don't do that !!!
You can't do what you want, not if you are calling One.__init__ from Two.__init__.
If you want to alter what attributes are set, simply don't call One.__init__() here. Set your own attributes instead:
class One(object):
def __init__(self, name):
self.name = name
class Two(One):
def __init__(self, other):
self.other = other
Now self.name will never be set. This most likely will break the rest of functionality in One, something you probably don't want to do. The rest of the methods in that class are likely to rely on certain attributes having been set.
In OOP terms, if Two is not a special kind of One object, don't inherit from One. If Two is a kind of One object, don't try to make it into something else.

How to extend Python class init

I have created a base class:
class Thing():
def __init__(self, name):
self.name = name
I want to extend the class and add to the init method so the that SubThing has both a name and a time property. How do I do it?
class SubThing(Thing):
# something here to extend the init and add a "time" property
def __repr__(self):
return '<%s %s>' % (self.name, self.time)
Any help would be awesome.
You can just define __init__ in the subclass and call super to call the parents' __init__ methods appropriately:
class SubThing(Thing):
def __init__(self, *args, **kwargs):
super(SubThing, self).__init__(*args, **kwargs)
self.time = datetime.now()
If you're still on Python 2.x, make sure the base class is a subclass of object, as super won't work with old-style classes:
class Thing(object):
...
You should write another __init__ method in SubThing and then call the constructor of the superclass to initialize its fields.
This Q&A should provide you some more examples.
You can eliminate the arguments in super:
class SubThing(Thing):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.time = datetime.now()

Categories

Resources