How do I add arguments to a subclass in Python 3 - python

class Wolf:
def __init__(self, legs):
self.legs = 4
class Dog(Wolf):
def __init__(self, color):
self.color = color
fido = Dog(legs = 4, color = "brown")
This would spute out an error message. How would I do something like that where I add parameters to the subclass that doesn't pertain to the superclass.

Try this:
class Wolf:
def __init__(self, legs):
self.legs = 4
class Dog(Wolf):
def __init__(self, legs, color):
super().__init__(legs)
self.color = color
fido = Dog(legs=4, color="brown")

That's not how inheritance works. When you inherit from another class, the super-class's parameters are not automatically added to the sub-class's parameter list. You must explicitly accept the desired parameters in your sub-class's constructor and pass them on to the super class's constructor:
class Wolf:
def __init__(self, legs):
self.legs = 4
class Dog(Wolf):
def __init__(self, color, legs):
super().__init__(legs)
self.color = color
fido = Dog(legs = 4, color = "brown")

Here's an example from a tutorial which explains inheritance and shows how to do this. You need to call the parent class's init function as in this similar example from this tutorial:
class Pet(object):
def __init__(self, name, species):
self.name = name
self.species = species
def getName(self):
return self.name
def getSpecies(self):
return self.species
def __str__(self):
return "%s is a %s" % (self.name, self.species)
class Dog(Pet):
def __init__(self, name, chases_cats):
Pet.__init__(self, name, "Dog")
self.chases_cats = chases_cats
def chasesCats(self):
return self.chases_cats

You still have to pass in the legs argument for Dog, and then use super:
class Wolf:
def __init__(self, legs):
self.legs = 4
class Dog(Wolf):
def __init__(self, color, legs):
super().__init__(legs)
self.color = color
fido = Dog(legs = 4, color = "brown")

Related

Attribute error: can't set attribute for pygame Sprite inheritance

