Python RPG Inheritance Issues? - python

I am running into a TypeError for my super().init method stating it only takes one positional argument and three are given. I assumed that the Enchanted class inherited the other parameters from the parent classes Weapons and Item, but I seemed to have missed something?
Using python 3.5 and link to the GitHub repository if needed is here: PythonRPG.
#base item class
class Item(object):
def __init__(self, name, description):
self.name = name
self.description = description
def __init__(self):
return "{}\n=====\n{}\nDamage: {}".format(self.name, self.description)
#start weapons
class Weapons(Item):
def __init__(self, name, description, attack):
self.attack = attack
super().__init__(name, description)
def __str__(self):
return "{}\n=====\n{}\nDamage: {}".format(self.name, self.description, self.attack)
class Enchanted(Weapons):
def __init__(self):
#error appears here
super().__init__(name="Enchanted Longsword", description="A prestine longsword you found with small runes inscribed along the Cross Guard. You feel a small magical force emanating from the weapon as you hold it.", attack = 12)

You have two __init__ methods in your Item class. The second overwrites the first, and since it takes only one positional parameter (self), the error is thrown. Simple fix: get rid of the second __init__.
I'm not certain, but perhaps you meant your second __init__ to be __str__?
class Item(object):
def __init__(self, name, description):
self.name = name
self.description = description
def __str__(self):
return "{}\n=====\n{}\nDamage: {}".format(self.name, self.description)

A nice answer is already provided by jme. I am guessing that you are trying something like method overloading or creating different constructors for Item class.
In a python class you can not have two methods of same name. But you can achieve this property by providing default values to the method parameters. Something like this:
class Item(object):
def __init__(self, name=None, description=None):
self.name = name
self.description = description

Related

Modifying Function Arguments in Python to Access Private Fields Within a Class

I'm trying to create a Class with a list of fields. See code below.
class Character:
# Private Fields:
__age = 18
__weight = 200
__height = 72
def __init__(self, name):
self.__name = name
#property
def get_age(self):
return self.__age
#property
def get_name(self):
return self.__name
#property
def get_weight(self):
return self.__weight
#property
def get_height(self):
return self.__height
person = Character("someone")
print("name =", person.get_name,",", "age =", person.get_age)
Is there a way to avoid writing the #property for every private field you want to access? For instance is there a way to pass an attribute into a more general getter function like:
def get_attr(self,attr):
#set attr to __attr
#return self.attr
I tried using the join function, but it didn't work
Thanks for any help
To answer the question as asked, the simple solution is to compensate for the name mangling that is done with private members. e.g. to get the __age attribute you'd use: person._Character__age
But, this would be a terrible idea and I wouldn't recommend it. If you need them to be easily accessible, just remove the underscores. If they really need to be private, they shouldn't be easily accessible from outside the class anyway, so putting in a way to make them accessible defeats the purpose.

Change a inherited class to another inherited class keeping the attributes

I need to change a inherited class to another inherited class where only one of the attributes has changed
i need to "Promote" a Cashier to a Manager, the only thing that is suppose to change is the salary
both Cashier and Manager are inherited classes of Employee (where I'm not sure if I'm using the "hasattr" function the right way)
class Employee:
def __init__(self,name):
self.name=name
if(hasattr(self,'shifts')==False):
self.shifts=[]
class Manager(Employee):
def __init__(self,name,salary):
Employee.__init__(self,name)
self.salary=salary
class Cashier(Employee):
def __init__(self,name,salarey_per_hours):
Employee.__init__(self,name)
self.salery_per_hours=salarey_per_hours
def promote(self,salary):
return Manager(self.name,salary)
P.s It's my first time uploading a question
What you could do is create the addition method of your class and add self to the manager class you are returning like so:
class Employee(object):
def __init__(self, name):
self.name=name
if not hasattr(self, 'shifts'):
self.shifts = []
def __add__(self, other):
if isinstance(other, Employee):
for key, value in other.__dict__.items():
if key == 'salary':
continue
self.__setattr__(key, value)
return self
class Manager(Employee):
def __init__(self, name, salary):
super().__init__(name)
self.salary = salary
class Cashier(Employee):
def __init__(self,name,salary):
super().__init__(name)
self.salary = salary
def promote(self, salary):
manager = Manager(self.name, salary)
manager += self
return manager
cashier = Cashier('hank', 22)
cashier.shifts = [1, 2, 3, 4]
print(cashier.shifts)
promoted_cashier = cashier.promote(30)
print(promoted_cashier.shifts)
Here you make sure that everything except the "salary" is transferred to the promoted class. And since both the Manager and the Cashier are an Employee this should work nicely. I changed your code a bit to what I'm used to since there was some unusual coding with you Calling Employee in the init which I assumed you did not explicitly needed. Sorry if that was not the case.
You can change the object's class by obj.__class__ to the another class by
doing obj.__class__ = SomeClass
Beware that is can lead to strange behaviours if it is handled incorrectly.
by modifying your code
class Employee:
def __init__(self,name):
self.name=name
if(hasattr(self,'shifts')==False):
self.shifts=[]
class Manager(Employee):
def __init__(self,name,salary):
Employee.__init__(self,name)
self.salary=salary
class Cashier(Employee):
def __init__(self,name,salarey_per_hours):
Employee.__init__(self,name)
self.salery_per_hours=salarey_per_hours
def promote(self,salary):
self.__class__ = Manager
# return Manager(self.name,salary)
You can also take a look at this post changing the class of a python object (casting)

