In this code
money = .3
Things = ["Nothing"]
def main():
print "go to buy things"
print "Current Money %s" % (money)
print "Things you have:"
for item in Things:
print item
wait = raw_input()
buythings(money)
def buythings(money):
print "Type Buy to buy things"
buy = raw_input()
if buy == "buy":
money = money - .3
Things.append("Things")
main()
else:
print "you bought nothing"
print ""
main()
Why after buying the things does the money not go down? This has been a problem to me for a while now and I cant seem to understand how the scope works in this situation.
The global variable money is shadowed by the function parameter money in buythings(money) function. You should remove the parameter for it to work:
def main():
global money
global Things
...
def buythings():
global money
global Things
...
However, A better approach, as alfasin pointed out, would be passing money and Things as parameters to both functions and not using global keyword at all:
def main(money, things):
...
for item in things:
print item
wait = raw_input()
buythings(money, things)
def buythings(money, things):
...
if buy == "buy":
money = money - .3
Things.append("Things")
main(money, things)
else:
...
main(money, things)
>>> money = .3
>>> Things = ["Nothing"]
>>> main(money, Things)
Hope this helps.
You can use a global variable in other functions by declaring it as global in each function that assigns to it:
money = 0
def set_money_to_one():
global money # Needed to modify global copy of money
money = 1
def print_money():
print money # No need for global declaration to read value of money
set_money_to_one()
print_money() # Prints 1
In your case :
def buythings():
global money
Python wants to make sure that you really know what you're
playing with by explicitly requiring the global keyword.
Related
So what I mean by this is if you have this code for example:
def test():
money = 0
x = input('')
if x == "givemoney":
money = money + 100
print(money)
test()
else:
print("something")
test()
It'll reset money to 0 every time the function goes which makes sense but how could I stop that?
You should use a loop. Here money is defined and initialized in a scope local to the function when the function is called; this is why it reset for every call to test().
def test():
money = 0
x = input('')
while x != 'stop':
if x == "givemoney":
money = money + 100
print(money)
x = input('')
else:
print("something")
test()
In the first version you can make money an optional parameter to the function. When you call it from the top-level you allow it to default. When you make the recursive call, you pass the current value in place of the default.
def test(money = 0):
x = input('')
if x == "givemoney":
money = money + 100
print(money)
test(money)
else:
print("something")
test()
In this code I tried to make that when you type "work", you get any number from 1 to 50 and it adds itself to the total balance (5 times). But when I do this, the previous amount of the variable resets to the new amount.
import random
balance = 0
def work(balance):
earned_money = random.randint(1, 50)
balance += earned_money
print(balance)
for x in range(5):
user_input = input()
if user_input == "work":
work(balance)
Even though the global keyword solves your problem, depending on who you ask, global variables are considered bad practice in Python. I try to avoid them unless there's absolutely no other way, and in this case, it's quite easy to come up with a solution that doesn't need globals:
import random
balance = 0
def work(balance):
earned_money = random.randint(1, 50)
return balance + earned_money
for x in range(5):
user_input = input()
if user_input == "work":
balance = work(balance)
print(balance)
I recommend learning more about variable scope.
Here is working code. You need to declare it as global.
import random
balance = 0
def work():
global balance
earned_money = random.randint(1, 50)
balance += earned_money
print(balance)
for x in range(5):
user_input = input()
if user_input == "work":
work()
The outer balance variable is in global scoping whereas the balance variable inside function is in local scoping.
If you want to make local scope variable global then you need to use global keyword inside your function.
import random
balance = 0
def work():
global balance # This makes balance variable global.
earned_money = random.randint(1, 50)
balance += earned_money
print(balance)
for x in range(5):
user_input = input()
if user_input == "work":
work()
so my classes won't recognize each other but when left to work alone they do work as seen in the consult printing at the bottom of the post. However the deposit and transfer are popping up the ram location, however, they do work fine when done separately just as shown in the consult action in the bottom part.
<function cashier.deposit at 0x0000023A9B656048>
import datetime
now = datetime.datetime.now()
class account:
owner = "name"
pin = "1823"
balance = 800
def __init__(self, transfer, withdraw, deposit, consult):
self.transfer = transfer
self.withdraw = withdraw
self.deposit = deposit
self.consult = consult
class cashier:
def __init__(self, withdraw, deposit, transfer, consult):
self.transfer = transfer
self.consult = consult
self.withdraw = withdraw
self.deposit = deposit
def deposit(self):
print("Please type pin to proceed")
if account.pin == 1823:
print("who would you like to send money to?")
else:
print("Invalid pin")
def transfer(self):
pass
def withdraw(self):
withdrawal = input("How much money do you want to withdraw? (there is a limit up to 10,000$ a day!)")
account.balance -= int(withdrawal)
if int(withdrawal) > 10000:
print("withdrawals cannot be larger than 10,000 a day!")
exit()
elif int(withdrawal) > account.balance:
print("your account does not have enough funds to complete your transaction")
else:
print("Transaction succesfull!\nPlease collect your money")
print('Today is', now)
print("Hello %s!\nWhat can I do for you today?" % account.owner)
action = input("Use commands such as withdraw, deposit, transfer or consult to continue!\n")
if action == "withdraw":
print(cashier.withdraw)
if action == "consult":
print("Your account's balance is %s $" % str(account.balance))
if action == "transfer":
print(cashier.transfer)
if action == "deposit":
print(cashier.deposit)
else:
print("Uknown command, exiting programm")
exit()
The various functions of cashier do not return anything, so the prints are just printing the string representation of the function. You could have the functions return a string like so:
class Cashier: # PEP8 calls for CamelCase here
def deposit(self):
pin = input("Please type pin to proceed")
if Account.pin == pin:
return "who would you like to send money to?"
else:
return "Invalid pin"
def transfer(self):
pass
def withdraw(self):
withdrawal = input("How much money do you want to withdraw? (there is a limit up to 10,000$ a day!)")
Account.balance -= int(withdrawal)
if int(withdrawal) > 10000:
return "withdrawals cannot be larger than 10,000 a day!"
elif int(withdrawal) > Account.balance:
return "your Account does not have enough funds to complete your transaction"
else:
return "Transaction successful!\nPlease collect your money"
Edit: I removed the initializer, as these weren't doing anything. You need to instantiate your cashier with cashier = Cashier(), then call the functions of Cashier with cashier.withdraw().
There's a fundamental misunderstanding on how classes work here (and OOP in general). You should read through the Python docs on building classes before you go any further.
To use your classes, you need to instantate them like this
Cashier = cashier()//I don't think you should ask for the transfer details while instantiating. But you can do that by adding the parameter in the bracket
Now you can use the functions on the Cashier variable like:-
Cashier.deposit(deposit_money)
I think if you don't want to instantiate and want a function which does not actually store data, you can use a static function but I don't think that is what you want to do
background: I'm currently writing a text-based adventure and each enemy has a certain amount of turns you can attack it before it attacks back.
So to handle this the code sets an argument in the function for the fight dictating how many times you can attack.
def fight_sequence(rounds):
while rounds > 0:
attack = input()
if attack == magic:
magic("you teleport away to safety. Congratulations you have stayed alive through your journey and found a few souvenoirs. nice job!", 1, "you muster up all of your energy, chant the spell.... and nothing happens.Cthulu... seems unimpressed", 1, "")
elif attack == sword:
sword(1)
def magic(teleportmessage, teleportsuccess, firemessage, firefail, winmessage):
x = 0
while x == 0:
fightorflight = input("""you open the book to cast a spell
Do you want to try teleporting or use a fireball?""").lower()
if "teleport" in fightorflight:
if teleportsuccess = 1:
print(teleportmessage)
x = 1
else:
choice = input("You can't teleport out of this battle. Would you like to try a fireball?")
if choice == yes:
fightorflight = "fireball"
else:
x = 1
elif "fire" in fightorflight:
print(firemessage)
if firefail == 1:
choice = input("do you want to try to teleport instead?").lower()
if "yes" in choice:
fightorflight = "teleport"
else:
x = 1
else:
print(winmessage)
else:
print("Sorry not sure what you mean")
def sword(attacksuccess):
if attacksuccess == 1:
print("You pull out the sword and swing at the monster damaging it severely.")
else:
print("You pull out the sword and swing at the monster, but its immune to blunt objects.")
fight_sequence(3)
both magic() and sword() need to be able to decrease rounds by 1, originally i just did that before entering the magic or sword function. however some items to attack with allow you to attack more than once if you want so that won't work for them. Such as magic if they also choose to teleport. Is there a way to allow me to change the variable rounds while inside of another function?
I think using a return value might help but I'm not sure how to go about it
You can simply add a new argument to the magic function, and pass the 'rounds' variable through when you call it.
e.g.
def fight_sequence(rounds):
...
magic("some message", false, "fired", false, "you won", rounds)
def magic(teleportmessage, teleportsuccess, firemessage, firefail, winmessage, rounds):
Your passing through the variable (not just the value) so it will change in every context where rounds can be seen.
HTH.
I would recommend using classes to create this game rather than lots of functions, an example of a class in a hero game below.
class Hero:
def __init__(self):
self.health = 10
def eatApple(self):
self.health += 1
def takeDamage(self):
self.health -= 1
init function runs as class is initialized.
player = Hero()
print(player.health) # will print 10
player.takeDamage()
print(player.health) # will print 9
This way you can have global variables for you functions which can be changed in each function and is much more organised.
I went through this a couple of times and couldnt fine anything wrong so its probably something over my head. I also apology for what is going to be an assault on your eyes, its my first year of programming and have probably made multiple etiquette errors.
print('Befor we begin, when you are given options, I ask you to type your input as show, failure to do so will break the program and you will lose all of your progress')
def test():
print('it has worked!')
def stats():
global attack
global health
global mashealth
global agility
if race in ('human','Human'):
maxatk=4
maxagi=4
attack = lambda:random.randint(2,maxatk)
maxhealth = 20
health=20
agility = random.randint(maxagi,10)
elif race in ('Orc','orc'):
attack = random.randint(3,maxATK)
maxhealth = 25
agility = random.radint(maxAGI,10)
def main():
while True:
print('What would you like to do')
print('Rest Shop Fight')
answer=input('-')
if answer in ('Rest','rest'):
health=maxhealth
continue
def character():
global race
global name
global gender
print('What would you like the name of your character to be?')
name=input('-')
print()
print('What race will your character be?')
print('Human Orc Elf')
while True:
race = input('-')
if race in ('human','Human','Orc','orc','Elf','elf'):
break
else:
print('Not a valid response, try again')
continue
print()
print('What gender is your character')
gender=input('-')
print()
def goblin():
goblinatk=1
goblinhealth=100
while True:
print('You have encountered a goblin, what will you do?')
do=input('-')
if do == 'attack':
damage=attack()
goblinhealth=goblinhealth-damage
print('You did',damage,'damage to the goblin')
print('The goblin has',goblinhealth,'hp')
goblinatk=lambda:random.randint(3,10)
health=health-goblinatk
print('the goblin did',goblinatk,'to you')
print('you have',health,'hp')
if goblinhealth <0:
print('The goblin has died')
break
if health <0:
print('you have died')
break
character()
stats()
goblin()
test()
The error is here
Traceback (most recent call last):
File "H:\16CoFrega\Code\Python\GAME.py", line 255, in <module>
goblin()
File "H:\16CoFrega\Code\Python\GAME.py", line 238, in goblin
health=health-goblinatk
UnboundLocalError: local variable 'health' referenced before assignment
You need to specify the health as a global variable, otherwise it would be considered as a local variable, since you assign to it inside the function. Example -
def goblin():
global health
goblinatk=1
goblinhealth=100
def goblin():
............
health=health-goblinatk
............
Look at this function definition. Your function doesn't know what health is, so it won't allow you to subscript something from health.
So, somehow function has to recognize what is health. Two most common way:
First way, Declare health as a global variable. Now health can be recognized globally. But this is not the best way, as dealing with global variables are hard and error-prone and you are already handling too many global variables. So, i won't recommend it. I would rather suggest you to replace all the global variables with method 2. To understand why I am telling this, Read This Question
Second way, the recommended way, is to pass the health variable as the parameter of the function and at the end, return it from the function.
Like this:
def goblin(health):
............
health=health-goblinatk
............
return health
If you are already returning something, don't worry. With python, you can return more than one variable as a tuple.
return statement:
return a,b,c
calling statement:
a,b,c = func()
Hope this helps :)