Can an attributed object access its object's other attributes in python? - 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.

Related

Python: AttributeError while trying to create child from ABC

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.

Python, how to create specific classes under a big class

New python learner here. I'm trying to design a text rpg in python and i want the stats of all the enemy mobs to increase at a specific part of the game. Here is my code for some those enemies:
class Executioner:
def __init__(self,name):
self.name = name
self.potions = 0
self.maxhp = 800
self.hp = self.maxhp
self.attack = 25
self.ability = 0
self.defense = 0
self.goldgain = 333
self.expgain = 250
ExecutionerIG = Executioner("Executioner the slayer")
class Goblin:
def __init__(self,name):
self.name = name
self.potions = 0
self.maxhp = 80
self.hp = self.maxhp
self.attack = 25
self.ability = 0
self.defense = 0
self.goldgain = 5.5
self.expgain = 22
GoblinIG = Goblin("Goblin")
class Zombie:
def __init__(self,name):
self.name = name
self.potions = 0
self.maxhp = 180
self.hp = self.maxhp
self.attack = 19
self.ability = 0
self.defense = 0
self.goldgain = 7
self.expgain = 28
ZombieIG = Zombie("Zombie")
I thought a way to do this quickly can be defining those classes into a big class called enemies and then call enemies.hp, enemies.attack etc to increase.
But i don't know how to do that either as i am quite weeb for now.
Suggestions on how to increase class stats by specifying small classes under a big class or ways to increase class stats quickly(just typing them up and increasing them won't work as i will be having lots of enemy mobs) will be greatly thanked.
What Daniel Roseman said is true, you should try to not repeat yourself when writing code. If two objects are very similar and only differ in some certain values, then really they are the same object. All bikes are bikes, but some have different wheel sizes for example. Here, your objects are all one Enemy object, and can be implemented as such.
I will however answer your question, because subclassing is very important to know. To subclass a class, you pass in the parent class in a definition, like so:
class Zombie(Enemy):
Then if the Enemy class is defined like so for example:
class Enemy:
def attack():
<your code>
You could call attack on a Zombie object. It will look for attack() in the Zombie class, and if it doesn't find it, it will look for it in the parent class. If it doesn't find it in the parent class it will look for it in that parent's class and so on, moving all the way up to the top level object. If it doesn't find it at all an Exception will be raised. If you defined attack() in the Zombie class for example, then it would overwrite attack() in the Enemy class.
Given all this though, Daniel Roseman is right. You should put all this in one class and pass in hp etc upon construction.
No, that wouldn't be a good way to do it. Nested classes are rarely useful in Python, and certainly wouldn't be helpful here.
Much better to simply put them into a list you can loop through:
enemies = []
...
ExecutionerIG = Executioner("Executioner the slayer")
enemies.append(ExecutionerIG)
and so on. Then, when necessary, just iterate:
for enemy in enemies:
enemy.hp += 1
As an aside, I would question whether you really need separate classes for Executioner, Zombie and Goblin; they only differ in the numbers for each stat, so perhaps you should just accept those values in the __init__ method for a generic Enemy class.

What are the main advantages of using a class over a callable function?

Today I am wondering what are the advantages of a class over a callable function. I know it seems like a weird question, but I am coding a space-based game, and I would like to make something that shows lifesupport(oxygen, nitrogen)
Basically, I have this function:
oxygen = 100
nitrogen = 100
air = [oxygen, nitrogen]
print("You can use check(air) to see the quantities of n2 and o2 available.")
def check(variable):
if variable == air:
print("Your oxygen level is {0}% and your nitrogen level is {1}%.".format(oxygen, nitrogen))
So whenever the player types check(air), they can see the amount of nitrogen and oxygen in their ship. Using this I can also allow them to see other functions by expanding the check() function. I'm wondering if it's better to do this for a game rather than to do this:
class Lifesupport(object):
def __init__(self, oxygen, nitrogen):
self.oxygen = oxygen
self.nitrogen = nitrogen
air = Lifesupport(40, 60)
#This part is for checking functionality
print("Your air has {0}% oxygen and {1}% nitrogen".format(air.oxygen, air.nitrogen))
Personally, I prefer the function, though I don't really know which is better to use for this purpose. I know you can type air.oxygen to see just oxygen levels, and I could probably use a check() function to solely print a class "bundle" like 'air'.."
Basically... What are the real advantages of using a class over a function like the code I showed? Is it better to use a class or a function, or both, for my purposes?
For printing the oxygen and nitrogen, you would do:
class Lifesupport(object):
def __init__(self, oxygen, nitrogen):
self.oxygen = oxygen
self.nitrogen = nitrogen
def __str__(self):
return "Your air has {0}% oxygen and {1}% nitrogen".format(self.oxygen, self.nitrogen)
Then later, whenever you want to show the Lifesupport levels, you simply do:
air = Lifesupport(40, 60)
print(air)
The __str__ method overrides the default __str__ method of a class and so when you do print(air), it will print the custom text.
As for class vs. method, it is recommended that you use classes, especially when you know that are going to be expanding your program, since you can create multiple instances of a class that will all have attributes that can be modified independent of each other. Below is an example:
Example
class A:
def __init__(self,num):
self.val = num
a = A(4)
b = A(5)
>>> print(a.val)
4
>>> a.val = 6
>>> print(a.val)
6
>>> print(b.val)
5
Class is an Instance factory. You can create multiple instance (a.k.a copy) of class customize & extend it to your need (by Inheritance & overloading methods).
If you ever want to re-use or customize already written function, then using it inside the class is way to go. I suggest you go through the class coding basic if you have not already done that to make up your mind. a simple example could be
class Person:
def __init__(self, name, job=None, pay=0):
self.name = name
self.job = job
self.pay = pay
def giveRaise(self, percent):
self.pay = int(self.pay * (1 + percent))
class Manager(Person):
def __init__(self, name, pay): # Redefine constructor
Person.__init__(self, name, 'mgr', pay) # Run original with 'mgr'
def giveRaise(self, percent, bonus=.10):
Person.giveRaise(self, percent + bonus)
I'm customizing the original class 'init' method by inheriting original class & adding new information in Manager Class

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