Instantiating object and using class definition __init__ - python

so for this piece of code, the program has to instantiate the object "acc1" where acc1 = BankAccount(1000), where 1000 is the balance. Using the class definition for Bank Account, and using a display method, acc1.display(), the code should print "balance=1000". My code is printing the balance is part, but not taking into account the 1000 part.
class BankAccount:
def __init__ (self,balance):
self.balance = balance
acc1 = BankAccount("1000")
acc1.display()
print("Balance=",acc1,sep="")

You are trying to print the object itself rather than its balance. You will get the default value printed for the BankAccount class (something like <__main__.BankAccount object at 0x7f2e4aff3978>).
There are several ways to resolve the issue:-
First print just the balance property
print("balance=",acc1.balance,sep="")
If you want to modify the class you can define the display method. This isn't ideal as it limits the way the display information can be used. It has to be displayed to standard out, it cant be joined to other strings etc. It is less flexible.
It would be better to define __str__ and return the display string which can be displayed, concatenate etc.
class BankAccount:
def __init__ (self,balance):
self.balance = balance
def display(self):
print('balance=%s' % self.balance)
def __str__(self):
return 'balance=%s' % self.balance
acc1 = BankAccount("1000")
acc1.display() # use display
print(acc1) # use __str__

Related

Print data from Class instance

I want to print out data from a Class instance. I tried including data in str function, but this only works when value is at its default (an empty list). After I've added data to the list, I can only get memory objects printed. Can anyone help me troubleshoot why this is happening? I want to be able to print out objects to use in debugging.
class Student():
def __init__(self,name,year):
self.name=name
self.year=year
self.grades=[]
def add_grade(self, grade):
if type(grade)==Grade:
self.grades.append(grade)
else:
pass
def __str__(self):
return str(self.grades)
pieter=Student("Pieter Bruegel the Elder", 8)
print(pieter)
[] # Returns empty list of grades as it was initiated.
class Grade():
minimum_passing=65
def __init__(self,score):
self.score=score
def is_passing(self):
if self.score >= self.minimum_passing:
return True
else:
return False
pieter.add_grade(Grade(100))
pieter.add_grade(Grade(40))
print(pieter)
[<__main__.Grade object at 0x000002B354BF16A0>, <__main__.Grade object at 0x000002B354BF13A0>]
When you tell it to print 'pieter', you are telling it to attempt to print the object. What you need to do is add a print function in the class or use the specific variable in the object. Try adding something like this:
def print_object(self):
print("The name is: {self.name}")
print("The year is: {self.year}")
print("The grades are: {self.grades}")
If you have any questions, please feel free to reach out.
The list.__str__() method uses repr() to get the representation of the list elements. So this is showing the repr() of all the Grade objects in the grades list. Since Grade doesn't have a custom __repr__() method, you get the default representation.
You could define Grade.__repr__() to specify how you want grades to appear, or you could change the Student.__str__() method to get the scores from the Grade objects in the list.
class Student():
def __init__(self,name,year):
self.name=name
self.year=year
self.grades=[]
def add_grade(self, grade):
if isinstance(grade, Grade):
self.grades.append(grade)
else:
pass
def __str__(self):
return str([str(g) for g in self.grades])
The problem is that Grade itself doesn't have a neat representation as a string. Thus the str(self.grades) call can only print a list of abstract instance data. You could include a representation:
class Grade():
# the other stuff goes here...
def __repr__(self):
return f'Grade({self.score}'
Now print(pieter) prints:
[Grade(100), Grade(40)]
However, I wouldn't use the __str__() magic method to print the grades. It's clearer to define an explicit method such as print_grades() for this purpose. __str__() is normally used to output all the relevant information about a class (for debug purposes etc.) So in your case, it should return all the student info too, not just the grades.
In your add_grade method, you are inserting a Grade object into self.grades, and then when you print self.grades you get a list of Grade objects. Maybe you want to insert the score field from the Grade object into self.grades. That way you can print out the scores when you print out a student. Like so:
def add_grade(self, grade):
if type(grade) == Grade:
self.grades.append(grade.score)
Also, there is no need to write the else statement in the method, it will automatically return None if the argument passed to it is not a Grade object.

