Python: AttributeError while trying to create child from ABC - python

I am trying to refactor some old code from a beginner CS course I took in college
I've noticed a lot has changed with python since then (2017)
I also haven't been able to find any very informative online tutorial for writing and using abstract base classes with python (except the docs), So I have very little to go off of for what is the correct way to write and use ABCs.
Currently I'm having two problems, one is more explicit:
Traceback (most recent call last):
File "Hero.py", line 10, in <module>
from Enemy import Enemy
File "C:\Users\Ben\vscodeSCM\CS171Assignments\Zelda_CLI_game\Enemy.py", line 15, in <module>
class Enemy(metaclass = abc.ABCMeta):
File "C:\Users\Ben\vscodeSCM\CS171Assignments\Zelda_CLI_game\Enemy.py", line 32, in Enemy
def __init__(self):
AttributeError: 'function' object has no attribute '__bases__'
which comes up when I try to run 'Hero.py' a script
from Enemy import Enemy
class Hero(Enemy):
'''Class Level Docstring: Hero Object has six attributes,
most of which are nessecary for gameplay mechanisms.
Name is purely descriptive (no impact on game implementation details)
Therefore, name is the only mutable value, able to be set via constructor.
'''
def __init__(self,name):
self._name = name
self.__health = 200
self.__defense_mode = False
self.__bombs = 20
self.__elixers = 5
self.__arrows = 10
def __repr__(self):
'''Returns Hero attributes.
(health and defense_mode are implementation details)'''
return "Health: {} / 200\n\
\r\rBombs: {} / 20\n\
\r\rElixers: {} / 6\n\
\r\rArrows: {} / 10".format(self.__health, self.__bombs, self.__elixer, self.__arrows)
def __str__(self):
'''Returns Hero description'''
return "{}, a young warrior, clad in green".format(self.__name)
def health_bar(self):
'''Returns Hero health points vs. max health points.'''
return "{}/200".format(self.__health)
###Basic. Defense and Special attack for hero, overriden from Enemy.
def basic_attack(self, enemy):
'''Offensive attack that leaves Hero vulnerable.'''
self.__defense_mode = False
enemy.do_Damage(50)
def basic_name(self):
'''Provides name for Hero basic attack.'''
return "used the Master sword"
def defense_attack(self, enemy):
'''Parry attack that bolsters hero defense
while also doing small amount of damage to enemy.'''
self.__defense_mode = True
enemy.do_Damage(20)
def defense_name(self):
'''Returns the name for Hero defense attack'''
return "shield strike"
def special_attack(self, enemy):
'''A Powerful attack that does higher amount of damage
and also swaps defense mode. If defense mode is true,
does the standard amount of damage. If false, does
greater damage but also hurts the Hero.
'''
if self.__bombs:
if self.__defense_mode:
self.__defense_mode = False
enemy.do_damage(75)
return "The bomb had a Direct Hit.\nYour enemy recoils"
else:
self.__defense_mode = True
enemy.do_damage(110)
self.__health -= 20
return "The bomb was very powerful.\n\
Your enemy was badly injured,\n\
but you were damaged as well by the shrapnel"
else:
return "no bombs remaining.\nCannot use Special attack."
def special_name(self):
'''Returns the name of the special attack.'''
return "Used a bomb"
#Hero unique moves- Arrow and Elixer
def use_arrow(self, enemy):
'''Projectile attack:
highly effective, has no effect on defense mode.
but limited availability/ uses- 10 available arrows per game.'''
enemy.do_Damage(95)
self.__arrows= self.__arrows - 1
def arrow_name(self):
'''Returns the name of arrow attack.'''
return "{} fired an arrow".format(self.__name)
def arrow_count(self):
'''Returns the number of arrows remaining vs. maximum.'''
return "{} / 10".format(self.__arrows)
def elixer(self):
'''Replenishes half of the Heros max health point.
but limited uses, also turns defense mode to false.
'''
self.__defense_mode = False
self.__health + 100
self.__elixers - 1
def elixer_name(self):
'''Returns the name of the elixer move.'''
return "drank an elixer"
def elixer_count(self):
'''Returns the amount of remaining elixers vs max elixers.'''
return "{} / 5".format(self.__elixers)
def do_damage(self, damage):
'''
subtracts damage argument from Hero health.
If Defense mode is true, cuts value of damage argument in half.
'''
if(self.__defense_Mode):
self.__health -= damage // 2
else:
self.__health -= damage
def reset_health(self):
'''Sets the health points of Hero to maximum.'''
self.__health = 200
def reset_ammo(self):
'''Sets all the ammunititions to the max/initial value'''
self.__arrows = 10
self.__elixers = 5
if __name__ == "__main__":
print("Running in standby mode")
and here is Enemy abstract base class
import abc
from abc import ABCMeta
#cant figure out how to inherit from metaclass = ABCe
class Enemy(metaclass = abc.ABCMeta):
'''Class Level Docstring: creates signitures/ abstract methods
for init (constructor), __str__ (string repr.) and all helper
methods for gameplay.
NOTE: getter methods are retained from version 1, despite
this class adhering to the Descriptor protocol/ being a
new style class.
This is done in order to facilitate documentation.
When users implement the enemy class they will understand
how use of their object attributes makes gameplay/ battle possible.
'''
version = 2.0
#abc._abc_init
def __init__(self):
'''Construct enemy by passing in name, desc & health as args.'''
pass
#abc.abstractmethod
def __str__(self):
'''Returns all attributes/properties of the enemy.'''
return NotImplemented
#property
#abc.abstractmethod
def get_name(self):
'''Returns the enemy name.'''
return NotImplemented
#property
#abc.abstractmethod
def get_description(self):
'''Returns a short message describing
the enemy and providing any nessecary information.
'''
return NotImplemented
#property
#abc.abstractmethod
def get_health(self):
'''An enemy with 0 health is defeated.'''
return NotImplemented
#gamified versions of setters/mutator methods
#abc.abstractmethod
def do_damage(self, damage):
'''Positive numeric argument does damage,
and negative numeric argument heals enemy.'''
return NotImplemented
#abc.abstractmethod
def reset_health(self):
'''Returns health to starting HP.'''
return NotImplemented
# the attacks include: basic attack, defensive attack, special attack
# each of these has a corresponding method for returning the attack name.
#abc.abstractmethod
def basic_attack(self, enemy):
'''A less powerful, more commonly used attack.'''
return NotImplemented
#abc.abstractmethod
def basic_name(self):
'''returns basic attack name.'''
return NotImplemented
#abc.abstractmethod
def defense_attack(self, enemy):
'''Defensive move that counters or blocks an opponent
attack. some may infrequently do damage to opponent.
'''
return NotImplemented
#abc.abstractmethod
def defense_name(self):
'''Returns defense attack name.'''
return NotImplemented
#abc.abstractmethod
def special_attack(self, enemy):
'''Special attack which has limited availability, that may deal
extra damanage, have increased accurarcy, or some other desired effect.
'''
return NotImplemented
#abc.abstractmethod
def special_name(self):
'''Returns name of special attack,'''
return NotImplemented
The other issue is intellisense or vscode having issues finding the files for my imports, despite them all being in the same directory. It underlines the first occurence of 'Enemy' in from Enemy import Enemy
Is it a problem that my class name and the file containing the class have the same title?
I bring up this second issue, with the imports, because I suspect it may be the cause of the first error. This import line was causing errors earlier, until I edited some things in my preferences: python language specific configuration files ( commented out the line "python.jediEnabled" : false, because of a solution from a different stackoverflow post where a user was having a similar problem) After saving these changes, the Hero.py and Enemy.py scripts worked momentarily, but soon after I started getting this AttributeError mentioned above.

