The Python assignment operator, function definitions, and variable definitions - python

Well... I was having a terrible time getting part of my code working, but I rearranged things and it suddenly started working correctly. Not sure what I did to be honest, so I guess that will be the subject of this question. I'm building a simple text-based card game that uses decks uploaded from two .txt files. It's aimed at Magic: the Gathering, but would probably work with others if people got creative with it. To provide a rough overview, here is how things are arranged:
import random
def shuffle(board1):
def game():
#board=[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]
#performs most of the actions relating to the game
board[0]=20
board[10]=20
def gameboard(board2):
#displays game board
def draw(board3, numcards, player):
#draws cards
def upload(deckname):
#uploads cards from file
def life(board4):
#asks about which player the life total is changing on, by how much, etc.
#and then does it
def maketoken(board5):
#creates tokens, counters, etc. based on user input
def move(board5):
#accepts user input and moves cards from zone to zone
def play(board6):
#handles casting spells, using abilities, triggered abilities, etc.
#main body of program is below function definitions
board=[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]
deckname1=input("\nWhat is the name of deck 1?")
deckname2=input("\nWhat is the name of deck 2?")
deck1=upload(deckname1)
deck2=uplaod(deckname2)
board[1]=deck1
board[11]=deck2
#this is where a lot of the other variables get set
game()
(note: most of the code has been removed for brevity and prettiness, as my code is pretty ugly)
I have a college-level C++ background, and just recently decided to pick up ye olde keyboard for the heck of it, so the assignment operator (=) not working the way I expect is driving me CRAZY. Therefore, I was also wondering if there was a way to get the functionality of the C++ '=' in Python, since I upload the decks from .txt files, and want to be through with the upload() function as soon as that's done (I use deck1=upload(deckname) (same for deck2). I want to use 'deck1' and 'deck2' to refill the decks after each game, but if I understand how '=' works in python, entering board[1]=deck1 means board[1] will point to the storage area of deck1 and changes to board[1] will change deck1, BUT I DON'T WANT THAT... GRRRR!!!!!!11). I'm sure there's a solution out there somewhere since it's making me nutty, but I haven't been able to find it. Thanks!!!
edit: This was the error I received when things were set up this way:
Traceback (most recent call last):
File "C:\Users\inventor487\Desktop\simplepy.py", line 444, in <module>
game()
File "C:\Users\inventor487\Desktop\simplepy.py", line 114, in game
board[1]=deck1
UnboundLocalError: local variable 'board' referenced before assignment
Summary:
Do I need to pass board to the game() function, even if it's set up as a global variable (or at least I thought it was)? Everything seems to work fine when I assign it inside the game() function (commented out to show this). (edit: nevermind... I'm an idiot.)
Does assigning part of board to a value inside game() make it a local variable (e.g. where I have board[0]=20)? (edit: yes, it does apparently...)

As you've discovered, the = operator in Python doesn't make a copy of an object like it does in C++ for example. If you want to make a copy to store in another variable you have to be explicit about it.
board[1] = deck1[:] # the slicing operator copies a subset or the whole list
A more general method is to use the copy module
import copy
board[1] = copy.copy(deck1)
board[1] = copy.deepcopy(deck1)

edit: a class solves all of this very easily, and much more cleanly. Having a separate variable for each zone, etc. makes everything a lot smoother. Thanks for the help, though guys. Much appreciated.

Related

return Point(int(firstArg), int(secondArg)) # firstArg and secondArg are just x and y number values

I am trying to have pyautogui move the mouse whenever it detects a color but for some reason whenever I try running it keeps on prompting this error, I have run this code before and it worked perfectly fine. pls help
Code
Output
You are getting that error because "locateAllOnScreen" returns a "generator" which can be looped through which contains all instances of the image. You may be looking for "locateOnScreen".
Here are some example on how to use the two different functions:
# Will loop through all instances of 'img.png'
for pos in pyautogui.locateAllOnScreen('img.png')
# Code here...
# Will find one instance and return the position
pyautogui.locateOnScreen('img.png')
This link has some good information on the different methods

Python: binding (otherwise) undefined names to an object

