Access attribute from another class in the same module? - python

Let's say I have person.py and it looks something like this:
class Eyes:
def __init__(self, color):
self.color = color
class Head:
def __init__(self, size):
self.have_dark_eyes = ???
self.size = size
Then:
import person
eyes = person.Eyes("blue")
head = person.Head("small")
if (head.has_dark_eyes == True):
...
I would like to use the attribute color to set have_dark_eyes to True or False, but how? I was thinking of replacing ??? with True if color == "brown" else False but it won't work.
Thanks.

You would have to, at the very least, provide Head with an instance of Eyes, e.g. something like
class Eyes:
def __init__(self, color):
self.color = color
class Head:
def __init__(self, eyes, size):
self.have_dark_eyes = eyes.color == 'brown'
self.size = size
Example usage:
dark_eyes = Eyes('brown')
head = Head(dark_eyes, 10)
print(head.have_dark_eyes) # Prints True

Related

Passing object attributes to its methods (Python)

It is apparently impossible to pass attributes of an object to its own methods:
def drawBox(color):
print("A new box of color ", color)
return
class Box:
def __init__(self, color):
self.defaultColor = color
self.color = color
def update(self, color = self.defaultColor):
self.color = color
drawBox(color)
This does not work:
Traceback (most recent call last):
File "<string>", line 5, in <module>
File "<string>", line 9, in Box
NameError: name 'self' is not defined
I found a way to bypass this issue like this:
def drawBox(color):
print("A new box of color ", color)
return
class Box:
def __init__(self, color):
self.defaultColor = color
self.color = color
def update(self, color = None):
if color == None:
self.color = self.defaultColor
else:
self.color = color
drawBox(color)
Is there a better (more elegant?) way to do this?
The reason you can't use self.color as a default parameter value is that the default is evaluated at the time the method is defined (not at the time that it's called), and at the time the method is defined, there is no self object yet.
Assuming that a valid color is always a truthy value, I would write this as:
class Box:
def __init__(self, color):
self.default_color = self.color = color
def draw(self):
print(f"A new box of color {self.color}")
def update(self, color=None):
self.color = color or self.default_color
self.draw()

Using Builder Pattern, I got AttributeError: 'NoneType' object has no attribute to builder class function

I try to instance my class that made by using builder pattern
class Cat:
def __init__(self,height,weight, color):
self.height = height
self.weight = weight
self.color = color
def print(self):
print("%d %d %s" %(self.height,self.weight,self.color))
class CatBuilder:
def __init__(self):
self.weight = None
self.height = None
self.color = None
def setWeight(self,weight):
self.weight = weight
def setHeight(self,height):
self.height = height
def setColor(self,color):
self.color = color
def build(self):
cat = Cat(self.height,self.weight,self.color)
return cat
then I use below code to run cat1.print()
#error version
cat1 = CatBuilder().setColor("red").setHeight(190).setWeight(80)
cat1.print()
#correct version
cat_builder = CatBuilder()
cat_builder.setColor("red")
cat_builder.setHeight(180)
cat_builder.setWeight(50)
cat2 = cat_builder.build()
cat2.print()
I think both of code is right, but #error version is not working..
How can I fix it??
I got my answer!
I have to append return self code like below:
class Cat:
def __init__(self,height,weight, color):
self.height = height
self.weight = weight
self.color = color
def print(self):
print("%d %d %s" %(self.height,self.weight,self.color))
class CatBuilder:
def __init__(self):
self.weight = None
self.height = None
self.color = None
def setWeight(self,weight):
self.weight = weight
return self
def setHeight(self,height):
self.height = height
return self
def setColor(self,color):
self.color = color
return self
def build(self):
cat = Cat(self.height,self.weight,self.color)
return cat

why can't I call my method from within class in python?

I am trying to make a button class for my game, using pygame. But in the button class, I cannot call methods that are contained in the class itself.
I am new to classes so I may be missing something important, I'm sorry
I tried to add self to the isHovering() Method but it still doesn't work.
import pygame
class Button():
def __init__(self, pos, value, img, screen):
self.pos = pos
self.value = value
self.img = img
self.screen = screen
### Getters and Setters ###===============
### Get/Set Value : True/False ###
def setValue(self, value):
self.value = value
def getValue(self):
return self.value
### Get/Set Pos ###
def setPos(self, pos):
self.pos = pos
def getPos(self):
return self.pos
### Get/Set Img ###
def setImg(self, img):
self.img = img
def getImg(self):
return self.img
#==========================================
def isHovering(self):
pos = getPos()
imgRect = pygame.rect(pos[0], pos[1], 105, 105)
if imgRect.collidepoint(pygame.mouse.get_pos()):
return True
else:
return False
def update(self, screen):
if isHovering():
image = pygame.transform.scale(self.img(95, 95))
else:
image = pygame.transform.scale(self.img(105, 105))
screen.blit(image, self.pos)
I thought that when update(screen) was called in my main loop, that it would call isHovering(), and return a True or False, but instead I get this error:
NameError: name 'isHovering' is not defined
In def update(self, screen),
The if statement should be if self.isHovering().
If not, the interpreter will look for a isHovering function somewhere in the current module, like if you had defined it outside your class.
Using the self. prefix will indicate that you are indeed trying to call a method of the instance as JacoblRR already pointed out in a comment.

Python - create inherited class attribute under condition

