Number of arguments in Tkinter classes - python

relative noob here! I'm running 2.7, if that helps.
I'm trying to call a function defined in my main application class in a different function (I think that's called inheritance?) But I keep having problems with the number of args I put into my function!
Here's the function (is it called a method? if not, what's a method) I'm trying to call:
def student_list_updater(self, list):
self.StudentListDisplay.delete(0, END)
for student in list:
self.StudentListDisplay.insert(END, student)
And here's the function I'm calling it in (it's inheriting student_list_updater, right?):
def OnRemoveClick(self, student_list_updater):
self.student_list_updater = student_list_updater
index = self.StudentListDisplay.curselection()
index = int(index[0])
del student_list_temp[index]
self.student_list_updater(student_list_temp)
Thank you for the help in advance!

It's a little difficult to understand your question without more of the code, but hopefully this answer points you in the right direction.
First, to clarify, methods are just functions that can be accessed through an instance of a class, so yes, these are methods, but they're also functions--don't get too hung up on it. Next, I don't think inheritance is necessary here--inheritance will be one class inheriting attributes from another, and I believe all of your methods are only in one class (correct me if I'm mistaken).
Now, as to your code: it's giving you an error that one of your methods takes a number of arguments, and you gave it a different number. Well, to me, it looks like you only need to pass one argument for this whole process: student_list_temp to student_list_updater(). Once again, I can't say for sure that this will solve your problems, based on the lack of code you posted, but this might work:
def student_list_updater(self, studentlist): #change list to studentlist,
self.StudentListDisplay.delete(0, END) #Python already has a list() method
for student in studentlist:
self.StudentListDisplay.insert(END, student)
def OnRemoveClick(self): #Remove student_list_updater from the args, it has no value
#self.student_list_updater = student_list_updater #this doesn't do anything
index = self.StudentListDisplay.curselection() #This part I can't really comment on
index = int(index[0]) #without knowing the contents of the
del student_list_temp[index] #Listbox and student_list_temp,
self.student_list_updater(student_list_temp) #but this should call student_list_updater()
#and update the Listbox if it's working
The last thing I want to point out is how you call OnRemoveClick() will probably change. If you're calling it from a Button, it would look like this:
self.btn = Button(self, text='GO', command=self.OnRemoveClick)
Note that you're not passing any arguments to it.
Hope that helps. You also might want to take a look at https://docs.python.org/2/tutorial/classes.html and https://docs.python.org/2/tutorial/modules.html to clear up any classes and functions questions you might have.

Related

Python cannot instantiate an imported class

I have been developing a full-stack application that checks for files and uploads them to a cloud. However, I have come across an interesting problem that I was not able to solve.
I have a problem with instantiating a class, as you will see below:
class UploadFastq:
def __int__(self,
some_list, some_str, some_obj, **kwargs):
self.some_list = some_list
self.some_obj = some_obj
self.some_str = some_str
def process(self):
self.some_methods_calling_processes()
...
As you can imagine, I have trimmed the original code for privacy concerns (company dictates, sorry). This class is to handle some-backend related processes, and arguments only contain back related variables. Also, this class is on the different py script, which imports again back-related functions.
Now, the problem is, when I import to another script and try to call and instantiate the class, something funny happens...
from lib.some_back_related_script import UploadFastq
uploads = UploadFastq(some_list=the_list,some_str=the_str,some_obj=the_obj)
uploads.process
OUTPUT:
TypeError: UploadFastq() takes no arguments
I have looked if there are indentation problems, I could not find any. (I am using PyCharm as IDE, and reformatting the file also did not solve)
I have also tried this on an another script(the gui script) and could partially solve it as:
from lib.some_back_related_script import UploadFastq
uploader = UploadFastq()
uploader.__int__( ##TODO how is this possible???)
some_list=the_list,some_str=the_str,some_obj=the_obj
)
However, on the script the class suppose to be called, "__init__" method did not solve the case, and produced this error:
TypeError: UploadFastq.__init__() takes exactly one argument (the instance to initialize)
At this point I am clueless about what is going on and how to solve it. I have experiencing something like this for first time. I also could not find this kind of problem on the internet. soo, I would be much grateful if you could explain how to approach the problem.
P.S.: I work as a bioinformatician/python developer for a quite time and I found many many solutions on this platform. But, this is actually my first question on the stackoverflow!!!
Cheers!
You mispelled the constructor name __init__ as __int__ :
def __int__(self, some_list, some_str, some_obj, **kwargs):
Thus the default constructor (which takes as arguments only the "instance to initialize") was called, and the interpreter is complaining about the given arguments.
TypeError: UploadFastq.__init__() takes exactly one argument (the instance to initialize)

