I am trying to create a subclass of the ttk.Frame object, but I noticed that I am unable to pass keyword arguments into its parent's init method. Here is a stripped down version of my class:
from tkinter import ttk
class MyFrame(ttk.Frame):
def __init__(self, parent, **kwargs):
super(MyFrame, self).__init__(parent, kwargs)
Here's an example of attempting to create an instance of my class (root is assigned to tkinter.Tk()):
my_frame = MyFrame(root, borderwidth=5)
And here's the error when I try to create an instance of my class:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\csmith\Documents\tmp\frame_test.py", line 5, in __init__
ttk.Frame.__init__(self, parent, kwargs)
TypeError: __init__() takes from 1 to 2 positional arguments but 3 were given
I know it's possible to create Frame objects while using keyword arguments, but I can't seem to figure out why I can't create a class that passes it's keyword arguments to Frame's init method. Any advice is appreciated!
When passing in kwargs to a function, use **kwargs instead. This means 'unpack as kwargs', since you have to put all kwargs in one by one. 'Unpack' means that Python will do that for you.
Change
super(MyFrame, self).__init__(parent, kwargs)
to
super(MyFrame, self).__init__(parent, **kwargs)
Related
I'm trying to code a method from a class that uses a decorator from another class. The problem is that I need information stored in the Class that contains the decorator (ClassWithDecorator.decorator_param). To achieve that I'm using partial, injecting self as the first argument, but when I do that the self, from the class that uses the decorator " gets lost" somehow and I end up getting an error. Note that this does not happen if I remove partial() from my_decorator() and "self" will be correctly stored inside *args.
See the code sample:
from functools import partial
class ClassWithDecorator:
def __init__(self):
self.decorator_param = "PARAM"
def my_decorator(self, decorated_func):
def my_callable(ClassWithDecorator_instance, *args, **kwargs):
# Do something with decorator_param
print(ClassWithDecorator_instance.decorator_param)
return decorated_func(*args, **kwargs)
return partial(my_callable, self)
decorator_instance = ClassWithDecorator()
class WillCallDecorator:
def __init__(self):
self.other_param = "WillCallDecorator variable"
#decorator_instance.my_decorator
def decorated_method(self):
pass
WillCallDecorator().decorated_method()
I get
PARAM
Traceback (most recent call last):
File "****/decorator.py", line 32, in <module>
WillCallDecorator().decorated_method()
File "****/decorator.py", line 12, in my_callable
return decorated_func(*args, **kwargs)
TypeError: decorated_method() missing 1 required positional argument: 'self'
How can I pass the self corresponding to WillCallDecorator() into decorated_method() but at the same time pass information from its own class to my_callable() ?
It seems that you may want to use partialmethod instead of partial:
From the docs:
class functools.partialmethod(func, /, *args, **keywords)
When func is a non-descriptor callable, an appropriate bound method is created dynamically. This behaves like a normal Python function when used as a method: the self argument will be inserted as the first positional argument, even before the args and keywords supplied to the partialmethod constructor.
So much simpler just to use the self variable you already have. There is absolutely no reason to be using partial or partialmethod here at all:
from functools import partial
class ClassWithDecorator:
def __init__(self):
self.decorator_param = "PARAM"
def my_decorator(self, decorated_func):
def my_callable(*args, **kwargs):
# Do something with decorator_param
print(self.decorator_param)
return decorated_func(*args, **kwargs)
return my_callable
decorator_instance = ClassWithDecorator()
class WillCallDecorator:
def __init__(self):
self.other_param = "WillCallDecorator variable"
#decorator_instance.my_decorator
def decorated_method(self):
pass
WillCallDecorator().decorated_method()
Also, to answer your question about why your code didn't work, when you access something.decorated_method() the code checks whether decorated_method is a function and if so turns it internally into a call WillCallDecorator.decorated_method(something). But the value returned from partial is a functools.partial object, not a function. So the class lookup binding won't happen here.
In more detail, something.method(arg) is equivalent to SomethingClass.method.__get__(something, arg) when something doesn't have an attribute method and its type SomethingClass does have the attribute and the attribute has a method __get__ but the full set of steps for attribute lookup is quite complicated.
The following code fails
from collections import namedtuple
import pickle
class Foo(namedtuple("_Foo", ["a", "b"])):
def __new__(cls, **kwargs):
self = super().__new__(cls, **kwargs)
# some custom code
return self
foo = Foo(a=1, b=2)
pickle.loads(pickle.dumps(foo))
with
Traceback (most recent call last):
File "bar.py", line 10, in <module>
pickle.loads(pickle.dumps(foo))
TypeError: __new__() takes 1 positional argument but 3 were given
It works if I remove the new __new__ implementation, but I want to have some custom code there. How do I need to change the implementation of __new__ to not have the error?
I'm running Python 3.5.
The reason is quite simple; in general, the C API passes stuff around as positional parameters, instead of named parameters. Therefore, you just need to provide for this with *args:
from collections import namedtuple
import pickle
class Foo(namedtuple("_Foo", ["a", "b"])):
def __new__(cls, *args, **kwargs):
self = super().__new__(cls, *args, **kwargs)
# some custom code
return self
foo = Foo(a=1, b=2)
pickle.loads(pickle.dumps(foo))
This question already has answers here:
Deriving a class from TestCase throws two errors
(2 answers)
Closed 6 years ago.
I have this plain vanilla unit test, which works as expected, as long I leave out the constructor.
import sys
import unittest
class Instance_test(unittest.TestCase):
def __init__(self):
super(Instance_test, self).__init__()
self.attribute = "new"
def test_something(self):
pass
def test_other(self):
self.assertTrue(True)
pass
def setUp(self):
pass
def tearDown(self):
pass
def suite():
return unittest.makeSuite(Instance_test, "test")
def main():
runner = unittest.TextTestRunner(sys.stdout)
runner.run(suite())
if __name__ == "__main__":
main()
With the constructor in place in get this backtrace:
Traceback (most recent call last):
File "f:\gt\check.py", line 31, in main()
File "f:\gt\check.py", line 28, in main
runner.run(suite())
File "f:\gt\check.py", line 24, in suite
return unittest.makeSuite(Instance_test, "test")
File "C:\Python34\lib\unittest\loader.py", line 374, in makeSuite
testCaseClass)
File "C:\Python34\lib\unittest\loader.py", line 70, in loadTestsFromTestCase
loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames))
File "C:\Python34\lib\unittest\suite.py", line 24, in __init__
self.addTests(tests)
File "C:\Python34\lib\unittest\suite.py", line 60, in addTests
for test in tests:
TypeError: __init__() takes 1 positional argument but 2 were given
What's wrong and how else could I have a central attribute to be shared by different test_xxx methods?
I would use unittest.TestCase's setUp() and tearDown() methods instead of init. Just do the same thing you're doing except use the setUp method.
It is happening because __init__ takes more than one arguments, you forgot to provide args and kwargs as arguments. It should have been like this -
class Instance_test(unittest.TestCase):
def __init__(self, *args, **kwargs):
super(Instance_test, self).__init__(*args, **kwargs)
self.attribute = "new"
Besides why are you overriding __init__ anyway (as 2achary suggested) when setUp method is exactly meant for this purpose.
class Instance_test(unittest.TestCase):
def setUp(self):
self.attribute = 'new'
Just add an argument named methodName to your own __init__ method and you should be good to go:
class Instance_test(unittest.TestCase):
def __init__(self, methodName='runTest'):
super(Instance_test, self).__init__(methodName=methodName)
self.attribute = "new"
...
The base TestCase.__init__ signature is as follows:
class TestCase(object):
def __init__(self, methodName='runTest'):
...
As you can see, it takes an additional methodName argument, and there is no constructor without it, hence the failure you encountered.
I am trying to subclass pysam's Tabixfile class and add additional attributes on instantiation.
class MyTabixfile(pysam.Tabixfile):
def __init__(self, filename, mode='r', *args, **kwargs):
super().__init__(filename, mode=mode, *args, **kwargs)
self.x = 'foo'
When I try to instantiate my MyTabixfile subclass, I get a TypeError: object.__init__() takes no parameters:
>>> mt = MyTabixfile('actn2-oligos-forward.tsv.gz')
Traceback (most recent call last):
File "<ipython-input-11-553015ac7d43>", line 1, in <module>
mt = MyTabixfile('actn2-oligos-forward.tsv.gz')
File "mytabix.py", line 4, in __init__
super().__init__(filename, mode=mode, *args, **kwargs)
TypeError: object.__init__() takes no parameters
I also tried calling the Tabixfile constructor explicitly:
class MyTabixfile(pysam.Tabixfile):
def __init__(self, filename, mode='r', *args, **kwargs):
pysam.Tabixfile.__init__(self, filename, mode=mode, *args, **kwargs)
self.x = 'foo'
but this still raises TypeError: object.__init__() takes no parameters.
This class is actually implemented in Cython; the constructor code is below:
cdef class Tabixfile:
'''*(filename, mode='r')*
opens a :term:`tabix file` for reading. A missing
index (*filename* + ".tbi") will raise an exception.
'''
def __cinit__(self, filename, mode = 'r', *args, **kwargs ):
self.tabixfile = NULL
self._open( filename, mode, *args, **kwargs )
I read through the Cython documentation on __cinit__ and __init__ which says
Any arguments passed to the constructor will be passed to both the
__cinit__() method and the __init__() method. 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.
The pysam developers did take the care to add *args and **kwargs to the Tabixfile.__cinit__ method, and my subclass __init__ matches the signature of __cinit__ so I do not understand why I'm unable to override the initialization of Tabixfile.
I'm developing with Python 3.3.1, Cython v.0.19.1, and pysam v.0.7.5.
The documentation is a little confusing here, in that it assumes that you're familiar with using __new__ and __init__.
The __cinit__ method is roughly equivalent to a __new__ method in Python.*
Like __new__, __cinit__ is not called by your super().__init__; it's called before Python even gets to your subclass's __init__ method. The reason __cinit__ needs to handle the signature of your subclass __init__ methods is the exact same reason __new__ does.
If your subclass does explicitly call super().__init__, that looks for an __init__ method in a superclass—again, like __new__, a __cinit__ is not an __init__. So, unless you've also defined an __init__, it will pass through to object.
You can see the sequence with the following code.
cinit.pyx:
cdef class Foo:
def __cinit__(self, a, b, *args, **kw):
print('Foo.cinit', a, b, args, kw)
def __init__(self, *args, **kw):
print('Foo.init', args, kw)
init.py:
import pyximport; pyximport.install()
import cinit
class Bar(cinit.Foo):
def __new__(cls, *args, **kw):
print('Bar.new', args, kw)
return super().__new__(cls, *args, **kw)
def __init__(self, a, b, c, d):
print('Bar.init', a, b, c, d)
super().__init__(a, b, c, d)
b = Bar(1, 2, 3, 4)
When run, you'll see something like:
Bar.new (1, 2, 3, 4) {}
Foo.cinit 1 2 (3, 4) {}
Bar.init 1 2 3 4
Foo.init (1, 2, 3, 4) {}
So, the right fix here depends on what you're trying to do, but it's one of these:
Add an __init__ method to the Cython base class.
Remove the super().__init__ call entirely.
Change the super().__init__ to not pass any params.
Add an appropriate __new__ method to the Python subclass.
I suspect in this case it's #2 you want.
* It's worth noting that __cinit__ definitely isn't identical to __new__. Instead of getting a cls parameter, you get a partially-constructed self object (where you can trust __class__ and C attributes but not Python attributes or methods), the __new__ methods of all classes in the MRO have already been called before any __cinit__; the __cinit__ of your bases gets called automatically instead of manually; you don't get to return a different object besides the one that's been requested; etc. It's just that it's called before the __init__, and expected to take pass-through parameters, in the same way as __new__ is.
I would have commented rather than posting an answer but I don't have enough StackOverflow foo as yet.
#abarnert's post is excellent and very helpful. I would just add a few pysam specifics here as I have just done subclassing on pysam.AlignmentFile in a very similar way.
Option #4 was the cleanest/easiest choice which meant only changes in my own subclass __new__ to filter out the unknown params:
def __new__(cls, file_path, mode, label=None, identifier=None, *args, **kwargs):
# Suck up label and identifier unknown to pysam.AlignmentFile.__cinit__
return super().__new__(cls, file_path, mode, *args, **kwargs)
It should also be noted that the pysam file classes don't seem to have explicit __init__ method's, so you also need to omit param pass through as that goes straight to object.__init__ which does not accept parameters:
def __init__(self, label=None, identifier=None, *args, **kwargs):
# Handle subclass params/attrs here
# pysam.AlignmentFile doesn't have an __init__ so passes straight through to
# object which doesn't take params. __cinit__ via new takes care of params
super(pysam.AlignmentFile, self).__init__()
I'm trying to create a custom control based on wx.richtext.RichTextCtrl and I'm running into a problem. Whenever I attempt to add the custom control to a sizer, wxPython chokes with the error
Traceback (most recent call last):
File "pyebook.py", line 46, in <module>
frame = MainFrame(None, 'pyebook')
File "pyebook.py", line 14, in __init__
self.mainPanel.GetSizer().Add(ReaderControl(self.mainPanel), 1, wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
File "/usr/local/lib/wxPython-unicode-2.8.11.0/lib/python2.6/site-packages/wx-2.8-mac-unicode/wx/_core.py", line 12685, in Add
return _core_.Sizer_Add(*args, **kwargs)
TypeError: wx.Window, wx.Sizer, wx.Size, or (w,h) expected for item
The custom control is at this time extremely simple and looks like this
class ReaderControl(wx.richtext.RichTextCtrl):
def __init__(self, parent, id=-1, value=''):
wx.richtext.RichTextCtrl(parent, id, value, style=wx.richtext.RE_READONLY, name='ReaderControl')
The code I'm using to add the control to the sizer is:
self.mainPanel.GetSizer().Add(ReaderControl(self.mainPanel), 1, wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
Any ideas what I'm doing wrong here?
I think you need to call __ init __ explicitly, so you can pass in 'self'. Otherwise, you are just creating a new instance of RichTextCtrl, not initialising your subclass properly.
IOW:
class ReaderControl(wx.richtext.RichTextCtrl):
def __init__(self, parent, id=-1, value=''):
wx.richtext.RichTextCtrl.__init__(self, parent, id, value, style=wx.richtext.RE_READONLY, name='ReaderControl'