I'm trying to use aggregation but I think I get it wrong

So I wrote a code with a Bank class that has a validation method and a SavingAccount class that has a withdraw method, and I'm trying to use aggregation to associate them both, but it isn't working. The validation is validating everything. The focus should be in the Bank class and the SavingAccount class and at the end I put the instance that shouldn't work (acc1.withdraw()), I put the others class so that you can try out the code, anyway here is the code :
Expected behavior: Without calling the add_acc() function, the validation should return False and it shouldn't let me use the withdraw() function.
Actual behavior: I'm able to withdraw() even when the add_acc() wasn't called
from abc import ABC, abstractmethod
class Account(ABC):
def __init__(self, agency, acc_number, balance):
self.agency = agency
self.acc_number = acc_number
self.balance = balance
#abstractmethod
def withdraw(self, value):
pass
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
class Bank:
def __init__(self):
self.account = {}
def add_acc(self, client, account):
self.account.update({f'{client}': account})
def validation(self):
if self.account:
for acc in self.account:
if acc in self.account:
return True
else:
return False
class SavingAccount(Account):
if Bank.validation:
def withdraw(self, value):
if self.balance < value:
print('Insufficient funds.')
return
self.balance -= value
print(f'{value} dollars withdrawn. Current balance: {self.balance}$')
else:
print('Account information invalid.')
class Client(Person):
def __init__(self, name, age, acc_type):
super().__init__(name, age)
self.acc_type = acc_type
bank1 = Bank()
acc1 = SavingAccount(33333, 33330, 2000)
client1 = Client('Matthew', 40, acc1)
acc1.withdraw(500) # Right here this shouldn't work without me adding bank1.add_acc()
There are two major issues with your code.
The first is that your implementation of SavingsAccount is seriously flawed. You're checking if Bank.validate is truthy, rather than calling it. But even if you did call it, it wouldn't make any sense at the location you have the call. You are attempting to do the validation when the class is defined, not when you create an instance of the class, or try to withdraw funds. That doesn't make any sense. The concept of savings accounts (i.e. the definition of the class) should be able exist even if there haven't been any banks founded yet. Do the validation some time later! And probably you need to be validating with some specific instance of the Bank class, not with the Bank class directly.
The second issue is that your Bank.validate method doesn't do anything useful. It loops over all the keys in the self.accounts dictionary, but then just checks the first one to see if it's in the dictionary (which is always will be, if you reached that part of the code), and then returns. Probably you want that function to be checking one specific account, not checking in general for arbitrary accounts. That account (or an account number, or something) should probably be an argument to the function.

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.

balance() method doesn't work when called in python

I have a BankAccount class that allows a balance to be set, an amount to be deposited/withdrawn, and then a method that returns the balance.
class BankAccount():
def __init__(self, initialBalance = 0):
self.balance = initialBalance
def __repr__(self):
return("BankAccount({})".format(self.balance))
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
self.balance -= amount
def balance(self):
return(self.balance())
But when I call the balance method (b.balance()), it get a float object not callable error. But if I call it b.balance it returns the proper amount. I'm confused on what the error means and why the method call doesn't work. Thanks in advance!
It won't work to have an attribute and a method with the same name. You can either give the attribute a different name:
class BankAccount():
def __init__(self, initialBalance = 0):
self._balance = initialBalance
def __repr__(self):
return("BankAccount({})".format(self._balance))
def deposit(self, amount):
self._balance += amount
def withdraw(self, amount):
self._balance -= amount
def balance(self):
return(self._balance)
print(BankAccount())
or access the attribute directly (and remove the accessor method):
class BankAccount():
def __init__(self, initialBalance = 0):
self.balance = initialBalance
def __repr__(self):
return("BankAccount({})".format(self.balance))
def __repr__(self):
return("BankAccount({})".format(self.balance))
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
self.balance -= amount
print(BankAccount())
When you define a class, all of its methods are defined, but not called. This includes def balance(self):. Those methods are accessible via references e.g. self.methodname. When you create an instance of this class, it calls the __init__ method, defined as def __init__(self, initialBalance=0):. This method assigns an instance attribute self.balance and gives it a floating-point value. When you do that, the old instance attribute that self.balance used to point to - the method you defined - is no longer referred to, so it's collected by Python's garbage collector and disappears.
The key is that Python does not have one bag for function names and another bag for other names. It simply has a bag for all instance attribute names, whether they point to a floating-point number, a function, or something else.
Also, if you hadn't overwritten self.balance, that method would've been even more problematic, as it calls itself (recursion) with no way of stopping. So, when you call it, it will call itself, which will then call itself, forever, producing an error.
Simply remove the defined def balance(self): method entirely. It doesn't look like it was doing anything useful, and you can simply access the self.balance attribute directly.
remove
def balance(self)
it is an attribute. If you have an attribute, you don't need a method to retrieve it
self.balance() calls the balance method of the BankAccount class and b.balance is the balance attribute of the BankAccount object.
Since return is not a method you cannot do return(something) and that's why you are getting an error saying self.balance() which is a float is not callable. But when you do b.balance it is a perfectly valid syntax and it returns the value of balance attribute which is the current balance.
Suggestion: Have different names for attribute balance and method balance(probably check_balance).