Using constructor parameter variable names during object instantiation in Python?

When declaring a new instance of an object in python, why would someone use the names of the variables from the parameters at instatntiation time? Say you have the following object:
class Thing:
def __init__(self,var1=None,var2=None):
self.var1=var1
self.var2=var2
The programmer from here decides to create an instance of this object at some point later and enters it in the following way:
NewObj = Thing(var1=newVar,var2=otherVar)
Is there a reason why someone would enter it that way vs. just entering the newVar/otherVar variables into the constructor parameters without using "var1=" and "var2="? Like below:
NewObj = Thing(newVar,otherVar)
I'm fairly novice at using python, and I couldn't find anything about this specific sort of syntax even if it seems like a fairly simple/straightforward question
The reason is clarity, not for the computer, but for yourself and other humans.
class Calculation:
def __init__(self, low=None, high=None, mean=None):
self.low=low
self.high=high
self.mean=mean
...
# with naming (notice how ordering is not important)
calc = Calculation(mean=0.5, low=0, high=1)
# without naming (now order is important and it is less clear what the numbers are used for)
calc = Calculation(0, 1, 0.5)
Note that the same can be done for any function, not only when initializing an object.

Python: Need the result of functools.partial(function) to be known as something else

My software supports python to automate tasks (Maya). When I undo or redo in this software it prints the last command, unfortunately for Python this is the memory address of the function rather than something actually useful. So the user sees the output Undo: <functools.partial object at 0x000002235DEDDF48> instead of something actually useful like Undo: Set Key on something at frame x
There appears to be no option to make Maya print a useful result from within it's own functionality, so now I want to ask if there's some obscure way cheese it with python to have that instance call itself something useful in a way the software will print while hopefully not interfering with the functionality. I'll try anything at this point!
def testFunc():
pass
test = partial(testFunc)
test results in <functools.partial object at 0x000002235DEA95E8>
If anyone can think of a more accurate title please edit / suggest.
Thanks to kindall giving me a lead in the comments I was able to find an answer. Subclassing partial and defining __repr__() is the key.
By grabbing the *args on __init__() and storing it as self.result we can use it on __repr__() to return the last argument given to *args as the result given by Maya when using Undo/Redo.
class rpartial(partial):
def __init__(self, *args):
self.result = args[-1]
def __repr__(self):
return self.result
rpartial(function, arg1, arg2, undoredo)
The string given to rpartial on the last line for undoredo is what will be printed by Maya when using Undo/Redo.

Sending a Class' attribute to an outside function in the function call

