Class method: 'self' not being read - python

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()

Related

How to implement a sole class of methods inside an external class

I have an external class to represent my data idk. Inside the class idk I want to have another class change which will contain various methods to change instances of the class idk. What I've tried is below. Note: this is a simplified example of my actual code
class idk:
def __init__(self):
self.x=1
self.y=2
class change:
def func(self):
self.x=10
self.y=100
var=idk()
var.change.func()
print(var.x, var.y)
However this gives the error:
TypeError: func() missing 1 required positional argument 'self'
How do I change the code to make it work?
Well, first of all, your are getting this error because you are accessing the func function as a class attribute and not by an instance of it (putting a class definition inside another class won't make it an instance).
If it makes sense, you cloud put those "change methods" in the idk class directly (that would be a normal approach):
class idk:
def __init__(self):
self.x = 1
self.y = 2
def func(self):
self.x = 10
self.y = 100
var = idk()
var.func()
print(var.x, var.y) # Output: 10 100
If you really want/need to separate those methods, you could do another class. The way I would implement that class is with static methods where all of them recieve an idk instance as the first parameter:
class idk:
def __init__(self):
self.x = 1
self.y = 2
class idkChanger:
#staticmethod
def func(idk_obj):
idk_obj.x = 10
idk_obj.y = 100
var = idk()
idkChanger.func(var)
print(var.x, var.y) # Output: 10 100
If you really really want/need to have that "changer" class inside of the idk class you can define it there, but this is not common at all. Also, you will have to pass the instance as well, that Changer class:
class idk:
def __init__(self):
self.x = 1
self.y = 2
class Changer:
#staticmethod
def func(idk_obj):
idk_obj.x = 10
idk_obj.y = 100
var = idk()
idk.Changer.func(var)
print(var.x, var.y) # Output: 10 100
Final notes:
You could not mark (decorate) the func as static and it will work the same, but this will bring more confution for several reasons (e.g., you would tecnically saying that func is an instance method. Which is not, because the objects you want to change are not Change's instances but idk's).

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.

Messing around with OOP in Python

I'm playing around with OOP in Python and I am trying to figure out some stuff related to inheritance. . I have some code here that has a few classes. A class called Blacksmith which behaves as expected and a class called Hero which I am trying to call a function from but I recieve an unexpected output.
class Character(object):
def __init__(self,name):
self.health=100
self.name = name
# self.player = player
def printName(self):
print self.name
#def printPlayerName(self):
# print self.player
class Blacksmith(Character):
def __init__(self,name, forgeName):
super(Blacksmith, self).__init__(name)
#self.name = "Billy"
self.forge = Forge(forgeName)
class Hero(Character):
playerName = "Player"
def __init__(self,name):
super(Hero, self).__init__(name)
def setplayername(self,inputplayername):
playerName = inputplayername
class Forge:
def __init__(self,forgeName):
self.name = forgeName
bs = Blacksmith("Billy", "Billy's Forge")
print bs.health
bs.printName()
print bs.forge.name
player1 = Hero("Methos")
print player1.name
player1.setplayername("Chris")
#print playher1.playerName
print player1.playerName
Output is:
raina#DESKTOP-291MTC0 ~/python
$ python learningoopclasses01.py
100
Billy
Billy's Forge
Methos
Player
Can anyone explain why this output says "Player" and not "Chris". Another question I have is I am not entirely sure how the init methods work. What does super do in these cases? What does calling init with a name value do exactly? Thanks.
__init__ is called when an object of that Class is created. With this method, we will also use the self variable to represent the instance of the object itself. It has to be explicitly declared in Python to be defined on an object.
For example,
class Student():
def __init__(self, score1, score2, score3):
self.scores = [score1, score2, score3]
If you want to assign scores to Student 1, you would only need to use because stu_1 already has score as an attribute in it:
stu_1 = Student(80, 90, 85)
In addition, __init__ also will notify you if any parameters are entered incorrectly according to what it has been set up.
super() is used to first call the parent(super) class of Blacksmith, which is Character, and allows you access Character's property.
The call to super() in Blacksmith's __init__ method is equal to its superclass, which in this case is Character.
You could also replace super(Blacksmith, self).__init__(name) with Character.__init__(self, name). The two are equivalent.
A slight adjustment to
def setplayername(self,inputplayername):
self.playerName = inputplayername
In the Hero class will fix it, if you don't want to change anything else.

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.

Python–Object AttributeError when accessing class attribute python

I have three classes: Item, Weapon, and BrassSword
When I try to access one of BrassSword's attributes ex.(name,image,etc.) It says, AttributeError: class BrassSword has no attribute 'image'
Here's the code:
import pygame, math, random
class Item(object):
def __init__(self,name,image,reuseable,value):
self.image=pygame.image.load(image)
self.itemattrs = ['name','image','reuseable','value']
self.path = image
self.name = name
self.x=0
self.y=0
self.reusable = reuseable
self.value = value
self.rect = [self.x,self.y,self.image.get_size()[0],self.image.get_size()[1]]
def onUse(self):
pass
def onThrow(self):
pass
class Weapon(Item):
def __init__(self,name,image,value,damage,maxdamage,speed):
super(Weapon,self).__init__('Weapon',image,True,value)
self.itemattrs = ['name','image','damage','maxdamage','value','speed']
self.damage=damage
self.maxdamage=maxdamage
self.speed = speed # Cooldown in frames
self.cooldown = 0
def onUpdate(self):
self.cooldown -= 1
def onUse(self,targetEntity):
if self.cooldown > 0:
return
self.cooldown = speed
targetEntity.hp-=random.range(damage,maxdamage)
if targetEntity.hp <= 0:
targetEntity.onDie()
def onThrow(self):
pass # TODO: Add throwing weapons
class BrassSword(Weapon):
def __init__(self):
super(BrassSword,self).__init__('item.weapon.brass_sword','testlevel/Ball.png',True,value,3,10,12)
You didn't post the code that's actually causing the error - namely where you access the attribute. However, you can't access an instance attribute by referring to the class - they are stored in a separate __dict__. Your superclass sets these attributes when it is instantiated in __init__(), as a property of self. After this, they can only be accessed through that self instance.
If you are trying to access the attribute similarly to this:
a = BrassSword.image
instead, you want to access it something like this:
sword = BrassSword()
a = sword.image
or:
sword = BrassSword().image
If you want to have a single image shared across all BrassSword instances, you need to declare it a class attribute like this:
class BrassSword(Weapon):
image = 'path/to/image'
def __init__(...):
...

Categories

Resources