Global variables in Python aa - python

Hi I'm making a program on python and I'm having trouble adding a global variable to my program so I'm just going to post my code and show you how I tried to do it.
So this is my class:
import globalvariables
class Bus :
def __init__(self, Number, Capacity, Destination, Seats):
self.Bus_Number = Number
self.Bus_Capacity = Capacity
self.Bus_Destination = Destination
self.Seats_taken = Seats
def Book(self):
self.Seats_taken = Seats + 1
def ShowBus(self):
return (str(self.Bus_Number) + ", " + str(self.Bus_Capacity) + ", " + str(self.Bus_Destination) + ", " + str(self.Seats_taken))
and this is my module for global variables
Seats = 0
and this is what I'm trying to run:
import Transport
import globalvariables
Big_Red = Transport.Bus(1, 50, "NYC", 0)
Big_Red.Book()
print(Big_Red.ShowBus())
I'm getting this error:
Traceback (most recent call last):
File "D:\Python\Assignment 3\Tester.py", line 5, in <module>
Big_Red.Book()
File "D:\Python\Assignment 3\Transport.py", line 14, in Book
self.Seats_taken = Seats + 1
NameError: global name 'Seats' is not defined

The variable Seats is local to __init__ function and can't be accessed outside of it.
So,
self.Seats_taken = Seats + 1
Should be :
self.Seats_taken = self.Seats_taken + 1
or :
self.Seats_taken += 1
Instead of using global variables inside class you should use class attributes:
class Bus :
seats = 50 #shared across all instances
def __init__(self):
#code
def Book(self):
self.Seats_taken = self.seats + 1

Globals should be avoided. In case you still want it to be :
def Book(self):
self.Seats_taken = globalvariables.Seats + 1