I'm trying to generalise a function in my script by sending in the Class' attribute through the function call to an outside function, but since I have to call the attribute self.attribute_name inside the class and object_name.attribute.name outside it, I either get an error that no self.attribute_nameexists outside the code or that no object_name.attribute.nameexists inside. The part of my code concerned is as follows(this is just a fragment of the full code with many parts omitted):
class My_Window:
self.get_info_box = Entry(root)
self.entry_button = Button(root, text="Choose", command =lambda: self.retrieve_input(self.get_info_box, solitaire.cards))
def retrieve_input(self, box, entity):
self.user_input = box.get()
entity = input_check(box)
def input_check(which_box): # Function outside class
my_window.which_box.delete(0, "end") # This is want I would like to do if it worked
return 0
my_window = My_Window()
Something in the back of my head tells me it might be possible to use lambda again to accomplish this but I'm still not sure how to use them properly and I couldn't find any active questions covering this specific case.
Anyone have any ideas how to work this out?
I think what you want is
def input_check(which_box):
getattr(my_window,which_box).delete(0, "end")
return 0
input_check("get_info_box")
but its hard to tell for sure
Try it without the my_window.
def input_check(which_box):
which_box.delete(0, "end")
return 0
Incidentally, entity = input_check(box) won't cause solitaire.cards to retain the value returned by input_check, because assignment doesn't propagate upwards like that. If you want to change solitaire.cards, you'll need to do solitaire.cards = input_check(box) instead. If solitaire isn't visible inside retrieve_input, then you'll need to make it an attribute of self.

Is there a reason not to send super().__init__() a dictionary instead of **kwds?

