There are a lot of answers saying this is unadvisable, but I found a scenario where I think it's useful. Please correct me if I'm wrong and there's a better way.
I'm building a chess game, where individual pieces inherit from the superclass Chesspiece:
class ChessPiece:
def __init__(self, pos, color, num, piece):
...
Each piece has a method that defines the moves it can take:
class Knight(ChessPiece):
def __init__(self, pos, color=None, num=''):
ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
def possible_moves(self):
pos_moves = []
# Up, Right (1 space, 2 spaces)
try:
if 1 <= self.x + 2 <= len(Config.board) and 1 <= self.y - 1 <= len(Config.board):
if Config.board[self.x + 2][self.y - 1] == '___':
pos_moves.append(f'{Config.tile_convert(self.x + 2)}{Config.tile_convert(self.y - 1, True)}')
except Exception: pass
#Up, Left
...
return pos_moves
I'd like to implement a move() function. The code for the move() function will be the same for each piece, except for the fact that it has to compare the suggested move with the possible moves, which vary for each piece. I could make a move() function for each piece, but that would just be repeating code 6 times.
So, I'd like to define move() in Chesspiece and reference each piece's possible_moves() function.
It is simpler to implement an empty possible_moves in the parent:
class ChessPiece:
...
def possible_moves(self):
raise NotImplementedError
def move(self, pos):
if pos in self.possible_moves():
...
Or even return an empty set of movements in the parent class:
def possible_moves(self):
return set()
But I think the first is better, so it forces all subclasses to implement it in order to been useful.
Related
I think I'm misusing the concept of subclass. I'm working on a hobby project with Grids and Cells.
What I have, is the implementation of a Cell class, and its subclass HexCell which basically redefines many of the attributes/methods like so:
class Cell:
def __init__(self, row_loc, col_loc):
self.row = row_loc
self.col = col_loc
self.links = set()
self.neighbors = 4*[None]
def __repr__(self):
return f'Cell #({self.row},{self.col})'
def link(self, other, bidir = True):
self.links.add(other)
if bidir: other.links.add(self)
Then I have a subclass that is the HexGrid which follows a similar structure with new parameters.
class HexCell(Cell):
def __init__(self, r_out, th_around):
# I'm indexing Hex cells around a center cell
# instead of by rows and columns; Prefixed hex
# as they follow the hexagon, and not regular polar coordinates.
self.hex_r = r_out
self.hex_th = th_around
self.neighbors = 6*[None]
self.links = set()
def __repr__(self):
return f"HexCell #[{self.hex_r}, {self.hex_th}]"
def bind(self, other, to_dir):
to_dir = to_dir % 6
if (self.neighbors[to_dir] is None):
self.neighbors[to_dir] = other
other.neighbors[to_dir - 3] = self
# Hexagonal grids share neighbors.
other_1 = other.neighbors[to_dir - 2]
if (self.neighbors[to_dir - 1] is None) & (other_1 is not None):
self.bind(other_1, to_dir - 1)
other_5 = other.neighbors[to_dir - 4]
if (self.neighbors[to_dir - 5] is None) & (other_5 is not None):
self.bind(other_5, to_dir - 5)
In this case, the method self.link(other) is shared, but other attributes change from rectangular grid to hexagonal like the locaion from (row, col) to (hex_r, hex_th), or neighbors as a 4-list or 6-list. Thus I'd like these attributes to be dependent on a another cell-type attribute and transferred down to the subclass.
Correct use of subclassing needs to obey the following substitution principle:
If there are some objects x_1 of type T_1 and x_2 of type T_2 such that issubclass(T_2, T_1) == True, then any property that applies to x_1 must also apply for x_2.
In other words, you expect subclassing to implement new behaviours, not to change existing behaviours.
In you example, the change of coordinate system itself is a change of behaviour and thus HexCell should not inherit from Cell.
What you can do is create a base class BaseCell that encapsulates the common behaviour between Cell and HexCell and inherit from it.
class BaseCell:
def __init__(self):
self.links = set()
self.neighbors = []
def add_neighbor(self, other):
self.neighbors.append(other)
def link(self, other, bidirectional=True):
self.links.add(other)
if bidirectional:
other.link(self, bidirectional=False)
class Cell(BaseCell):
def __init__(self, row_loc, col_loc):
self.row = row_loc
self.col = col_loc
super().__init__()
def __repr__(self):
return f'Cell #({self.row},{self.col})'
class HexCell(Cell):
def __init__(self, r_out, th_around):
self.hex_r = r_out
self.hex_th = th_around
super().__init__()
def __repr__(self):
return f"HexCell #[{self.hex_r}, {self.hex_th}]"
def bind(self, other, to_dir):
...
Your Cell class is in fact not an abstract "Cell", but a square cell in two-dimensional space (has exactly 4 neighbours, has "row" and "col" position). Such cell may not be subclassed by a hex cell, because hex cell is just a different type of cell : )
As you noticed, the only common things are link() method and links attribute. If you insist on subclassing, you could create something like:
class LinkedObject():
def __init__(self):
self.links = set()
def link(self, other, bidir = True):
self.links.add(other)
if bidir: other.links.add(self)
class SquareCell(LinkedObject):
# "Cell" class here
class HexCell(LinkedObject):
# HexCell here
So I have a school project where we need to make a few classes for a GPS system. I'm having an issue figuring out the function dist(self,other): shown at the bottom of my code. Other definitions later in the project heavily rely on it, but i'm stumped at this point. The dist function calculates the Manhattan distance (x1-x2)+(y1-y2) of a location defined by instance variables x and y, and another location other which is given as a Tuple
class GPS_Location:
def __init__(self,x,y):
self.x=x
self.y=y
def __str__(self):
return '(%s,%s)' % (self.x,self.y)
def __repr__(self):
return 'GPS_Location(%s,%s)' % (self.x,self.y)
def __eq__(self,other):
self.other = other
if (self.x,self.y) == other:
return True
else:
return False
def dist(self,other):
self.other = other
return abs(self.x - (other[0])) + abs(self.y - (other[1])) #TypeError
When testing the code, I keep getting "TypeError: 'GPS_Location' object is not iterable". I have tried so many tweaks, and I just can't figure out what i'm doing wrong.
Any help would be greatly appreciated!
Ensure that line 8 is indented by 4 spaces like the rest of the methods.
There doesn't seem to be any reason to assign other to self.other in __eq__() and dist().
The only other issue you might be having could be related to how you are calling these methods (you mentioned that the argument other is just a tuple), this works:
x = GPS_Location(1, 1)
x == (1, 1)
# True
x == (2, 2)
# False
x.dist((1, 1))
# 0
x.dist((2, 2))
# 2
If you in fact need to pass a second GPS_Location as the other argument to dist, then it needs to be updated as follows:
def dist(self, other):
return abs(self.x - other.x) + abs(self.y - other.y)
Call it like so:
x = GPS_Location(1, 1)
y = GPS_Location(2, 2)
x.dist(y)
# 2
I have
class Point:
def __init__(self, initX, initY):
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def __str__(self):
return "x=" + str(self.x) + ", y=" + str(self.y)
class Rectangle:
def __init__(self, initP, initW, initH):
self.__location = initP
self.__width = initW
self.__height = initH
def getWidth(self):
return self.__width
def getHeight(self):
return self.__height
def getLocation(self):
return self.__location
#---------------------------------------------------------------
#string
def __str__(self):
return "x=" + str(self.__location.x) + ", y=" + str(self.__location.y) +", Width=" + str(self.getWidth()) + ", Height=" +str(self.getHeight())
def area(self):
return self.getWidth() * self.getHeight()
def calculatePerimeter(self):
return self.getWidth()*2 +self.getHeight()*2
def transpose(self):
temp = self.__width
self.__width = self.__height
self.__height = temp
def encloses(self, otherP):
return ((self.getWidth() + self.getLocation().getX()) > otherP.getX()\
and (self.getLocation().getX()) <otherP.getX() \
and (self.getHeight() + self.getLocation().getY()) >otherP.getY()\
and self.getLocation().getY() < otherP.getY())
def computeDiagonal(self):
d = (self.getWidth()**2 + self.getHeight()**2) ** 0.5
return d
def detectCollision(firstRectangle, secondRectangle):
print(firstRectangle.getWidth())
print(secondRectangle)
first = Rectangle(Point(1,0), 4, 3)
second = Rectangle(Point(4,0), 4, 3)
Rectangle.detectCollision(first, second)
I am trying to detect a collision. I'm a bit stuck. (detectCollision)
I am having trouble getting the value from the point class to the rectangle class.
Does anybody have any idea?
The function detectCollision is wrong. I was testing and I could get the width, and the height with getHeight() but I could not get the values inside Point.
I am having trouble getting the value from the point class to the rectangle class.
I think you need to read through a good tutorial on classes. Maybe the chapter in the official tutorial, or maybe a third-party tutorial. StackOverflow is not a good place to learn basic concepts.
You don't actually want to get a value from the point class, you want to get the value from a particular point instance. After all, there are lots of points in the world, and each one has different x and y values, and you're trying to check if some particular point has collided with the rectangle.
How do you know which instance? You take one as a parameter. And then you can access that object's members, methods, etc., just like you do with a string or any other object.
class Rectangle(object):
# existing stuff
def collision_check(self, point):
return (self.left <= point.getX() <= self.right and
self.top <= point.getY() <= self.bottom)
That's it.
Except that you probably don't want getX and getY methods in the first place; better to just do point.x and point.y.
Also, I've obviously had to make some assumptions about how you defined Rectangle (left/top/bottom/right? left/right/width/top? topleftpoint/bottomrightpoint?) and about what you mean by "collision" (hitting the edge of the rectangle, or the interior of the rectangle?), etc., since you didn't explain any of that. But hopefully you can adapt this to whatever your actual design is.
So, how do you use this? You just pass a point as an argument to the method, same as you do with, say, len:
>>> rect = Rectangle(10, 10, 20, 20)
>>> point1 = Point(5, 5)
>>> rect.collision_check(point1)
False
>>> point2 = Point(15, 15)
>>> rect.collision_check(point2)
True
Now that you've shown us more of your code, it looks like you're trying to collision-check two rectangles—and, more importantly, your problem is that your rectangle uses a Point as its top-left origin, and you don't know how to access the coordinations of that.
From your description, "I was testing and I could get the width, and the height with getHeight() but I could not get the values inside Point", you seem to still be missing the key issue here. You don't want to get the values inside Point. Point is a class—a factory for creating actual point objects. You want to get the values inside one of those actual point objects, the one you've stored in a rectangle object's __location and made available through a getLocation method. (As I already explained, you should get rid of those getter methods and just have a location attribute, but let's forget that for now.)
So, the way you get the particular point object you're interested in is to call getLocation() on the rectangle, and then the way you get the x and y values for that particular point object is to call its getX and getY methods. So, here's an example of using all those methods:
firstLocation = firstRectangle.getLocation()
firstLeft = firstLocation.getX()
Or you can combine those calls into one expression:
firstLeft = firstRectangle.getLocation().getX()
So, you can do something like this:
def detectCollision(firstRectangle, secondRectangle):
firstLeft = firstRectangle.getLocation().getX()
firstRight = firstLeft + firstRectangle.getWidth()
# similar code for top and bottom, and for second rectangle
return ((firstLeft <= secondLeft <= firstRight or
firstLeft <= secondRight <= firstRight) and
(firstTop <= secondTop <= firstBottom or
firstTop <= secondBottom <= firstBottom))
I am new in python, and I have small problem, I have two classes, and it wrotes this : set_gyro_angle() takes exactly 1 argument (2 given) how can I call set_gyro_angle() method from Machine method?
class Gyro(object):
"""gyroskop senzor"""
def __init__(self,gyro_start_angle = 0):
self.gyro_angle = 0
def get_gyro_angle():
return self.gyro_angle
def set_gyro_angle(angle):
self.gyro_angle = angle
return 0
class Maschine(object):
def __init__(self, state = "normal",length = 10,width = 15):
self.length = length
self.width = width
self.gyro = Gyro()
def setPoint(self,alpha):
self.gyro.set_gyro_angle(alpha)
return 0
Main:
maschine = Maschine()
maschine.setPoint()
If you want to create an instance method, you need to add an extra argument that will be a pointer to your instance. Usually it's self:
class Gyro(object):
"""gyroskop senzor"""
def __init__(self,gyro_start_angle = 0):
self.gyro_angle = 0
def get_gyro_angle(self):
return self.gyro_angle
def set_gyro_angle(self, angle):
self.gyro_angle = angle
return 0
And i think you want setPoint to be like this:
def setPoint(self, alpha):
self.gyro.set_gyro_angle(alpha)
All of your instance methods should have another parameter, self, before the others; this is the instance itself, and is passed automatically:
def set_gyro_angle(self, angle):
Alternatively, skip the setter:
self.gyro.gyro_angle = alpha
Machine.gyro.set_gyro_angle(45)
However you need to fix your code by adding the self parameter as the first parameter of your class methods.
I have code for a Range class like this:
class Range:
def __init__(self, start, end):
self.setStart(start)
self.setEnd(end)
def getStart(self):
return self.start
def setStart(self, s):
self.start = s
def getEnd(self):
return self.end
def setEnd(self, e):
self.end = e
def getLength(self):
return len(range(self.start, self.end))
def overlaps(self, r):
if (r.getStart() < self.getEnd() and r.getEnd() >= self.getEnd()) or \
(self.getStart() < r.getEnd() and self.getEnd() >= r.getEnd()) or \
(self.getStart() >= r.getStart() and self.getEnd() <= r.getEnd()) or \
(r.getStart() >= self.getStart() and r.getEnd() <= self.getEnd()):
return True
else:
return False
My assignment is to create a subclass of Range, called DNAFeature, that represents a Range that also has a strand and a sequence name:
Implement setStrand and getStrand, which set and return strand information, and setSeqName and getSeqName, which set or return the name of the sequence the feature belongs to.
If a feature is on the minus (reverse) strand, getStrand() should return ‐1. If a feature is on the plus strand, getStrand() should return 1. If strand is not set, getStrand() should return 0.
I have tried to write something but doesn't look right at all for me, can everyone please help me with this, thank you so much guys, this is my code:
class DNAFeature(Range):
def __init__(self, strand, sequence):
self.setStrand(strand)
self.setSeqName(sequence)
def getSeqName(self):
return self.plus or minus
def setSeqName(self, seq):
self.sequence = seq
def getStrand(self):
if self.getSeqName(self.strand) == 'plus':
return 1
if self.getSeqName(self.strand) == 'minus':
return -1
else:
return 0
def setStrand(self, strand):
self.strand = strand
In general it is much easier to answer questions if you provide a specific error message or thing that is going wrong. Here's what happened when I tried to run the above:
First up:
`SyntaxError: invalid syntax`
on if seq == POSITIVE. What's wrong here? Oh yes, you're missing a colon after the conditional. If you add that the file at least parses. So let's try doing some coding:
# Your code here, then:
feature = DNAFeature()
Running that gives:
TypeError: __init__() takes exactly 3 positional arguments (1 given)
Oh, OK, we need to pass some arguments to the initialiser of DNAFeature. Let's put this on the + strand, and call it foo:
feature = DNAFeature(1, "foo")
Now we get:
AttributeError: 'DNAFeature' object has no attribute 'setStrand'
What's that about? OK, you haven't defined setStrand. (Note: you shouldn't have to. But more on that later.) Let's define it:
def setStrand(self, strand):
self.strand = strand
I don't want to go through the rest of the problems with the code (hint: you need to define variables before you use them), but this is the sort of thing you should be doing.
Right, something different. The above is bad code. I hope you've written the Range class and that it hasn't been provided as part of the course, because if it has you're taking a badly-taught course. The main problem is the use of getters and setters -- I'm guessing you're Java-born and bred? In Python you don't need to write getters and setters for everything, because you can always add them in later if you need them. Instead, just use class attributes. Look at the following code for Range:
class Range:
def __init__(self, start, end):
self.start = start
self.end = end
def length(self):
return self.end - self.start
def overlaps(self, other):
return not(self.end < other.start or other.end < self.start)
Isn't that much nicer? No more nasty accessors, no icky comparisons in the overlaps method... It helps if you work out the logic that your code is trying to implement before you implement it.
See if you can write a better DNAFeature now.
You still haven't told me what getStrand should, do, but here's what I think you're aiming towards. Suppose the strand name that gets passed to __init__ is of the form "+name" or "-name". You can then do the following:
def __init__(self, strand):
sequence = strand[0] #first character of strand
if sequence == "+":
self.strand = 1
self.sequence= strand[1:]
elif sequence == "-":
self.strand = -1
self.sequence = strand[1:]
else:
self.strand = 0
self.sequence = strand
See if you can work out how that works.
In the most generic case (without making any assumptions), it seems that this is what you need:
class DNAFeature(Range):
def __init__(self, start, end):
self.setStart(start)
self.setEnd(end)
self.strand = None
self.sequencename = None
def setStrand(self, s):
self.strand = s
def getStrand(self):
if self.sequenceName == 'plus':
return 1
elif self.sequenceName == 'minus':
return -1
else:
return 0
def setSequenceName(self, s):
self.sequencename = s
def getSequenceName(self, s):
return self.sequenceName
You will notice that here, I have redefined init. There is a reason for this. I remember that in one of your earlier questions, you had mentioned that this was a Java assignment, just renamed to python. In Java, constructors are not inherited (correct me if I'm wrong). Therefore, if the same grading rubric is being used, you will lose marks for not redefining the constructor here.
Hope this helps