class Car:
"Car Class"
# Class Attribute
fuel = "Electric"
# initialize dunder
def __init__(self, color: str, mileage: int):
self.color = color
self.mileage = mileage
# Print dunder
def __str__(self) -> str:
return f"color: {self.color}, mileage: {self.mileage}"
# Instance method
def drive(self):
return f"Ventured {self.mileage} miles" 30 OOP in Python
30.1 Class
30.1.1 Define Class
30.1.2 Create instance
Create instance of a class Car
car_blue = Car("blue", 20000)
print(car_blue)
#> color: blue, mileage: 20000# Doc string
car_blue.__doc__
#> 'Car Class'car_blue.__dict__
#> {'color': 'blue', 'mileage': 20000}Access Attribute
car_turbo.fuel
#> NameError: name 'car_turbo' is not definedAccess method
car_turbo.drive()
#> NameError: name 'car_turbo' is not defined30.1.3 Class attribute vs Instance attribute
- Class Attributes:
- Defined directly in the class body.
- Shared across all instances of the class.
- Accessed using the class name or through an instance.
- Changes to a class attribute affect all instances that haven’t overridden the attribute.
Example:
class Manual:
A = "hi"
B = "there"
# Accessing class attributes
print(Manual.A) # Output: hi
#> hi
print(Manual.B) # Output: there
#> there
# Creating instances
m1 = Manual()
m2 = Manual()
# Accessing class attributes through instances
print(m1.A)
#> hi
print(m2.A)
#> hi
# Modifying class attribute
Manual.A = "hello"
print(m1.A)
#> hello
print(m2.A)
#> hello- Instance Attributes:
- Defined within the
__init__method. - Unique to each instance of the class.
- Accessed using the instance name.
- Changes to an instance attribute affect only that instance.
- Defined within the
Example:
class Manual:
def __init__(self):
self.A = "hi"
self.B = "there"
# Creating instances
m1 = Manual()
m2 = Manual()
# Accessing instance attributes
print(m1.A)
#> hi
print(m2.A)
#> hi
# Modifying instance attribute
m1.A = "hello"
print(m1.A)
#> hello
print(m2.A)
#> hi30.1.3.1 Key Differences
- Scope and Sharing:
- Class Attributes: Shared by all instances of the class. If you change a class attribute, the change is reflected in all instances unless overridden.
- Instance Attributes: Unique to each instance. Changing an instance attribute affects only that particular instance.
- Definition and Initialization:
- Class Attributes: Defined directly in the class body, outside any methods.
- Instance Attributes: Defined within the
__init__method, which is called when a new instance of the class is created.
- Usage Context:
- Class Attributes: Useful for constants or attributes that should be shared across all instances.
- Instance Attributes: Used for attributes that need to be unique to each instance, such as data specific to that instance.
30.1.3.2 Summary
- Use class attributes when you want to share data across all instances of the class.
- Use instance attributes when you need each instance of the class to have its own unique data.
30.1.4 Interitance
class Parent:
hair_color = "brown"
class Child(Parent):
passch1 = Child()
ch1.hair_color
#> 'brown'Overwrite Parent
class Parent:
hair_color = "brown"
class Child(Parent):
hair_color = "purple" # Overwritech2 = Child()
ch2.hair_color
#> 'purple'Extend Parent Attribute
class Parent:
speaks = ["English"]
class Child(Parent):
def __init__(self):
super().__init__()
self.speaks.append("German")ch3 = Child()
ch3.hair_color
#> AttributeError: 'Child' object has no attribute 'hair_color'# Check parent class
type(ch3)
#> <class '__main__.Child'>
isinstance(ch3, Parent)
#> True30.1.5 Multiple Child from Parent Class
Parent Class
class Dog:
species = "Canis familiaris"
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name} is {self.age} years old"
def speak(self, sound="..."):
return f"{self.name} says {sound}"Child Class
Each dog breed bark differently
class JackRussellTerrier(Dog):
def speak(self, sound="Arf"):
return super().speak(sound)
class Bulldog(Dog):
def speak(self, sound="Woof"):
return super().speak(sound)
class Dachshund(Dog):
passmiles = JackRussellTerrier("Miles", 4)
buddy = Dachshund("Buddy", 9)
jack = Bulldog("Jack", 3)
jim = Bulldog("Jim", 5)miles.speak()
#> 'Miles says Arf'
buddy.speak()
#> 'Buddy says ...'30.2 Class Method (Dunder)
30.2.1 String Representation __repr__, __str__
class Pair:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return 'Pair({0.x!r}, {0.y!r})'.format(self)
def __str__(self):
return '({0.x!s}, {0.y!s})'.format(self)p = Pair(3, 4)
p
#> Pair(3, 4)the special !r formatting code indicates that the output of __repr__() should be used
print('p is {0!r}'.format(p))
#> p is Pair(3, 4)
print('p is {0}'.format(p))
#> p is (3, 4)30.2.2 String Formatting __format__()
_formats = {
'ymd' : '{d.year}-{d.month}-{d.day}',
'mdy' : '{d.month}/{d.day}/{d.year}',
'dmy' : '{d.day}/{d.month}/{d.year}'
}
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def __format__(self, code):
if code == '':
code = 'ymd'
fmt = _formats[code]
return fmt.format(d=self)d = Date(2012, 12, 21)
format(d)
#> '2012-12-21'
format(d, 'mdy')
#> '12/21/2012'
'The date is {:ymd}'.format(d)
#> 'The date is 2012-12-21'30.2.3 Setter / Getter
class Person:
def __init__(self, first_name):
self.first_name = first_name
# Getter function
@property
def first_name(self):
return self._first_name
# Setter function
@first_name.setter
def first_name(self, value):
if not isinstance(value, str):
raise TypeError('Expected a string')
self._first_name = value
# Deleter function (optional)
@first_name.deleter
def first_name(self):
raise AttributeError("Can't delete attribute")a = Person('Guido')
a.first_name
#> 'Guido'a.first_name = 42
#> TypeError: Expected a string