I just started building a text based game yesterday as an exercise in learning Python (I'm using 3.3). I say "text based game," but I mean more of a MUD than a choose-your-own adventure. Anyway, I was really excited when I figured out how to handle inheritance and multiple inheritance using super() yesterday, but I found that the argument-passing really cluttered up the code, and required juggling lots of little loose variables. Also, creating save files seemed pretty nightmarish.
So, I thought, "What if certain class hierarchies just took one argument, a dictionary, and just passed the dictionary back?" To give you an example, here are two classes trimmed down to their init methods:
class Actor:
def __init__(self, in_dict,**kwds):
super().__init__(**kwds)
self._everything = in_dict
self._name = in_dict["name"]
self._size = in_dict["size"]
self._location = in_dict["location"]
self._triggers = in_dict["triggers"]
self._effects = in_dict["effects"]
self._goals = in_dict["goals"]
self._action_list = in_dict["action list"]
self._last_action = ''
self._current_action = '' # both ._last_action and ._current_action get updated by .update_action()
class Item(Actor):
def __init__(self,in_dict,**kwds)
super().__init__(in_dict,**kwds)
self._can_contain = in_dict("can contain") #boolean entry
self._inventory = in_dict("can contain") #either a list or dict entry
class Player(Actor):
def __init__(self, in_dict,**kwds):
super().__init__(in_dict,**kwds)
self._inventory = in_dict["inventory"] #entry should be a Container object
self._stats = in_dict["stats"]
Example dict that would be passed:
playerdict = {'name' : '', 'size' : '0', 'location' : '', 'triggers' : None, 'effects' : None, 'goals' : None, 'action list' = None, 'inventory' : Container(), 'stats' : None,}
(The None's get replaced by {} once the dictionary has been passed.)
So, in_dict gets passed to the previous class instead of a huge payload of **kwds.
I like this because:
It makes my code a lot neater and more manageable.
As long as the dicts have at least some entry for the key called, it doesn't break the code. Also, it doesn't matter if a given argument never gets used.
It seems like file IO just got a lot easier (dictionaries of player data stored as dicts, dictionaries of item data stored as dicts, etc.)
I get the point of **kwds (EDIT: apparently I didn't), and it hasn't seemed cumbersome when passing fewer arguments. This just appears to be a comfortable way of dealing with a need for a large number of attributes at the the creation of each instance.
That said, I'm still a major python noob. So, my question is this: Is there an underlying reason why passing the same dict repeatedly through super() to the base class would be a worse idea than just toughing it out with nasty (big and cluttered) **kwds passes? (e.g. issues with the interpreter that someone at my level would be ignorant of.)
EDIT:
Previously, creating a new Player might have looked like this, with an argument passed for each attribute.
bob = Player('bob', Location = 'here', ... etc.)
The number of arguments needed blew up, and I only included the attributes that really needed to be present to not break method calls from the Engine object.
This is the impression I'm getting from the answers and comments thus far:
There's nothing "wrong" with sending the same dictionary along, as long as nothing has the opportunity to modify its contents (Kirk Strauser) and the dictionary always has what it's supposed to have (goncalopp). The real answer is that the question was amiss, and using in_dict instead of **kwds is redundant.
Would this be correct? (Also, thanks for the great and varied feedback!)
I'm not sure I understand your question exactly, because I don't see how the code looked before you made the change to use in_dict. It sounds like you have been listing out dozens of keywords in the call to super (which is understandably not what you want), but this is not necessary. If your child class has a dict with all of this information, it can be turned into kwargs when you make the call with **in_dict. So:
class Actor:
def __init__(self, **kwds):
class Item(Actor):
def __init__(self, **kwds)
self._everything = kwds
super().__init__(**kwds)
I don't see a reason to add another dict for this, since you can just manipulate and pass the dict created for kwds anyway
Edit:
As for the question of the efficiency of using the ** expansion of the dict versus listing the arguments explicitly, I did a very unscientific timing test with this code:
import time
def some_func(**kwargs):
for k,v in kwargs.items():
pass
def main():
name = 'felix'
location = 'here'
user_type = 'player'
kwds = {'name': name,
'location': location,
'user_type': user_type}
start = time.time()
for i in range(10000000):
some_func(**kwds)
end = time.time()
print 'Time using expansion:\t{0}s'.format(start - end)
start = time.time()
for i in range(10000000):
some_func(name=name, location=location, user_type=user_type)
end = time.time()
print 'Time without expansion:\t{0}s'.format(start - end)
if __name__ == '__main__':
main()
Running this 10,000,000 times gives a slight (and probably statistically meaningless) advantage passing around a dict and using **.
Time using expansion: -7.9877269268s
Time without expansion: -8.06108212471s
If we print the IDs of the dict objects (kwds outside and kwargs inside the function), you will see that python creates a new dict for the function to use in either case, but in fact the function only gets one dict forever. After the initial definition of the function (where the kwargs dict is created) all subsequent calls are just updating the values of that dict belonging to the function, no matter how you call it. (See also this enlightening SO question about how mutable default parameters are handled in python, which is somewhat related)
So from a performance perspective, you can pick whichever makes sense to you. It should not meaningfully impact how python operates behind the scenes.
I've done that myself where in_dict was a dict with lots of keys, or a settings object, or some other "blob" of something with lots of interesting attributes. That's perfectly OK if it makes your code cleaner, particularly if you name it clearly like settings_object or config_dict or similar.
That shouldn't be the usual case, though. Normally it's better to explicitly pass a small set of individual variables. It makes the code much cleaner and easier to reason about. It's possible that a client could pass in_dict = None by accident and you wouldn't know until some method tried to access it. Suppose Actor.__init__ didn't peel apart in_dict but just stored it like self.settings = in_dict. Sometime later, Actor.method comes along and tries to access it, then boom! Dead process. If you're calling Actor.__init__(var1, var2, ...), then the caller will raise an exception much earlier and provide you with more context about what actually went wrong.
So yes, by all means: feel free to do that when it's appropriate. Just be aware that it's not appropriate very often, and the desire to do it might be a smell telling you to restructure your code.
This is not python specific, but the greatest problem I can see with passing arguments like this is that it breaks encapsulation. Any class may modify the arguments, and it's much more difficult to tell which arguments are expected in each class - making your code difficult to understand, and harder to debug.
Consider explicitly consuming the arguments in each class, and calling the super's __init__ on the remaining. You don't need to make them explicit:
class ClassA( object ):
def __init__(self, arg1, arg2=""):
pass
class ClassB( ClassA ):
def __init__(self, arg3, arg4="", *args, **kwargs):
ClassA.__init__(self, *args, **kwargs)
ClassB(3,4,1,2)
You can also leave the variables uninitialized and use methods to set them. You can then use different methods in the different classes, and all subclasses will have access to the superclass methods.

Categories

Resources