This question derives from a very specific use-case. Some clever person has put together a repl.it that allows you to use the Python version of the Processing libraries on the web:
https://repl.it/#templates/Python-Processing
The Python code looks like this:
from browser import document, window, alert
def sketch(p):
#this p is needed. it will be the processing sketch itself.
# to do things like background(0) instead do p.background(0)
def setup():
p.createCanvas(700, 410)
p.background(0)
p.rectMode(p.CENTER)
def draw():
#p.background(0)
p.fill(255,255,0,128)
p.ellipse(p.mouseX,p.mouseY,50,50)
def mousePressed(self):
p.background(0)
def keyPressed(self):
if p.key==" ":
print("Hallo")
p.setup = setup
p.draw = draw
p.mousePressed = mousePressed
p.keyPressed = keyPressed
myp5 = window.p5.new(sketch)
The issue is that I'd really like the user code to just look like this (as it would in a desktop version of the Python version of Processing):
def setup():
createCanvas(700, 410)
background(0)
rectMode(CENTER)
def draw():
fill(255,255,0,128)
ellipse(mouseX, mouseY, 50, 50)
def mousePressed(self):
background(0)
def keyPressed(self):
if key==" ":
print("Hallo")
...but as you can see in the repl, all of the calls have to be prefaced by "p." so that they are acting on the processing sketch, and wrapped in a function that takes that sketch as an argument.
I guess the question boils down to this: is there a way of wrapping things so that otherwise undefined global variables/functions (e.g. createCanvas) get bound to a particular object (in this case resolving to p.createCanvas)?
I recognize that it's kind of unpythonic, since it's about making implicit what was otherwise explicit, but in this case, it would serve to make the code more in the spirit of the Processing libraries.

Last imported file overwrites statements from previous files. Better ways of specifying imported variables?

Hey stackoverflow community,
i’m new to this forum and to python developing in general and have a problem with Alexa/ Python overriding the similar named variable from different files.
In my language learning skill I want Alexa to specifically link a “start specific practice” intent from the user to a specific practice file and from this file to import an intro, keyword and answer to give back to the user.
My problem with the importing, is that Python takes the last imported file and overrides the statements of the previous files.
I know I could probably change the variable names according to the practices but then wouldn't I have have to create a lot of individual handler functions which link the user intent to a specific file/function and basically look and act all the same?
Is there a better way more efficient of doing the specifying of those variables when importing or inside the functions?
import files and variables
from übung_1 import intro_1, keywords_1, real_1
from übung_2 import intro_1, keywords_1, real_1
working with the variables
def get_practice_response(practice_number):
print("get_practice_response")
session_attributes = {}
card_title = "Übung"
number = randint(0, len(keywords_1))
print(intro_1 + keywords_1[number])
speech_output = intro_1 + keywords_1[number]
session_attributes["answer"] = real_1[number]
session_attributes["practice_number"] = practice_number
session_attributes["keyword"] = keywords_1[number]
reprompt_text = "test"
should_end_session = False
return build_response(session_attributes, build_speechlet_response(
card_title, speech_output, reprompt_text, should_end_session))
I expected giving out the content of the specifically asked file and not variable content from the most recent files.
Sadly I haven't found a solution for this specific problem and hope someone could help me pointing me in the right direction.
Thank you very much in advance.
Might be easiest to import the modules like so:
import übung_1
import übung_2
The refer to the contents as übung_1.intro_1, übung_2.intro_1, übung_1.keywords_1 and so on.
As you point out, these two lines
from übung_1 import intro_1, keywords_1, real_1
from übung_2 import intro_1, keywords_1, real_1
don't work the way you want because the second import overrides the first. This has to happen because you can't have two different variables in the same namespace called intro_1.
You can get around this by doing
import übung_1
import übung_2
and then in your code you explicitly state the namespace you want:
print(übung_1.intro_1 + übung_1.keywords_1[number])

Seemingly random error appearing when executing code in Python 3.3

