Appropriate way to use (read/write) a variable with different instances? - python

I have $100 cash to get some food.
Then, what is an appropriate/correct way to read/write a variable (cash) globally with different instance(apple, banana)?
#-- class definitions
class Wallet():
cash=100
class Food():
w=Wallet()
def __init__(self,price):
self.price=price
def purchase(self,num):
self.w.cash-=num*self.price
#-- instantiation
apple=Food(price=5)
banana=Food(price=10)
#-- call method
apple.purchase(num=3) #5*3=15
banana.purchase(num=4) #10*4=40
print(apple.w.cash) #100-(15+40)=45
print(banana.w.cash) #100-(15+40)=45
This looks correct, but I feel uncomfortable because this code overwrites cash, which is defined as a class variable in Wallet class. (Class variables should not be overwritten, from my understanding.)
Would it be better not to define Wallet class but to define cash as a global variable?
If I insist on defining Wallet class, how should cash be treated in instantiated classes?

Maybe you meant static class variables shouldn't be overwritten.
Also I'm not sure why food has a purchase method. Perhaps you can create another class such as Customer, then have Wallet be a class variable of Customer.
You can do something like:
class Wallet:
def __init__(self, cash):
self.cash = cash
def add_cash(self, amount):
self.cash += amount
def subtract_cash(self, amount):
self.cash -= amount
class Customer:
def __init__(self, wallet):
self.wallet = wallet
def purchase(self, food):
self.wallet.subtract_cash(food.value)
class Food:
def __init__(self, value):
self.value = value

Related

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.

Initialising nested classes in Python

