Access instance variable from another class - python

I would like to access the instance variable (orders) of a class (DailyOrders) from the init of another, keeping in mind that the class containing the instance variable is a parent to the other. This seems to perfect use for inheritance but I couldn't get it to work. Here is the code.
class DailyOrders():
PRICE_PER_DOZEN = 6.5
def __init__(self, day):
self.orders = []
self.day = day
def total_eggs(self):
total_eggs = 0
for order in self.orders:
total_eggs += order.eggs
return total_eggs
def show_report(self):
if self.total_eggs() < 0:
print("No Orders")
else:
print(f"Summary:\nTotal Eggs Ordered: {self.total_eggs()}\n*********")
print(f"Average Eggs Per Customer: {self.total_eggs() / len(self.orders):.0f}")
class EggOrder():
def __init__(self, eggs=0, name=""):
if not name:
self.new_order()
else:
self.name = name
self.eggs = eggs
orders.append(self)
def new_order(self):
self.name = string_checker("Name: ")
self.eggs = num_checker("Number of Eggs: ")
def get_dozens(self):
if self.eggs % 12 != 0:
dozens = int(math.ceil(self.eggs / 12))
else:
dozens = self.eggs / 12
return dozens
def show_order(self):
print(f"{self.name} ordered {self.eggs} eggs. The price is ${self.get_dozens() * DailyOrders.PRICE_PER_DOZEN}.")
if __name__ == "__main__":
friday = DailyOrders("Friday")
friday_order = EggOrder(12, "Someone")
friday_order.show_order()
friday.show_report()
saturday = DailyOrders("Saturday")
saturday_order = EggOrder(19, "Something")
saturday_order.show_order()
saturday.show_report()
I have not tried using inheritance before, but one proposed solution is to use
super(EggOrder, self).__init__() but this made me provide the day, as it was going to create another instance of the DailyOrders class. I don't want this.

Just inherit and user super to call the parent initializer:
class DailyOrders:
def __init__(self, day):
self.orders = []
# ...
class EggOrder(DailyOrders):
def __init__(self, day, eggs=0, name=""):
super().__init__(day)
# Now self.orders is available.
Keep in mind that if the parent initializer receives any parameter, the child must receive it as well in order to be able to pass it.
Not providing a day param ...
If you don't want to provide a day param you should have another class with the interface/functionality that's common to the others, and the inherit from such base class:
class BaseOrders:
def __init__(self):
self.orders = []
# ...
class DailyOrders(BaseOrders):
def __init__(self, day):
super().__init__()
# Now self.orders is available.
self.day = day
# ...
class EggOrder(BaseOrders):
def __init__(self, eggs=0, name=""):
super().__init__()
# Now self.orders is available.

Related

python OOP - child variable updating set parent

I have a parent class with 3 items in it. I am trying to create a child class that when called updates a set item in the parent class.
class NOS:
def __init__(self):
self.Bike = 0
self.car = 0
self.plane = 0
class buy(NOS):
def __init__(self, mode):
NOS.__init__(self)
self.mode = mode
def buy_comp(self, value):
self.mode += value
if i called it like below
a = buy('bike')
a.buy_comp(4)
I am trying to get to a situation where bike would equal 4. The above did not work. Neither did the below where i tried to use buy as a function instead of a class.
def buy(self, mode, value):
self.mode += value
a= NOS()
a.buy('bike', 5)
Here i got the error - AttributeError: 'NOS' object has no attribute 'bike'
In the first example you posted, your child class "buy" is not actually a child class, because it is not inheriting from "NOS".
Not exactly sure what you're trying to achieve. Maybe this is helpful?
class Parent:
def __init__(self):
self.foo = "Parent Foo"
class Child(Parent):
def __init__(self):
Parent.__init__(self)
def set_foo(self, new_foo):
self.foo = new_foo
child = Child()
print(child.foo)
child.set_foo("New Foo")
print(child.foo)
Output:
Parent Foo
New Foo
EDIT - Oh, I think I get it now. Something like this maybe?
class NOS:
def __init__(self):
self.bike = 0
self.car = 0
self.plane = 0
class Buy(NOS):
def __init__(self, item_name):
NOS.__init__(self)
self.item_name = item_name
def buy_comp(self, amount):
try:
old_value = getattr(self, self.item_name)
except NameError:
# No such item exists
pass
else:
setattr(self, self.item_name, old_value + amount)
a = Buy("bike")
print(a.bike)
a.buy_comp(4)
print(a.bike)
However, I think that if you're relying on getattr and setattr, there's bound to be a better way. I have a feeling that this may be an instance of an XY problem. Can you tell us more about the actual use case? I'm sure there's a more elegant solution you could benefit from.