class WineComponents(object):
def __init__(self, aroma, body, acidity, flavor, color):
self.aroma = aroma
self.body = body
self.acidity = acidity
self.flavor = flavor
self.color = color
which can be instantiated like so:
wine = Color(aroma='80%', body='30%', acidity='35%', flavor='90%', color='Red')
then I want to be able to create specific classes that will inherit WineComponents():
class Color(WineComponents):
def receipe(self):
pass
and also to have its own attributes, under certain conditions, like so:
class Color(WineComponents):
if self.color == 'Red':
region = 'France'
type = 'Bordeaux'
def receipe(self):
pass
calling the attribute with:
print wine.region
but this does not work:
if self.color == 'Red':
NameError: name 'self' is not defined
is there a workaround this?
Here my five pence:
class WineComponents(object):
def __init__(self, aroma, body, acidity, flavor, color):
self.aroma = aroma
self.body = body
self.acidity = acidity
self.flavor = flavor
self.color = color
class Color(WineComponents):
def __init__(self, aroma, body, acidity, flavor, color):
super(Color, self).__init__(aroma, body, acidity, flavor, color)
if self.color == 'Red':
self.region = 'France'
self.type = 'Bordeaux'
def receipe(self):
pass
if __name__ == '__main__':
wine = Color(aroma='80%', body='30%', acidity='35%', flavor='90%',
color='Red')
print (wine.region, wine.type)
You could do this with a property
class Wine(object):
def __init__(self, aroma, body, acidity, flavor, color):
self.aroma = aroma
self.body = body
self.acidity = acidity
self.flavor = flavor
self.color = color
#property
def region(self):
if self.color == 'Red':
return 'France'
else:
raise NotImplementedError('unknown region for this wine')
Which can be called as this:
>>> wine = Wine(aroma='80%', body='30%', acidity='35%', flavor='90%', color='Red')
>>> wine.region
'France'

Subclassing in python of instantiated superclass

Is it possible to subclass in Python using an already instantiated superclass?
I don't exactly know how to frame the question so let me give an example. Suppose I have a class Rectangle and I want to build another class ColoredRectangle. But I don't want each ColoredRectangle of the same dimensions to be its own new Rectangle. So when I initiate ColoredRectangle I would pass it an already instantiated Rectangle.
Ie., I want
r = Rectangle([1,2])
r_red = ColoredRectangle(r, "red")
r_blue = ColoredRectangle(r, "blue")
But now r_red and r_blue should be able to get all rectangle methods and attributes. For example suppose Rectangle had an area() attribute.
r.area
2
r_red.area
2
r_red and r_blue should "point" to the same Rectangle. I know I could do this by writing:
class ColoredRectangle(Rectangle):
def __init__(self, rectangle, color):
self.color = color
self.rectangle = rectangle
But then I'd have to write
r_red.rectangle.area
which is ugly.
Inheritance
Inheritance is such a nice thing in python, and I don't think you have to resort to getattr hacks, if you want those, scroll down.
You can force the class dictionary to refer to another object:
class Rectangle(object):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class ColoredRectangle(Rectangle):
def __init__(self, rect, color):
self.__dict__ = rect.__dict__
self.color = color
rect = Rectangle(3, 5)
crect = ColoredRectangle(rect, color="blue")
print crect.width, crect.height, crect.color
#3 5 blue
These two will refer to the same Rectangle object:
crect.width=10
print rect.width, rect.height
#10 5
This is an exellent talk on metaprogramming, and while it's title implies Python3 a lot of it also applies to python 2.x: David Beazley - Python3 Metaprogramming
getattr hacking
If for any reason however, you would want to have multiple ColoredRectangle refer to the same base Rectangle then these will conflict with each other:
eve = Rectangle(3, 5)
kain = ColoredRectangle(eve, color="blue")
abel = ColoredRectangle(eve, color="red")
print eve.color, kain.color, abel.color
#red red red
If you'd like different "proxy objects", which can get attributes from the base Rectangle but not interfere with each other, you have to resort to getattr hacking, which is fun too:
class ColoredRectangle(Rectangle):
def __init__(self, rect, color):
self.rect = rect
self.color = color
def __getattr__(self,attr):
return getattr(self.rect,attr)
eve = Rectangle(3, 5)
This will avoid the interference:
kain = ColoredRectangle(eve, color="blue")
abel = ColoredRectangle(eve, color="red")
print kain.color, abel.color
#blue red
About __getattr__ versus __getattribute__:
A key difference between getattr and getattribute is that
getattr is only invoked if the attribute wasn't found the usual ways. It's good for implementing a fallback for missing attributes,
and is probably the one of two you want. source
Because only non found attributes will be handled by __getattr__ you can also partially update your proxies, which might be confusing:
kain.width=10
print eve.area(), kain.area(), abel.area()
# 15 50 15
To avoid this you can override __setattr__:
def __setattr__(self, attr, value):
if attr == "color":
return super(ColoredRectangle,self).setattr(attr,value)
raise YourFavoriteException
What you seem to be asking to do is to redirect attribute accesses to the underlying Rectangle object. The __getattr__ method can do this for you.
class ColoredRectangle(object):
def __init__(self, rectangle, color):
self.color = color
self.rectangle = rectangle
def __getattr__(self,attr):
return getattr(self.rectangle,attr)
Owerwrite all attributes by rectangle attributes. They are in __dict__ property.
import copy
class Rectangle(object):
def __init__(self, area):
self.area = area
class ColoredRectangle(Rectangle):
def __init__(self, rectangle, color):
self.__dict__ = copy.deepcopy(rectangle.__dict__)
self.color = color

Categories