Python–Object AttributeError when accessing class attribute python - 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__(...):
...

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

Python AttributeError: property cannot overwrite inherited attribute?

I still don't fully understand when and how to use properties. Here I have a class SpecialCar which is inheriting Car. The variable summer_tire should basically be equivalent to tire, except for the name. So whenever I am asking for either of those two, I want to get summer_tire.
Using #property results in an error. Deleting the #property line will print 0, but I want to get 2.
class Car():
def __init__(self):
self.tire = 0
class SpecialCar(Car):
def __init__(self):
Car.__init__(self)
self.summer_tire = 2
self.winter_tire = 5
#property
def tire(self):
return self.summer_tire
i = SpecialCar()
print(i.tire)
You declared a property that doesn't have a setter, thus self.tire = 0 in Car.__init__ fails.
You could give your new property a setter:
class SpecialCar(Car):
def __init__(self):
Car.__init__(self)
self.summer_tire = 2
self.winter_tire = 5
#property
def tire(self):
return self.summer_tire
#tire.setter
def tire(self, new_tire):
self.summer_tire = new_tire
or you could avoid calling Car.__init__ altogether, or make Car.tire a class attribute, set as part of the class and replaced with the property in subclasses.

Can't call static method inside class

I am trying to call a static method inside a class to populate the class variable.
import sys
import os
from HelpingData import *
class Inventory(object):
shipping_cost = 400.0
total_stock = calculate_total_stock.__func__()
def __init__(self, attributes={}):
self.inventory = {}
if attributes is None:
self.inventory = {}
else:
for key in attributes:
self.inventory[key] = attributes[key]
def getValue(self,attribute):
return self.inventory[attribute]
def setValue(self,attribute,value):
self.inventory[attribute]=value
#staticmethod
def calculate_total_stock():
total_stock = dict((item, 0) for item in product_names)
for nation in product_stock:
for item in nation:
total_stock[item] += nation[item]
return total_stock
And this is the error I am getting:
total_stock = calculate_total_stock.__func__()
NameError: name'calculate_total_stock' is not defined
What am I missing here?
You really don't need any workaround here, just give the calling method an additional level of direction.
In the example below you can call the PrintThis() method both internal and external to its defining class.
External:
Call as you normally would
MyClass.PrintThis('42')
Internal:
You must add self or the containing class
MyClass.PrintThis('42')
self.PrintThis('42')
To produce the error:
class MyClass:
def __init__(self):
self.MyValue = 0
def IncrementValue(self):
self.MyValue += 1
PrintThis(f'From MyClass {self.MyValue}')
#staticmethod
def PrintThis(arg):
print(f'My Value: {arg}')
The Fix:
class MyClass:
def __init__(self):
self.MyValue = 0
def IncrementValue(self):
self.MyValue += 1
self.PrintThis(f'From MyClass {self.MyValue}')
#staticmethod
def PrintThis(arg):
print(f'My Value: {arg}')
Run It
class Run:
def __init__(self):
mc = MyClass()
MyClass.PrintThis('From Outside')
mc.IncrementValue()
mc.IncrementValue()
My Value: From Outside
My Value: From MyClass 1
My Value: From MyClass 2
Why?
I'm not sure :-)
The only thing I noticed is that the static method (PrintThis) is a function, while the non-static method is a bound method.
I am sure there is some explanation to this behavior in Pythons documentation. Please share if you look it up :-)
I know this question is a few years old at this point, however it was the first hit when I googled the fault.
The code at the top level of the Inventory definition (i.e. class attributes and method definitions) runs before the name Inventory exists, so you can't call its own methods within the definition. As you have a #staticmethod, which doesn't require any class or instance argument, why not move it outside?
def calculate_total_stock(product_names, product_stock):
total_stock = dict((item, 0) for item in product_names)
for nation in product_stock:
for item in nation:
total_stock[item] += nation[item]
return total_stock
class Inventory(object):
SHIPPING_COST = 400.0
TOTAL_STOCK = calculate_total_stock(product_names, product_stock)
def __init__(self, attributes=None):
self.inventory = {}
if attributes is not None:
for key in attributes:
self.inventory[key] = attributes[key]
def get_value(self, attribute):
return self.inventory[attribute]
def set_value(self, attribute, value):
self.inventory[attribute] = value
Note that I have done some tidying up, particularly in terms of style and making the explicit arguments to calculate_total_stock.

Creating an object with a reference to the object that created it

I have a program where an object creates another object. However, the second object that gets created needs to be able to access the first. Is this possible?
EG (pseudocode)
class parentObject():
parentVar = 1
# Create Child
x = childObject()
class childObject():
#Assign Var to the Var of the childs parent
childVar = parent.parentVar
>>> x.childVar = 1
is there a straitforward way to do this?
UPDATE:
I don't want to inheret the class, I need to be able to access the actual object that created it, as each object created from that class has different values.
Why not inherit the class?
class parentObject():
parentVar = 1
class childObject(parentObject):
childVar = parentObject.parentVar
>>> x = childObject()
>>> print(x.childVar)
1
If you are going to have different instances of the class, you should do it as this instead:
class parentObject(object):
def __init__(self):
self.parentVar = 1
class childObject(parentObject):
def __init__(self):
super(childObject, self).__init__()
self.childVar = self.parentVar
>>> x = childObject()
>>> print(x.childVar)
1
If you want a reference to the "parent" class, but inheritance is illogical, consider sending self in to the constructor:
class Room:
def __init__(self, name):
self.name = name
self.furniture = []
def add_chair(self):
self.furniture.append(Chair(self))
def __str__(self):
return '{} with {}'.format(self.name, self.furniture)
class Chair:
def __init__(self, room):
self.room = room
def __str__(self):
return 'Chair in {}'.format(self.room.name)
r = Room('Kitchen')
r.add_chair()
r.add_chair()
print r
print r.furniture[0]
Output:
Kitchen with [<__main__.Chair instance at 0x01F45F58>, <__main__.Chair instance at 0x01F45F80>]
Chair in Kitchen

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

Categories

Resources