I think the line with the issue is
#abc._abc_init
over your init function in Enemy- where did you see you need to do that?
As a general note, things that start with underscores are considered private and should not be used when imported from other packages.

Related

Can an attributed object access its object's other attributes in python?

I am new to python and apologize in advance if this is too bad.
Suppose i dynamically make an object an attribute of another object.
Can the assigned as an attribute object access the assigned to object's other attributes without inheritance or passing as an argument?
e.g:-
class human:
def __init__(self):
self.health = 100
class fire:
def __init__(self):
self.fire = 10
def damage(self):
????.health -= self.fire #can i do anything to get bill's health?
bill = human()
bill.fired = fire()
bill.fired.damage() #the fired object wants to know the bill object's health
I know i can pass bill's health as an argument to the damage function:-
class human:
def __init__(self):
self.health = 100
class fire:
def __init__(self):
self.fire = 10
def damage(self, obj):
obj.health -= self.fire
bill = human()
bill.fired = fire()
print bill.health
bill.fired.damage(bill) #now the fired object knows bill's health
print bill.health #works fine
But is there any other way or is this a dead end? Apart from the inheritance.
(I'm using python v2.7, but of course would like to know v3 solution too)
Once again i apologize if this question is too bad or has been answered.
I tried to read this one Can an attribute access another attribute?, but i couldn't understand it, its too complex. And if i google this question the results only lead to "How to access objects attributes" e.g this https://www.geeksforgeeks.org/accessing-attributes-methods-python/. And this one How to access attribute of object from another object's method, which is one of attributes in Python? uses inheritance.
Yes, you can pass the human into fire when it is created since they seem to be linked to one another:
class Human:
def __init__(self):
self.health = 100
class Fire:
def __init__(self, human):
self.fire = 10
self.human = human
def damage(self):
self.human.health -= self.fire
bill = Human()
bill.fired = Fire(bill)
bill.fired.damage() #the fired object damages bill object's health
I'm not sure what's your goal, but as I mentioned, your issue looks like a code smell to me (an indication that something's not right).
Assuming you want the human instances to catch fire (i.e. create a fire instance) and then deduce the fire damage their health, consider the refactoring below:
class human:
def __init__(self):
self.health = 100
self.fire = None
def set_on_fire(self):
self.fire = fire()
def suffer_burn_damage(self):
if self.fire is not None:
self.health -= self.fire.damage
class fire:
def __init__(self):
self.damage = 10
bill = human()
print(bill.health) # output: 100
bill.set_on_fire()
bill.suffer_burn_damage()
print(bill.health) # output: 90
This way, you do not need the fire instances to know about the human's health in the first place. It's the human's "job" to keep track of whether it is burned or not, and when to deduce its own damage.
This makes sense in a more abstract meaning, too - which is one of the points of using OOP. A fire in real life has a certain energy. A human that catches fire will have its "health" deduced from whatever amount of energy that fire has. The fire itself has no business knowing about the human's health, or anything else for that matter.

Python inheritance questions

I'm a self-taught programmer and currently I'm learning elementary python; read material with inheritance in python yet still don't understand how it works.
Here, players is a child class of Football; however, it can't inherit the function/ objects (.getCoach) from Football. (error: p.getCoach())- the final line.
May I know which part goes wrong?
class Football:
def __init__(self, name, ranking, coach):
self.name= name
self.rank = ranking
self.coach= coach
def getName(self):
print(self.name)
def getRank(self):
print(self.name+ "was in" + self.rank)
def getCoach(self):
if self.rank<5:
print(self.coach+ "is a bad coach")
else:
print(self.coach+ "is a good coach")
class Players(Football):
def __init__(self, Appearances, Score):
self.Appearances= Appearances
self.Score= Score
def getapp(self):
print(self.Appearances)
def getscore(self):
print(self.Score)
p = Players(5,5, "Vincent")
p.getCoach()
You called Players with the arguments to Football's __init__ method, yet your Players.__init__ doesn't call Football.__init__. And it appears you want to pass two additional values when initializing a Player.
The following uses super to call the parent's __init__ function. Note that Player.__init__ needs to take all of the parameters needed for Football.__init__, unless you have some other way of coming up with them. It lets the Football class initialize the fields it cares about (via super()), then Player initializes its own fields. Note that this code assumes Python 3 because it calls super() with no arguments; the syntax for Python 2 is a bit different.
class Football:
def __init__(self, name, ranking, coach):
self.name= name
self.rank = ranking
self.coach= coach
def getName(self):
print(self.name)
def getRank(self):
print(self.name+ "was in" + self.rank)
def getCoach(self):
if self.rank<5:
print(self.coach+ "is a bad coach")
else:
print(self.coach+ "is a good coach")
class Players(Football):
def __init__(self, name, ranking, coach, Appearances, Score):
super().__init__(name, ranking, coach) # calls Football.__init__()
self.Appearances= Appearances
self.Score= Score
def getapp(self):
print(self.Appearances)
def getscore(self):
print(self.Score)
p = Players(5,5, "Vincent", 2, 3)
p.getCoach()
There are lot of issues here. First.
relationship does not make sense.
parent class is not derived from object class so inheritance will not work. (alteast in 2.7)
it seems like your player class contains information about Coach and not actual player. and so it doesn't make any sense. if you want to store information about Coach. Create a Has-A relationship. in simpler terms create a separate class named Coach and instantiate it inside the class Footballer. or player.
I would suggest to go through PEP8 standards.
However not addressing other issue, just to see what's wrong with the code.
This might work.
class Player(object):
def __init__(self, name, ranking, coach):
self.name= name
self.rank = ranking
self.coach= coach
def get_name(self):
print(self.name)
def get_rank(self):
print(self.name + "was in" + self.rank)
def get_coach(self):
if self.rank<5:
print(self.coach + "is a bad coach")
else:
print(self.coach + "is a good coach")
class Footballer(Player):
def __init__(self, name, ranking, coach, appearances, score):
self.appearances= appearances
self.score= score
super(Footballer, self).__init__(name, ranking, coach)
def get_appearances(self):
print(self.Appearances)
def get_score(self):
print(self.Score)
p = Footballer("name", 20.2, "coach name", "appearances", 200)
p.get_coach()
Here you can say, a Footballer is a player.

How to know inside a class, what is the object is working on?

I don't know if this is possible, but let me explain what I try to achieve (in Python):
class Vehicle:
def __init__(self, pos, vel):
self.pos = pos
self.vel = vel
def __str__(self):
#I want to know here if it is either Sedan or Sport
if Sedan:
print "Sedan"
elif Sport
print "Sport"
else:
print "unknown"
Sedan = Vehicle( pos1, vel1)
Sport = vehicle( pos2, vel2)
print Sedan
Note: I just use method: __str__ as example, but it can be any other method I define inside the class.
The point is that I want to execute some code inside the class definition depending on the instance that was created. I don't know if it possible what I am trying to do, or if there is a way to know inside the class what instance is working on, or this is just crazy.
What you want is not possible (nor does it make sense). A class (or a function) cannot "know" the names of the variables located somewhere else. What you describe (execute some code depending on the context) is a typical use case for inheritance. So, to add context-dependent code, just extend your base class
class Sedan(Vehicle):
def __str__(self):
return "Sedan"
class Sport(Vehicle):
def __str__(self):
return "Sport"
I would pass something like kind through __init__:
def __init__(self, pos, vel, kind):
self.pos = pos
self.vel = vel
self.kind = kind
def __str__(self):
print(self.kind)
Though, in Python, __str__ should return, not print.
I.e.:
def __str__(self):
return self.kind
In Python, names (like Sedan in your example) are just references to objects. If you are relying on the references a variable has for functionality, you are probably doing it wrong.
Consider this simple example:
sedan = Vehicle(...)
sport = Vehicle(...)
cars = [sedan, sport]
for car in cars:
print(car)
Now each Vehicle has multiple references - the first is accessible via sedan and cars[0] and, inside the first iteration of the for loop, car. In __str__, which reference should be used?
It is much, much better to make the data needed for the class instances to work explicit, for example:
class Vehicle:
def __init__(self, pos, vel, type_=None):
self.pos = pos
self.vel = vel
self.type_ = type_
def __str__(self):
return "unknown" if self.type_ is None else self.type_
sedan = Vehicle(pos1, vel1, "sedan")
Alternatively, if there will be more fundamental differences between the two types of Vehicle (e.g. different attributes, rather than just different values for the attributes), consider inheritance:
class Sedan(Vehicle):
def __str__(self):
return "sedan"

Class method: 'self' not being read

For some reason, in a method I make, I put self as the first parameter, but it doesn't read it. When I try to run the method, it says it needs the 'self' positional argument.
class Monster():
name = "Snake"
health = 100
def decreaseHealth(self):
if health <= 0:
print('Dead')
health -= 4
Monster.decreaseHealth()
The issue is that you're calling decreaseHealth() on the class itself, whereas you should be calling it on an instance of the class:
m = Monster()
m.decreaseHealth()
This will automatically bind self to m.
P.S. To refer to health inside the method, use self.health.
Here is a version that fixes a couple of other (mainly stylistic) issues:
class Monster(object):
def __init__(self):
self.name = "Snake"
self.health = 100
def decreaseHealth(self):
if self.health <= 0:
print('Dead')
else:
self.health -= 4
m = Monster()
m.decreaseHealth()
If you want a class method, better make it one:
#classmethod
def decreaseHealth(cls):
...
Then you can call Monster.decreaseHealth, and you can access the class variables with cls. (e.g. cls.health). But, then, you are operating on "global" state associated with the class itself.
You probably want to make an instance of your monster instead:
class Monster:
''' Base class for monsters '''
class Snake(Monster):
def __init__(self):
self.name = "Snake"
self.health = 100
def decreaseHealth(self):
self.health -= 4
if self.health <= 0:
print("dead")
mysnake = Snake()
mysnake.decreaseHealth()
That's because the way you are calling decreaseHealth() there is no self argument. self refers to the object on which a method was called. Here you have no object, you are trying to call the method on the class name.
You need to create a Monster object, then call your method:
monster = Monster();
monster.decreaseHealth()

Managing Instances in Python

I am new to Python and this is my first time asking a stackOverflow question, but a long time reader. I am working on a simple card based game but am having trouble managing instances of my Hand class. If you look below you can see that the hand class is a simple container for cards(which are just int values) and each Player class contains a hand class. However, whenever I create multiple instances of my Player class they all seem to manipulate a single instance of the Hand class. From my experience in C and Java it seems that I am somehow making my Hand class static. If anyone could help with this problem I would appreciate it greatly.
Thank you,
Thad
To clarify: An example of this situation would be
p = player.Player()
p1 = player.Player()
p.recieveCard(15)
p1.recieveCard(21)
p.viewHand()
which would result in:
[15,21]
even though only one card was added to p
Hand class:
class Hand:
index = 0
cards = [] #Collections of cards
#Constructor
def __init__(self):
self.index
self.cards
def addCard(self, card):
"""Adds a card to current hand"""
self.cards.append(card)
return card
def discardCard(self, card):
"""Discards a card from current hand"""
self.cards.remove(card)
return card
def viewCards(self):
"""Returns a collection of cards"""
return self.cards
def fold(self):
"""Folds the current hand"""
temp = self.cards
self.cards = []
return temp
Player Class
import hand
class Player:
name = ""
position = 0
chips = 0
dealer = 0
pHand = []
def __init__ (self, nm, pos, buyIn, deal):
self.name = nm
self.position = pos
self.chips = buyIn
self.dealer = deal
self.pHand = hand.Hand()
return
def recieveCard(self, card):
"""Recieve card from the dealer"""
self.pHand.addCard(card)
return card
def discardCard(self, card):
"""Throw away a card"""
self.pHand.discardCard(card)
return card
def viewHand(self):
"""View the players hand"""
return self.pHand.viewCards()
def getChips(self):
"""Get the number of chips the player currently holds"""
return self.chips
def setChips(self, chip):
"""Sets the number of chips the player holds"""
self.chips = chip
return
def makeDealer(self):
"""Makes this player the dealer"""
self.dealer = 1
return
def notDealer(self):
"""Makes this player not the dealer"""
self.dealer = 0
return
def isDealer(self):
"""Returns flag wether this player is the dealer"""
return self.dealer
def getPosition(self):
"""Returns position of the player"""
return self.position
def getName(self):
"""Returns name of the player"""
return self.name
From my experience in C and Java it seems that I am somehow making my Hand class static.
Actually, that is basically what you're doing. Well, not really making the class static, but making the variable static.
When you write declarations like this:
class Hand:
cards = []
that variable (cards) is associated with the class, not with the instance. To make an analogy to Java, every statement in a Python class that isn't part of a method of that class basically runs in a static initializer. You could almost think of it like this:
class Hand {
static {
cards = new object[];
}
}
(merely a rough analogy, of course)
To create an instance variable in Python, you have to set it as an attribute of the instance, which requires you to wait until you have a reference to the instance. In practice, this means you initialize it in the constructor, like so:
class Hand:
def __init__(self):
self.cards = []
Your problem is quite simple
if you assign lists to the body of python classes, when you append items to it, they will be store at Class level, not at instance level.
you can solve this problem by adding the line:
def __init__(self):
self.cards = []
this is a very known case of python pitfall, and I recommend you the reading:
http://zephyrfalcon.org/labs/python_pitfalls.html
As other answers noted, you were confused about class variables vs. instance variables. I suggest you review the basics of how Python classes work. Here is an answer I wrote for another question; reading this might help you.
How to define a class in Python

Categories

Resources