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

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)

Related

Achieving single-responsibility principle with python abstract classes

I want to separate the DB models from the actual classes. But i need two static functions for fetching data from the DB regardless of the subclass type. the implementation for both functions are the same across all DB models.
pyright showing an error that cls inside get() and get_all() functions doesn't have a db property.
from abc import ABC, abstractstaticmethod
class DogsDB:
lists = ["DOG1", "DOG2", "DOG3"]
#classmethod
def get(cls, id):
return cls.lists[id]
class CatsDB:
lists = ["CAT1", "CAT2", "CAT3"]
#classmethod
def get(cls, id):
return cls.lists[id]
class Animal(ABC):
def __init__(self, name):
self.name = name
#abstractstaticmethod
def save(m):
pass
#abstractstaticmethod
def _from_model(obj):
pass
#classmethod
def get(cls, id):
obj = cls.db.get(id)
return cls._from_model(obj)
#classmethod
def get_all(cls):
objs = cls.db.lists
lists = []
for obj in objs:
e = cls._from_model(obj)
lists.append(e)
return lists
def __repr__(self):
return self.name
class DogSound:
def __init__(self, name):
self.name = name
def sound(self):
print(self.name, ": DOG SOUND!!")
class Dog(Animal, DogSound):
db = DogsDB
def __init__(self, name, age):
super(Dog, self).__init__(name)
self.age = age
#staticmethod
def save(m):
print(m)
#staticmethod
def _from_model(obj):
return Dog(obj, 4)
class Cat(Animal):
db = CatsDB
def __init__(self, name, age):
super().__init__(name)
self.age = age
#staticmethod
def save(m):
print(m)
#staticmethod
def _from_model(obj):
return Cat(obj, 4)
print(Cat.get(1))
print(Dog.get(1))
print(Cat.get_all())
print(Dog.get_all())
Dog.get(1).sound()
I cannot duplicate your first error.
Your second issue is a result of method sound implicitly returning None since it has no return statement and you have print(Dog.get(1).sound()), which will print out the return value from that method. You either want to change this to just Dog.get(1).sound() or modify the sound method to return what it is currently being printed and remove the print statement (my choice).
As an aside, I found this class structure a bit difficult to follow. Why do you need a separate DogSound class with a name attribute which should belong to Animal? Also, it seems to me that age could/should be an attribute of Animal since both cats and dogs have an age.
from abc import ABC, abstractstaticmethod
class DogsDB:
lists = ["DOG1", "DOG2", "DOG3"]
#classmethod
def get(cls, id):
return cls.lists[id]
class CatsDB:
lists = ["CAT1", "CAT2", "CAT3"]
#classmethod
def get(cls, id):
return cls.lists[id]
class Animal(ABC):
def __init__(self, name, age):
self.name = name
self.age = age
#abstractstaticmethod
def save(m):
pass
#abstractstaticmethod
def _from_model(obj):
pass
#classmethod
def get(cls, id):
obj = cls.db.get(id)
return cls._from_model(obj)
#classmethod
def get_all(cls):
objs = cls.db.lists
lists = []
for obj in objs:
e = cls._from_model(obj)
lists.append(e)
return lists
def __repr__(self):
return self.name
class Dog(Animal):
db = DogsDB
def __init__(self, name, age):
super().__init__(name, age)
def sound(self):
return f"{self.name}: DOG SOUND!!"
#staticmethod
def save(m):
print(m)
#staticmethod
def _from_model(obj):
return Dog(obj, 4)
class Cat(Animal):
db = CatsDB
def __init__(self, name, age):
super().__init__(name, age)
self.age = age
#staticmethod
def save(m):
print(m)
#staticmethod
def _from_model(obj):
return Cat(obj, 4)
print(Cat.get(1))
print(Dog.get(1))
print(Cat.get_all())
print(Dog.get_all())
print(Dog.get(1).sound())
Prints:
CAT2
DOG2
[CAT1, CAT2, CAT3]
[DOG1, DOG2, DOG3]
DOG2: DOG SOUND!!
If for some reason you want DogSound to be a separate class, then there is no need for the name attribute to be duplicated:
...
class DogSound: # A "Mixin" class
def sound(self):
return f"{self.name}: DOG SOUND!!"
class Dog(Animal, DogSound):
db = DogsDB
def __init__(self, name, age):
super().__init__(name, age)
#staticmethod
def save(m):
print(m)
#staticmethod
def _from_model(obj):
return Dog(obj, 4)
...