Resolving Diamond Inheritance within Python Classes

Consider the following python code:
class Parent(object):
def __init__(self, name, serial_number):
self.name = name
self.serial_number = serial_number
class ChildA(Parent):
def __init__(self, name, serial_number):
self.name = name
self.serial_number = serial_number
super(ChildA, self).__init__(name = self.name, serial_number = self.serial_number)
def speak(self):
print("I am from Child A")
class ChildB(Parent):
def __init__(self, name, serial_number):
self.name = name
self.serial_number = serial_number
super(ChildB, self).__init__(name = self.name, serial_number = self.serial_number)
def speak(self):
print("I am from Child B")
class GrandChild(ChildA, ChildB):
def __init__(self, a_name, b_name, a_serial_number, b_serial_number):
self.a_name = a_name
self.b_name = b_name
self.a_serial_number = a_serial_number
self.b_serial_number = b_serial_number
super(GrandChild, self).__init_( something )
When running the super function in GrandChild, what is the proper way to format the __init__ arguments so that ChildA and ChildB both get the correct arguments?
Also how do you access the two different versions of the speak method (ChildA's version and ChildB's version) from within the GrandChild class?
so, when you call super from the grandchild, ChildA's __init__ method will be called because super follows the __mro__ property (parents left to right then grandparents left-to-right, then great grandparents, ...)
Since ChildA's init also calls super, then all the super calls will be chained, calling child b's __init__ and eventually the parent init.
For that to work, your interface generally needs to be consistent. That is positional arguments need to mean the same things, and be in the order.
In situations where that's not the case, keyword arguments may work better.
class Parent:
def __init__(self, name, serial, **kwargs):
self.name = name
self.serial = serial
class ChildA(Parent):
def __init__(self, a_name, a_serial, **kwargs):
self.a_name = a_name
self.a_serial = a_serial
super().__init__(**kwargs)
class ChildB(Parent):
def __init__(self, b_name, b_serial, **kwargs):
self.b_name = b_name
self.b_serial = b_serial
super().__init__(**kwargs)
class GrandChild(ChildA, ChildB):
def __init__(self):
super().__init__(name = "blah", a_name = "a blah", b_name = "b blah", a_serial = 99, b_serial = 99, serial = 30)
Also note that in your code name and serial are reused as instance properties between all the classes and that's probably not what you want.
In python, you can explicitly call a particular method on (one of) your parent class(es):
ChildA.__init__(self, a_name, a_serial)
ChildB.__init__(self, b_name, b_serial)
Note that you need to put the self in explicitly when calling this way.
You can also – as you did – use the super() way, which will call the "first" parent. The exact order is dynamic, but by default it will do left-to-right, depth-first, pre-order scans of your inheritance hierarchy. Hence, your super() call will only call __init__ on ChildA.

Creating objects from static properties in python

