Calling functions using a variable with self - python

I have some code for an autocomplete combobox that I copied from the internet and I'm modifying it so I can change its properties like width, height, bindings, etc. through keywords like a normal combobox. I'm not sure how to go about calling the functions for the properties though because I need self. behind it to initiate the function. Here's a snippet of what I have:
from tkinter import *
from tkinter import ttk
class App(Tk):
def __init__(self):
Tk.__init__(self)
self.CBBox = AutoCompleteBox(self, height=3)
self.CBBox.pack()
class AutoCompleteBox(ttk.Combobox):
def __init__(self, parent, **kwargs):
ttk.Combobox.__init__(self, parent)
for key, value in kwargs.items():
key(value)
def height(self, height):
self.config(height=height)
def width(self, width):
self.config(width=width)
my_app = App()
my_app.mainloop()

If you're asking about how to pass kwargs to the base class, just pass them when you call __init__:
class AutoCompleteBox(ttk.Combobox):
def __init__(self, parent, **kwargs):
ttk.Combobox.__init__(self, parent, **kwargs)
To call a method on the base class, use super. Or, do like you do in the __init__ and invoke the class. super is preferred.
For example, if you wanted to call the configure method of the base class you could do something like this:
def width(self, width):
super().config(width=width)
In this case it's not necessary, since self.config(...) will automatically call the config method on the base class since you haven't overridden that method in your class.
If you defined your own config method to do something else in addition to the default behavior, it might look something like this:
def config(self, **kwargs):
print("config called:", kwargs)
super().config(**kwargs)
Notice that if you remove the last line, when you call config it will print the message but it won't actually configure the widget.

Related

NameError: class is not defined when overriding a class in python

Why can't python seem to find the InterfaceWithNoMenu class
class Settings(Screen):
class SettingsWithNoMenu(kivy.uix.settings.SettingsWithNoMenu):
def __init__(self, *args, **kwargs):
self.interface_cls = InterfaceWithNoMenu
kivy.uix.settings.SettingsWithNoMenu.__init__( self, *args, **kwargs )
class InterfaceWithNoMenu(kivy.uix.settings.ContentPanel):
def add_widget(self, widget):
if self.container is not None and len(self.container.children) > 0:
raise Exception(
'ContentNoMenu cannot accept more than one settings panel')
super(InterfaceWithNoMenu, self).add_widget(widget)
kivy.uix.settings.InterfaceWithNoMenu.__init__( self, *args, **kwargs )
actionview = ObjectProperty(None)
settings_content = ObjectProperty(None)
def __init__(self, **kwargs):
super(Settings, self).__init__(**kwargs)
...
I'm trying to change the look/feel/behaviour of the Settings module in a kivy-based python GUI app.
Unfortunately, when I create a settings window with the above program that creates an instance of the locally-overridden self.SettingsWithNoMenu class, I get the following error:
self.interface_cls = InterfaceWithNoMenu
NameError: name 'InterfaceWithNoMenu' is not defined
I don't understand. It's right there. I mean, the class InterfaceWithNoMenu is literally defined right underneath the one (SettingsWithNoMenu) that's referencing it.
Why can't the class SettingsWithNoMenu find InterfaceWithNoMenu? Why am I getting a NameError here?
InterfaceWithNoMenu is defined in the namespace of the Settings class, not the global or local namespace. You should be able to do:
self.interface_cls = Settings.InterfaceWithNoMenu
since Settings is available in the global namespace.
Nested class definitions are a little awkward IMO and I would usually recommend not using them, especially if there's a dependency between "sibling" classes like this that requires the nested class to access its enclosing class.
You're using the InterfaceWithNoMenu class before defining it, as you declare an instance of it in the SettingWithNoMenu class.
Can you swap those class definitions around and see if that fixes this error? (and you might need to make the reference more specific, with a self or a this or something)

how to return objects straight after instantiating classes in python?

