Purpose and functionality of Method vs function - python

I am trying to understand what is the difference between creating a method inside a class and creating a function. Both gives same output. Pls provide clarity
class Point:
""" Create a new Point, at coordinates x, y """
def __init__(self, x=0, y=0, z=0):
""" Create a new point at x, y """
self.x = x
self.y = y
self.z = ((self.x ** 2) + (self.y ** 2)) ** 0.5
and this function:
def distance(x,y):
z=((x ** 2) + (y ** 2)) ** 0.5
return z
both looks same to me
p = Point(3,4)
p.x, p.y, p.z
output:
(3, 4, 5.0)
and
distance(3,4)
output: 5.0

The difference is the self reference - the class instance has access to its own local variables and its methods. In this small example there isn't any need for a class. The function returns a tuple object much like the Point object you created. There is a rule: If a class has two methods and one of them is __init__, you shouldn't have a class. In your case, you didn't even reach the two method bar. (That rule is somewhat tongue-in-cheek but applies most of the time).
The usefulness of classes is when you have multiple methods that naturally share a set of data and you want to keep them together. Or if you have different types of objects that share common functionality and you want them to look the same externally.

It is mostly the same.
Some differences:
The function is available to be used outside the class.
If you include the calculation in the init constructor, the value is saved as an attribute of the class. It will not be recalculated every time you want to see the value.

With a class you should preferably do something like that :
class Point:
""" Create a new Point, at coordinates x, y """
def __init__(self, x=0, y=0):
""" Create a new point at x, y """
self.x = x
self.y = y
def distance(self):
self.z = ((self.x ** 2) + (self.y ** 2)) ** 0.5
return self.z
p = Point(3, 4)
print(p.distance())
The main advantage is to avoid the need of passing parameters to the method while they are required for a function

Here is the gist of it. A method can depend on the state (instance) variables in a class. A free function outside of a class has no such dependence.
Example:
>>> x = "foo"
foo
>>> x.length()
3
The method length() has no arguments; however you can see it is dependent on the string's state (its character sequence).

Related

how to extend the method of a class in a child class without returning all its variables

Let's say I have a base class:
class B:
def __init__(self):
self.b = 3
return
def method(self,x):
y = 2 * self.b
x *= 2
y *= (x + 2)
return x
It does some manipulations based on self.b and produces a variable y that is not returned by method.
Say, I have now a class A which derives from B and will look very similar, but it shall extend the functionality of B's method() by doing additional operations:
class A(B)
def __init__(self):
super().__init__()
return
def extended_method(self,x):
x = self.method(x)
if y<=10:
y = 10
return x,y
def what_As_extended_method_should_do(self,x):
y = 2 * self.b
x *= 2
y *= (x + 2)
if y<=10:
y = 10
return x,y
extended_method shows the way I'd like to write it, but this will cause an undefined variable error. The method what_As_extended_method_should_do shows what the method should do. But instead of having to write the same code inside B.method() several times (say I have 10 different subclasses which all slightly change B.method(), I would like to have some wrapper that automatically takes B.method()'s y into its scope and can operate on it without undefined variable error. The crux is that I cannot simply return x AND y from B.method(), just x. Does anybody know a solution?
Thanks!

How to control type casting for my classes

How can I control type casting for my classes? I know there is __int__() or __str__(), but what about other types? E.g. I have two classes Point and Vector. Is it possible to do something like:
point = Point(1, 2, 3)
# the following should not call Vector._init_(), but use a cast defined in Point
# (something like Point.__Vector__())
vector = Vector(point)
Is this possible? If so, how can I achieve such behavior? If not, for what standard types could I define a cast function to allow e.g. tuple(point) or list(point) - couldn't find this in the python documentation.
You could do it via the use of classmethods.
For example,
from point import Point # where the Point object is defined
class Vector:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
#classmethod
def from_point(cls, point):
if not isinstance(point, Point): # some validation first
raise TypeError("The given object is not of type 'Point'")
return cls(point.x, point.y, point.z)
# other methods
Thus, then if you have a p = Point(1, 0, 0) then you can do v = Vector.from_point(p) and you'll have what you were looking for.
There are quite some improvements that could be applied but depend on the details of Point.