I have a Category class which has different names for each categories, the names of the categories can be unknown, good and bad, all categories share the same behavior so i don't want to create sub classes for each type of category, the problem comes when i am trying to
create the different categories in this way:
Category.GOOD
This statement should return a category object with his name setting to 'good' so i try
the following:
class Category(object):
def __init__(self, name):
self.name = name
#property
def GOOD(self):
category = Category(name='good')
return category
#property
def BAD(self):
category = Category(name='bad')
return category
Then i created and use the category with the following output:
c = Category.GOOD
c.name
AttributeError: 'property' object has no attribute 'name'
Realizing that this doesn't work i try a java like approach:
class Category(object):
GOOD = Category(name='good')
BAD = Category(name='bad')
def __init__(self, name):
self.name = name
What i get here is a undefined name "Category" error, so my question is if there is a pythonic way to create a category object like this.
You probably want to use classmethods:
class Category(object):
#classmethod
def GOOD(cls):
category = cls(name='GOOD')
return category
Now you can do c = Category.GOOD().
You cannot do this with a property; you either have to use a classmethod, or create your own descriptor for that:
class classproperty(property):
def __get__(self, inst, cls):
return self.fget(cls)
I'm abusing the property decorator here; it implements __set__ and __del__ as well, but we can just ignore those here for convenience sake.
Then use that instead of property:
class Category(object):
def __init__(self, name):
self.name = name
#classproperty
def GOOD(cls):
return cls(name='good')
#classproperty
def BAD(cls):
return cls(name='bad')
Now accessing Category.GOOD works:
>>> Category.GOOD
<__main__.Category object at 0x10f49df50>
>>> Category.GOOD.name
'good'
I'd use module variables for this. Consider you have the module category.py:
class Category(object):
# stuff...
now you put the two global objects in it:
GOOD = Category(name='good')
BAD = Category(name='bad')
You can use it like that:
from path.to.category import GOOD, BAD
I don't say that this is pythonic but I think this approach is elegant.
The main point that you could not use class definition inside that class definition itself. So the most straight way to achieve what you are want is to use class/static methods as shown below, or even package constants.
class Category(object):
def __init__(self, name):
self.name = name
#classmethod
def GOOD(cls):
return Category(name='good')
#classmethod
def BAD(cls):
return Category(name='bad')
print Category.GOOD().name
or
class Category(object):
def __init__(self, name):
self.name = name
#staticmethod
def GOOD():
return Category(name='good')
#staticmethod
def BAD():
return Category(name='bad')
print Category.GOOD().name

Subclass not inheriting parent class

I'm having trouble with my code. I'm trying to create a subclass which inherits the parent class's attributes and methods but it doesn't work. Here's what I have so far:
class Employee(object):
def __init__(self, emp, name, seat):
self.emp = emp
self.name = name
self.seat = seat
Something is wrong with the block of code below - the subclass.
Do I have to create the __init__ again? And how do I create a new attribute for the subclass. From reading questions, it sounds like __init__ in the subclass will override the parent class - is that true if I call it to define another attribute?
class Manager(Employee):
def __init__(self, reports):
self.reports = reports
reports = []
reports.append(self.name) #getting an error that name isn't an attribute. Why?
def totalreports(self):
return reports
I want the names from the Employee class to be in the reports list.
For example, if I have:
emp_1 = Employee('345', 'Big Bird', '22 A')
emp_2 = Employee('234', 'Bert Ernie', '21 B')
mgr_3 = Manager('212', 'Count Dracula', '10 C')
print mgr_3.totalreports()
I want reports = ['Big Bird', 'Bert Ernie'] but it doesn't work
You never called the parent class's __init__ function, which is where those attributes are defined:
class Manager(Employee):
def __init__(self, reports):
super(Manager, self).__init__()
self.reports = reports
To do this, you'd have to modify the Employee class's __init__ function and give the parameters default values:
class Employee(object):
def __init__(self, emp=None, name=None, seat=None):
self.emp = emp
self.name = name
self.seat = seat
Also, this code will not work at all:
def totalreports(self):
return reports
reports's scope is only within the __init__ function, so it will be undefined. You'd have to use self.reports instead of reports.
As for your final question, your structure won't really allow you to do this nicely. I would create a third class to handle employees and managers:
class Business(object):
def __init__(self, name):
self.name = name
self.employees = []
self.managers = []
def employee_names(self);
return [employee.name for employee in self.employees]
You'd have to add employees to the business by appending them to the appropriate list objects.
You need to run the superclass's init() in the appropriate place, plus capture the (unknown to the subclass) arguments and pass them up:
class Manager(Employee):
def __init__(self, reports, *args, **kwargs):
self.reports = reports
reports = []
super(Manager, self).__init__(*args, **kwargs)
reports.append(self.name) #getting an error that name isn't an attribute. Why?

Categories