Assistance needed with variables in classes - python

I'm having some trouble with variables in classes.
Say I have this class:
some_number = 200
class Numbers:
def __init__(self):
self.number = some_number
def edit(self):
self.number = self.number - 50
def printNumber(self):
print(self.number)
If I run this in the shell:
number = Numbers()
number.edit()
number.printNumber()
This prints the number 150
But this:
print(some_number)
Prints the number 200. How do I get the variable some_number to change in a class, after which printing it returns the edited value?

The variable some_number is outside of the class, therefore it wouldn't change. The easiest way would be to avoid classes and modify the variable some_number. The alternative would be to drop some_number altogether and do the following:
class Numbers:
def __init__(self, number):
self.number = number
def edit(self):
self.number = self.number - 50
def printNumber(self):
print(self.number)
That would be called like this:
number = Numbers(150)
number.edit()
number.printNumber()
There are many ways to edit that variable, but these are two potential methods.
As a caution, global variables can get tricky, but declaring it globally would work as well.

You can access and alter some_number from the global scope by
class Numbers:
def edit(self):
global some_number
some_number = some_number - 50
def printNumber(self):
print(some_number)
However, in general, the need to modify global variables usually points to a design problem ...

You declared the variable some_number as a global variable. The whole point of adding the attribute .number to the Numbers class would be for it to be specific to each instance of the class.
You can change some_number as a side effect, but why would you want to?
Here's how you would if you have a good reason:
some_number = 200
class Numbers:
def __init__(self):
self.number = some_number
def edit(self):
global some_number
self.number = self.number - 50
some_number = self.number
def printNumber(self):
print(self.number)
n = Numbers()
n.edit()
n.printNumber()
print(some_number)
More likely though, you'd want something like this:
some_number = 200
class Number:
def __init__(self, number):
self.number = number
def edit(self, change):
self.number = self.number + change
def __str__(self):
return str(self.number)
n = Number(some_number)
n.edit(-50)
print(n)
print(some_number)
It's generally a really bad idea to keep stuff around in global variables, unless there's a really good reason to have one value available to all of your program, with changes to that value affecting the entire program. And even in those cases, it's often better to explicitly pass the value around, instead of just referring to it, betting it has the correct value.
In the case of a class, if you do have a good reason, you should consider a class attribute instead of a global variable, which will keep the one value the same for all instances of the class, but at least there are no global side effects.

Related

python: add number every new object

I want to change the name of the object each time a object is created so that there's an accumulator adding everytime an object is created. In this example i want the first object.name to be B1 and then the second object.name to be B2 and then B3 and so on. This is what im trying to get
class Object:
def __init__(self):
self.name = "B" + (accumulator)
this is what I tried but i am not really getting anywhere
class BankAccount:
def __init__(self, balance):
self.account_number = "B" + str(number = number + 1)
self.balance = balance
I cant think of a way to avoid the issue of trying to set a variable to equal plus one of itself because itself isn't defined yet.
The simplest approach here is a class variable that stores the next value to use, which you increment after use:
class BankAccount:
_nextnum = 1
def __init__(self, balance):
self.account_number = "B" + str(self._nextnum)
type(self)._nextnum += 1 # Must set it on the class, or you only make a shadowing instance attribute
self.balance = balance
This isn't thread safe without locking though, so if you want thread-safety, itertools.count can do the same job in a thread-safe (at least on CPython) manner:
import itertools
class BankAccount:
_numgenerator = itertools.count(1)
def __init__(self, balance):
self.account_number = "B" + str(next(self._numgenerator))
self.balance = balance
Since itertools.count's work is done at the C layer with the GIL held, it operates atomically, both returning the next number and moving the count along as a single atomic operation.
You can have a class level variable maintain how many objects were created, and then use that to determine the name
class BankAccount:
count = 0
def __init__(self):
self.name = "B" + str(BankAccount.count)
BankAccount.count += 1
This is not thread safe however, as mentioned by #ShadowRanger. It's likly a better idea to use itertools.count as they suggest.

Python object keeping data from previous?

I've seen multiple instances of this question like this one, but it fails to identify what exactly I am doing wrong since I don't have default arguments.
What am I doing wrong? Python object instantiation keeping data from previous instantiation?
#Table.py
class Table:
def __init__(self, players):
self.deck = Deck()
And this is Main
t = Table(2)
print len(t.deck.cards)
t = Table(2)
print len(t.deck.cards)
I would expect this to print 48 each time, but instead it prints
48 and then 96
Why is this? Shouldn't this member variable be overridden every time?
#Deck.py
from Card import *
import random
class Deck:
suits = ['H','C','D','S']
numbers = [2,3,4,5,6,7,8,9,10,11,12,13,14]
cards = []
def __init__(self):
for num in self.numbers:
for suit in self.suits:
c = Card(num,suit)
self.cards.append(c);
random.shuffle(self.cards)
Card.py
class Card:
def __init__(self, num, suit):
self.num = num
self.suit = suit
def __repr__(self):
return str(self.num) + str(self.suit)
def __str__(self):
return str(self.num) + str(self.suit)
Initialize cards in the constructor, like this:
def __init__(self):
self.cards = []
for num in self.numbers:
for suit in self.suits:
c = Card(num,suit)
self.cards.append(c);
random.shuffle(self.cards)
That way, every time a new instance of the class is created, cards will be freshly initialized.
Your approach didn't work as you wished, since cards is a class data member, shared among all instances of class Deck.
suits, numbers and cards are class variables. So when doing self.cards.append(c) you add to a class variable, which is shared by all instances of all Deck instances.
Put them into __init__ instead:
def __init__(self):
self.cards = []
for num in self.numbers:
for suit in self.suits:
c = Card(num,suit)
self.cards.append(c);
random.shuffle(self.cards)
You are using class variables instead of instance variables. See, for example, python class variables
So even though you instantiate a new instance, you don't get a new instance of the static class variables.
Suits, numbers, cards. If you want instance variables, use "self.", and do it in the init function.
You are appending cards each time you instantiate, but you are appending them to the class variable. Thus you end up with twice as many.