When you import globalvariables, you gain access to names qualified by the module name: globalvariables.Seats. To import Seats into the namespace of another module, use from globalvariables import Seats. (In desperate cases, you can import all names from a module: from globalvariables import *, but usually you don't want this.)
When you define a function, it has its own local namespace. It includes all function's arguments.
Seats = 100
def foo(Seats):
# returns the value of variable named Seats
# defined within "def foo", *not* defined by "Seats = 100"
return Seats
print foo(200) # prints 200
print foo() # fails because Seats are not set
To initialize a function parameter, use default value:
def foo(seats=0):
print foo() # prints 0
print foo(55) # prints 55
Also, global variables are evil. Global constants are good.
You want to use a global variable to track seats taken. You'll be much better off if you encapsulate it into a class that only allows reasonable access, does not allow to set the value arbitrarily, log access if needed, etc:
class SeatsDispenser(object):
def __init__(self, initial_count):
self.count = initial_count
def allocate(self, number_of_seats):
self.count -= number_of_seats
if self.count < 0:
raise ValueError("Overcommitted!")
def seats_left(self):
return self.number_of_seats
Naming your variables, classes, constants, and functions with the same Title Case is impractical. Usually variables are lower_case, functions are lowerCamelCase, classes are TitleCamelCase and constants are ALL_CAPS.
A reasonable piece of code would look like this:
import constants # modules are usually lower case
import transport
def Bus(object):
def __init__(self, number, capacity, seats=constants.SEATS):
self.number = number
self.capacity = capacity
self.seats = seats
big_red = Bus(constants.NYC_BUS_NUMBER, 50, 25)
default_blue = Bus(1, 20) # seats not passed, the default value is used
seats_dispenser = SeatsDispenser(100)
seats_dispenser.allocate(big_red.count)
seats_dispenser.allocate(default_blue.count)
print seats_dispenser.seats.left()

Related

How to get inputs to properly work with classes, Python

As of now I am wondering how to get inputs into a class.
class Contact:
def __init__(self, name = "",strengthPts = 0, power = ""):
self.name = name
self.power = power
self.strengthPts = strengthPts
def addStrengthPts(self, points):
self.strengthPts = self.strengthPts + points
def main():
namequestion = raw_input("put in a name")
time.sleep(1)
powerquestion = raw_input("What power would you like?")
newContact = Contact(namequestion , powerquestion)
print("The fabulous superhero, " + newContact.name)
time.sleep(1)
print(newContact.name + "'s superpower is " + newContact.power )
print(newContact.power)
main()
my output is
The fabulous superhero, test
test's superpower is
My main problem, (on the bottom of the output) is that the power's input is not relaying. I am not sure if this is a simple mistake with how I added it to my "newContact" or a problem with how inputs work with classes.
Your class' initializer is defined with three positional arguments (def __init__(self, name = "",strengthPts = 0, power = ""):). However, in this example you've passed two (newContact = Contact(namequestion , powerquestion)) which would mean that you're setting the value of powerquestion to your class instance's strengthPts instance variable. Since you've elected not to pass the power argument, it will use the default value of a blank string ("").
Either pass a value for the strengthPts positional variable, or use a named argument for power:
newContact = Contact(namequestion, power=powerquestion)

Use exec to modify global variables in a class

I've recently been refactoring some of my code to use OOP, and I've run into a problem where I can't quite get either global vars, exec(), or a combination of the two to work. The relevant code is below:
# invObject class. Has all properties of an inventory object.
class invObject:
def __init__(self, name, info, output, effect):
self.name = name # Used in menus.
self.info = info # Describes effect.
self.output = output # Printed on use.
self.effect = effect # Executed on use.
def printInfo(self): # Function for name and description.
return "{} - {}".format(self.name, self.info)
def use(self): # Function to use items. It's that easy.
global dodgeChance
global maxHp
global hp
global atk
exec(self.effect)
print(self.output) # Prints output. Also very simple.
print("{} {} {} {}".format(dodgeChance, maxHp, hp, atk)) # debugging
...
inventory[slot].use() # does not update values
Basic rundown: inventory[slot].use() should call the use() function of the object. use() should execute the code stored in inventory[slot].effect.
The output from the debugging line doesn't change anything, even inside the function. I've already tried making it return exec(self.effect) to no avail. print(self.output) does work.
EDIT: Here's a minimal reproducible example that includes everything it needs to run, not just the most important things.
# Assign base stats
dodgeChance = 0
maxHp = int(input("Input maximum HP. > "))
hp = int(input("Input current HP. > "))
# invObject class. Has all properties of an inventory object.
class invObject:
def __init__(self, name, info, output, effect):
self.name = name # Used in menus.
self.info = info # Describes effect.
self.output = output # Printed on use.
self.effect = effect # Executed on use.
def printInfo(self): # Function for name and description.
return "{} - {}".format(self.name, self.info)
def use(self): # Function to use items. It's that easy.
global dodgeChance
global maxHp
global hp
global atk
exec(self.effect)
print(self.output) # Prints output. Also very simple.
print("{} {} {} {}".format(dodgeChance, maxHp, hp, atk)) # debugging
empty = invObject("None", "Vacant slot.", "There's nothing in that slot!", "")
apple = invObject("Apple", "Gives 20 health.", "Ate the apple. Gained 20 health.", "hp = hp + 20\nif hp > maxHp: hp = maxHp")
drink = invObject("Drink", "Some kind of energy drink. Raises dodge chance to 75%.", "Drank the drink. Dodge chance is now 75%!", "dodgeChance = 75")
# Assign base inventory
inventory = [apple, drink, empty]
slot = int(input("Input slot number to use. ")) - 1
inventory[slot].use() # does not update values
# Show final stats
print("New HP value: " + str(hp))
print("Dodge chance: " + str(dodgeChance) + "%")
print()
print("Inventory contents:")
print("Slot 1: " + str(inventory[0].name))
print("Slot 2: " + str(inventory[1].name))
print("Slot 3: " + str(inventory[2].name))
EDIT 2: Another thing: the code works if I don't use exec() (e.g. change it out for hp += 20).
exec() has optional arguments for you to provide the global and local variable contexts.
But you didn't provide them.

Is there a way to fix Name Error due to scope?

I have a function that creates a player object but when referencing the object, I get a NameError. I think it is happening due to local scope but global should fix it...
I just started out OOP and this code is working in the python shell but it is not working in script mode.
endl = lambda a: print("\n"*a)
class Score:
_tie = 0
def __init__(self):
self._name = ""
self._wins = 0
self._loses = 0
def get_name(self):
print
self._name = input().upper()
def inc_score(self, wlt):
if wlt=="w": self._wins += 1
elif wlt=="l": self._loses += 1
elif wlt=="t": _tie += 1
else: raise ValueError("Bad Input")
def player_num(): #Gets number of players
while True:
clear()
endl(10)
print("1 player or 2 players?")
endl(5)
pnum = input('Enter 1 or 2: '.rjust(55))
try:
assert int(pnum) == 1 or int(pnum) == 2
clear()
return int(pnum)
except:
print("\n\nPlease enter 1 or 2.")
def create_player(): #Creates players
global p1
p1 = Score()
yield 0 #stops here if there is only 1 player
global p2
p2 = Score()
def pr_(): #testing object
input(p1._wins)
input(p2._wins)
for i in range(player_num()):
create_player()
input(p1)
input(p1._wins())
pr_()
wherever I reference p1 I should get the required object attributes but I'm getting this error
Traceback (most recent call last):
File "G:/Python/TicTacTwo.py", line 83, in <module>
input(p1)
NameError: name 'p1' is not defined
Your issue is not with global but with the yield in create_player(), which turns the function into a generator.
What you could do:
Actually run through the generator, by executing list(create_player()) (not nice, but works).
But I suggest you re-design your code instead, e.g. by calling the method with the number of players:
def create_player(num): #Creates players
if num >= 1:
global p1
p1 = Score()
if num >= 2:
global p2
p2 = Score()
If you fix this issue, the next issues will be
1) input(p1) will print the string representation of p1 and the input will be lost, you probably want p1.get_name() instead.
2) input(p1._wins()) will raise TypeError: 'int' object is not callable
I will redesign the app to introduce really powerful python constructs that may help you when getting into OOP.
Your objects are going to represent players, then don't call them Score, call them Player.
Using _tie like that makes it a class variable, so the value is shared for all the players. With only two participants this may be true but this will come to hurt you when you try to extend to more players. Keep it as a instance variable.
I am a fan of __slots__. It is a class special variable that tells the instance variables what attributes they can have. This will prevent to insert new attributes by mistake and also improve the memory needed for each instance, you can remove this line and it will work but I suggest you leave it. __slots__ is any kind of iterable. Using tuples as they are inmutable is my recomendation.
Properties are also a really nice feature. They will act as instance attribute but allow you to specify how they behave when you get the value (a = instance.property), assign them a value (instance.property = value), or delete the value (del instance.property). Name seems to be a really nice fit for a property. The getter will just return the value stored in _name, the setter will remove the leading and trailing spaces and will capitalize the first letter of each word, and the deletter will set the default name again.
Using a single function to compute a result is not very descriptive. Let's do it with 3 functions.
The code could look like this:
# DEFAULT_NAME is a contant so that we only have to modify it here if we want another
# default name instead of having to change it in several places
DEFAULT_NAME = "Unknown"
class Player:
# ( and ) are not needed but I'll keep them for clarity
__slots__ = ("_name", "_wins", "_loses", "_ties")
# We give a default name in case none is provided when the instance is built
def __init__(self, name=DEFAULT_NAME):
self._name = name
self._wins = 0
self._loses = 0
self._ties = 0
# This is part of the name property, more specifically the getter and the documentation
#property
def name(self):
""" The name of the player """
return self._name
# This is the setter of the name property, it removes spaces with .strip() and
# capitalizes first letters of each word with .title()
#name.setter
def name(self, name):
self._name = name.strip().title()
# This is the last part, the deleter, that assigns the default name again
#name.deleter
def name(self):
self._name = DEFAULT_NAME
def won(self):
self._wins += 1
def lost(self):
self._loses += 1
def tied(self):
self._ties += 1
Now that's all we need for the player itself. The game should have a different class where the players are created.
class Game:
_min_players = 1
_max_players = 2
def __init__(self, players):
# Check that the number of players is correct
if not(self._min_players <= players <= self._max_players):
raise ValueError("Number of players is invalid")
self._players = []
for i in range(1, players+1):
self._players.append(Player(input("Insert player {}'s name: ".format(i))))
#property
def players(self):
# We return a copy of the list to avoid mutating the inner list
return self._players.copy()
Now the game would be created as follows:
def new_game():
return Game(int(input("How many players? ")))
After that you would create new methods for the game like playing matches that will call the players won, lost or tied method, etc.
I hope that some of the concepts introduced here are useful for you, like properties, slots, delegating object creation to the owner object, etc.

