Overview of Object-Oriented Technology
- Class: A blueprint for creating objects that share common attributes and methods. It defines properties and behaviors that all instances of that class will have.
- Method: A function defined within a class.
- Class Variable: A variable shared by all instances of a class. It is defined inside the class but outside any method. Class variables are often not used as instance variables.
- Data Member: A class variable or instance variable that holds data related to the class and its objects.
- Method Overriding: If an inherited method from a parent class doesn't meet the needs of the child class, it can be redefined. This process is called overriding.
- Instance Variable: A variable defined inside a method, belonging only to the current instance of the class.
- Inheritance: A mechanism where a derived class (child class) inherits fields and methods from a base class (parent class). It allows an object of a derived class to be treated as an object of the base class, modeling an "is-a" relationship.
- Instantiation: The process of creating an instance (a concrete object) of a class.
- Object: An instance of a class, defined by its data structure. Objects include both data members (class and instance variables) and methods.
Classes
A class represents a category of things that share common attributes and behaviors. For example, the general characteristics shared by all humans.
Syntax
class ClassName:
"""
Docstring describing the class.
"""
variable_name = variable_value # Static variable or field, common to all instances
def __init__(self):
pass
def method_name(self):
pass
Accessing Class Members
-
To view all variables (fields) and methods (functions) of a class:
ClassName.__dict__ -
Manipulating static variables (static fields) using the class name: It is best not to use
__dict__for this purpose.- Not recommended: Using
ClassName.__dict__to read all members or manipulate individual ones (add, delete, modify). - Recommended: Use
ClassName.variable_namefor accessing and modifying static variables.
# Accessing static variables via class name print(Person.level) Person.create = 'creative' del Person.mind Person.level = 'modified' - Not recommended: Using
-
Manipulating methods (dynamic methods) of a clas:
- Not recommended: Via
ClassName.__dict__. - Recommended: Via
ClassName.method_name.
- Not recommended: Via
Example
class Person:
'''
This class defines a human.
'''
level = 'Advanced Animal'
mind = 'Has Thoughts'
language = 'Has Language'
def __init__(self):
pass
def work(self):
print('Starting work!')
def eat(self):
pass
Class Objects
Class objects support two operations: attribute reference and instantiation.
- Attribute Reference: Uses the standard syntax
obj.name. - Instantiation: Creates an instance of the class.
class MyClass:
"""A simple class example"""
i = 12345
def f(self):
return 'hello world'
# Instantiate the class
x = MyClass() # x is an abstract object, MyClass() is instantiation
# Access class attributes and methods
print("Attribute i of MyClass:", x.i)
print("Method f output of MyClass:", x.f())
Output:
Attribute i of MyClass: 12345
Method f output of MyClass: hello world
Many classes prefer to create objects with an initial state. Therefore, a class may define a special method named __init__() (the constructor):
def __init__(self):
self.data = []
When a class defines an __init__() method, class instantiation automatical invokes __init__() for the newly created instance. Example:
x = MyClass()
The __init__() method can have parameters. The parameters are passed to the class during instantiation. Example:
class Complex:
def __init__(self, realpart, imagpart):
self.r = realpart
self.i = imagpart
x = Complex(3.0, -4.5)
print(x.r, x.i) # Output: 3.0 -4.5
self Represents the Instance, Not the Class
Class methods differ from ordinary functions only in that they must have an extra first parameter name, conventionally called self. self is the memory address of the object.
class Test:
def prt(self):
print(self)
print(self.__class__)
t = Test()
t.prt()
Output:
<__main__.Test instance at 0x100771878>
__main__.Test
The result clearly shows that self represents an instance of the class, the address of the current object, while self.__class__ points to the class.
self is not a Python keyword; you can replace it with another name, like runoob, and it will still work:
class Test:
def prt(runoob):
print(runoob)
print(runoob.__class__)
t = Test()
t.prt()
Output:
<__main__.Test instance at 0x100771878>
__main__.Test
Class Methods
Inside a class, you use the def keyword to define a method. Unlike regular funcsions, class methods must include the parameter self as the first parameter, representing the class instance.
# Class definition
class People:
# Basic attributes
name = ''
age = 0
# Private attribute (cannot be accessed directly outside the class)
__weight = 0
# Constructor method
def __init__(self, n, a, w):
self.name = n
self.age = a
self.__weight = w
def speak(self):
print("%s says: I am %d years old." % (self.name, self.age))
# Instantiate the class
p = People('John', 10, 30)
p.speak()
Output:
John says: I am 10 years old.
Instance Objects
Consider the following code:
class Person:
'''
This class defines a human.
'''
level = 'Advanced Animal'
mind = 'Has Thoughts'
language = 'Has Language'
def __init__(self, name, age, sex, area, hobby): # Constructor
self.name = name # self.name is an object attribute
self.age = age
self.sex = sex
self.area = area
self.hobby = hobby
def work(self, eye):
self.eye = eye
print('Starting work!')
def eat(self):
pass
aduan = Person('A Duan', 18, 'Male', 'Handan, Hebei', 'Not Female')
The line aduan = Person(...) is called instantiation. It gives an object its individual uniqueness compared to other objects.
Instantiation process:
- An object space is created, including a pointer to the class. The space address is returned to
self. - The
__init__method is automatically executed, with the empty object space passed toselfand the remaining arguments passed to the corresponding parameters. - The
initmethod runs, encapsulating attributes into the object. The completed object is then returned to the class name call.
Object Attributes
-
Finding all attributes in an object:
aduan = Person('A Duan', 18, 'Male', 'Handan', 'Not Female') print(aduan.__dict__) -
Manipulating attributes of an object:
aduan = Person('A Duan', 18, 'Male', 'Handan', 'Not Female') print(aduan.name) # Read aduan.job = 'Student' # Add del aduan.age # Delete aduan.sex = 'Laddyboy' # Modify print(aduan.__dict__) -
Manipulating static variables (static fields) of a class via an object: Only read operations are allowed; add, delete, or modify operations are not permitted.
aduan = Person('A Duan', 18, 'Male', 'Handan', 'Not Female') print(aduan.level) # Read - class static field aduan.level = 'Medium Animal' # This creates a new attribute in the object, does NOT change the class-level variable print(aduan.level) # Prints the object's new attribute del aduan.level # Deletes the object's attribute print(Person.__dict__) # Class level remains unchanged -
Executing class methods from an object:
aying = Person('A Ying', 17, 'Female', 'Hengshui', 'Driving...') print(aying) aying.work('Big Eyes') print(aying.__dict__) # 1. self is a positional parameter, conventionally named, representing the object space. # 2. Attributes can be added to an object even in non-__init__ methods, but the method must be executed first.
Namespaces: Class vs. Object
- Class Namespace: Created when the class is defined. It holds class-level variables and methods.
- Object Namespace: Created during instantiation. It holds object-specific attributes. Each object has its own independent namespace, linked to the class via a pointer. Objects cannot directly access or interfere with each other's namespaces.
Lookup Order
- Object Lookup Order: First, it looks in the object's own namespace. If the variable or method is not found there, it looks in the class namespace via the class pointer stored in the object.
- Class Lookup Order: Directly searches within the class itself.
class A:
home = 'Oldboy Education'
name = 'Wu Da'
def __init__(self, name, age):
self.name = name
self.age = age
def job(self):
print('I am driving...')
# Lookup order for objects
obj1 = A('oldboy', 1000)
print(obj1.name) # Finds 'name' in object space first (from __init__)
print(obj1.job) # Looks for 'job' in object space, not found -> looks in class space
print(obj1.home) # Not in object space -> looks in class space
# Lookup order for class
print(A.name) # Finds 'name' directly in class space (the class variable, not the instance one)
# Object independence
obj1 = A('oldboy', 1000)
obj2 = A('alex', 10000)
print(obj1.name) # oldboy
print(obj2.name) # alex
Composition
Composition means giving an object of one class an attribute that is an object of another class.
class GameRole:
def __init__(self, name, ad, hp):
self.name = name
self.ad = ad
self.hp = hp
def attack(self, target):
target.hp -= self.ad
print(f'{self.name} attacks {target.name}, {target.name} loses {self.ad} HP. {target.name} has {target.hp} HP remaining.')
def equip_weapon(self, weapon):
self.weapon = weapon
class Sword:
def __init__(self, name, ad):
self.name = name
self.ad = ad
def fight(self, attacker, defender):
defender.hp -= self.ad
print(f'{attacker.name} uses {self.name} to slash {defender.name}. {defender.name} has {defender.hp} HP remaining.')
aduan = GameRole('A Duan', 10, 100)
mike = GameRole('Mike', 120, 300)
slaughter_knife = Sword('Slaughter Knife', 200)
# Less ideal: the action initiator should be the person, not the sword itself
# slaughter_knife.fight(aduan, mike)
# More logical: assign the weapon to the person first
aduan.equip_weapon(slaughter_knife) # Encapsulate a weapon attribute for aduan
# Now use the weapon via the person
aduan.weapon.fight(aduan, mike)
mike.attack(aduan)