How to define a method which return attributes from other class

In my program I have the following question. How can I define a method in the class 'Mouse' 'Keyboard' and 'Screen' so that they return the room and the computer they belong to + the associated attribute of the class (so for the keyboard class this would be: 'The computer {} is located in the room {} and has the language {} ') Can someone tell me how I can define such a method for these 3 classes without changing the program itself? The method should be defined for each class seperated. The exact task is "Extend the output of maus, screen and tastatur so that the user knows which computer in which room they belong".
I've tried to define the following method:
def as_text(self):
return "{} an {} in {}".format(self.__name, self.__computer, self.__computer.__room)
In my three classes I have build in a reference to computer. My Class Computer has a reference to room, so I've tried to call it with self.__computer.__room but that doesn't work and I'm not sure how to call this attribute.
Here is my code:
class Room:
__name = "unknown"
def __init__(self, name):
self.__name = name
def get_name(self):
return self.__name
class Device:
__name = "Device"
def __init__(self, name):
self.__name = name
def as_text(self):
return "%s" % (self.__name)
class Computer(Device):
__ip = "unknown"
def __init__(self, name, ip, room):
super().__init__(name)
self.__ip = ip
self.__room = room
def as_text(self):
return "%s with ip=%s" % (super().as_text(), self.__ip)
class Laptop(Computer):
def __init__(self, name, room, ip, with_case = True):
super().__init__(name, room, ip)
self.__with_case = with_case
def as_text(self):
if (self.__with_case):
return "%s with case" % super().as_text()
else:
return super().as_text()
class Screen(Device):
__width = "1920"
__height = "1080"
def __init__(self, name, width, height, computer):
super().__init__(name)
self.__width = width
self.__height = height
self.__computer = computer
def as_text(self):
return "{} an {}".format(self.__name, self.__computer)
class Tastatur(Device):
__language = 'English'
def __init__(self, name, language, computer):
super().__init__(name)
self.__language = language
self.__computer = computer
class Maus(Device):
__type = 'Gaming Mouse'
def __init__(self, name, type, computer):
super().__init__(name)
self.__type = type
self.__computer = computer
Your request is not particularly clear, here what I got.
This is my method for the Screen Class:
def as_text(self):
return "{} an {} in {}".format(super().as_text(), self.__computer.as_text(), self.__computer.display_room())
super().as_text()
This calls the method as_text from the parent class Device.
self.__computer.as_text()
This calls the as_text() method from an instance of the Computer class, which is an attribute of your class Screen.
self.__computer.display_room()
This calls the method display_room() from the Computer class, I created it and returns the Room's name.
def display_room(self):
return self.__room.get_name()
Now you should be able to write methods for Tastur and Maus yourself.

How can I define a method where I need attributes from different classes?