Having errors in inheritance in Python

I am trying to create a program with a real estate agent in mind. In this program, I am trying to cover the aspects of a home to rent with all the basic parameters in mind. However I am having some errors.
class Apartment:
def __init__(self, aptNumber, address, bedrooms, baths):
self._aptNumber = aptNumber
self._address = address
self._bedrooms = int(bedrooms)
self._baths = float(baths)
def _securiyDep(self):
securityDeposit = 330(bedrooms)
def _rent(self):
rent = 250(bedrooms) + 150(bathrooms)+200
def _renter(self):
renter = "vacant"
def setSecDep(self, deposit):
self._deposit = 1000
def setRent(self, rentMoney):
self._rentMoney = 800
def setRenter(self, renter):
self._renter = "Vacant"
def getData(self, Data):
self._Data = Data
def isVacant(self, vacancy):
self._vacancy = "True" or "False"
def __repr__(self):
s = self._aptNumber+ " located at " + str(self._address) + " is rented at" + self._rent
s+= " and has a security deposit = " + self._deposit + " and is = " + self._vacancy
return s
# Test code for the Apartment Class
if __name__ == "__main__":
apts = []
apts.append(Apartment("10C", "107 E. Main", 3, 1.5))
apts.append(Apartment("14B", "109 E. Main", 4, 2))
apts.append(Apartment("13", "2207 W. Broadway", "5", "2.5"))
for apt in apts:
print(apt)
print()
apts[0].setRent("1245")
apts[0].setRenter("Rocky Quartzite")
apts[1].setSecDep("1000")
apts[1].setRenter("Millie Milton")
print(apts[0].getData())
print()
for apt in apts:
if not apt.isVacant():
print(apt)
print()
apts[0].setRenter("")
print(apts[0])
I am having the error
print(apts[0].getData())
<__main__.Apartment object at 0x0000000003124208>
TypeError: getData() missing 1 required positional argument: 'Data'
<__main__.Apartment object at 0x0000000003124240>
Can someone please help with the errors?
You call getData with no arguments on line 60: print(apts[0].getData()). In your definition it takes a parameter called Data.
You define getData to take two arguments, namely self which is a refernce to the instance you are operating on, and which is "automatically" supplied, and the second argument is Data
def getData(self, Data):
self._Data = Data
But when you call getData you don't supply the second argument Data
apts[0].getData()
Thus the interpreter tells you that 1 required argument is missing, which in your case is Data.
Since getData in your case actually sets the Data variable, it would be better to call it setData
If you want to retrive the Data variable the getter would have to looke something like this
def getData(self):
return self._Data

