Follow up code snippet review question I posted: https://codereview.stackexchange.com/questions/143576/oop-designing-class-inheritances
This is homework so off the bat I am not asking to do it for me but to clarify for me so that I understand.
Here is what the assignment said about the program I have to write:
Required:
Write an OO Python Program showing how your sub-classes inherit from the super-class(es) You can have one class (preferred but not a must) or two or more super classes.
What I have to do is write a oo python program showing sub-classing inherited by a class or two or more superclasses. The program has to be about shapes. As an example of what I mean by that is:
Shape: Square
Attributes:
length
width
Methods:
area
perimeter
I have more shapes of course but from that I find the common attributes and methods from all the shapes and make superclasses and sup-classes.
My super classes are like: 2dShapes, circles and 3dShapes. My sub-classes are like length and width. My methods are area and perimeter. Note I am rambling at this point. The code snippet below does not show this instead I was thinking about making a superclass for attributes and methods and than sub-classes for the shapes? maybe?
Question: is this a good class structure? Is there a better way to structure the classes here? Here is an example of what I'm thinking about how to do this.
class Shapes(attributes):
def __init__(self):
attributes.__init__(self)
# not sure how to go on to make the attributes like height and length, radius ect. Maybe like this???
def height(self):
raise NotImplementedError # again this program is not supose to actually do anything. The program is just for us to understand inheritance with classes and super classes.
class 2dShapes(Shapes):
class Square(self):
def height(self):
# ???
So at this point I am so confused about where to start. Also I am super new to python so be gentle to me :p
class Vehicle(object):
#class variable shared between all instances of objects.
number_of_vehicles = 0
def __init__(self,length,width,color,wheels):
self.length = length
self.width = width
self.color = color
self.wheels = wheels
Vehicle.number_of_vehicles += 1 #increasing class varaible count
def get_length(self):
print("I am %d meters long!"%self.length)
def get_wdith(self):
print("I am %d meters wide!"%self.width)
def get_color(self):
print("My color is %s!"%self.color)
def get_wheels(self):
print("I have %d number of wheels"%self.wheels)
#calling my methods so I don't need to call each of their own
def get_stats(self):
self.get_length()
self.get_wheels()
self.get_wdith()
self.get_color()
def honk(self):
print("beep beep")
class Car(Vehicle):
def vroom(self):
print("Cars go vroom vroom")
class Cooper(Car):
def drift(self):
print("Mini Coopers can drift well")
class Airplanes(Vehicle):
def fly(self):
print("weeeeee I'm flying")
class Tank(Vehicle):
#custom init because tanks have guns!~
#taking the gun size and tossing the rest of the arguments to the parent.
#if the parent doesn't find a __init__ it will keep going up until one is found or unelss we call it.
#Here we made a new __init__ so it doesn't go looking for one, but we call super() which is calling for the
#parent's __init__
def __init__(self,gun_size,*args):
self.gun_size = gun_size
super(Tank,self).__init__(*args)
def fire(self):
print("pew pew pew")
#I have my custom get_stats but still calls parent's one so I don't repeat code.
def get_stats(self):
print("my gun is this big: %d " %self.gun_size)
super(Tank,self).get_stats()
a = Cooper(150,150,"blue",4)
a.drift()
a.vroom()
a.honk()
a.get_stats()
print(a.number_of_vehicles)
b = Airplanes(200,150,"white",2)
b.fly()
print(b.number_of_vehicles)
c = Tank(500,500,250,"Green",18)
c.fire()
print(c.number_of_vehicles)
c.get_stats()
Outputs:
Mini Coopers can drift well
Cars go vroom vroom
beep beep
I am 150 meters long!
I have 4 number of wheels
I am 150 meters wide!
My color is blue!
1 #vehicle count
weeeeee I'm flying # start of plan section
2 #vehicle count
pew pew pew #start of tank section
3 #vehicle count
my gun is this big: 500
I am 500 meters long!
I have 18 number of wheels
I am 250 meters wide!
My color is Green!
So the point of this post was to show you relationship of inheritance.
We have a base class called Vehicle which is a sub class of object. Don't worry about object if you want you can read up on it.
Vehicle class has some attibutes that all vehicles would have, length, width, color, wheels. It also have a class variable called number_of_vehicles which keeps track of how many object instances Vehicle has, basically how many Vehicles we "made". We also have some class methods, that access and uses the attributes we defined in __init__. You can do math on them and what not but for now we are just using them as print statements to show they work. We have a special class method that calls other methods in the same class. So get_stats calls the other get_x methods in the instance. This allows me to call those 4 methods with just "one" method call from my object, see a.get_stats(). We can still call the other methods on it's own like get_color.
We have a sub class called Car which is a vehicle, so we inherit it. Only cars can go vroom, but all cars can go vroom so we have a vroom method only at the car level. The trick is to think what does this class have that is unique to only instances of this class, and if not, can I put it in the parent's class. All vehicles have wheels and so on, but not all vehicles can go vroom (for this example only).
We have a sub class of Car, the cooper (mini cooper), which only it can drift (once again for this example only in real life the drift method would go in vehicle cause all vehicles can draft but bear with me). So this cooper is the only car that can drift so it's down here instead of in the Car class.
Tank's sub class is interesting. Here we have the basic of a vehicle but we have something new, a gun! So our vehicle class can't handle a gun, so we have to make a new __init__ method. We assign the object variable gun_size and then pass the rest of the tank's attribute to Vehicle's __init__ since the rest of the attributes are the same as Vehicle. We call super(Tank,self).__init__(*args) which is basically saying, I am a tank, this is me, please handle the rest of my attribute, parent of mine. Since tanks have a special attribute of a gun, we have to modify our get_stats method, to deal with the gun_size attribute, but the rest of the stats on the tank are the same as vehicle, so we just call our parents to handle the rest after we deal with our gun.
Now I know this is a very silly example, but I do hope you find some useful information in here. There are other majors points I haven't touched upon but this is a starting point. So back to your original question. Think abstract, the highest level would be a shape, then Rectangles are a type of shape so they would inherit it. Squares are special rectangles, so they would inherit rectangles and so on.
If you have questions don't hesitate to ask.
I do not want to give answers that are too specific, because homework, but here is an example which I think might orient you in the right direction.
In Object Oriented Programming, there is the concept of polymorphism: when instances of many different subclasses are related by some common superclass. You often see it explained as "a B is an A", like in "a Pear is a Fruit", "a Cat is an Animal", "a Triangle is a Shape". Subclasses all share a common set of methods and members present in the superclass, but their implementation of these methods can vary.
Here is an example (sorry, C style, not a Python person) with animals. The method hearNoise() accepts an animal, but will also work correctly if a subtype is passed to it:
abstract class Animal {
abstract String makeNoise();
}
class Cat extends Animal {
String makeNoise() {
return "Meow!";
}
}
class Dog extends Animal {
String makeNoise() {
return "Woof!";
}
}
void hearNoise(Animal a) {
println(a.makeNoise());
}
int main() {
hearNoise(new Cat()); //returns "Meow!"
hearNoise(new Dog()); //returns "Woof!"
}
The same principles can be applied to geometric shapes. They all have common methods and members : their perimeter, their area, their color, etc. When you have a shape, you can expect with 100% certitude you will be able to call a method to calculate a perimeter. However, the implementation, the way that specific shape subclass handles perimeter calculation, is what differs from shape to shape.
Let's start with the Shapes class. In the __init__ , you have to set the attributes. First you need 2 parameters other than self. You can use the same names as the attributes or pick different names.
class Shapes(attributes):
def __init__(self, length, width):
self.length = length
self.width = width
If you want to inherit from Shapes class, other than putting it in () in 2dShapes class definition, you have to call the __init__ of the Shapes class and pass a reference and other parameters to it, like this:
class 2dShapes(Shapes):
def __init__(self, length, width):
Shapes.__init__(self, length, width)
If you want to use a method of Shapes in 2dShapes, you have to call it just like how we did for the __init__, lets say there is a method called area() in Shapes. In the 2dShapes, you can access it by Shapes.area(self). Here is an example:
class Shapes(attributes):
def __init__(self, length, width):
self.length = length
self.width = width
def area(self):
return self.length * self.width
class 2dShapes(Shapes):
def __init__(self, length, width):
Shapes.__init__(self, length, width)
def area(self):
return Shapes.area(self)
One of the big ideas of using inheritance is to be able to re-use code.. If you have a large body of functions that are the same for several classes, having one parent class that holds those functions allows you to only write them once, and to modify them all at the same time. for example, if you want to implement classes for the shapes: Square, Rectangle, and Parallelogram, you could create a parent class: Quadrangle that contains things like a generic area or perimeter functions:
class quadrangle(object):
def __init__(self, length, width):
self.length = length
self.width = width
def area(self):
return self.length * self.width
def perimeter(self):
return 2*self.length + 2*self.width
class square(quadrangle):
def __init__(self, length):
super(square, self).__init__(length, length) #creates a quadrangle with equal lenght and width
class rectangle(quadrangle): #renamed for convienience sake but otherwise the same
pass
class parallelogram(quadrangle):
def __init__(self, length, width, angle): #angle in radians
self.angle = angle
super(parallelogram, self).__init__(length, width)
def perimeter(self): #override parent's perimiter
#do some math
return 5
Be careful not to confuse "attributes" with "methods". There is an #property syntax which wraps attribute access within a method call, but you should ignore that for now.
class Shapes(attributes):
def __init__(self):
For this part, just absorb the arguments here. e.g:
def __init__(self, *args, **kwargs):
pass
Reason being, that you are only using Shapes to pass them to a subclass. *args absorbs lists of named arguments, while **kwargs absorbs dictionaries. So this init() will accept my_shapes_instance = Shapes(length, width, height) because it has *args, and it would accept Shapes(length, width, height, {'cost': '10'}) because it has **kwargs as well.
If it were __init__(length, width, height) and you passed (length, width, height, color) then it would not work. But if you use *args then it will accept anything. Will it use all of these arguments? Only if you define that it does.
You can ignore **kwargs for now since you are not initializing these objects with dictionaries.
attributes.__init__(self)
# not sure how to go on to make the attributes like height and length, radius ect. Maybe like this???
def height(self):
raise NotImplementedError # again this program is not supose to actually do anything. The program is just for us to understand inheritance with classes and super classes.
What you have done above is define a method "height", not an attribute "height". What you want is more like this:
def __init__(self, height):
self.height = height
Even better is this, but do it in the Square subclass:
class Square(Shapes):
def __init__(self, height, *args, **kwargs):
super(Square, self).__init__(height, *args, **kwargs)
self.height = height
Now you can subclass Square with Rectangle, adding in new args as you go. Just follow a similar init pattern as above. Rectangle will not need you to add a height method, since it is already available from the parent.
Related
I want to have certain attributes & methods only available in a class instance, if the parameters meet certain conditions. The different cases are not exclusive. I already have a working solution (incl. the suggestion from ShadowRanger):
class polygon():
def __new__(cls, vert, axis=None):
triangle = vert == 3
solidrev = axis is not None
if triangle and not solidrev:
return super().__new__(_triangle)
elif solidrev and not triangle:
return super().__new__(_solid)
elif solidrev and triangle:
return super().__new__(_triangle_solid)
else:
return super().__new__(cls)
def __init__(self, vert, axis=None):
self.polygon_attribute = 1
def polygon_method(self):
print('polygon')
class _triangle(polygon):
def __init__(self, vert, axis=None):
super().__init__(vert, axis)
self.triangle_attribute = 2
def triangle_method(self):
print('triangle')
class _solid(polygon):
def __init__(self, vert, axis):
super().__init__(vert, axis)
self.solid_attribute = 3
def solid_method(self):
print('solid of revolution')
class _triangle_solid(_triangle, _solid):
def __init__(self, vert, axis):
super().__init__(vert, axis)
Availability of attributes & methods depends on the instance parameters:
The attributes & methods from the base class should always be available.
If the first parameter equals 3, the attributes & methods from subclass _triangle should be available.
If the second parameter is defined, the attributes & methods from subclass _solid should be available.
All combinations:
P = polygon(2)
P = polygon(2,axis=0)
P = polygon(3)
P = polygon(3,axis=0)
Is there a more elegant way to do this? In the ideal case, I want to get rid of the _triangle_solid class. Also, I don't get why I need to define the default argument for axis in some cases but not all of them.
Full project: https://github.com/gerritnowald/polygon
This is an example of trying to overdo use of inheritance. Inheritance makes logical sense when there is an "is a" relationship between the child and its parent class. A triangle is a polygon, so no problems there; it's a reasonable inheritance chain. A solid of revolution, while possibly built from a polygon, is not a polygon, and trying to wedge that into the inheritance hierarchy is creating problems. It's even worse because a solid of revolution may not even be defined in terms of a polygon at all.
I'd strongly recommend defining your solids of revolution with an attribute representing whatever is being revolved to produce it, not as a subclass of that revolved figure.
All that said, polygon itself should not be responsible for knowing all of its subclasses, and if it is, it should still be the parent of a triangle. Your design as currently rendered has a polygon class that nothing is an instance of; the __new__ is returning something that is not a polygon, and that's confusing as heck. You can write the hierarchy in a safer, if still not idiomatic OO way, by doing:
# Tweaked name; it's not just the base anymore; using PEP8 class name capitalization rules
class Polygon:
def __new__(cls, vert, *args, **kwargs): # Accept and ignore the arguments we don't care
# about, __init__ will handle them
if vert == 3:
return super().__new__(Triangle)
else:
return super().__new__(cls)
def __init__(self, vert, axis):
self.polygon_attribute = 1
def polygon_method(self):
print('polygon')
class Triangle(Polygon):
def __init__(self, vert, axis):
super().__init__(vert, axis)
self.triangle_attribute = 2
def triangle_method(self):
print('triangle')
t = Polygon(3, None)
p = Polygon(4, None)
print(type(t), type(p))
# Indicates t is a Triangle, p is a Polygon
Try it online!
I have a geometric base class ExtrudedSurface and a child class Cylinder, which is a 'kind of' extruded surface.
The base class has a method to translate itself (not in-place) by constructing a modified version of itself.
I would like to re-use this method by the child class Cylinder, and have it return a new, translated Cylinder. However, as implemented now this does not work because Cylinder has a different __init__ signature which is called in translate.
What is the best way to achieve this? How can Cylinder use the inherited method translate and return a new Cylinder object?
EDIT 1: I think it has to do with LSP violation but I'm not sure.
class ExtrudedSurface:
def __init__(self, profile: Curve, direction: Vector, length: float):
"""
An extruded surface geometry created by sweeping a 3D 'profile' curve along a
'direction' for a given 'length'.
"""
self.profile = profile
self.direction = direction.normalize()
self.length = length
def translate(self, translation: Vector) -> ExtrudedSurface:
"""Return a translated version of itself."""
return self.__class__(
self.profile.translate(translation),
self.length,
self.direction
)
class Cylinder(ExtrudedSurface):
def __init__(self, point: Point, direction: Vector, radius: float, length: float):
"""Cylinder surface.
Args:
point: Center point of extruded circle.
direction: Direction vector of the cylinder (axis).
radius: Cylinder radius.
length: Extrusion length in 'direction'.
"""
direction = direction.normalize()
profile = Circle(point, direction, radius)
super().__init__(profile, direction, length)
Short story short: the by-the-book approach there is to override the translate() method, as well, and call the updated constructor from there.
Now, you can refactor your class initialization and separate attribute setting from other needed actions, and then create a new class-method to clone an instance with all the attributes from a first instance, and just call this initialization, if needed.
If no initialization is needed, just a "clone" method is needed. If you happen to call this method __copy__, then you can use the copy.copy call for that, which is almost as if it was an operator in Python, and it can, by itself, have value for your end-users.
Moreover -- if your class requires no initialization besides setting plain attributes, and no calculations at all, copy.copy will just work out of the box with your instances - no extra __copy__ method needed:
from copy import copy
class ExtrudedSurface:
def __init__(self, profile: Curve, direction: Vector, length: float):
"""
An extruded surface geometry created by sweeping a 3D 'profile' curve along a
'direction' for a given 'length'.
"""
self.profile = profile
self.direction = direction.normalize()
self.length = length
def translate(self, translation: Vector) -> ExtrudedSurface:
"""Return a translated version of itself."""
new_profile = self.profile.translate(translation)
new_instance = copy(self)
new_instance.profile = new_profile
return new_instance
class Cylinder(ExtrudedSurface):
def __init__(self, point: Point, direction: Vector, radius: float, length: float):
...
Just attempt to the fact that copy will not recursively copy the attributes, so, if the self.direction vector is a mutable object in the framework you are using, this will keep both clones bound to the same vector, and if it changes in the original, that change will be reflected in the clone. By the nature of your code I am assuming everything is immutable there and all transforms create new instances: then this will work. Otherwise, you should also copy the original .direction attribute into the new instance.
In time: yes, that is an LSP violation - but I always think that LSP is given more importance than it should when it come to practical matters.
The more generic code I described, should your initialization be more complex would be:
class Base:
def __init__(self, base_args):
#code to set initial attributes
...
# call inner init with possible sideeffects:
self.inner_init()
def inner_init(self):
# code with side effects (like, attaching instance to a container)
# should be written in order to be repeat-proof - calls on the same instance have no effect
...
def __copy__(self):
new_instance = self.__class__.new()
new_instance.__dict__.update(self.__dict__)
self.inner_init()
I'm having an issue trying to make a rectangle class that is subclassed from a Points class, which (bare with me) is subclassed from nd.array.
I'm aware (or so I think) of the nuances of subclassing an nd.array. What i'm trying to achieve is being able to construct a rectangle with a center point, height, width, and rotation and not need to directly feed in discrete points. Below is my implementation of the rectangle constructor, the #init_args wrapper takes type hinted inputs and calls the class constructor which works. It appears that it isn't even getting into the __init__ method and jumps straight to the Points __new__() method.
class Points(np.ndarray):
def __new__(cls, list_of_points):
if type(list_of_points) == 'numpy.ndarray':
obj = list_of_points
else:
obj = np.vstack(list_of_points).view(cls)
return obj
class Rectangle(Points):
#init_args
def __init__(self, rect_attributes: RectAttributes):
self.points = np.zeros((4, 2))
print('in rect constructor')
self.attributes = rect_attributes
self.construct_rect_from_attributes()
super().__init__(self.points)
I don't think I really need the super().__init__ call, I was just hoping that it would delay the Points constructor. What I really need is just to be able to initialize the rectangle with attributes that aren't contained in the superclass.
The comment of chepner is make sense. Another thing that's confusing to me as that you want Rectangle to be a subclass of ndarray, yet you assign to it a self.points (initialized to zeros) of a different ndarray. ndarray.__new__ itself constructs the array and its data, so your Rectangle class is essentially already constructed from the list_of_points passed to Points.__new__.
Meanwhile you have a self.points that has no association with the points passed to the constructor.
From your example it's not clear what rect_attributes is supposed to be or what construct_rect_from_attributes() is supposed to do. But I think what you probably want is something like this (and IMO you should allow construction of Rectangle from a list of points as well, but that's up to whatever your requirements are):
class Rectangle(Points):
# put in the type hints anything else array-like
def __new__(cls, data: Union[list, np.ndarray, RectAttributes]):
if isinstance(data, RectAttributes):
# assuming you want to store the original attributes somewhere
self.attributes = data
# rect_from_attributes should be a classmethod and
# should return an array of points
data = cls.rect_from_attributes(data)
else:
# a default value if the rect was constructed another
# way, or inversely construct a RectAttributes from points
self.attributes = ...
return super().__new__(cls, data)
The standard way to compute areas of rectangles using 'class' in Python3 is via something like the following
class Rectangles:
def __init__(self,length,width):
self.a = length
self.b = width
def Area(self):
return self.a*self.b
example = Rectangles(2,3)
print('Area of the specific example=',example.Area())
Obviously, the above code will generate a print of the answer to be equal to 6.
My question: How to hold up the assignment of length=2 and width=3 up until the very last line. In other words, I would like to avoid ''pre-defining'' that example with fixed length and width, so that I could assign length and width directly to Area(), which would lead to direct calculation of areas. Following is my tentative approach (the following code only shows my intention and it doesn't work in Python, and that's why I am wondering about a way to make it work...)
class Rectangles:
def __init__(self,length,width):
self.a = length
self.b = width
def Area(self,length,width): ##I intend to assign values to length and width DIRECTLY in the method Area()
return self.a*self.b
example =Rectangles() ##I would NOT like to assign length and width too early at this stage. How to correctly express that??
example.Area(2,3) ##Inside Area() is where I WOULD like to assign a length and width to example.Area(4,7) ##In this way I don't need to define a fixed example for each given (length, width)
In this way, I don't have to define a bunch of fixed instances (say, example (4,7), example (2,3), etc...) for computing their areas. Could anyone help me out with this?
Thanks in advance!
I don't see why you would need an object-oriented approach here. If you wanted to have an area function organized under Rectangles, use a static method:
class Rectangles:
#staticmethod
def Area(length, width):
return length * width
example = Rectangles()
example.Area(2,3)
example.Area(4,7)
# OR
Rectangles.Area(2,3)
Rectangles.Area(4,7)
I'm not sure why you would need this, though.
If you want to instantiate a Rectangle without providing the params you can define default values in the class constructor:
class Rectangles:
def __init__(self, length=0, width=0):
self.a = length
self.b = width
And to define the length and width values through the Area() method, just add them as parameters:
def Area(self, length, width):
self.a = length
self.b = width
return self.a*self.b
Your 'tentative approach' is not going to work. You have provided two parameters to the initialization method __init__() of your Rectangles class but when you're creating instances, you're not providing those 2 required positional arguments: 'length' and 'width'. This will throw an error.
I think you are trying to have something like the following code.
Code
class Rectangle:
def __init__(self):
pass # Left blank for future use
#staticmethod
def area(self, length, width):
return length*width
example =Rectangle()
example.area(2,3)
example.area(4,7)
This question already has answers here:
Why do you need explicitly have the "self" argument in a Python method? [duplicate]
(10 answers)
Closed 6 years ago.
Consider this code:
class example(object):
def __init__ (): # No self
test() # No self
def test(x,y): # No self
return x+y
def test1(x,y): # No self
return x-y
print(example.test(10,5))
print(example.test1(10,5))
15
5
This works as expected. I believe I can write a whole program not using self. What am I missing? What is this self; why is it needed in some practical way?
I have read a lot about it - (stack, Python documentation), but I just don't understand why it's needed, since I can obviously create a program without it.
You can perfectly create a program without it. But then you'd be missing one of the key features of classes. If you can do without self, I'd argue you can do without classes and just do something purely with functions :)
Classes allow you to create objects which have a PROPERTY associated to them, and self allows you to access those values. So say you have a square.
g code:
class Square(object):
def __init__ (self, length, height):
self.length = length # THIS square's length, not others
self.height = height # THIS square's height, not other
def print_length_and_height(self):
print(self.length, self.height) # THIS square's length and height
square1 = Square(2,2)
square2 = Square(4,4)
square1.print_length_and_height() # 2 2
square2.print_length_and_height() # 4 4
Now, this example is quite silly, of course, but i think it shows what SELF specifically is for: it refers to the particular instance of an object.
By all means, if you don't see the point to it, just do away with classes and just use functions, there nothing wrong with that.
You haven't utilised a class or object properly. Cutting out the garbage code, your program reduces to:
def test(x,y): #No class
return x+y
def test1(x,y): #No class
return x-y
print(example.test(10,5))
print(example.test1(10,5))
Output:
15
5
Your "class" is no more useful than if you wrapped your program in the nested structures:
if True:
for i in range(1):
...
A proper object will have attributes (data fields) and functions that operate on that data (see below). Your code has an empty object; hence, you have nothing on which to operate, no need for self, and no need for a class at all.
Rather, use a class when you need to encapsulate a data representation and associated operations. Below, I've reused some of your code to make example do some trivial complex number work. There are many extensions and improvements to make in this; I kept it relatively close to your original work.
class example(object):
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
sign = ' + ' if self.b >= 0 else ' - '
return str(self.a) + sign + str(abs(self.b)) + 'i'
def add(self, x):
self.a += x.a
self.b += x.b
def sub(self, x):
self.a -= x.a
self.b -= x.b
complex1 = example(10, 5)
complex2 = example(-3, 2)
complex1.add(complex2)
print(complex1)
complex2.sub(complex1)
print(complex2)
Output:
7 + 7i
-10 - 5i
Are you familiar with Object-Oriented Paradigm?
If you don't you should check it. Python is a Object-Oriented Language and self lets you define your object properties.
An example:
You have a class named Vehicle. A vehicle could be a bike, a car, even a plane. So something you can include is a name and a type.
class Vehicle():
def init(self, name, type): # Constructor
self.name = name
self.type = type
def info(self):
print("I'm a ")
print(self.name)
That's all, now you have a vehicle with name and type. Every instance of Vehicle would have a name and a type different or not and every intance can access its own variables. I'm sorry I can't explain it better. Firstable you need to know Object-Oriented Paradigm knowledge. Please comment my answer if you have doubts & I'll answer you or give a link where it comes explained better.