Spurious arguments to __new__ when using metaclass - python-3.x

I am trying to learn about metaclasses in python 3.7 and have the following code
class Foo(type):
def __new__(cls, *args, **kwargs):
return super().__new__(cls, *args, **kwargs)
def __init__(self, *args, **kwargs):
return super().__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
return super().__call__(cls, *args, **kwargs)
class Bar(metaclass=Foo):
def __new__(cls, *args, **kwargs):
print(cls)
print(args)
print(kwargs)
return super().__new__(cls, *args, **kwargs)
def __init__(self, *args, **kwargs):
return super().__init__(*args, **kwargs)
b = Bar()
When I run it I get the output
<class '__main__.Bar'>
(<class '__main__.Bar'>,)
{}
and the error
File "meta/main.py", line 91, in __new__
return super().__new__(cls, *args, **kwargs)
TypeError: object.__new__() takes no arguments
where the line correpondse to the __new__ call in Bar
I can't work out why the second <class '__main__.Bar'> is being passed in. If I change Bar to not use the Foo metaclass (i.e. change class Bar(metaclass=Foo): to class Bar:) I get
<class '__main__.Bar'>
()
{}
and no errors (as I would expect). Thanks for any help

You are passing in an extra argument on calls:
def __call__(cls, *args, **kwargs):
return super().__call__(cls, *args, **kwargs)
__call__ is not an implicit static method, drop that cls argument:
def __call__(cls, *args, **kwargs):
return super().__call__(*args, **kwargs)

Related

Passing non-default arguments to Tk class constructor

I want to pass two Queues objects to a class the inherits Tk.tk.
main.py code:
qin = Queue()
qout = Queue()
gui = GUI(qin,qout)
gui.mainloop()
gui.py code:
class GUI(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs) #if im calling tk.Tk.__init__(self) nothing is displayed to the screen so *args, **kwargs is requirement for some reason, not sure why
self.qin = args[0]
self.qout = args[1]
...
error:
gui = GUI(qin,qout)
File "/home/a/gui.py", line 20, in __init__
tk.Tk.__init__(self, *args, **kwargs)
File "/usr/lib/python3.9/tkinter/__init__.py", line 2270, in __init__
self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
TypeError: create() argument 1 must be str or None, not Queue
How to fix this error?
Thanks.
Just pop the args off of kwargs before passing kwargs to the superclass init.
class GUI(tk.Tk):
def __init__(self, *args, **kwargs):
qin = kwargs.pop("qin", None)
qout = kwargs.pop("qout", None)
tk.Tk.__init__(self, *args, **kwargs)
Or, if you don't care about the caller being able to pass in other arguments through to the superlcass, just define them as normal arguments:
class GUI(tk.Tk):
def __init__(self, qin, qout):
tk.Tk.__init__(self)

wrapper class with __enter__/__exit__ in python3

I have a code that perfectly works in Python 2, but it doesn't work in Python 3.
There is an aggregator class data and a few classes to work with specific data formats.
class data():
def __init__(self, file, format="x"):
if format == "x":
self.data = xdata(file)
elif format == "y":
self.data = ydata(file)
# Redirect functions to the specific class
self.__enter__ = self.data.__enter__
self.__exit__ = self.data.__exit__
class xdata():
def __init__(self, file):
#do something
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
#do something
class ydata():
def __init__(self, file):
#do something
def __enter__(self):
return self
def __exit__(self,exc_type, exc_value, traceback):
#do something
In python2 I was able to execute the following code without any errors,
with data("newfile.x") as sd:
#do something with data
but python3 returns an error AttributeError: __enter__
Any ideas on how to fix it?
__enter__ and __exit__ will be resolved as descriptors, which means that resolution bypasses the attributes of the class. You can provide your own descriptors for __enter__ and __exit__ using property:
class xdata():
def __init__(self, file):
self.attr = 'x'
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
class ydata():
def __init__(self, file):
self.attr = 'y'
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
class data():
def __init__(self, file, format="x"):
if format == "x":
self.data = xdata(file)
elif format == "y":
self.data = ydata(file)
# Redirect functions to the specific class
#property
def __enter__(self):
return self.data.__enter__
#property
def __exit__(self):
return self.data.__exit__
with data("", "x") as d:
print(d.attr) # x
with data("", "y") as d:
print(d.attr) # y