class attribute doesnt work properly (python)

While trying to come up with a Hand class for a card game
I encountered a strange behavior from an attribute
if I try to set self.number as seen below it wont show the proper output
but if I make the same argument through a function total() it works properly
my question is: why does the attribute self.number not getting the value of len(self.cards)?
class Hand (object):
def __init__(self,number=0,cards=[]):
self.cards=cards
self.number=len(self.cards)
def total(self):
return len(self.cards)
hand=Hand()
hand.cards.append(9)
print hand.cards
print len(hand.cards)
print hand.number
print hand.total()
output:
[9]
1
0 #I want this to be equal to 1
1
The attribute self.number is set at instantiation, use a property instead.
class Hand (object):
def __init__(self, cards=None):
self.cards = cards or []
#property
def number(self):
return len(self.cards)
def total(self):
return len(self.cards)
Setting an instance variable to an expression does not create a binding between the inputs of that expression and the value of the instance variable. In other terms, setting self.number to len(self.cards) does not mean that self.number will get updated whenever you update self.cards.
Your program is functioning properly: When you create your object, len(self.cards) is 0, so self.number gets set to 0. When you change hand.cards, there are no statements changing self.number, so it stays 0.
The proper way to make your self.number attribute update is to use a getter-setter pair in order to make sure that self.number changes whenever self.cards changes.
The Pythonic way to create a getter-setter pair is to use the #property decorator.
In your case, you could do it like this:
class Hand(object):
def __init__(self, number = 0, cards = None):
self.cards = cards or []
#property
def number(self):
return len(self.cards)
This way, even though it looks to anyone uses your class that they are reading an attribute, what actually happens under the hood is that the number() method gets called and correctly computes the current length of self.cards.

Python - How use properties without creating an object

How do I write a class to make this code work.
class number:
def double(self):
return n*2
print(number(44).double)
>> 88
Well, you could decorate the number.double method with property:
class number:
def __init__(self, number):
self.number = number
#property
def double(self):
return self.number * 2
print(number(42).double) # 84
If you know the type of your argument, it'd be better to inherit number from it. For example
class number(int):
#property
def double(self):
return type(self)(self * 2)
print(number(42).double) # 84
print(number(42).double.double) # 168
Here you are:
class Number(object):
def __init__(self, n):
self.n = n
def double(self):
return 2*self.n
print(Number(44).double())
A couple of notes:
Since double() is a method of the class Number (and not an attribute), you need to use parentheses to call it.
In Python it's considered standard practice to give classes uppercase names.
If you want to define an instance variable (in this case n, or 44, you must def the __init__() function which gives the interpreter instructions for how to create, or initialize an instance of your class.
Hope this helps, and good luck!

Python 2.7: Using a class's functions in another program/binding

Say I have a class ("Classname" which does, among other things, the following:
def price(self): return self.price
def number(self): return self.number
I have another function within that class that I would like to do the following: for each individual object within the class, multiply self.price by self.number, and add the sum of that to a variable to be used within a program.
def total(self,price,number):
tot = 0
for i in Classname:
price = price(self)
number = number(self)
objvalue = price * number
total += objvalue
return total
Within the program that uses the Classname function, I'm struggling to use the 'total' function defined within the class. I just want the calcTotal function defined in the program to return the 'total' value defined by the function total within the class.
I've tried:
def calcTotal:
totalhere = Classname.total()
which returns
"unbound method sumInventory() must be called with Classname instance as first argument (got nothing instead)"
so I then tried
totalhere = Classname.total(Classname)
which returns
"unbound method sumInventory() must be called with Classname instance as first argument (got type instance instead.)"
I'm not sure how to bind the method to return what I want it to return.
By the sounds of "for each individual object within the class", it seems as if you're confusing classes with containers. Classes aren't naturally containers for all instances of themselves.
I think you might want to use two separate classes, like this...
class Order(object):
def __init__(self):
self._items = []
def add_item(self, item):
self._items.append(item)
def get_total_price(self):
total = 0
for item in self._items:
total += item.price() * item.quantity()
return total
class OrderItem(object):
def __init__(self, price, quantity):
self._price = price
self._quantity = quantity
def price(self):
return self._price
def quantity(self):
return self._quantity
...then you can do something like this...
>>> o = Order()
>>> o.add_item(OrderItem(1.99, 2))
>>> o.add_item(OrderItem(0.99, 5))
>>> print o.get_total_price()
8.93

Categories

Resources