Python Forum
Still difficulties using oop
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Still difficulties using oop
#1
Hi,

I'l trying to use oop, but some stufs remain still unclear for me, both in practice and in "thinking" oop.

The following test is quite basic on the principle, but I do not understand what's wrong (it highlights i've not figured out some topics Undecided
  • bad use of inheritance?
  • wrong snippet?

Thanks for any advice

# -*- coding: utf-8 -*-

import math, os


###
class Point:
    
    # constructor
    def __init__(self, num:int = 0, 
                       x:float = 0., 
                       y:float = 0., 
                       z:float = 0., 
                       tol:float = 0.01) :
        self._num = num
        self._x = x
        self._y = y
        self._z = z
        self._tol = tol


    ###    
    def IncrementNum(self):
        '''
            Method to increment the point number
            (internally used)
        '''
        self._num += 1
        
        
    ###    
    def GetNum(self):
        """
            methode to get the point number 
        """
        return self._num


    ###    
    def GetCoordinates(self):
        """
            methode to get the point coordinates
        """
        return [self._x, self._y, self._z]
    
    
###
class Vector(Point):
    
    ## constructor
    def __init__(self, num:int = 0, 
                       x:float = 0., 
                       y:float = 0., 
                       z:float = 0., 
                       tol:float = 0.01) :
        super().__init__(num, x, y, z, tol)
        
        
    ###
    def Calculate(self, other_self):
        '''
            Method to calculate the vector coordinates using pt1-pt2 instances
            write coordinates components in self
        '''
        self._vector = [(other_self._x - self._x), (other_self._y - self._y), (other_self._z - self._z)]


    ###
    def GetCoordinates(self, other_self) -> list:
        '''
            Method to get the vector coordinates
            
        return: list (coordinates components)

        '''
        if not '_vector' in self.__dict__: 
            vect = Vector.Calculate(self, other_self)
        else: 
            vect = [self._x, self._y, self._z]
        
        return vect


    ###    
    def Norm() -> float:
        '''
            Method to calculate the norm of the vector

        Returns: norm (float)
        '''
        vect = Vector.GetCoordinates()
        
        vect_x = vect[0]
        vect_y = vect[1]
        vect_z = vect[2]

        return math.sqrt(vect_x**2 + vect_y**2 +vect_z**2)
    


if __name__ == "__main__":
    
    num_pt = 1
    pt1 = Point(num  = num_pt, 
                x    = 1.5, 
                y    = 0.1, 
                z    = -0.2)
    
    num_pt = pt1.GetNum()
    
    pt2 = Point(num  = num_pt, 
                x    = 1., 
                y    = 0., 
                z    = 0.)

    vect = Vector()
    coord = vect.GetCoordinates(pt1, pt2)
    # norm = Vector.Norm()
    # print(f"... vector = {vect.__dict__}")    
    
    # coord = Vector.GetCoordinates(pt1, pt2)
Error:
TypeError: Vector.GetCoordinates() takes 2 positional arguments but 3 were given
Reply
#2
Error occurs because you're passing three arguments to the GetCoordinates method when it only expects two (including self).
The way you write code is messy and hard to read.
Here is code without type hint and i follow pep-8,so a little shorter 35 lines your's 120 lines.
import math

class Point:
    """Class representing a point in 3D space."""
    def __init__(self, x=0.0, y=0.0, z=0.0):
        self.x = x
        self.y = y
        self.z = z

class Vector:
    """Class representing a vector in 3D space."""
    def __init__(self, pt1, pt2):
        self.coordinates = [
            pt2.x - pt1.x,
            pt2.y - pt1.y,
            pt2.z - pt1.z
        ]

    def get_coordinates(self):
        """Return the vector coordinates as a list."""
        return self.coordinates

    def norm(self):
        """Calculate and return the Euclidean norm of the vector."""
        return math.sqrt(sum(v ** 2 for v in self.coordinates))

if __name__ == "__main__":
    pt1 = Point(x=1.5, y=0.1, z=-0.2)
    pt2 = Point(x=1.0, y=0.0, z=0.0)
    vect = Vector(pt1, pt2)
    coord = vect.get_coordinates()
    norm = vect.norm()
    print(f"Vector coordinates: {coord}")
    print(f"Norm: {norm}")
Output:
Vector coordinates: [-0.5, -0.1, 0.2] Norm: 0.5477225575051661
With type hints it can be done like this,using first a @dataclass to make it nicer.
from dataclasses import dataclass
import math