I got a program from my professor where I had to do some tasks. Since we're currently learning "inheritance with classes", I'm not that good at it yet. We had been given a task where it says "Extend the output of mouse, screen and keyboard so that the user knows which computer in which room they belong to". I did this, but I'm not sure if it's true or if there is a better solution. Can anyone explain to me whether the whole thing can be solved better and if so, why? I have defined a method in each class where I get this as an output, but is there a better way where I only have to define a method once and this applies to all other classes? Thanks in advance
Here is my code:
class Room:
__name = "unknown"
def __init__(self, name):
self.__name = name
def get_name(self):
return self.__name
class Device:
__name = "Device"
def __init__(self, name):
self.__name = name
def as_text(self):
return "%s" % (self.__name)
class Computer(Device):
__ip = "unknown"
def __init__(self, name, ip, room):
super().__init__(name)
self.__ip = ip
self.__room = room
def as_text(self):
return "%s with ip=%s" % (super().as_text(), self.__ip)
class Laptop(Computer):
def __init__(self, name, room, ip, with_case = True):
super().__init__(name, room, ip)
self.__with_case = with_case
def as_text(self):
if (self.__with_case):
return "%s with case" % super().as_text()
else:
return super().as_text()
class Screen(Device):
__width = "1920"
__height = "1080"
def __init__(self, name, room, width, height, computer):
super().__init__(name)
self.__room = room
self.__width = width
self.__height = height
self.__computer = computer
def as_text(self):
return "%s with resolution %dx%d" % (super().as_text(), self.__width, self.__height)
def screen_location_computer(self):
return 'The screen with the height {} and width {} is located in Room {} and belongs to the computer {}'.format(self.__height, self.__width , self.__room.get_name(), self.__computer)
class Keyboard(Device):
__language = 'English'
def __init__(self, name, room, language, computer):
super().__init__(name, room)
self.__language = language
self.__computer = computer
def keyboard_location_computer(self):
return 'The keyboard with the language {] is located in Room {} and belongs to the computer {}'.format(self.__language, self.__room.get_name(), self.__computer)
class Mouse(Device):
__type = 'Gaming Mouse'
def __init__(self, name, room, type, computer):
super().__init__(name, room)
self.__type = type
self.__computer = computer
def mouse_location_computer(self):
return 'The mouse with the type {] is located in Room {} and belongs to the computer {}'.format(self.__type, self.__room.get_name(), self.__computer)
You should try to regroup all common attributes and methods in the super class and leave only the specialized attributes in the subclasses, this way you can minimize the need of methods and arguments.
Idem for the method I renamed 'description' : one part is common to all instances (the end) while another (the beginning) is specific. Let super handle the common part and pass it the specialized part to assemble the whole string.
(I removed some classes and attributes for the simplicity of the example)
class Device():
__computer = None
__name = None
__room = None
def __init__(self, name, computer, room):
self.__name = name
self.__computer = computer
self.__room = room
def room(self):
return self.__room
def name(self):
return self.__name
def description(self, str):
return '{} is located in Room "{}" and belongs to the computer "{}"'.format(str, self.__room, self.__computer.name())
class Computer(Device):
def __init__(self, name, room):
super().__init__(name, self, room)
class Screen(Device):
__width = "1920"
__height = "1080"
def __init__(self, name, computer, width, height):
super().__init__(name, computer, computer.room())
self.__width = width
self.__height = height
def description(self):
return super().description('The screen with the height {} and width {}'.format(self.__height, self.__width))
class Mouse(Device):
__type = 'Gaming Mouse'
def __init__(self, name, computer, type):
super().__init__(name, computer, computer.room())
self.__type = type
def description(self):
return super().description('The mouse with the type {}'.format(self.__type))
cpt = Computer("Acer", "Classroom 12")
cpt2 = Computer("iMac 15", "Classroom 38")
screen = Screen("27 inch", cpt2, 1920, 1080)
mouse = Mouse("Mouse", cpt, "Gaming")
print (screen.description())
print (mouse.description())

Printing a list from a class in Python