Python thinks I'm passing 3 arguments although I'm only passing 2

I've written a basic Vector class for dealing with vectors. Calculating their magnitude and whatnot. I have two overloaded constructors one taking two arguments x and y and one taking a tuple (x,y).
When I initialize a variable it gives me the error:
TypeError: __init__() takes 2 positional arguments but 3 were given.
What am I missing?
class Vector:
x = 0.0
y = 0.0
def __init__(self, x, y):
self.x = x
self.y = y
def __init__(self, coordinates):
self.x = coordinates[0]
self.y = coordinates[1]
v1 = Vector(1,3)
print(v1.x)
Python doesn't support overloading, so you overwrite the first __init__ method with the second one. The second one takes only 1 argument: coordinates. That Python writes 3 arguments is because the instance (self) is passed implicitly.
Remove the second __init__ method and it will work correctly.
If you like multiple constructors you could for example implement the additional ones as classmethods:
class Vector:
x = 0.0
y = 0.0
def __init__(self, x, y):
self.x = x
self.y = y
#classmethod
def from_list(cls, coordinates):
return cls(coordinates[0], coordinates[1])
# you could also use unpacking:
# return cls(*coordinates)
Test:
>>> v1 = Vector(1,3)
>>> print(v1.x)
1
>>> v2 = Vector.from_list([2, 1])
>>> print(v2.x)
2
Python doesn't do overloaded constructors - its a dynamic language and has no preknowledge of type that would allow this to happen. Your code defines an __init__ then replaces it completely with a second __init__. If you want to do something like this, you could define a factory method with the alternate signature and call that.
You do not have overloaded constructors: the second method definition overwrites the first. Thus, your only surviving constructor takes only two arguments. You supplied 3 -- self is implied.

When to store things as part of an instance vs returning them?

I was just wondering when to store things as part of a class instance versus when to use a method to return things. For example, which of the following would be better:
class MClass():
def __init__(self):
self.x = self.get_x()
self.get_y()
self.z = None
self.get_z()
def get_x(self):
return 2
def get_y(self):
self.y = 5 * self.x
def get_z(self):
return self.get_x() * self.x
What are the conventions regarding this sort of thing and when should I assign things to self and when should I return values? Is this essentially a public/private sort of distinction?
You shouldn't return anything from __init__.
Python is not Java. You don't need to include get for everything.
If x is always 2 and y is always 10 and z is always 12, that is a lot of code.
Making some assumptions, I would write that class:
class MClass(object):
def __init__(self, x):
self.x = x
def y(self):
return self.x * 5
def z(self):
return self.x + self.y()
>>> c = MClass(2)
>>> c.x
2
>>> c.y() # note parentheses
10
>>> c.z()
12
This allows x to change later (e.g. c.x = 4) and still give the correct values for y and z.
You can use the #property decorator:
class MClass():
def __init__(self):
self.x = 2
#property
def y(self):
return 5 * self.x
#here a plus method for the setter
#y.setter
def y(self,value):
self.x = y/5
#property
def z(self):
return self.x * self.x
It's a good way of organizing yours acessors
There's no "conventions" regarding this, AFAIK, although there're common practices, different from one language to the next.
In python, the general belief is that "everything is public", and there's no reason at all to have a getter method just to return the value of a instance variable. You may, however, need such a method if you need to perform operations on the instance when such variable is accessed.
Your get_y method, for example, only makes sense if you need to recalculate the expression (5 * self.x) every time you access the value. Otherwise, you should simply define the y variable in the instance in __init__ - it's faster (because you don't recalculate the value every time) and it makes your intentions clear (because anyone looking at your code will immediately know that the value does not change)
Finally, some people prefer using properties instead of writing bare get/set methods. There's more info in this question
I read your question as a general Object Oriented development question, rather than a python specific one. As such, the general rule of member data would be to save the data as a member of the class only if it's relevant as part of a particular instance.
As an example, if you have a Screen object which has two dimensions, height and width. Those two should be stored as members. The area associated with a particular instance would return the value associated with a particular instance's height and width.
If there are certain things that seem like they should be calculated on the fly, but might be called over and over again, you can cache them as members as well, but that's really something you should do after you determine that it is a valid trade off (extra member in exchange for faster run time).
get should always do what it says. get_y() and get_z() don't do that.
Better do:
class MClass(object):
def __init__(self):
self.x = 2
#property
def y(self):
return 5 * self.x
#property
def z(self):
return self.x * self.x
This makes y and z always depend on the value of x.
You can do
c = MClass()
print c.y, c.z # 10, 4
c.x = 20
print c.y, c.z # 100, 400

