Python Classes
#Python supports the object-oriented programming paradigm through classes
#class is like the blueprint for a house
#create several houses and even a complete neighborhood. Each concrete house is an object or instance that’s derived from the blueprint
#Each instance can have its own properties, such as color, owner, and interior design
#properties carry what’s commonly known as the object’s state
#Instances can also have different behaviors, such as locking the doors
# In Python, attributes are variables defined inside a class with the purpose of storing all the required data for the class to work.
#Methods are functions that you define within a class
#Attributes and methods are collectively referred to as members of a class or object
#Finally, you can use classes to build class hierarchies. This way, you’ll promote code reuse and remove repetition throughout your codebase.
class ClassName: #classbody pass
#As an example of how to define attributes and methods, say that you need a Circle class to model different circles in a drawing application. #Initially, your class will have a single attribute to hold the radius. It’ll also have a method to calculate the circle’s area: #In Python, the first argument of most methods is self #This argument holds a reference to the current object so that you can use it inside the class import math class Circle: def __init__(self, radius): #This method is known as the object initializer because it defines and sets the initial values for your attributes self.radius = radius def calculate_area(self): return round(math.pi * self.radius **2, 2) #this is a method
#The action of creating concrete objects from an existing class is known as instantiation.
# In Python, the class constructor accepts the same arguments as the .__init__() method. In this example, the Circle class expects the radius argument
circle_1 = Circle(42) circle_2 = Circle(7)
circle_1

#Accessing Attributes and Methods
#obj.attribute_name
#obj.method_name()
circle_1.radius

circle_1.calculate_area()

circle_1.radius = 100
circle_1.radius

#Public vs Non-Public Members
#In Python, all attributes are accessible in one way or another.
Â
#Public Use the normal naming pattern. radius, calculate_area()
#Non-public Include a leading underscore in names. _radius, _calculate_area()
#https://realpython.com/python-classes/
#Name Mangling
#add two leading underscores to attribute and method names. This naming convention triggers what’s known as name mangling.
#In other words, mangled names aren’t available for direct access. They’re not part of a class’s public API.
class SampleClass: def __init__(self, value): self.__value = value def __method(self): print(self.__value)
sample_instance = SampleClass("Hello!")
vars(sample_instance)

vars(SampleClass)

#both of these error out: sample_instance.__value sample_instance.__method() #In this class, .__value and .__method() have two leading underscores, so their names are mangled to ._SampleClass__value and ._SampleClass__method() #Because of this internal renaming, you can’t access the attributes from outside the class
#correct way to do it sample_instance._SampleClass__value sample_instance._SampleClass__method()

#Classes are the building blocks of object-oriented programming in Python.
#In short, Python classes can help you write more organized, structured, maintainable, reusable, flexible, and user-friendly code.
#Class attributes: A class attribute is a variable that you define in the class body directly.
#All the objects that you create from a particular class share the same class attributes with the same original values.
#f you change a class attribute, then that change affects all the derived objects.
class ObjectCounter: num_instances = 0 def __init__(self): ObjectCounter.num_instances += 1 #type(self).num_instances += 1
ObjectCounter()

ObjectCounter.num_instances

#Instance attributes: An instance is a variable that you define inside a method. Instance attributes belong to a concrete instance of a given class
class Car: def __init__(self, make, model, year, color): self.make = make self.model = model self.year = year self.color = color self.started = False self.speed = 0 self.max_speed = 200
toyota_camry = Car("Toyota", "Camry", 2022, "Red")
toyota_camry.make

toyota_camry.speed

ford_mustang = Car("Ford", "Mustang", 2022, "Black")
ford_mustang.make

#dict attribute
#In Python, both classes and instances have a special attribute called .__dict__. This attribute holds a dictionary containing the writable members of the underlying class or instance.
class SampleClass: class_attr = 100 def __init__(self, instance_attr): self.instance_attr = instance_attr def method(self): print(f"Class attribute: {self.class_attr}") print(f"Instance attribute: {self.instance_attr}")
SampleClass.class_attr

SampleClass.__dict__

SampleClass.__dict__["class_attr"]

instance = SampleClass("Hello!") instance.instance_attr instance.method() instance.__dict__ instance.__dict__["instance_attr"] instance.__dict__["instance_attr"] = "Hello, Pythonista!" instance.instance_attr

#Dynamic Class and Instance Attributes
class Record: """Hold a record of data."""
john = { "name": "John Doe", "position": "Python Developer", "department": "Engineering", "salary": 80000, "hire_date": "2020-01-01", "is_manager": False, }
john_record = Record() for field, value in john.items(): setattr(john_record, field, value) john_record.name 'John Doe' john_record.department 'Engineering' john_record.__dict__ { 'name': 'John Doe', 'position': 'Python Developer', 'department': 'Engineering', 'salary': 80000, 'hire_date': '2020-01-01', 'is_manager': False }

class User: pass # Add instance attributes dynamically jane = User() jane.name = "Jane Doe" jane.job = "Data Engineer" jane.__dict__ {'name': 'Jane Doe', 'job': 'Data Engineer'} # Add methods dynamically def __init__(self, name, job): self.name = name self.job = job User.__init__ = __init__ User.__dict__ linda = User("Linda Smith", "Team Lead") linda.__dict__

#you’ve just used a pass statement as a placeholder, which is Python’s way of doing nothing.
#Even though this capability of Python may seem neat, you must use it carefully because it can make your code difficult to understand and reason about.
#validate the radius to ensure that it only stores positive numbers. How would you do that without changing your class interface? #The quickest approach to this problem is to use a property and implement the validation logic in the setter method. import math class Circle: def __init__(self, radius): self.radius = radius #To turn an existing attribute like .radius into a property, you typically use the @property decorator to write the getter method # The getter method must return the value of the attribute. In this example, the getter returns the circle’s radius, which is stored in the non-public ._radius attribute. @property def radius(self): return self._radius @radius.setter def radius(self, value): if not isinstance(value, int | float) or value <= 0: raise ValueError("positive number expected") self._radius = value def calculate_area(self): return round(math.pi * self._radius**2, 2)
circle_1 = Circle(100) circle_1.radius

circle_1.radius = 0
circle_2 = Circle(-100)
class Square: def __init__(self, side): self.side = side @property def side(self): return self._side @side.setter def side(self, value): if not isinstance(value, int | float) or value <= 0: raise ValueError("positive number expected") self._side = value def calculate_area(self): return round(self._side**2, 2)
class PositiveNumber: def __set_name__(self, owner, name): self._name = name def __get__(self, instance, owner): return instance.__dict__[self._name] def __set__(self, instance, value): if not isinstance(value, int | float) or value <= 0: raise ValueError("positive number expected") instance.__dict__[self._name] = value class Circle: radius = PositiveNumber() def __init__(self, radius): self.radius = radius def calculate_area(self): return round(math.pi * self.radius**2, 2) class Square: side = PositiveNumber() def __init__(self, side): self.side = side def calculate_area(self): return round(self.side**2, 2)
circle = Circle(100)
circle.radius = 0
#https://realpython.com/python-classes/
#time to watch youtube videos
Ryan is a Data Scientist at a fintech company, where he focuses on fraud prevention in underwriting and risk. Before that, he worked as a Data Analyst at a tax software company. He holds a degree in Electrical Engineering from UCF.