When I'm trying to open file into tkinter text widget, but receiving error "'str' object has no attribute 'tk'"

While building an editor based on TKINTER, I have added several options into menu bar, but open command returns some error. 'str' object has no attribute 'tk'
Please be informed, I was checking with code structure & tkinter text insert document. But cant get any suitable way for this issue.
self.tk.call((self._w, 'insert', index, chars) + args)
I was checking, for Custom.insert(END, contents, END + '-1c').
But still cant get rid of this on going issue.
Sorry for my bad English &
Thanks in advance.
root = tk.Tk()
def open():
print('Open an existing file from the system.')
# return 'EOF'
file = filedialog.askopenfile(parent=root, mode='rb', title='Select a file')
if file != None:
contents = file.read()
#import pdb;
#pdb.set_trace()
CustomText.insert(END, contents, END + '-1c')
file.close()
#t = threading.Thread(target=open__file)
class TextLineNumbers(tk.Canvas):
def __init__(self, *args, **kwargs):
tk.Canvas.__init__(self, *args, **kwargs)
self.textwidget = None
def attach(self, text_widget):
self.textwidget = text_widget
def redraw(self, *args):
'''redraw line numbers'''
self.delete("all")
i = self.textwidget.index("#0,0")
while True :
dline= self.textwidget.dlineinfo(i)
if dline is None: break
y = dline[1]
linenum = str(i).split(".")[0]
self.create_text(2,y,anchor="nw", text=linenum)
i = self.textwidget.index("%s+1line" % i)
class CustomText(tk.Text):
def __init__(self, *args, **kwargs):
tk.Text.__init__(self, *args, **kwargs)
# create a proxy for the underlying widget
self._orig = self._w + "_orig"
self.tk.call("rename", self._w, self._orig)
self.tk.createcommand(self._w, self._proxy)
def _proxy(self, *args):
# let the actual widget perform the requested action
cmd = (self._orig,) + args
result = self.tk.call(cmd)
# generate an event if something was added or deleted,
# or the cursor position changed
if (args[0] in ("insert", "replace", "delete") or
args[0:3] == ("mark", "set", "insert") or
args[0:2] == ("xview", "moveto") or
args[0:2] == ("xview", "scroll") or
args[0:2] == ("yview", "moveto") or
args[0:2] == ("yview", "scroll")
):
self.event_generate("<<Change>>", when="tail")
# return what the actual widget returned
return result
class Example(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.text = CustomText(self)
self.vsb = tk.Scrollbar(orient="vertical", command=self.text.yview)
self.text.configure(yscrollcommand=self.vsb.set)
self.text.tag_configure("bigfont", font=("Helvetica", "24", "bold"))
self.linenumbers = TextLineNumbers(self, width=30)
self.linenumbers.attach(self.text)
self.vsb.pack(side="right", fill="y")
self.linenumbers.pack(side="left", fill="y")
self.text.pack(side="right", fill="both", expand=True)
self.text.bind("<<Change>>", self._on_change)
self.text.bind("<Configure>", self._on_change)
def _on_change(self, event):
self.linenumbers.redraw()
menubar = Menu(root, background='#000099', foreground='white',
activebackground='#004c99', activeforeground='white')
filemanu = Menu(menubar, tearoff=0, background="grey", foreground='black',
activebackground='#004c99', activeforeground='white')
menubar.add_cascade(label='File', menu=filemanu)
filemanu.add_command(label='open', command=open)
root.config(bg='#2A2C2B', menu=Example.menubar)
if __name__ == "__main__":
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\\AppData\Local\Programs\Python\Python37-32\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "C:/Users//PycharmProjects/Sub_packages/open_issue.py", line 15, in open
CustomText.insert(END, contents, END + '-1c')
File "C:\Users\\AppData\Local\Programs\Python\Python37-32\lib\tkinter\__init__.py", line 3272, in insert
self.tk.call((self._w, 'insert', index, chars) + args)
AttributeError: 'str' object has no attribute 'tk'```
Consider this code:
CustomText.insert(END, contents, END + '-1c')
You are calling the insert method on the class rather than an instance of a class.
You need to save a reference to the instance, and provide a way for your function to access that instance. In your case, the instance is saved as an attribute on the Example class, so you can save a reference to the instance of Example and use that:
def open():
...
example.text.insert(END, contents, END + '-1c')
...
if __name__ == "__main__":
example = Example(root)
example.pack(side="top", fill="both", expand=True)
root.mainloop()
Even better would be to move open into the Example class so that you can use self.text. You also should move the code for creating the menu into the __init__ function.
class Example(tk.Frame):
def __init__(self, *args, **kwargs):
...
menubar = Menu(root, background='#000099', foreground='white',
activebackground='#004c99', activeforeground='white')
filemanu = Menu(menubar, tearoff=0, background="grey", foreground='black',
activebackground='#004c99', activeforeground='white')
menubar.add_cascade(label='File', menu=filemanu)
filemanu.add_command(label='open', command=self.open)
...
def open(self):
...
self.text.insert(END, contents, END + '-1c')
...

Memoized objects still have their __init__() invoked?

So I am creating a memoized class, and have been observing a strange behavior.
Here's the snippet of the code:
class SomeClass(object):
_Memoized = {}
def __new__(cls, id: str, *args, **kwargs):
if id not in cls._Memoized:
print('New Instance')
cls._Memoized[id] = super(SomeClass, cls).__new__(cls, *args, **kwargs)
else:
print('Existing Instance')
return cls._Memoized[id]
def __init__(self, id: str):
print('Running init')
self.id = id
def test():
w1 = SomeClass(id='w1')
wx = SomeClass(id='w1')
print(id(w1), id(wx), id(w1) == id(wx))
test()
Running the above code results in:
New Instance
Running init
Existing Instance
Running init <===-------------------???
140008534476784 140008534476784 True
My questions: During the second invocation of SomeClass(), why does it execute the __init__ constructor? Wasn't the __init__ constructor only invoked on instantiating? Is there a way to prevent the __init__ from being invoked?
The purpose of __new__ is to create a new instance, which is why Python calls __init__ on it. You might instead override __call__ on a metaclass to avoid creating a new instance.
class MemoMeta(type):
def __init__(self, name, bases, namespace):
super().__init__(name, bases, namespace)
self.cache = {}
def __call__(self, id_, *args, **kwargs):
if id_ not in self.cache:
print('New Instance')
self.cache[id_] = super().__call__(id_, *args, **kwargs)
else:
print('Existing Instance')
return self.cache[id_]
class SomeClass(metaclass=MemoMeta):
def __init__(self, id_, *args, **kwargs):
print('Running init')
self.id = id_
def test():
w1 = SomeClass(id_='w1')
wx = SomeClass(id_='w1')
print(id(w1), id(wx), id(w1) == id(wx))
test()

Subclassing pyqtgraph.ParameterTree issue

I've been using the ParameterTree class very effectively, I like it a lot! I have a lot of parameters so I thought of subclassing ParameterTree in order to clear up some code. Unfortunately when I do this I get a weird-looking non-functional ParameterTree:
All the edit fields and comboboxes are gone
This is how I'm subclassing it
class CamParamTree(ParameterTree):
def __init__(self, *args, **kwargs):
super(ParameterTree, self).__init__(*args, **kwargs)
params = [.......]
self.p = Parameter.create(name='params', type='group', children=params)
self.setParameters(self.p, showTop=False)
and then in the main GUI I instanciate it like this:
class GUI(QtGui.QMainWindow):
def __init__(self, *args, **kwargs):
self.tree = CamParamTree()
What am I doing wrong?
Cheers
Just a simple error: you need
super(CamParamTree, self).__init__(*args, **kwargs)
instead of
super(ParameterTree, self).__init__(*args, **kwargs)

Resources