If I have these classes:
class MyObject:
def __init__(self, version):
self.version = version
class Builder:
def __init__(self,version='0.0.1'):
self.version = version
def build(self):
return MyObject(self.version)
is it possible to have the returned object by simply calling
versioned_obj = Builder('0.0.1')
#instead of going through
builder = Builder('0.0.1')
versioned_object = builder.build()
?
have tried using __init__() but it must return None
You have a class, MyObject. If you want to instantiate MyObject in a single call, you don't need a builder at all, just initialize MyObject directly:
versioned_obj = MyObject('0.0.1')
Going back to what you asked, about using the Builder class without invoking build(): while it is possible to have the constructor of the builder class return a MyObject by overriding __new__ I really don't recommend it.
Without understanding why you have a builder class (that seems to be doing nothing) it's harder to recommend alternatives, but there definitely are some. I can't recommend any of these based on the code you've shown, because they add complexity for no reason. But maybe they will give you an idea that will fit the code you haven't shown.
Builder as Function
In the example above, there's no obvious reason why the builder needs to exist, let alone be a class, but if you wanted a builder and it didn't have to be a class, then it could simply be a function:
def build_myobj(version):
return MyObject(version)
versioned_obj = build_myobj('0.0.1')
Builder Factory Method
Or, if the Builder class is really suited to be a class and you just haven't shown us why, you could add a factory method to the builder:
class Builder:
def __init__(self,version='0.0.1'):
self.version = version
def build(self):
return MyObject(self.version)
#staticmethod
def factory(version):
return Builder(version).build()
versioned_obj = Builder.factory('0.0.1')
Function Using Builder
If you don't want to modify the builder class, but you want a single call:
def build_myobj(version):
return Builder(version).build()
versioned_obj = build_myobj('0.0.1')
What you seem to be referring to is the object.__new__ method. When you create an instance of a class, the __new__ method is called to create and actually return the object that will be assigned, and then it's __init__ method is called before assignment.
example
class otherclass:
def __init__(self, *args, **kwargs):
print('hello from otherclass')
class myclass:
# __new__ is a staticmethod by default so it recieves cls rather than self (no self exists yet..)
def __new__(cls, *args, **kwargs):
print('creating new instance of otherclass from myclass')
return otherclass(*args, **kwargs)
calling myclass() prints:
creating new instance of otherclass from myclass
hello from otherclass
Out[2]: <__main__.otherclass at 0x22192e0a898>
note: I didn't include it in the example, but __init__ gets called with the same *args, **kwargs as __new__

Python __init__() how don't override important functionality?

I need to add another option in a class, a simple 'edit=False'. Whithout override completely init().
I found this piece of code written for kivy:
class TitleBox(BoxLayout):
def __init__(self, **kwargs):
# make sure we aren't overriding any important functionality
super(TitleBox, self).__init__(**kwargs)
But when I try to edit for my purposes I receive: "TypeError: init() takes at most 2 arguments (3 given)"
class Person_Dialog(tkSimpleDialog.Dialog):
def __init__(self, edit=False, **kwargs):
super(Person_Dialog, self).__init__(**kwargs)
self.edit = edit
Given an __init__ signature of:
def __init__(self, edit=False, **kwargs):
When you do this:
add = Person_Dialog(root, 'Add person')
Python creates an instance and assigns it to the self argument. Then it assigns root to the edit argument. Then it takes 'Add a person' and finds no other positional arguments to assign it to.
To fix this add another argument to __init__:
class Person_Dialog(tkSimpleDialog.Dialog):
def __init__(self, parent, edit=False, **kwargs): # added parent argument
super(Person_Dialog, self).__init__(parent, **kwargs)
self.edit = edit
Note that we also pass parent to the superclass because tkSimpleDialog.Dialog has this signature __init__(self, parent, title=None).
Unfortunately, your code now fails with TypeError: must be type, not classobj because tkSimpleDialog.Dialog is an old style class and you can't use super() with old style classes. (Python 3 does away with old style classes, so you won't have this issue there.)
So to fix this replace the call to super() with a direct reference to the superclass:
class Person_Dialog(tkSimpleDialog.Dialog):
def __init__(self, parent, edit=False, **kwargs):
# referencing the superclass directly
tkSimpleDialog.Dialog.__init__(self, parent, **kwargs)
self.edit = edit
Now your code will work.

how to make child class call parent class __init__ automatically?