My simplified code is below: it creates an animal, and places it inside a zoo. I want to print the list of animals within the zoo. Going round in circles with this!
class Animal(object):
def __init__(self, name):
self.name = name
class Zoo(object):
def __init__(self):
self.animals = []
def __str__(self):
rep = ", ".join(self.animals)
return rep
def add(self, name):
self.animals.append(Animal(name))
def main():
while True:
zoo = Zoo()
animal = input("add an animal: ")
zoo.add(animal)
print(zoo)
main()
The added __repr__ Method to the Animal returns us the name.
The zoo = Zoo() has to be outside of the loop, this makes sure that we do not create a new zoo with every iteration.
Then we print the list (zoo.animals).
class Animal(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return self.name
class Zoo(object):
def __init__(self):
self.animals = []
def __str__(self):
rep = ", ".join(self.animals)
return rep
def add(self, name):
self.animals.append(Animal(name))
def main():
zoo = Zoo()
while True:
animal = input("add an animal: ")
zoo.add(animal)
print(zoo.animals)
main()
You can simply refer to the name property of Animal in your Zoo.__str__(), e.g.:
def __str__(self):
return ', '.join(animal.name for animal in self.animals)
Now print(zoo) should work correctly.
However this doesn't provide a lot of encapsulation if say you wanted to change what it means to print an animal, e.g. height, size, etc. So perhaps a more encapsulated form would be:
class Animal(object):
...
def __str__(self):
return self.name
class Zoo(object):
...
def __str__(self):
return ", ".join(str(animal) for animal in self.animals)
Now when you print(zoo) the Animal class is responsible for its own string presentation.
Just as a note: you probably should create the Animal instance outside of Zoo, what happens if you decide to create a class hierarchy of Animals (e.g. Mammal) that has different behaviours, your Zoo class would only know about Animals.
class Animal(object):
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
class Zoo(object):
def __init__(self):
self.animals = []
def __str__(self):
return ", ".join(str(animal) for animal in self.animals)
def add(self, animal):
self.animals.append(animal)
def main():
zoo = Zoo()
while True:
animal = Animal(input("add an animal: "))
zoo.add(animal)
print(zoo)
main()
This would still behave properly if you create a Mammal class:
class Mammal(Animal):
...
zoo.add(Mammal(...))
print(zoo)

How do i solve Attribute Error?

We need a way for our Tribute object to reduce their hunger. Create a method eat(food), which takes in a Food object. It will reduce the hunger of the player by the food.get food value(). If object that's passed to the eat method is of the type Medicine, it will increment the player’s health by medicine.get medicine value().
Pre-defined code:
class Tribute(Person):
def reduce_hunger(self, hunger):
self.hunger = self.hunger - hunger
if self.hunger < 0:
self.hunger = 0
else:
return self.hunger
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(LivingThing):
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 named_col(col):
# Only accepts tuple/list
type_col = type(col)
if type_col != list and type_col != tuple:
return None
return type_col(map(lambda x: x.get_name() if isinstance(x, NamedObject) else x, col))
class Food(Thing):
def __init__(self, name, food_value):
self.name = name
self.food_value = food_value
def get_food_value(self):
return self.food_value
class Medicine(Food):
def __init__(self, name, food_value, medicine_value):
super().__init__(name, food_value)
self.medicine_value = medicine_value
def get_medicine_value(self):
return self.medicine_value
class MobileObject(NamedObject):
def __init__(self, name, place):
super().__init__(name)
self.place = place
def get_place(self):
return self.place
This is my code:
def eat(self, food):
if food in self.get_inventory():
self.inventory.remove(food) # not self.inventory = self.inventory.remove(food) cos self.inventory is a list. if use = it wont return anything
self.reduce_hunger(food.get_food_value())
if isinstance(self, medicine):
self.health = self.health + food.get_medicine_value()
if self.health > 100:
self.health = 100
elif self.health <0:
self.health = 0
To test my code:
cc = Tribute("Chee Chin", 100)
chicken = Food("chicken", 5)
cc.take(chicken) # Chee Chin took chicken
cc.take(aloe_vera) # Chee Chin took aloe vera
print(named_col(cc.get_inventory())) # ['chicken', 'aloe vera']
i got this error:
AttributeError: 'Food' object has no attribute 'owner'
instead of the expected output 'Chee Chin took chicken'.
What's wrong with my code?
Food is a subclass of Thing but Food's __init__ doesn't call Thing's __init__. That is the source of the problem. Food's __init__ needs the following line at its start:
super().__init__(name)

Categories