Two objects in a method from a class?

I'm having a small issue with this code, I am currently learning about classes and trying to separate the two objects I have created to use both of them in a method from the class.
import math
class Segment:
def __init__(self, xcoord = 0, ycoord = 0):
self.x = xcoord
self.y = ycoord
def get(self):
return (self.x, self.y)
def setx(self, xcoord):
self.x = xcoord
def sety(self, ycoord):
self.y = ycoord
def length(self, xcoord, ycoord):
return math.sqrt(math.pow(xcoord-ycoord,2)+(xcoord-ycoord,2))
p1 = Segment(3,4)
p2 = Segment()
p2.setx(5)
p2.sety(5)
s = Segment(p1,p2)
print(Segment.get(p1))
print(Segment.get(p2))
print(s.length())
I know that I am missing parameters in my length() method, or perhaps I have not? I would like to understand how I am able to have the objects interact with on another after I have defined them.
For further clarity, I am trying to print the distance between the two objects using the parameters I have assigned to them.
Okay, let's forget the code for a second. Firstly, let's talk about naming things. Your Segment class is not a class of segments - it's a class of points. So let's start by renaming your class Point.
class Point:
def __init__(self, xcoord = 0, ycoord = 0):
self.x = xcoord
self.y = ycoord
Better already, no?
Now, imagine you're looking at someone else's code, trying to use that. Their Points have a length() method that you can call. What do you expect that to do? What could that... possibly do? A number of things, all because length is an awful descriptor for something that a Point is doing. It's certainly not a property of the Point - a point is 0-dimensional.
So let's rethink that function. There are two obvious ways to make this API - your Point class could have a distance_to(other_point) method, that would accept one argument - another Point. Optionally, you could have a module-level function segment_length(point1, point2) that would give you the length of the segment defined by the two Point objects.
So, the module-level function:
def segment_length(p1, p2):
return math.sqrt((p2.x-p1.x)**2 + (p2.y-p1.y)**2)
I'll leave the Point method to you, should you wish to attempt it. It looks very similar, just using self in lieu of one of the points.
Lets walk through this:
p1 is an instance of the Segment class with attributes x=3, y=4.
p2 is an instance of the Segment class with attributes x=0, y=0,
When you set p2 to (5, 5) you could do it with p2 = Segment(5, 5),
s will have attributes x=p1 (an instance of Segment, not a coordinate) and y=p2 (another instance of Segment).
Calculating the length.
Your length method should look like this:
def length(self, xcoord, ycoord):
return math.sqrt(math.pow(xcoord - self.x,2)+math.pow(ycoord - self.y,2))
This now uses the x and y coordinates of the class instance (in the example below, p1) and calculates the length between them, and the xcoord and ycoord parameters provided.
And you would call this with:
p2x, p2y = p2.get()
print(p1.length(p2x, p2y))
Firstly, you're missing a second math.pow() in your line:
return math.sqrt(math.pow(xcoord-ycoord,2)+(xcoord-ycoord,2))
Secondly with your call s = Segment(p1, p2) the x and y values of your Segment s are equal to the segments p1 and p2.
At the moment your values should read:
p1.get()
> (3, 4)
p2.get()
> (5, 5)
After the assignment of s you get:
s.get()
> ((3, 4), (5, 5))
This is problematic, because math.pow() has no idea what to do with a Segment object.

Categories