Access global variable from external class

How to structure an application so global variables work ?
I'm looking for the simplest way to do something like :
main.py
from Class_A import Class_A
my_var = 3
c_a = Class_A()
print c_a.get_var()
# ==> NameError: global name 'my_var' is not defined
Class_A.py
class Class_A:
def get_var():
global my_var
return my_var
Answer :
Nagasaki45's answer is good, here is how to implement it :
main.py
from Class_A import Class_A
import Global_Vars
c_a = Class_A()
print Global_Vars.my_var
print c_a.get_var()
Global_Vars.my_var += 12
print c_a.get_var()
Class_A.py
import Global_Vars
class Class_A:
def get_var(self):
return Global_Vars.my_var
Global_Vars.py
import random
my_var = random.randint(0,100)
This outputs :
17
17
29
If two modules depends on same variable I would move it outside to a 3rd module and import it from there to both of them. Doing so you will end up with much less problems of circular imports.
In any case, the global keyword is used for setting global variables. Don't use it if you only want to get a variable value.
If you want some object to be shared by other objects, pass this objects to the others, ie:
shared.py :
import random
class Shared(object):
def __init__(self):
self.var = random.randint(0,100)
lib.py :
class MyClass(object):
def __init__(self, shared):
self.shared = shared
def get_var(self):
return self.shared.var
main.py :
from shared import Shared
import lib
shared = Shared()
obj = lib.MyClass(shared)
print "shared.var : ", shared.var
print "obj.get_var() : ", obj.get_var()
shared.var = 200
print "shared.var : ", shared.var
print "obj.get_var() : ", obj.get_var()
Note that you don't need any "global" variable here.
Hum ... am I totally wrong, of the real answer does not have anything to do with globals ?
You have to know that Python variables are mostly tags that references values. And integer are immutable. So, after that call:
gv.my_var += 12
gv.my_var references a different value (36). This only change this reference. Other references to 24 are not affected.

Categories