What are the main advantages of using a class over a callable function?

Today I am wondering what are the advantages of a class over a callable function. I know it seems like a weird question, but I am coding a space-based game, and I would like to make something that shows lifesupport(oxygen, nitrogen)
Basically, I have this function:
oxygen = 100
nitrogen = 100
air = [oxygen, nitrogen]
print("You can use check(air) to see the quantities of n2 and o2 available.")
def check(variable):
if variable == air:
print("Your oxygen level is {0}% and your nitrogen level is {1}%.".format(oxygen, nitrogen))
So whenever the player types check(air), they can see the amount of nitrogen and oxygen in their ship. Using this I can also allow them to see other functions by expanding the check() function. I'm wondering if it's better to do this for a game rather than to do this:
class Lifesupport(object):
def __init__(self, oxygen, nitrogen):
self.oxygen = oxygen
self.nitrogen = nitrogen
air = Lifesupport(40, 60)
#This part is for checking functionality
print("Your air has {0}% oxygen and {1}% nitrogen".format(air.oxygen, air.nitrogen))
Personally, I prefer the function, though I don't really know which is better to use for this purpose. I know you can type air.oxygen to see just oxygen levels, and I could probably use a check() function to solely print a class "bundle" like 'air'.."
Basically... What are the real advantages of using a class over a function like the code I showed? Is it better to use a class or a function, or both, for my purposes?
For printing the oxygen and nitrogen, you would do:
class Lifesupport(object):
def __init__(self, oxygen, nitrogen):
self.oxygen = oxygen
self.nitrogen = nitrogen
def __str__(self):
return "Your air has {0}% oxygen and {1}% nitrogen".format(self.oxygen, self.nitrogen)
Then later, whenever you want to show the Lifesupport levels, you simply do:
air = Lifesupport(40, 60)
print(air)
The __str__ method overrides the default __str__ method of a class and so when you do print(air), it will print the custom text.
As for class vs. method, it is recommended that you use classes, especially when you know that are going to be expanding your program, since you can create multiple instances of a class that will all have attributes that can be modified independent of each other. Below is an example:
Example
class A:
def __init__(self,num):
self.val = num
a = A(4)
b = A(5)
>>> print(a.val)
4
>>> a.val = 6
>>> print(a.val)
6
>>> print(b.val)
5
Class is an Instance factory. You can create multiple instance (a.k.a copy) of class customize & extend it to your need (by Inheritance & overloading methods).
If you ever want to re-use or customize already written function, then using it inside the class is way to go. I suggest you go through the class coding basic if you have not already done that to make up your mind. a simple example could be
class Person:
def __init__(self, name, job=None, pay=0):
self.name = name
self.job = job
self.pay = pay
def giveRaise(self, percent):
self.pay = int(self.pay * (1 + percent))
class Manager(Person):
def __init__(self, name, pay): # Redefine constructor
Person.__init__(self, name, 'mgr', pay) # Run original with 'mgr'
def giveRaise(self, percent, bonus=.10):
Person.giveRaise(self, percent + bonus)
I'm customizing the original class 'init' method by inheriting original class & adding new information in Manager Class

Categories

Resources