Before I begin, I know there are many questions that sound a lot like this one, but my question is a little different... So here it is...
As the title may of suggested, I am trying to call a function defined in my main.py script in an imported module. However, this situation is a bit different than that of a circular import situation. I have been doing a lot with pygame recently, and decided that I was gonna make a module that contains classes for buttons, text, sounds, and so on. But I want this file to be generic so it can be used with any game or application I make. Buttons usually have draw functions and stuff like that, so I can easily pass those variables into the functions without problem. The problem comes when I get to the part where I want to check if the button is clicked, and if it is do something. I want to have it set up so that I can pass in a string argument for a command, and use the eval() command on it (python 2.7). However, it throws the error of the function not being defined. I know why this is, but I want to see if there is anything I can do to get around this issue to keep the module as "generic" as possible. Below is a basic set of code to help explain what I want to do.
module1.py
class Button(object):
def __init__(self,x=0,y=0,image=None,command=""):
self.x = x
self.y = y
self.image = image
self.command = command
"""
Image this part filled with draw commands and stuff...
These functions work perfectly fine
"""
#Now here is the issue - local is mouse position
def checkClick(self, local):
#If statments here to determine if mouse over button and
#if mouse is clicked... The part below fails
eval(self.command)
main.py
import module1
import pygame
def quitgame():
pygame.quit()
quit()
local = pygame.mouse.get_pos()
b = module1.Button(command="quitgame")
#At this point lets assume that the mouse is overtop the button and the
#following function in the button will run
b.checkClick(local)
The error, as I said before states that the function I try to call is not defined. I have found a workaround for this, so I don't want answers that tell me how I can change this so it does not take a command as input. I would like however, to make it so I can input a command as an argument. Maybe I am not inputing a command the way I should, but I would like to do it like this, especially because the tkinter module allows you to enter a command as input/a variable. Maybe there is not a way to do this like I wish, but I really want to keep this code as reusable as possible with no changing required between games, and I would rather not have to put this code into my games/applications every time I make them (like I said before the code example I gave was just an example, my actual button code is much larger than what I did above). Like I said before as well, I know that there are many questions that are just like this one, but they have not helped me at all with this issue. The others suggested using scripts that are imported as well which contain addition variables and such, but I would rather not do this. Also, I have a workaround that completely gets rid of the issue, but it is not nearly as neat or easy as this would be.
As always, any help would be appreciated and thanks ahead of time for your answers in case I don't get back to you right away.
I want to have it set up so that I can pass in a string argument for a command, and use the eval() command on it (python 2.7).
No, no, no. Pass it a function:
# In main.py
b = module1.Button(command=quitgame)
# In module1.py
def checkClick(self, local):
...
self.command()
eval is almost never the right tool for any job.
If you don't want to define a function just to pass it as a command parameter, you can use a lambda for short (single-expression) functions:
b = module1.Button(command=lambda: do_whatever(some, arguments))
Related
Long story short i am making an rpg game (text based) in python and need a little help. I am using the time library to give a delay between certain ASCII banners popping up in the console. However, i dont want to have to write out 'time.sleep()' or copy and paste it every time i want to use it. Therefore, i made a function which I would use to shorten the time it would take me to right out 'time.sleep()':
def wait(time):
time.sleep(time)
wait(1)
Whilst in theory i thought this would work (im new to python, i have much to learn yet), it gives me this error:
time.sleep(time)
AttributeError: 'int' object has no attribute 'sleep'
I was wondering if anyone could help/point me in the right direction on how to go about this problem. Thanks in advance!
You are using the same name for the module and the function parameter, so the parameter (a local variable) is shadowing the module (a global variable). Change the parameter:
def wait(how_long):
time.sleep(how_long)
You could use a different name for your parameter, so it doesn't hide the namespace within your function:
def wait(period):
time.sleep(period)
Or you could eliminate your function altogether by importing sleep under a different name:
from time import sleep as wait
I'm building a data visualization app in Python using Tkinter for the GUI and data science libraries Matplotlib, Seaborn, Pandas and NumPy for the backend.
I have the following line of code where button["command"] is the command for a Tkinter Button which is assigned to a function self.create_analytics_dashboard(button_plots) that creates a new Tkinter frame when the button is pressed. The button plots argument is an object responsible for displaying the right plots based on the button pressed.
button["command"] = self.create_analytics_dashboard(button_plots)
There are 3 pertinent Tkinter frames to this problem:
main_dashboard
plots_dashboard
analytics_dashboard
The first 2 frames simply contain buttons that navigate to the next frame and the anaylytics_dashboard frame has buttons which displays the actual visualizations.
The expected order of these frames is as listed above however, due to the line where I assign the button["command"] aforementioned, the program skips the plots_dashboard and goes from the main_dashboard directly to the analytics_dashboard.
There are no error messages however, if I remove the parenthesis and the parameter inside it (button_plots) as shown in the line below the program will display the frames in the right order without skipping the plots_dashboard frame:
button["command"] = self.create_analytics_dashboard
Obviously, I have tried Googling it but all the results just seem to talk about the difference between invoking functions with and without parenthesis or the purposes of each. This article helps explain the difference quite succinctly:
When we call a function with parentheses, the function gets execute and returns the result to the callable.
In another case, when we call a function without parentheses, a function reference is sent to the callable rather than executing the function itself.
So, since the function works as intended when I don't use parenthesis i.e. send a function reference to the callable rather than executing the function itself, I tried using a partial function from the functools library to see if it would work:
from functools import partial
...
button_command = partial(self.create_analytics_dashboard)
button["command"] = button_command(button_plots)
Unfortunately, nothing changed. The plots_dashboard frame was still skipped as before. I need a way to pass button_plots as an argument so it can be used in the self.create_analytics_dashboard function without using parenthesis since this will just execute the function rather than sending a function reference to the callable.
If there is another way of passing a variable from one function in a class to another function in the same class in Python then that could work as well. I simply need the button_plots variable to be available in the self.create_analytics_dashboard function one way or another.
Apologies for the long post but this has been bothering me for a long time so I tried to give as much as detail as possible. If anything in the question does not make sense please let me know. Any help is much appreciated.
Thanks
To use functools.partial you must include the arguments when you create the partial function:
button_command = partial(self.create_analytics_dashboard, button_plots)
button["command"] = button_command
Or, more concisely:
button["command"] = partial(self.create_analytics_dashboard, button_plots)
How can I put an inner python function at the bottom of the code, and call it from the beginning?
For example, VBA dosent care where the phisical inner function code is.
Rexx ( which is also interpreter) also does not care.
How can I do it in Python?
It is much easier to understand the code when the functions are at the bottom.
thanks
You could not do that.
Python runs your code from top to bottom, so it first needs to declare your functions to access them later. You can not first access, later declare, because you would tring to access non exsistent values.
In compiled code, it may works. But in script languages, like python, it does not. As far as I know.
You can do like this:
def main():
print(f(3))
def f(x):
return x * x
if __name__ == "__main__":
main()
Or do you talk about nested functions?
BTW: using the construct in the bottom of my example is anyway good practice to execute the main code only if the module is called as a script. And if it isn't, code shouldn't be extensively executed anyhow before the module is imported (and functions called from outside).
I usually write scripts to calculate or process things for my own consumption. Now I'm trying to write scripts for others.
I use both IDLE and a terminal, but I like just like the IDLE interface and find it more helpful. Today I "discovered" that I can add triple-quoted text under class and def and see them in real time when using IDLE, and I realize I can use those to help others know how to use these classes and methods.
But if run from a terminal this is all lost.
Question: Is it only IDLE users who are seeing these cues while they are typing a line that uses the class or method, or is this something that people using terminal could see while typing if they wanted to? I know that one could type A.__doc__ to see it for example, but the pop-up window is really convenient and helpful.
class A(object):
"""hey A!"""
def __init__(self, x):
"""hey __int__!"""
self.x = x
def sqrx(self):
"""hey sqrx!"""
print self.x**2
(just to see what would happen if)
But if I do this from a terminal all these prompts disappear.
nothing.
The "triple-quoted messages" are docstrings, and they appear in different contexts.
For example:
When hitting ctrl+q (or whatever key is bound to the "Quick Documentation" action) in PyCharm:
There is also an option to display the quick documentation pop-up while typing.
When calling help on the function:
>> help(foo)
Help on function foo in module __main__:
foo()
foo's docstring
I can not tell you about other IDEs as I don't use them.
I just finished watching this video https://www.youtube.com/watch?v=qO4ZN5uZSVg, and even though it teaches 2.0 edition Python, some notes pop up for the 3.0 uses of python. Nevertheless, in the end, some challenges are provided, one of them is this:
def returnTwo():
return 20,30
x,y = returnTwo()
print(x,y)
Whenever i try to see what the conclusion will be, this is what comes up
def returnTwo():
return 20,30
(red X in the 3.5 Shell) x,y = returnTwo()
SyntaxError: invalid syntax.
What can I do?
The python shell allows to interactively run commands. This is very useful when doing quick calculations of to quickly check some small pieces of code.
In this case, you want to define a function. Defining a function is just that: a definition. Later on, you actually call the function and make it run. The issue here is that a function is (often) defined in more than one line. That means, you actually hit enter before you finish to define the function. For that reason, you tell the shell that you finished with an extra enter:
This also applies if you define your function in a single line:
And that's the reason why you get a SyntaxError: The line x, y = returnTwo() is supposed to be in the function, but for that, it would need to indented (to the level of return 20, 30):
Like #jim said, just try pressing enter until you get the >>> prompt again!
Remember that the three little dots do have a meaning too.
This question was already answered in the comments by #helios35 and #jim!
I just elaborate and post as an answer here for future users.