@dataclass
class Point:
    """Class representing a point in 3D space."""
    x: float = 0.0
    y: float = 0.0
    z: float = 0.0

class Vector:
    """Class representing a vector in 3D space."""
    def __init__(self, pt1: Point, pt2: Point) -> None:
        self.coordinates: list[float] = [
            pt2.x - pt1.x,
            pt2.y - pt1.y,
            pt2.z - pt1.z
        ]

    def get_coordinates(self) -> list[float]:
        """Return the vector coordinates as a list."""
        return self.coordinates

    def norm(self) -> float:
        """Calculate and return the Euclidean norm of the vector."""
        return math.sqrt(sum(v ** 2 for v in self.coordinates))

if __name__ == "__main__":
    pt1 = Point(x=1.5, y=0.1, z=-0.2)
    pt2 = Point(x=1.0, y=0.0, z=0.0)
    vect = Vector(pt1, pt2)
    coord = vect.get_coordinates()
    norm = vect.norm()
    print(f"Vector coordinates: {coord}")
    print(f"Norm: {norm}")
Output:
Vector coordinates: [-0.5, -0.1, 0.2] Norm: 0.5477225575051661
Reply
#3
Ok I figured out .. i was thinking i needed to implement inheritance to use Point objects. I'm still blocked on some topics of the oop Shy

Your snippets are quite detailled , so thanks for your effort (i appreciate)
(on my side, i will continue to use "protected" variables (+ getters) .. even if nothing is really protected/private in python
Reply
#4
(Nov-10-2024, 11:09 AM)paul18fr Wrote: (on my side, i will continue to use "protected" variables (+ getters) .. even if nothing is really protected/private in python
You can but code will be unnecessary long and harder to read.
Take a look at Python Is Not Java.
Python Is Not Java Wrote:Getters and setters are evil. Evil, evil, I say! Python objects are not Java beans.
Do not write getters and setters.
This is what the ‘property’ built-in is for.
And do not take that to mean that you should write getters and setters,and then wrap them in property.
That means that until you prove that you need anything more than a simple attribute access,don’t write getters and setters.
They are a waste of CPU time, but more important,they are a waste of programmer time.
Not just for the people writing the code and tests,but for the people who have to read and understand them as well.

To explain a little about property and usage.
class Customer:
    def __init__(self, name, date_of_birth, account_num, email):
        self.name = name
        self.date_of_birth = date_of_birth
        self.account_num = account_num
        self.email = email
>>> my_customer = Customer("Bob", "12/12/1982", "1090", "[email protected]")
>>> my_customer.name
'Bob'
>>> my_customer.account_num
'1090'
# Set
>>> my_customer.name = 'Bob larsson'
# Get
>>> my_customer.name
'Bob larsson'
So in code over all work fine,and for a long time all that has been need is simple attribute access.
A new requirements is needed some costumer's sometime write a account number over a 1000,and need to warned about this.
Now can use property,this mean that client code stay unchanged(this is important) with added warnings about account number range.
class Customer:
    def __init__(self, name, date_of_birth, account_num, email):
        self.name = name
        self.date_of_birth = date_of_birth
        # Call the setter to validate account_num
        self.account_num = account_num
        self.email = email

    @property
    def account_num(self):
        return self._account_num

    @account_num.setter
    def account_num(self, value):
        if 1 <= int(value) <= 1000:
            self._account_num = value
        else:
            raise ValueError("Account number must be between 1 and 1000.")
>>> my_customer = Customer("Bob", "12/12/1982", "1255", "[email protected]")
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "<module6>", line 6, in __init__
  File "<module6>", line 18, in account_num
ValueError: Account number must be between 1 and 1000.

>>> my_customer = Customer("Bob", "12/12/1982", "125", "[email protected]")
>>> my_customer.account_num
'125'

# Can now not set number here either
>>> my_customer.account_num = '1888'
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "<module6>", line 18, in account_num
ValueError: Account number must be between 1 and 1000.
So all work as before,just that now will give a waring about account number range.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  difficulties to chage json data structure using json module in python Sibdar 1 2,683 Apr-03-2020, 06:47 PM
Last Post: micseydel
  String formatting difficulties mmk1995 3 3,718 Aug-09-2019, 11:18 AM
Last Post: wavic
  I have difficulties to use Pyshark jeanapala 1 3,345 Nov-21-2017, 01:45 AM
Last Post: Larz60+

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020