I have a working code with an HPBar class --> inherits from ProgressBar class --> inherits from pygame.sprite.Sprite. I decided to create a Widget class to have the following inheritance flow: HPBar --> ProgressBar --> Widget --> pygame.sprite.Sprite. The point in doing so is for flexibility especially when adding more widgets like buttons, textboxes, etc. However, in my revision I encountered Attribute error: can't set attribute. Details are as follows.
Somewhere in my code I have this HPBar instantiation:
hp_bar = HPBar(
x=x, y=y,
entity=self.player,
groups=[self.camera, self.extras],
)
Working Code:
This worked prior to the revision.
class HPBar(ProgressBar):
def __init__(self, entity, *args, **kwargs):
max_value = entity.stats["max_hp"]
value = entity.stats["hp"]
super().__init__(
max_value=max_value, value=value,
width=32, height=5,
*args, **kwargs
)
class ProgressBar(pygame.sprite.Sprite):
def __init__(
self,
x: float,
y: float,
width: int,
height: int,
groups: List[pygame.sprite.AbstractGroup] = [],
max_value: int,
value: int,
*args, **kwargs
):
super().__init__(groups)
#property
def image(self):
_image = # pygame surface
return _image
Revised Code:
The code with the Attribute error.
class ProgressBar(Widget):
def __init__(
self,
max_value: int,
value: int,
*args, **kwargs
):
super().__init__(*args, **kwargs)
#property
def image(self):
_image = # pygame surface
return _image
class Widget(pygame.sprite.Sprite):
def __init__(
self,
x: float, y: float,
width: int, height: int,
groups: List[pygame.sprite.AbstractGroup] = [],
):
super().__init__(groups)
self.image = pygame.Surface((width, height))
Traceback Error:
File "C:\Users\Hp\Documents\Working\Personal\platformer1\game_models\windows\platformer_window.py", line 127, in load_level
hp_bar = HPBar(
File "C:\Users\Hp\Documents\Working\Personal\platformer1\game_models\sprites\hp_bar.py", line 16, in __init__
super().__init__(
File "C:\Users\Hp\Documents\Working\Personal\platformer1\contrib\models\widgets\progress_bars\progress_bar.py", line 24, in __init__
super().__init__(*args, **kwargs)
File "C:\Users\Hp\Documents\Working\Personal\platformer1\contrib\models\widgets\widget.py", line 22, in __init__
self.image = pygame.Surface((width, height))
AttributeError: can't set attribute
Few debugging attempts:
I tried to print out the width and height arguments inside the Widget class to make sure I'm receiving and sending the correct data type:
In Widget class:
super().__init__(groups)
print(width, height)
print(type(width), type(height))
self.image = pygame.Surface((width, height))
Print result:
32 5
<class 'int'> <class 'int'>
Moreover, I have had this implementation resembling my Widget class implementation and this works fine:
class Player(pygame.sprite.Sprite):
def __init__(self, pos):
super().__init__()
self.image = pygame.Surface((16, 32))
Yes, of course. You can't have a method/property and an attribute with the same name. image can be either an attribute or a property. But you can't have 2 objects with the same name.
The following is not possible:
class Foo:
def __init__(self):
self.bar = 1
def bar(self):
return 2
print(Foo().bar())
print(Foo().bar())
TypeError: 'int' object is not callable
Also not possible:
class Foo:
def __init__(self):
self.bar = 1
#property
def bar(self):
return 2
print(Foo().bar)
self.bar = 1
AttributeError: can't set attribute 'bar'
However you can define a setter:
class Foo:
def __init__(self):
self._bar = 1
self.bar = 2
#property
def bar(self):
return self._bar
#bar.setter
def bar(self, value):
self._bar = value
print(Foo().bar)
2

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'

Python inherit variables from parent class

Sorry if I don't explain it that well but I'll try my best:
So I want to inherit the variables from the Parent class, but I don't want to pass them again when creating an instance of the Child class because I think that is redundant. I just want to use the eye color from the Parent for example. See the example code below for what I mean
This is what works:
class Parent:
def __init__(self, eye_color, length):
self.eye_color = str(eye_color)
self.length = length
class Child(Parent):
def __init__(self, gender, eye_color, length):
super().__init__(eye_color, length)
self.gender = str(gender)
x = Parent("Blue", 2)
y = Child("Men", "Blue", 2)
print(x.eye_color, x.length)
print(y.gender, x.length)
This is what I somehow want:
class Parent:
def __init__(self, eye_color, length):
self.eye_color = str(eye_color)
self.length = length
class Child(Parent):
def __init__(self, gender):
super().__init__(eye_color, length)
self.gender = str(gender)
x = Parent("Blue", 2)
y = Child("Men")
print(x.length, x.eye_color)
print(y.gender, x.length)
What you ask does not make sense:
class Parent:
def __init__(self, eye_color, length):
self.eye_color = str(eye_color)
self.length = length
class Child(Parent):
def __init__(self, gender):
super().__init__(eye_color, length)
self.gender = str(gender)
x = Parent("Blue", 2)
y = Child("Men")
print(x.length, x.eye_color)
print(y.gender, x.length)
In child, the parameters eye_color and length come from nowhere.
rassar example is good if you want to reuse a parent object.
You can also do the following:
class Parent:
# def __init__(self, eye_color=(default value here), length=(default value here)):
def __init__(self, eye_color="", length=0):
self.eye_color = str(eye_color)
self.length = length
OR
class Parent:
def __init__(self, eye_color="", length=0):
self.eye_color = str(eye_color)
self.length = length
class Child(Parent):
def __init__(self, gender, *args, **kwargs):
super().__init__(*args, **kwargs)
self.gender = str(gender)
x = Parent("Blue", 2)
y = Child("Men") # Work with first example of Parent
y = Child("Men", eye_color="Blue", length=2) # Work with first
y = Child("Men", "Blue", 2) # Work with second example
print(x.length, x.eye_color)
print(y.gender, y.length)
You could try passing a Parent instance to the Child initializer...That's probably the closest you'll get.
class Parent:
def __init__(self, eye_color, length):
self.eye_color = str(eye_color)
self.length = length
class Child(Parent):
def __init__(self, gender, parent):
super().__init__(parent.eye_color, parent.length)
self.gender = str(gender)
x = Parent("Blue", 2)
y = Child("Men", x)
print(x.length, x.eye_color)
print(y.gender, x.length)
Another thing you could do is hold a last_parent variable:
global last_parent
class Parent:
def __init__(self, eye_color, length):
self.eye_color = str(eye_color)
self.length = length
last_parent = self
class Child(Parent):
def __init__(self, gender):
super().__init__(last_parent.eye_color, last_parent.length)
self.gender = str(gender)
x = Parent("Blue", 2)
y = Child("Men")
print(x.length, x.eye_color)
print(y.gender, x.length)

How to determinate whose turn is? Card game (tkinter)

I'm making Palace card game with 4 players, I'm not sure how should I determinate whose on turn for throwing a card? Should I use smoething like counter or what ?
Use a simple counter modulo the number of players.
class Game(object):
def __init__(self):
self.count = 4
self.current_player = 0
def next_turn(self):
self.current_player += 1
self.current_player %= self.count
Since different cards may lead to different players having the next turn, and to the game switching between going clockwise and counterclockwise, I suggest you leave the decision to the card object.
from abc import ABCMeta, abstractmethod
class Card(object):
__metaclass__ = ABCMeta
def __init__(self, color, value):
"""
Color is either "r", "b", "g", or "y". Value is 0-9.
"""
self.color = color
self.value = value
def is_valid(self, color, value=None):
return color in self.color or value == self.value
#abstractmethod
def card_effect(self, game):
pass
This base class for all cards is the "contract" that a class has to fulfill to be considered a card. Since we're not planning on instantiating this class we make it an abstract class.
class Normal(Card):
def card_effect(self, game):
game.increment_player_index(1)
class Skip(Card):
def __init__(self, color):
super(Skip, self).__init__(color, "skip")
def card_effect(self, game):
game.increment_player_index(2)
class Turn(Card):
def __init__(self, color):
super(Turn, self).__init__(color, "turn")
def card_effect(self, game):
game.toggle_direction()
game.increment_player_index(2)
class PlusTwo(Card):
def __init__(self, color):
super(PlusTwo, self).__init__(color, "+2")
def card_effect(self, game):
game.increment_player_index(1)
game.current_player().punish(2)
game.increment_player_index(1)
For the black cards we do it like this:
class PlusFour(Card):
def __init__(self):
super(PlusFour, self).__init__("rbgy", "+4")
def card_effect(self, game):
color = game.current_player().choose_color()
game.set_color(color)
game.increment_player_index(1)
game.current_player().punish(4)
game.increment_player_index(1)

child class attribute different name

I want an attribute of a child class to have a different name than the same attribute of its parent class even though it means the same thing. For example, a parent class is Shape with attribute "height" and child class Circle with similar arttribute "Diameter". Below is a simplification of what I current have but I want the Circle class to use "diameter" instead of "height". What is the best way to handle this?
NOTE: I will inherit from Circle in another class that also needs to use "diameter" instead of "height". Thank you!
class Shape():
def __init__(self, shape, bar_args, height):
self.shape = shape
self.height = height
etc.
class Circle(Shape):
def __init__(self, height, foo_args, shape='circle'):
Shape.__init__(self, shape, height)
self.height = height
etc.
You could define a property which accesses the original attribute on read and write access:
class Circle(Shape):
def __init__(self, height, foo_args, shape='circle'):
Shape.__init__(self, shape, height) # assigns the attributes there
# other assignments
#property
def diameter(self):
"""The diameter property maps everything to the height attribute."""
return self.height
#diameter.setter
def diameter(self, new_value):
self.height = new_value
# deleter is not needed, as we don't want to delete this.
If you want this behaviour very often and you find property handling with setter and getter too unhandy, you can go a step higher and build your own descriptor class:
class AttrMap(object):
def __init__(self, name):
self.name = name
def __get__(self, obj, typ):
# Read access to obj's attribute.
if obj is None:
# access to class -> return descriptor object.
return self
return getattr(obj, self.name)
def __set__(self, obj, value):
return setattr(obj, self.name, value)
def __delete__(self, obj):
return delattr(obj, self.name)
With this, you can then do
class Circle(Shape):
diameter = AttrMap('height')
def __init__(self, height, foo_args, shape='circle'):
Shape.__init__(self, shape, height) # assigns the attributes there
# other assignments
and the diameter descriptor will redirect all accesses to it to the named attribute (here: height).

Categories