Passing object attributes to its methods (Python) - 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()

Related

Python programming - Output issue in "Create a class function"

I'm from NON computer science background (Biochemist). In Python programming under "Create a class function", I didn't get the output for the following written function:
class circle (object):
def __init__(self, 10, 'red'):
self.radius=10;
self.color='red'
I got syntax error like this:
File "<ipython-input-1-ab699251caa9>", line 2
def_init_(self,10,'red'):
^
SyntaxError: invalid syntax
you cant pass pure value you need to pass it with variables
class circle (object):
def __init__(self,number=10,color='red'):
self.radius=number
self.color=color
Function parameters must be variables, not numbers or strings. Change this:
class circle (object):
def __init__(self, 10, 'red'):
self.radius = 10
self.color = 'red'
to this:
class circle (object):
def __init__(self, radius, color):
self.radius = radius
self.color = color
This class can be instantiated with:
myCircleObject = circle(10, 'red')
or, if you want to make the association parameter-value more clear, you can instantiate with:
myCircleObject = circle(radius = 10, color = 'red')
You can also set default values for the parameters:
class circle (object):
def __init__(self, radius = 10, color = 'red'):
self.radius = radius
self.color = color
In this way, you can still instantiate the object as before but you can also init with:
myCircleObject = circle()
In this case, radius and color will have the default values specified in the signature (radius will be 10 and the color will be 'red').
Note that to avoid misinterpretations, parameters with default values can only be listed in the rightmost part. So if you want to specify a default value only for the parameter radius, you need to move it to the right:
class circle (object):
def __init__(self, color, radius = 10):
self.radius = radius
self.color = color

Access attribute from another class in the same module?

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

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