Let's say I want to create a class 'House' that has some attributes of its own, but also has a (nested?) 'Resident' class which has some attributes and has a mandatory attribute 'surname'. A house instance may exist though without any residents. How can create this so that I can eventually do the following?
myhouse = House()
residentX = myhouse.resident('Smith')
Currently I set this up as a nested class but run into trouble when I try and initialise myhouse given that it is requiring a surname at this point for the nested Resident class (which I don't necessarily have at this point)
class House:
def __init__(self):
self.someattribute = <someattribute>
self.resident = self.Resident()
class Resident:
def __init__(self, surname):
self.surname = surname
I know I can restructure the code to not use nested classes and then explicitly tie any resident to a house in my code. However, I would like to use the dot notation here (myhouse.resident) to automatically tie a resident to a house.
Also, I understand that nested classes in python are somewhat frowned upon - I'm open to suggestions on how to do the above in a more pythonic manner.
I would break out the Resident class and use a property/setter for .resident
Like this:
class House:
def __init__(self):
self.someattribute = <someattribute>
self._resident = None
#property
def resident(self):
return self._resident
#resident.setter
def resident(self, surname):
r = Resident(surname)
self._resident = r
class Resident:
def __init__(self, surname):
self.surname = surname
However, if you want .resident to be callable but also want to track the house's residents, you can still break out the Resident class, and use:
class House:
def __init__(self):
self.someattribute = <someattribute>
self.residents = []
def resident(self, surname):
'''
Add a resident to the house
'''
r = Resident(surname)
self.residents.append(r)
return r
class Resident:
def __init__(self, surname):
self.surname = surname

Learn Python, exercise 6.7

I'm reading Lutz & Ascher - Learn Python and I found this as a solution to one of the exercises:
class Lunch:
def __init__(self):
self.cust = Customer()
self.empl = Employee()
def order(self, foodName):
# start a Customer order simulation
self.cust.placeOrder(foodName, self.empl)
def result(self):
# ask the Customer what kind of Food it has
self.cust.printFood()
class Customer:
def __init__(self):
# initialize my food to None
self.food = None
def placeOrder(self, foodName, employee):
# place order with an Employee
self.food = employee.takeOrder(foodName)
def printFood(self):
# print the name of my food
print self.food.name
class Employee:
def takeOrder(self, foodName):
# return a Food, with requested name
return Food(foodName)
class Food:
def __init__(self, name):
# store food name
self.name = name
if __name__ == '__main__':
x = Lunch()
x.order('burritos')
x.result()
x.order('pizza')
x.result()`
What I don't understand is how the definition of the method placeOrder inside the customer class works, more specifically, there is no class employee (just Employee) whose method placeOrder could be used.
def placeOrder(self, foodName, employee):
# place order with an Employee
self.food = employee.takeOrder(foodName)
you may need to read a little bit about object oriented programming, and dynamic typing to grasp this. So basically, employee is an argument which will be passed at runtime, its type will be determined after the call to placeOrder. if you call PlaceOrder and put an instance of Employee or any class that has method takeOrder(), it will work. Imho, you should try to code an example from the beginning and test out what you learn, it will help you learn Python faster
`

python - how to have a parent invoke a method when a property changes

I have a class that holds an array of objects (also classes).
When an item from this array fires a method, I want the class to fire a method.
From the item in the array, how can I refer to a method on the "parent" class that it is under?
Teacher is not explicitly a "super" class of student, Teacher has that student as a property.
For example
class Teacher:
def __init__(self, list):
self.array = list
def gradeTest(self):
print("I fired!")
list = [{student}, {student}, {nextStudent}]
class Student:
def __init__(self, name):
self.name = name
def takeTest(paper):
# do something with paper
# contact student's teacher to "grade" paper
TLDR:
when list[0].takeTest(paper) fires, I'd like MasterClass to invoke MasterClass.gradeTest()

Python: How do I make a subclass from a superclass?

In Python, how do you make a subclass from a superclass?
# Initialize using Parent
#
class MySubClass(MySuperClass):
def __init__(self):
MySuperClass.__init__(self)
Or, even better, the use of Python's built-in function, super() (see the Python 2/Python 3 documentation for it) may be a slightly better method of calling the parent for initialization:
# Better initialize using Parent (less redundant).
#
class MySubClassBetter(MySuperClass):
def __init__(self):
super(MySubClassBetter, self).__init__()
Or, same exact thing as just above, except using the zero argument form of super(), which only works inside a class definition:
class MySubClassBetter(MySuperClass):
def __init__(self):
super().__init__()
A heroic little example:
class SuperHero(object): #superclass, inherits from default object
def getName(self):
raise NotImplementedError #you want to override this on the child classes
class SuperMan(SuperHero): #subclass, inherits from SuperHero
def getName(self):
return "Clark Kent"
class SuperManII(SuperHero): #another subclass
def getName(self):
return "Clark Kent, Jr."
if __name__ == "__main__":
sm = SuperMan()
print(sm.getName())
sm2 = SuperManII()
print(sm2.getName())
class MySubClass(MySuperClass):
def __init__(self):
MySuperClass.__init__(self)
# <the rest of your custom initialization code goes here>
The section on inheritance in the python documentation explains it in more detail
class Class1(object):
pass
class Class2(Class1):
pass
Class2 is a sub-class of Class1
In the answers above, the super is initialized without any (keyword) arguments. Often, however, you would like to do that, as well as pass on some 'custom' arguments of your own. Here is an example which illustrates this use case:
class SortedList(list):
def __init__(self, *args, reverse=False, **kwargs):
super().__init__(*args, **kwargs) # Initialize the super class
self.reverse = reverse
self.sort(reverse=self.reverse) # Do additional things with the custom keyword arguments
This is a subclass of list which, when initialized, immediately sorts itself in the direction specified by the reverse keyword argument, as the following tests illustrate:
import pytest
def test_1():
assert SortedList([5, 2, 3]) == [2, 3, 5]
def test_2():
SortedList([5, 2, 3], reverse=True) == [5, 3, 2]
def test_3():
with pytest.raises(TypeError):
sorted_list = SortedList([5, 2, 3], True) # This doesn't work because 'reverse' must be passed as a keyword argument
if __name__ == "__main__":
pytest.main([__file__])
Thanks to the passing on of *args to super, the list can be initialized and populated with items instead of only being empty. (Note that reverse is a keyword-only argument in accordance with PEP 3102).
There is another way to make subclasses in python dynamically with a function type():
SubClass = type('SubClass', (BaseClass,), {'set_x': set_x}) # Methods can be set, including __init__()
You usually want to use this method when working with metaclasses. When you want to do some lower level automations, that alters way how python creates class. Most likely you will not ever need to do it in this way, but when you do, than you already will know what you are doing.
class Subclass (SuperClass):
# Subclass stuff here
You use:
class DerivedClassName(BaseClassName):
For details, see the Python docs, section 9.5.
class Mammal(object):
#mammal stuff
class Dog(Mammal):
#doggie stuff
Subclassing in Python is done as follows:
class WindowElement:
def print(self):
pass
class Button(WindowElement):
def print(self):
pass
Here is a tutorial about Python that also contains classes and subclasses.
class BankAccount:
def __init__(self, balance=0):
self.balance = int(balance)
def checkBalance(self): ## Checking opening balance....
return self.balance
def deposit(self, deposit_amount=1000): ## takes in cash deposit amount and updates the balance accordingly.
self.deposit_amount = deposit_amount
self.balance += deposit_amount
return self.balance
def withdraw(self, withdraw_amount=500): ## takes in cash withdrawal amount and updates the balance accordingly
if self.balance < withdraw_amount: ## if amount is greater than balance return `"invalid transaction"`
return 'invalid transaction'
else:
self.balance -= withdraw_amount
return self.balance
class MinimumBalanceAccount(BankAccount): #subclass MinimumBalanceAccount of the BankAccount class
def __init__(self,balance=0, minimum_balance=500):
BankAccount.__init__(self, balance=0)
self.minimum_balance = minimum_balance
self.balance = balance - minimum_balance
#print "Subclass MinimumBalanceAccount of the BankAccount class created!"
def MinimumBalance(self):
return self.minimum_balance
c = BankAccount()
print(c.deposit(50))
print(c.withdraw(10))
b = MinimumBalanceAccount(100, 50)
print(b.deposit(50))
print(b.withdraw(10))
print(b.MinimumBalance())
this is a small code:
# create a parent class
class Person(object):
def __init__(self):
pass
def getclass(self):
return 'I am a Person'
# create two subclass from Parent_class
class Student(Person):
def __init__(self):
super(Student, self).__init__()
def getclass(self):
return 'I am a student'
class Teacher(Person):
def __init__(self):
super(Teacher, self).__init__()
def getclass(self):
return 'I am a teacher'
person1 = Person()
print(person1.getclass())
student1 = Student()
print(student1.getclass())
teacher1 = Teacher()
print(teacher1.getclass())
show result:
I am a Person
I am a student
I am a teacher
A minor addition to #thompsongunner's answer.
To pass args to your superclass (parent), just use the function signature of the parent class:
class MySubClassBetter(MySuperClass):
def __init__(self, someArg, someKwarg="someKwarg"):
super().__init__(someArg, someKwarg=someKwarg)
You are calling the parent's __init__() method as if you are constructing any other class which is why you don't need to include self.

Categories

Resources