I am working on procedural generation of character concepts. As part of this, I have set variables to change the pronouns in text so they will be correct for the generated character. However, for some reason, my code refuses to assign variables for this, resulting in output like "Bob is a Caucasian . works as..." It should read "Bob is a Caucasian male. He works as..."
Everywhere a pronoun or the word male or female should be, it prints nothing. Not to console, nor to file. When I strip the entire script down to just the code that calls for the function to run, and the function itself, it still wont assign the variables.
Other systems that use functions to assign variables are working 100% fine.
I've rewritten new functions 3 times to try different approaches to this. I've tried making the variables global. I don't know what else to try.
Here is the function.
def pronouns(sex1):
pronoun1, pronoun1alternate, pronoun2, pronoun2alternate = "", "", "", ""
if sex1 == "male":
pronoun1 = "He"
pronoun1alternate = "he"
pronoun2 = "His"
pronoun2alternate = "his"
elif sex1 == "female":
pronoun1 = "She"
pronoun1alternate = "she"
pronoun2 = "Her"
pronoun2alternate = "her"
return pronoun1, pronoun1alternate, pronoun2, pronoun2alternate
The variable sex1 is created much later on, and cannot be created within this function as I may want to expand this program for fantasy and sci-fi character concepts later on and decided to handle sex selection with each individual race in case I want to do something like Species-8472 in the future.
Here is how the function called later on.
p1, p1a, p2, p2a = pronouns(sex1)
I have been informed that python passes the values but not names. It didn't work when the function outputted p1 p1a and so on either. I was told using different names in the same order might fix the problem. It did not.
Here his how the variables are supposed to be used.
description = name + " is a " + race + " " + sex + ". " + p1 + " has " + eyes + " eyes. " + colorapp
It has no problem filling out the name, race, eye color, and the colorapp variable, but cannot ever, regardless of what I do, fill out sex, or any pronoun values.
No error messages occur when this code is run in isolation. It will print blank lines if asked to just print the pronouns or sex. However, sex1 will print successfully, as will almost every other variable in the program.
How are you assigning value to variable sex1? Make sure it is passed in lowercase. If this is the problem you can use sex1.lower() on lines 3 and 8 of the function.
Related
How to print variable name by input, Example:
a = 1
b = 2
what_variable = input('Which Variable?: ') #User for example introduces 'b'
Console: 2
You can write
print(globals()[what_variable])
but it's not a good approach. Use a dict instead
You can use exec:
var = input('Which Variable?: ')
exec("print(" + var + ")")
Output:
Which Variable?: b
2
>>
Just do the following:
print(eval(input('Which Variable?: ')))
You can also do
print(globals()[input('Which Variable?: ')])
While the other answers seem to address the obvious solution, it's not very 'Pythonic'. The main issues with these is, by far, safety. Let's say that your user inputs apiKey, and you happen to have a variable by that name... let's just say your bank statement is probably looking at a slight increase in magnitude. What most people in these answers don't realise is that using .globals()[input()] is no safer than eval(input()), because, shockingly, people store private info in variables. Alternatively, if it points to a method, e.g
a = print
b = os.system
eval(input())()
I could enter any function name there, and the damage would be done before the second () executes.
Why? Well, let's take a look at how exec and eval work (I won't go into the difference here, see this question for that). All they do is evaluate the string as Python code, and (simplifying here) return the value of the evaluation:
var1 = 3
print(eval("var1"))
# ====is equal to====
var1 = 3
print(var1)
(where var1 as a string obviously comes from the input typed in)
But if someone enters something malicious, this is essentially the basis of an SQL injection:
(where userInput is substituted by a user's input into an input())
userInput = "a + os.system('reboot now')"
print(eval(userInput))
# ====is equal to====
print(a + os.system('shutdown now')
and you suddenly find your computer's off.
Therefore, we'd either use a:
Dictionary (or object): x={a:1, b:2}, then do x[input()]
Array x=[1, 2], then do x[["a", "b"].index(input())]
Simply don't. Find a way to work around it. What's wrong with an if/else set? It's not good practise, because of the safety concerns outlined above. What most people seem to miss about dictionaries (or my array option) is that if you enter a malformed input (i.e not a or b), it would result in either uncaught errors being thrown, or undefineds being thrown around. And if you're going to do input validation, you're using an if statement anyway, so why not do it from the onset?
I am still pretty new to Python and learning! I searched around and some postings seem too complex for me at this time. Wondering why the car_brandp below is not joining with "and quite expensive" after the else function initiates? The first else line prints fine but it seems like I can't put that message as a variable?
I got the None Type error
car_brand =input ("What is the best car brand? ")
if car_brand == ("Range Rover"):
print (car_brand + " is the best car brand ever!")
else:
car_brandp = print (car_brand + " is just personal taste..")
print (car_brandp + " and quite expensive...")
This line:
car_brandp = print (car_brand + " is just personal taste..")
is suppose to be:
car_brandp = (car_brand + " is just personal taste..")
"print" is a procedure to display something in the console. A procedure differs from a function as it is not meant to return something of value, but rather perform something as a side effect (it will do something useful but you cannot interact with it). You may not assign the return value of the print function as it is meaningless.
Since you are still new to Python, it is a good idea to learn the proper habits early. In particular, PEP8 contains valuable information on style and conventions that most Python developers follow. Such recommendations are optional, but when followed, they help other developers understand your code better.
car_brand = input("What is the best car brand? ")
if car_brand == "Range Rover":
msg = car_brand + " is the best car brand ever!"
else:
msg = car_brand + " is just personal taste.."
msg += " and quite expensive..."
print(msg)
print() is like a void function in other languages. So, it does something, but it returns nothing (None, in python, like null in other languages).
So, in your next line, you are trying to add
None + "personal taste"
and you get an error because addition of string and None is not defined
So, you options are
consecutive prints
concatenate strings eg print( str(brand) + "personal taste")
formatted strings eg print( f'{brand} personal taste')
So I'm working a quiz on Python as a project for an Intro to Programming course.
My quiz works as intended except in the case that the quiz variable is not being affected by the new values of the blank array. On the run_quiz function I want to make the quiz variable update itself by changing the blanks to the correct answer after the user has provided it.
Here's my code:
#Declaration of variables
blank = ["___1___", "___2___", "___3___", "___4___"]
answers = []
tries = 5
difficulty = ""
quiz = ""
#Level 1: Easy
quiz1 = "Python is intended to be a highly " + blank[0] + " language. It is designed to have an uncluttered " + blank[1] + " layout, often using English " + blank[2] + " where other languages use " + blank[3] + ".\n"
#Level 2: Medium
quiz2 = "Python interpreters are available for many " + blank[0] + " allowing Python code to run on a wide variety of systems. " + blank[1] + " the reference implementation of Python, is " + blank[2] + " software and has a community-based development model, as do nearly all of its variant implementations. " + blank[1] + " is managed by the non-profit " + blank[3] + ".\n"
#Level 3: Hard
quiz3 = "Python features a " + blank[0] + " system and automatic " + blank[1] + " and supports multiple " + blank[2] + " including object-oriented, imperative, functional programming, and " + blank[3] + " styles. It has a large and comprehensive standard library.\n"
#Answer and quiz assignment
def assign():
global difficulty
global quiz
x = 0
while x == 0:
user_input = raw_input("Select a difficulty, Press 1 for Easy, 2 for Medium or 3 for Hard.\n")
if user_input == "1":
answers.extend(["readable", "visual", "keywords", "punctuation"])
difficulty = "Easy"
quiz = quiz1
x = 1
elif user_input == "2":
answers.extend(["operating systems", "cpython", "open source", "python software foundation"])
difficulty = "Medium"
quiz = quiz2
x = 1
elif user_input == "3":
answers.extend(["dynamic type", "memory management", "programming paradigms", "procedural"])
difficulty = "Hard"
quiz = quiz3
x = 1
else:
print "Error: You must select 1, 2 or 3.\n"
x = 0
def run_quiz():
n = 0
global tries
global blank
print "Welcome to the Python Quiz! This quiz follows a fill in the blank structure. You will have 5 tries to replace the 4 blanks on the difficulty you select. Let's begin!\n"
assign()
print "You have slected " + difficulty + ".\n"
print "Read the paragraph carefully and prepare to provide your answers.\n"
while n < 4 and tries > 0:
print quiz
user_input = raw_input("What is your answer for " + blank[n] + "? Remember, you have " + str(tries) + " tries left.\n")
if user_input.lower() == answers[n]:
print "That is correct!\n"
blank[n] = answers[n]
n += 1
else:
print "That is the wrong answer. Try again!\n"
tries -= 1
if n == 4 or tries == 0:
if n == 4:
print "Congratulations! You are an expert on Python!"
else:
print "You have no more tries left! You can always come back and play again!"
run_quiz()
I know my code has many areas of improvement but this is my first Python project so I guess that's expected.
The problem is that your variable, quiz, is just a fixed string, and although it looks like it has something to do with blanks, it actually doesn't. What you want is 'string interpolation'. Python allows this with the .format method of str objects. This is really the crux of your question, and using string interpolation it's easy to do. I'd advise you to take some time to learn .format, it's an incredibly helpful function in almost any script.
I've also updated your code a bit not to use global variables, as this is generally bad practice and can lead to confusing, difficult to track bugs. It may also impair the uncluttered visual layout :). Here is your modified code, which should be working now:
quizzes = [
("""\
Python is intended to be a highly {} language.\
It is designed to have an uncluttered {} layout,\
often using English {} where other languages use {}
""", ["readable", "visual", "keywords", "punctuation"], "Easy"),
("""\
Python interpreters are available for many {}\
allowing Python code to run on a wide variety of systems.\
{} the reference implementation of Python, is {}\
software and has a community-based development model, as\
do nearly all of its variant implementations. {} is managed by the non-profit {}
""", ["operating systems", "cpython", "open source", "python software foundation"], "Medium"),
("""\
Python features a {} system and automatic {} and\
supports multiple {} including object-oriented,\
imperative, functional programming, and\
{} styles. It has a large and comprehensive standard library.
""", ["dynamic type", "memory management", "programming paradigms", "procedural"], "Hard")
]
#Answer and quiz assignment
def assign():
while True:
user_input = raw_input("Select a difficulty, Press 1 for Easy, 2 for Medium or 3 for Hard.\n")
if user_input == "1":
return quizzes[0]
elif user_input == "2":
return quizzes[1]
elif user_input == "3":
return quizzes[2]
else:
print "Error: You must select 1, 2 or 3.\n"
continue
break
def run_quiz():
n = 0
#Declaration of variables
blank = ["___1___", "___2___", "___3___", "___4___"]
tries = 5
print "Welcome to the Python Quiz! This quiz follows a fill in the blank structure. You will have 5 tries to replace the 4 blanks on the difficulty you select. Let's begin!\n"
quiz, answers, difficulty = assign()
print "You have selected {}.\n".format(difficulty)
print "Read the paragraph carefully and prepare to provide your answers.\n"
while n < 4 and tries > 0:
print quiz.format(*blank)
user_input = raw_input("What is your answer for {}? Remember, you have {} tries left.\n".format(blank[n], tries))
if user_input.lower() == answers[n]:
print "That is correct!\n"
blank[n] = answers[n]
n += 1
else:
print "That is the wrong answer. Try again!\n"
tries -= 1
if n == 4 or tries == 0:
if n == 4:
print "Congratulations! You are an expert on Python!"
else:
print "You have no more tries left! You can always come back and play again!"
run_quiz()
A little more on string interpolation:
You're doing a lot of "start of string " + str(var) + " end of string". This can be achieved quite simply with "start of string {} end of string".format(var)" - it even automatically does the str conversion. I've changed your quiz variables to have "{}" where either "__1__" etc should be displayed or the user's answer. You can then do quiz.format(*blank*) to print the 'most recent' version of the quiz. * here 'unpacks' the elements of blank into separate arguments for format.
If you find it more easy to learn with example usage, here are two usages of format in a simpler context:
>>> "the value of 2 + 3 is {}".format(2 + 3)
'the value of 2 + 3 is 5'
>>> a = 10
>>> "a is {}".format(a)
'a is 10'
I've also stored the information about each quiz in a list of tuples, and assign now has a return value, rather than causing side effects. Apart from that, your code is still pretty much intact. Your original logic hasn't changed at all.
Regarding your comment about objects:
Technically, yes, quizzes is an object. However, as Python is a 'pure object oriented language', everything in Python is an object. 2 is an object. "abc" is an object. [1, 2, 3] is an object. Even functions are objects. You may be thinking in terms of JavaScript - with all of the brackets and parentheses, it kind of resembles a JS Object. However, quizzes is nothing more than a list (of tuples). You might also be thinking of instances of custom classes, but it's not one of those either. Instances require you to define a class first, using class ....
A bit more on what quizzes actually is - it's a list of tuples of strings, lists of strings and strings. This is a kind of complicated type signature, but it's just a lot of nested container types really. It firstly means that each element of quizzes is a 'tuple'. A tuples is pretty similar to a list, except that it can't be changed in place. Really, you could almost always use a list instead of a tuple, but my rule of thumb is that a heterogenous collection (meaning stuff of different types) should generally be a tuple. Each tuple has the quiz text, the answers, and the difficulty. I've put it in an object like this as it means it can be accessed by indexing (using quiz[n]), rather than by a bunch of if statements which then refer to quiz1, quiz2, etc. Generally, if you find yourself naming more than about two variables which are semantically similar like this, it would be a good idea to put them in a list, so you can index, and iterate etc.
Only now have I read your question properly.
You first make your strings quiz1, quiz2 an quiz3.
You only do that once.
After that you change your blanks array.
But you don't reconstruct your strings.
So they still have the old values.
Note that a copy of elements of the blanks array is made into e.g. quiz1.
That copy doesn't change automagically after the fact.
If you want to program it like this, you'll have to rebuild your quiz1, quiz2 and quiz3 strings explicitly each time you change your blanks array.
General advice: Don't use so many globals. Use function parameters instead. But for a first attempt I guess it's OK.
[edit]
A simple modification would be:
Replace your quiz, quiz1, quiz2 and quiz3 by functions get_quiz (), get_quiz1 () etc. that get the most recent version, including the altered elements of blanks.
This modification doesn't make this an elegant program. But you'll come to that with a bit more experience.
A long shot in case you wonder (but don't try to bridge that gap in one step):
In the end Quiz will probably be a class with methods and attributes, of which you have instances.
To be sure: I think that experimenting like this will make you a good programmer, more than copying some ready to go code!
I'm writing a Facebook bot which will eventually produce a couple of randomly-generated statuses a day. Right now, I'm at the stage where I've got the logic of selecting bits of phrases from dictionary entries and I've written it so it will just work in the Python shell for the time being - the Facebook authentication stuff will come later.
Right now though, I thought it'd be cool to randomise certain nouns within the phrases contained in the dictionaries, and I was doing this with random.choice() and running a function that should return a new random element every time I generate a status. But the problem is that whenever I call the phrase, I can see that it's generated one random noun, but that noun gets 'fixed' for some reason such that the same random noun is reproduced every time. When I run the function as part of building a status, it seems to be working fine, but for some reason I can't figure, any new random nouns are not passed to the dictionary. Naturally, it works when I restart the program, but if I'm aiming to do this as a bot, I'd ideally like to not have to restart the program every time I want a new status.
I've done some investigating and I think the problem is not to do with my actual random.choice() stuff or the functions that they're in, but that the dictionary gets 'fixed' before my random noun function can touch it (e.g. the random choice function produces a random selection from the fruit list, but the dictionary will only ever have the same fruit selected when I run the StatusBuilder function). I have tried out some potential solutions with global variables and so on, but nothing has worked. The excerpt demonstrated in the code below is, I think, the closest I've come.
from random import randint
from random import choice
from textwrap import fill
def RandomFruit():
return choice(["mango", "pomelo", "guava", "grapefruit", "watermelon"])
class DoTable():
def __init__(self, text):
self.text = text
DoDict = {
"do0": DoTable("sitting on the roof, completely naked, throwing Herb Alpert records at a dog like they were frisbees"),
"do1": DoTable("eating a " + RandomFruit() + " like it's a handfruit"),
"do2": DoTable("lurching around a supermarket"),
}
class BeTable():
def __init__(self, start_text, end_text):
self.start_text = start_text
self.end_text = end_text
BeDict = {
"be0": BeTable("I guess ", " is what my life has come to."),
"be1": BeTable("", ", just waiting for the police to arrive."),
"be2": BeTable("If ", " is wrong, then I don't ever want to be right!"),
}
def StatusBuilder():
#DoDict and BeDict will always have one entry selected from each, though
#BeDict will always have two attributes selected as part of the one entry.
DoRNG = randint(0,len(DoDict)-1)
BeRNG = randint(0,len(BeDict)-1)
#Logic to display concatenated strings
status = BeDict["be" + str(BeRNG)].start_text + DoDict["do" + str(DoRNG)].text + BeDict["be" + str(BeRNG)].end_text
#print the status with textwrapping and with the first letter always capitalised.
print fill((status[0].capitalize() + status[1:]), 80)
print
Controls()
def Controls():
command = raw_input("([RETURN] FOR ANOTHER ROUND OF BULLSHIT, [Q] TO QUIT): ")
if command.lower() == "q":
quit()
elif command.lower() == "":
print
RandomFruit()
StatusBuilder()
else:
print
print fill("Some kind of wise guy are you? Try again, this time with a PROPER command please.", 80)
print
Controls()
#Start the program
print "PHILBOT V1.0"
print "A social media status generator."
print
command = raw_input("(PRESS [RETURN] TO GET STARTED): ")
print
RandomFruit()
StatusBuilder()
The sample dictionaries are included in the code above (which is a massively pared down version of the program which will run if you want to play with it). So the output I'm having trouble with would be the element in DoDict with the key "do1". So for example, say the StatusBuilder function picks the same phrases ("do1" and "be2") consecutively, what I want it to do is produce a different fruit every time when called with the StatusBuilder function, like this:
"If eating a mango like it's a handfruit is wrong, then I don't ever want to be right!"
"If eating a grapefruit like it's a handfruit is wrong, then I don't ever want to be right!"
Currently, whichever fruit is selected first 'sticks' permanently whenever I run it through StatusBuilder(), even though the RandomFruit() function seems to be working normally.
EDIT: I've now tried a few suggested alternatives (using lambdas and using generators) and have tried working with a simpler data format (lists without classes), but the problem is still being reproduced. SO, I'm starting to think it's more to do with my order of operations, as in the dictionary entry gets 'written' after the RandomFruit function is initially run (yay!), but it doesn't get 're-written' when I run it again (boo!)...this gives me a problem, since I can either declare the function then the dictionary (where the function doesn't seem to be speaking to the dictionary after it is first used), or declare the dictionary then the function (which doesn't work since the dictionary is then trying to rely on an as-yet-undeclared function). So, I think this is the root of the problem - any help?
You could use the random.choice function as a value in your dictionary (I've simplified your example quite a bit):
import random
fruits = ["mango", "pomelo", "guava", "grapefruit", "watermelon"]
DoDict = {'do1': random.choice}
This way you get a random fruit every time you access the dictionary:
>>> for i in range(10): print DoDict['do1'](fruits)
guava
pomelo
watermelon
mango
watermelon
grapefruit
pomelo
watermelon
watermelon
watermelon
You could also use a generator:
def random_fruit():
while True:
yield random.choice(["mango", "pomelo", "guava", "grapefruit", "watermelon"])
DoDict = {'do1': random_fruit().next}
In this case you don't need to pass the list of fruits as a parameter:
>>> for i in range(10): print DoDict['do1']()
grapefruit
pomelo
pomelo
guava
grapefruit
pomelo
pomelo
pomelo
mango
guava
I have a database where each case holds info about handwritten digits, eg:
Digit1Seq : when in the sequence of 12 digits the "1" was drawn
Digit1Ht: the height of the digit "1"
Digit1Width: its width
Digit2Seq: same info for digit "2"
on up to digit "12"
I find I now need the information organized a little differently as well. In particular I want a new variables with the height and width of the first digit written, then the height and width of the second, etc., as SPSS vars
FirstDigitHt
FirstDigitWidth ...
TwelvthDigitWidth
Here's a Python program I wrote to do within SPSS what ought to be a very simple computation, but it runs into a sort of namespace problem:
BEGIN PROGRAM PYTHON.
import spss
indices = ["1", "2", "3","4","5", "6", "7", "8", "9", "10", "11", "12"]
seq=0
for i in indices:
spss.Submit("COMPUTE seq = COMDigit" + i + "Seq.")
spss.Submit("EXECUTE.")
spss.Submit("COMPUTE COM" + indices[seq] + "thWidth = COMDigit" + i + "Width.")
spss.Submit("COMPUTE COM" + indices[seq] + "thHgt = COMDigit" + i + "Hgt.")
spss.Submit("EXECUTE.")
END PROGRAM.
It's clear what's wrong here: the value of seq in the first COMPUTE command doesn't get back to Python, so that the right thing can happen in the next two COMPUTEcommands. Python's value of seq doesn't change, so I end up with SPSS code that gives me only two variables (COM1thWidth and COM1Hgt), into which COMDigit1Width, COMDigit2Width, etc. get written.
Is there any way to get Python to access SPSS's value of seq each time so that the string concatenation will create the correct COMPUTE? Or am I just thinking about this incorrectly?
Have googled extensively, but find no way to do this.
As I'm new to using Python in SPSS (and not all that much of wiz with SPSS) there may well be a far easier way to do this.
All suggestions most welcome.
Probably the easiest way to get your SPSS variable data into Python variables for manipulation is with the spss.Dataset class.
To do this, You will need:
1.) the dataset name of your SPSS Dataset
2.) either the name of the variable you want to pull data from or its index in your dataset.
If the name of the variable you want to extract data from is named 'seq' (as I believe it was in your question), then you can use something like:
BEGIN PROGRAM PYTHON.
from __future__ import with_statement
import spss
with spss.DataStep()
#the lines below create references to your dataset,
#to its variable list, and to its case data
lv_dataset = spss.Dataset(name = <name of your SPSS dataset>)
lv_caseData = lv_dataset.cases
lv_variables = lv_dataset.varlist
#the line below extracts all the data from the SPSS variable named 'seq' in the dataset referenced above into a list
#to make use of an SPSS cases object, you specify in square brackets which rows and which variables to extract from, such as:
#Each row you request to be extracted will be returned as a list of values, one value for each variable you request data for
#lv_theData = lv_caseData[rowStartIndex:rowEndIndex, columnStartIndex:columnEndIndex]
#This means that if you want to get data for one variable across many rows of data, you will get a list for each row of data, but each row's list will have only one value in it, hence in the code below, we grab the first element of each list returned
lv_variableData = [itm[0] for itm in lv_caseData[0:len(lv_caseData), lv_variables['seq'].index]]
END PROGRAM.
There are lots of ways to process the case data held by Statistics via Python, but the case data has to be read explicitly using the spss.Cursor, spssdata.Spssdata, or spss.Dataset class. It does not live in the Python namespace.
In this case the simplest thing to do would be to just substitute the formula for seq into the later references. There are many other ways to tackle this.
Also, get rid of those EXECUTE calls. They just force unnecessary data passes. Statistics will automatically pass the data when it needs to based on the command stream.
Hi I just stumbled across this, and you've probably moved on, but it might help other folks. I don't thing you actually need to access have Python access the SPSS values. I think something like this might work:
BEGIN PROGRAM PYTHON.
import spss
for i in range(1,13):
k = "COMPUTE seq = COMDigit" + str(i) + "Seq."
l = "Do if seq = " + str(i)+ "."
m = "COMPUTE COM" + str(i) + "thWidth = COMDigit" + str(i) + "Width."
n = "COMPUTE COM" + str(i) + "thHgt = COMDigit" + str(i) + "Hgt."
o = "End if."
print k
print l
print m
print n
print o
spss.Submit(k)
spss.Submit(l)
spss.Submit(m)
spss.Submit(n)
spss.Submit(o)
spss.Submit("EXECUTE.")
END PROGRAM.
But I'd have to see the data to make sure I'm understanding your problem correctly. Also, the print stuff makes the code look ugly, but its the only way I can keep a handle on whats going on under the hood. Cheerio!