Python: manage multiple classes (from serialization) with common properties

I am new to structure Python projects, so please forgive any wrong approaches that could be written down here.
Two JSON schemes represent two objects. These are serialised into classes and have properties in common.
Example:
class TwoWheelVeicle(object):
def __init__(self,v_family, v_subfamily):
self.Family = v_family
self.SubFamily = v_subfamily
self.OtherProp = "other"
class FourWheelVeicle(object):
def __init__(self,v_family):
self.Family = v_family
self.AnotherProp = "another"
def run_an_highway(vehicle):
if isinstance(vehicle,FourWheelVeicle):
return "Wrooom"
if isinstance(vehicle,TwoWheelVeicle):
if veichle.SubFamily in SubFams.NotAllowed:
return "ALT!"
else:
return "Brooom" #forgive me for the sound
class SubFams(object):
NotAllowed = ["Bicycle","50cc"]
Known = ["200cc","Motorbike"]
I am quite unsure of the procedure overall:
- Shall I create an abstract parent class?
- Is NotAllowed stored correctly? This is due to the need of changing its content (that is serialized from some global parameter JSON, it's a #TODO)
..or simply I should not want to do any of these?
Lastly, the code does not allow for any checks if the properties that I serialize are correct (what if SubFamily is unknown? Should it be checked in the decoder?).
A big thank you.
Looks like you should abstract vehicle with a Vehicle class and then subclass it for your different vehicle types.
Your if chain isn't needed if your different subclasses have their own version for that same method.
Something in the line with these:
class Vehicle(object):
def __init__(self, name, cc):
self.name = name
self.cc = cc
self.wheels = None
def runs_on_highway(self):
return self.cc > 50
def sound(self):
pass
class TwoWheels(Vehicle):
def __init__(self, name, cc):
Vehicle.__init__(self, name, cc)
self.wheels = 2
def sound(self):
return 'Brooom.'
class FourWheels(Vehicle):
def __init__(self, name, cc):
Vehicle.__init__(self, name, cc)
self.wheels = 4
def sound(self):
return 'Vruuum'
class ElectricWheels(Vehicle):
def __init__(self, name, cc):
Vehicle.__init__(self, name, 0)
self.wheels = 4
def runs_on_highway(self):
return True
def sound(self):
return 'zzzzz.'
v1 = TwoWheels('Bicycle', 50)
v2 = FourWheels('Motorbike', 200)
v3 = ElectricWheels('ElectricBike', 0)
print(v1.runs_on_highway())
print(v2.runs_on_highway())
print(v3.runs_on_highway())
print(v1.name, v1.cc, v1.wheels, v1.sound())
print(v2.name, v2.cc, v2.wheels, v2.sound())
print(v3.name, v3.cc, v3.wheels, v3.sound())

AttributeError: 'RangedWeapon' object has no attribute 'owner'

In the Person object, there is already a support for an inventory, and when the Person object takes a Weapon object or Food object, the object would go to the inventory. For the Tribute object, I want to retrieve the Weapon objects from the inventory by creating a new method in the Tribute class, get_weapons(), which would return a tuple of Weapon objects that the Tribute currently has in his inventory.
class Tribute(Person):
...
def get_weapons(self):
self.weapons=[]
for item in self.get_inventory():
if isinstance(item,Weapon):
self.weapons.append(item)
return tuple(self.weapons)
cc = Tribute("Chee Chin", 100)
chicken = Food("chicken", 5)
aloe_vera = Medicine("aloe vera", 2, 5)
bow = RangedWeapon("bow", 4, 10)
sword = Weapon("sword", 2, 5)
Base = Place("base")
Base.add_object(cc)
Base.add_object(chicken)
Base.add_object(aloe_vera)
Base.add_object(bow)
Base.add_object(sword)
cc.take(bow) # Chee Chin took bow
cc.take(sword) # Chee Chin took sword
cc.take(chicken) # Chee Chin took chicken
cc.take(aloe_vera) # Chee Chin took aloe_vera
But I keep getting AttributeError: 'RangedWeapon' object has no attribute 'owner'. I'm not sure what went wrong. I would really appreciate some help. Thank you!
import random
from collections import OrderedDict
######################
# Class: NamedObject #
######################
class NamedObject(object):
def __init__(self, name):
self.name = name
def get_name(self):
return self.name
#######################
# Class: MobileObject #
#######################
class MobileObject(NamedObject):
def __init__(self, name, place):
super().__init__(name)
self.place = place
def get_place(self):
return self.place
################
# Class: Thing #
################
class Thing(MobileObject):
def __init__(self, name):
super().__init__(name, None)
self.owner = None
def set_owner(self, owner):
self.owner = owner
def get_owner(self):
return self.owner
def is_owned(self):
return self.owner is not None
#################
# Class: Person #
#################
class Person(LivingThing):
def __init__(self, name, health, threshold):
self.inventory = []
super().__init__(name, health, threshold)
def take(self, thing):
# Can only take things in current location and not owned by others
if isinstance(thing, Thing) and thing in self.place.objects and not thing.is_owned():
thing.set_owner(self)
self.inventory.append(thing)
self.place.del_object(thing)
GAME_LOGGER.add_event("TOOK", self, thing)
else:
GAME_LOGGER.warning("{} cannot take {}.".format(self.get_name(), thing.get_name()))
def remove_item(self, thing):
#Can only remove things in inventory
if isinstance(thing, Thing) and thing in self.get_inventory() and thing.get_owner()==self:
thing.set_owner(None)
self.inventory.remove(thing)
else:
GAME_LOGGER.warning("{} does not own {}.".format(self.get_name(), thing.get_name()))
def go(self, direction):
new_place = self.place.get_neighbor_at(direction.upper())
if new_place is not None:
self.move_to(new_place)
else:
GAME_LOGGER.warning("{} cannot go {} from {}".format(self.get_name(), direction, self.get_place().get_name()))
def get_inventory(self):
return list(self.inventory)
def objects_around(self):
return list(filter(lambda t: t is not self, self.get_place().get_objects()))
def get_exits(self):
return self.get_place().get_exits()`
class Weapon(Thing):
def __init__(self, name, min_dmg, max_dmg):
self.name=name
self.min_dmg=min_dmg
self.max_dmg=max_dmg
def min_damage(self):
return self.min_dmg
def max_damage(self):
return self.max_dmg
def damage(self):
return random.randint(self.min_dmg,self.max_dmg)
class RangedWeapon(Weapon):
def __init__(self, name, min_dmg, max_dmg):
super().__init__(name, min_dmg, max_dmg)
self.shots=0
def shots_left(self):
return self.shots
def load(self, ammo):
if ammo.weapon_type()==self.name:
self.shots+=ammo.get_quantity()
ammo.remove_all()
def damage(self):
if self.shots==0:
return 0
else:
self.shots-=1
return super().damage()
not an expert on python 3.x, but:
class Weapon(Thing):
def __init__(self, name, min_dmg, max_dmg):
self.name=name
self.min_dmg=min_dmg
self.max_dmg=max_dmg
you never actually set Weapon to inherit from Thing - you need to call the super().__init__ line that's appropriate for Thing, in Weapon's ctor. its an annoying python quirk.
have a look here - at RangedWeapon's ctor - you're doing it right:
super().__init__(name, min_dmg, max_dmg)

Initialise child class with instance of parent class

Suppose I have a class:
class Person(object):
def __init__(self, name, hobbies):
self.name = name
self.hobbies = hobbies
... (and so on)
Now I want to initialise a child class, Employee, which extends person. I would like to initialise that class with an instance of the Person class. So I would like to do:
class Employee(Person):
def __init__(self, person, salary):
# Initialise the superclass from the given instance somehow
# I know I could do:
super(Employee, self).__init__(person.name, person.hobbies)
# But could I somehow do something like:
super(Employee, self).__init__(person)
# (In this case the difference is small, but it could
# be important in other cases)
# Add fields specific to an "Employee"
self.salary = salary
So that I can then call:
p1 = Person('Bob', ['Bowling', 'Skiing'])
employed_p1 = Employee(p1, 1000)
Is there any way I can do this, or do I explicitly have to call the parent class's constructor again?
Thanks a lot!
I thnk you want something like this:
class Person(object):
def __init__(self, name, hobbies):
self.name = name
self.hobbies = hobbies
def display(self):
print(self.name+' '+self.hobbies[0])
class Employee(Person):
def __init__(self, a, b =None,salary=None):
if b is None:
self.person = a
else:
self.person = Person(a,b)
self.name = self.person.name
self.hobbies = self.person.hobbies
self.salary = salary
bob = Employee('bob',['Bowling', 'Skiing'])
bob.display()
sue1 = Person('sue',['photography','music'])
sue2 = Employee(sue1,salary=123)
sue2.display()
I've added in the 'display' function just to make it easier to follow. Hope this helps.

Expanding the class from object

I have some classes:
class Window(object):
def __init__(self, name):
self.wind_name = name
def getWindowName(self):
return 'wnd' + self.wind_name
class Control(object):
def __init__(self, name, wnd):
self.contrl_name = name
setattr(self, 'getWindowName', wnd.getWindowName)
setattr(self, 'wind_name', wnd.wind_name)
def getControlName(self):
return (self.getWindowName(), 'unk' + self.contrl_name)
class Button(Control):
def __init__(self, name, wnd):
super(Button, self).__init__(name, wnd)
def getControlName(self):
return (self.getWindowName(), 'btn' + self.contrl_name)
wnd = Window('MyApp')
btnOK = Button('OK', wnd)
btnOK.getControlName() # work ok., return ('wndMyApp', 'btnOK')
btnOK.wind_name = 'NewApp'
btnOK.getControlName() # does not work properly., return ('wndMyApp', 'btnOK')
How can I extend the class Control|Button from the object of class Window to access the functions getWindowName and field wind_name in objects btnOK?
Is there a way without creating a field self.wnd = wnd in class Control, or add method setWindowName in Window...?
I can not inherit class Control from the class Window! This is not logical.
Python allows inheriting from multiple classes, i.e.
class Button(Control, Window):
...
But in this case you should know exactly what you are doing (speaking of Pythons Method Resolution Order (MRO)). I'd recommend reading this small book: Python Attributes and Methods.
You can use property for attributes
class Window(object):
def __init__(self, name):
self.wind_name = name
def getWindowName(self):
return 'wnd' + self.wind_name
class Control(object):
def __init__(self, name, wnd):
self.contrl_name = name
self.wnd = wnd
setattr(self, 'getWindowName', wnd.getWindowName)
def get_wind_name(self):
return self.wnd.wind_name
def set_wind_name(self, v):
self.wnd.wind_name = v
wind_name = property(get_wind_name, set_wind_name)
def getControlName(self):
return (self.getWindowName(), 'unk' + self.contrl_name)
class Button(Control):
def __init__(self, name, wnd):
super(Button, self).__init__(name, wnd)
def getControlName(self):
return (self.getWindowName(), 'btn' + self.contrl_name)
wnd = Window('MyApp')
btnOK = Button('OK', wnd)
print btnOK.getControlName() # work ok., return ('wndMyApp', 'btnOK')
btnOK.wind_name = 'NewApp'
print btnOK.getControlName()

Categories