Below is a portion of code from a program I have written that is by all means pretty basic.
pc1 = random.choice(cards)
cca1 = random.choice(cards)
while (pc1 == cca1):
cca1 = random.choice(cards)
ccb1 = random.choice(cards)
while (pc1 == ccb1) or (cca1 == ccb1):
ccb1 = random.choice(cards)
pc1, cca1 and ccb1 are just names of variables, shortened for ease of use. What this portion of code does is try to take 3 entries from a dictionary named cards. It uses the while functions to ensure that the chosen cards are not the same; they will always be different.
This goes on until I have 9 unique variables from my dictionary of 52, and it works fine except for sometimes producing the following error:
Traceback (most recent call last):
File "C:\Python33\Programs\Poker\1.0.py", line 231, in <module>
ccc2 = random.choice(cards)
File "C:\Python33\lib\random.py", line 252, in choice
return seq[i]
KeyError: 0
The variable in the error above (ccc2) is just a continuation of the previously shown code, and the variable supposedly causing the error changes every time.
The error only occurs sometimes (sometimes the program runs fine, other times it shows the error), and the line it occurred on also changes with every appearance.
I understand my code is inefficient but I'm really just looking to stop this error, and maybe some useful ideas/hints on how to improve.
Once again; does what its supposed to but unidentifiably returns the error mentioned at seemingly random times with a seemingly random site of cause.
Thanks in advance!
The way random.choice works is designed for sequences, not mappings. It picks indices, so will sometimes try cards[0], which evidently isn't a valid key. The reason that the error appears random is, of course, because it depends on the value picked by random!
You can fix this by explicitly choosing from a sequence:
random.choice(list(cards))
To improve your code more generally, note that random also includes sample:
rcards = random.sample(list(cards), 3) # pick three random cards
Note that in both cases, we randomly choose keys from the dictionary.

PyMel Rotate Raises Error

I want to randomly rotate a list of objects on a given axis with a random amount retrieved from a specified range.
This is what I came up with:
import pymel.core as pm
import random as rndm
def rndmRotateX(targets, axisType, range=[0,180]):
for obj in targets:
rValue=rndm.randint(range[0],range[1])
xDeg='%sDeg' % (rValue)
#if axisType=='world':
# pm.rotate(rValue,0,0, obj, ws=1)
#if axisType=='object':
# pm.rotate(rValue,0,0, obj, os=1)
pm.rotate(xDeg,0,0,r=True)
targetList= pm.ls(sl=1)
randRange=[0,75]
rotAxis='world'
rndmRotateX(targetList,rotAxis,randRange)
Im using pm.rotate() because it allows me to specify whether I want the rotations done in world or obj space (unlike setAttr, as far as I can tell).
The problem is, it raises this error when I try to run this:
# Error: MayaNodeError: file C:\Program Files\Autodesk\Maya2012\Python\lib\site-packages\pymel\internal\pmcmds.py line 140: #
It must be something with they way I enter the arguments for pm.rotate() (Im assuming this due to the line error PyMel spits out, which has to do with its arguments conversion function), but I cant figure out for the life of me wth I did wrong. :/
I think the problem is in this line
pm.rotate(rValue,0,0, obj, os=1)
obj should be the first argument, so it should be
pm.rotate(obj, (rValue,0,0), os=1)
but to make it even prettier you could use
obj.setRotation((rValue,0,0), os=1)
And also. Use pm.selected() instead of pm.ls(sl=1). It looks better
Another way to go about doing this..
from pymel.core import *
import random as rand
def rotateObjectsRandomly(axis, rotateRange):
rotateValue = rand.random() * rotateRange
for obj in objects:
PyNode(str(selected()) + ".r" + axis).set(rotateValue)
objectRotation = [[obj, obj.r.get()] for obj in selected()]
print "\nObjects have been rotated in the {0} axis {1} degrees.\n".format(axis, rotateValue)
return objectRotation
rotateObjectsRandomly("z", 360)
Since rand.random() returns a random value between 0 - 1, I just multiplied that by the rotateRange specified by the user..or in my preference I would just do away with that all together and just multiply it by 360...
You also don't need all the feedback I just think it looks nice when ran..
Objects have been rotated in the z axis 154.145898182 degrees.
# Result: [[nt.Transform(u'myCube'), dt.Vector([42.6541437517, 0.0, 154.145898182])]] #
Just as a straight debug of what you've got...
Issue 01: it's case sensitive
pm.rotate("20deg",0,0) will work fine, but pm.rotate("20Deg",0,0) will fail and throw a MayaNodeError because it thinks that you're looking for a node called '20Deg'. Basically, you want to build your string as per: xDeg='%sdeg' % (rValue)
Issue 02: you're relying on pm.rotate()'s implicit "will apply to selected objects" behaviour
You won't see this til you apply the above fix, but if you have two selected objects, and ran the (patched) rndmRotateX function on them, you'd get both objects rotating by the exact same amount, because pm.rotate() is operating on the selection (both objects) rather than a per-object rotation.
If you want a quick fix, you need to insert a pm.select(obj) before the rotate. And you possibly want to save the selection list and restore it...however IMHO, it's a Really Bad Idea to rely on selections like this, and so I'd push you towards Kim's answer.

Categories