i had a class called CacheObject,and many class extend from it.
now i need to add something common on all classes from this class so i write this
class CacheObject(object):
def __init__(self):
self.updatedict = dict()
but the child class didn't obtain the updatedict attribute.i know calling super init function was optional in python,but is there an easy way to force all of them to add the init rather than walk all the classes and modify them one by one?
I was in a situation where I wanted classes to always call their base classes' constructor in order before they call their own. The following is Python3 code that should do what you want:
class meta(type):
def __init__(cls,name,bases,dct):
def auto__call__init__(self, *a, **kw):
for base in cls.__bases__:
base.__init__(self, *a, **kw)
cls.__init__child_(self, *a, **kw)
cls.__init__child_ = cls.__init__
cls.__init__ = auto__call__init__
class A(metaclass=meta):
def __init__(self):
print("Parent")
class B(A):
def __init__(self):
print("Child")
To illustrate, it will behave as follows:
>>> B()
Parent
Child
<__main__.B object at 0x000001F8EF251F28>
>>> A()
Parent
<__main__.A object at 0x000001F8EF2BB2B0>
I suggest a non-code fix:
Document that super().__init__() should be called by your subclasses before they use any other methods defined in it.
This is not an uncommon restriction. See, for instance, the documentation for threading.Thread in the standard library, which says:
If the subclass overrides the constructor, it must make sure to invoke the base class constructor (Thread.__init__()) before doing anything else to the thread.
There are probably many other examples, I just happened to have that doc page open.
You can override __new__. As long as your base classes doesn't override __new__ without calling super().__new__, then you'll be fine.
class CacheObject(object):
def __new__(cls, *args, **kwargs):
instance = super().__new__(cls, *args, **kwargs)
instance.updatedict = {}
return instance
class Foo(CacheObject):
def __init__(self):
pass
However, as some commenters said, the motivation for this seems a little shady. You should perhaps just add the super calls instead.
This isn't what you asked for, but how about making updatedict a property, so that it doesn't need to be set in __init__:
class CacheObject(object):
#property
def updatedict(self):
try:
return self._updatedict
except AttributeError:
self._updatedict = dict()
return self._updatedict
Hopefully this achieves the real goal, that you don't want to have to touch every subclass (other than to make sure none uses an attribute called updatedict for something else, of course).
There are some odd gotchas, though, because it is different from setting updatedict in __init__ as in your question. For example, the content of CacheObject().__dict__ is different. It has no key updatedict because I've put that key in the class, not in each instance.
Regardless of motivation, another option is to use __init_subclass__() (Python 3.6+) to get this kind of behavior. (For example, I'm using it because I want users not familiar with the intricacies of Python to be able to inherit from a class to create specific engineering models, and I'm trying to keep the structure of the class they have to define very basic.)
In the case of your example,
class CacheObject:
def __init__(self) -> None:
self.updatedict = dict()
def __init_subclass__(cls) -> None:
orig_init = cls.__init__
#wraps(orig_init)
def __init__(self, *args, **kwargs):
orig_init(self, *args, **kwargs)
super(self.__class__, self).__init__()
cls.__init__ = __init__
What this does is any class that subclasses CacheObject will now, when created, have its __init__ function wrapped by the parent class—we're replacing it with a new function that calls the original, and then calls super() (the parent's) __init__ function. So now, even if the child class overrides the parent __init__, at the instance's creation time, its __init__ is then wrapped by a function that calls it and then calls its parent.
You can add a decorator to your classes :
def my_decorator(cls):
old_init = cls.__init__
def new_init(self):
self.updatedict = dict()
old_init(self)
cls.__init__ = new_init
return cls
#my_decorator
class SubClass(CacheObject):
pass
if you want to add the decorators to all the subclasses automatically, use a metaclass:
class myMeta(type):
def __new__(cls, name, parents, dct):
return my_decorator(super().__new__(cls, name, parents, dct))
class CacheObject(object, metaclass=myMeta):
pass

Deriving a class from TestCase throws two errors

I have some basic setup/teardown code that I want to reuse in a whole bunch of unit tests. So I got the bright idea of creating some derived classes to avoid repeating code in every test class.
In so doing, I received two strange errors. One, I cannot solve. Here is the unsolvable one:
AttributeError: 'TestDesktopRootController' object has no attribute '_testMethodName'
Here is my base class:
import unittest
import twill
import cherrypy
from cherrypy._cpwsgi import CPWSGIApp
class BaseControllerTest(unittest.TestCase):
def __init__(self):
self.controller = None
def setUp(self):
app = cherrypy.Application(self.controller)
wsgi = CPWSGIApp(app)
twill.add_wsgi_intercept('localhost', 8080, lambda : wsgi)
def tearDown(self):
twill.remove_wsgi_intercept('localhost', 8080)
And here is my derived class:
import twill
from base_controller_test import BaseControllerTest
class TestMyController(BaseControllerTest):
def __init__(self, args):
self.controller = MyController()
BaseControllerTest.__init__(self)
def test_root(self):
script = "find 'Contacts'"
twill.execute_string(script, initial_url='http://localhost:8080/')
The other strange error is:
TypeError: __init__() takes exactly 1 argument (2 given)
The "solution" to that was to add the word "args" to my __init__ function in the derived class. Is there any way to avoid that?
Remember, I have two errors in this one.
It's because you're overriding __init__() incorrectly. Almost certainly, you don't want to override __init__() at all; you should do everything in setUp(). I've been using unittest for >10 years and I don't think I've ever overridden __init__().
However, if you really do need to override __init__(), remember that you don't control where your constructor is called -- the framework calls it for you. So you have to provide a signature that it can call. From the source code (unittest/case.py), that signature is:
def __init__(self, methodName='runTest'):
The safe way to do this is to accept any arguments and just pass 'em up to the base class. Here is a working implementation:
class BaseTest(unittest.TestCase):
def __init__(self, *args, **kwargs):
unittest.TestCase.__init__(self, *args, **kwargs)
def setUp(self):
print "Base.setUp()"
def tearDown(self):
print "Base.tearDown()"
class TestSomething(BaseTest):
def __init__(self, *args, **kwargs):
BaseTest.__init__(self, *args, **kwargs)
self.controller = object()
def test_silly(self):
self.assertTrue(1+1 == 2)
In BaseController's __init__ you need to call unittest.TestCase's __init__ just like you did in TestMyController.
The call to construct a TestCase from the framework may be passing an argument. The best way to handle this for deriving classes is:
class my_subclass(parentclass):
def __init__(self, *args, **kw):
parentclass.__init__(self, *args, **kw)
...

Categories