Python_面向对象_总结

1. 类的定义和使用

类的定义是创建自定义数据类型的一种方式,在类中可以定义属性和方法。类的使用是通过实例化对象来访问类的属性和调用类的方法。

下面是类的定义和使用的示例:

# 定义一个Person类
class Person:
    # 构造方法,在创建对象时进行初始化
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    # 成员方法,用于获取人的描述信息
    def get_description(self):
        return f"我是{name},今年{age}岁。"

# 创建Person对象
person1 = Person("Alice", 25)
person2 = Person("Bob", 30)

# 访问对象的属性
print(person1.name)  # 输出:Alice
print(person2.age)   # 输出:30

# 调用对象的方法
description = person1.get_description()
print(description)  # 输出:我是Alice,今年25岁。

在上述示例中,我们定义了一个Person类,它具有nameage两个属性,以及get_description方法。通过实例化对象person1person2,我们可以访问对象的属性和调用对象的方法,从而操作类的行为和数据。

1.1类的作用

类的作用是用于创建自定义的数据类型,它可以封装数据和行为,并提供了代码重用和结构化的方式。下面是一些示例,说明类的应用和作用:

  1. 模拟现实世界的实体:
    例如,创建一个Car类来表示汽车,其中包括属性如品牌、颜色和速度,以及方法如启动、加速和停止。通过实例化不同的Car对象,可以模拟不同的汽车实体,对其进行操作和管理。

  2. 封装相关数据和行为:
    例如,创建一个BankAccount类来表示银行账户,其中包括属性如账户号码和余额,以及方法如存款、取款和查询余额。通过封装相关的数据和行为在一个类中,可以更好地组织和管理账户信息。

  3. 实现抽象和继承关系:
    例如,创建一个基类Animal,然后派生出子类如DogCat,通过继承和多态的特性,可以在不同的子类中共享通用的属性和方法,并实现各自特定的行为。

  4. 构建复杂的系统和应用:
    例如,创建一个Game类来表示游戏,其中包括属性如玩家、关卡和分数,以及方法如开始游戏、计算得分和保存游戏进度。通过组合和交互不同的类,可以构建复杂的游戏系统,并实现各种游戏逻辑和功能。

总而言之,类的作用在于提供一种机制来封装数据和行为,实现代码的组织和复用,并模拟现实世界的实体和关系,从而构建更加灵活和可扩展的程序和系统。

2.成员变量和成员方法

成员变量(Member Variables)是类中定义的变量,用于存储对象的状态和属性。每个对象都有自己的一组成员变量,它们的值可以在对象的生命周期中进行修改和访问。成员变量也被称为实例变量,因为它们属于类的实例或对象。

成员方法(Member Methods)是类中定义的函数,**用于执行特定的操作或实现某些功能。**成员方法可以访问和操作成员变量,并且可以在对象上被调用来执行特定的任务。每个对象都可以调用类中定义的成员方法,并且可以根据对象的不同,产生不同的行为和结果。

成员变量和成员方法是类的重要组成部分,它们共同定义了类的属性和行为。成员变量存储了对象的数据,而成员方法定义了对象的操作。通过封装成员变量和成员方法在一个类中,可以实现数据和行为的关联,并提供了良好的代码组织和可维护性。

2.1 成员方法的定义语法

成员方法(Member Methods)是类中定义的函数,用于执行特定的操作或实现某些功能。成员方法必须在类的内部进行定义,并且可以在对象上被调用来执行特定的任务。

成员方法的定义语法如下:

class ClassName:
    def method_name(self, parameter1, parameter2, ...):
        # 方法体
        # 执行操作或实现功能
        # 可以访问和操作成员变量

# 创建对象
obj = ClassName()

# 调用成员方法
obj.method_name(arg1, arg2, ...)

说明:

  • def 关键字用于定义成员方法。
  • method_name 是成员方法的名称,可以根据需要自定义。
  • self 是成员方法的第一个参数,用于引用调用该方法的对象本身。
  • parameter1, parameter2, … 是成员方法的参数列表,可以根据需要定义任意数量的参数。
  • 方法体中可以编写具体的代码逻辑,用于执行操作或实现功能。方法体可以访问和操作类中定义的成员变量。
  • 创建对象后,可以通过对象名.方法名() 的方式调用成员方法,并传递参数(如果有定义参数)。

下面是一个示例代码,演示了成员方法的定义和使用:

class Circle:
    def __init__(self, radius):
        self.radius = radius

    def calculate_area(self):
        area = 3.14 * self.radius ** 2
        return area

    def calculate_circumference(self):
        circumference = 2 * 3.14 * self.radius
        return circumference

# 创建Circle对象
my_circle = Circle(5)

# 调用成员方法
area = my_circle.calculate_area()
circumference = my_circle.calculate_circumference()

# 打印结果
print(f"The area of the circle is: {area}")
print(f"The circumference of the circle is: {circumference}")

输出结果:

The area of the circle is: 78.5
The circumference of the circle is: 31.400000000000002

在上述示例中,Circle类定义了两个成员方法 calculate_area()calculate_circumference(),用于计算圆的面积和周长。这两个方法通过访问成员变量 radius,执行相关的计算操作,并返回结果。通过创建 Circle 对象并调用成员方法,可以计算并获取圆的面积和周长。
下面是一个示例代码,演示了成员变量和成员方法的使用:

class Car:
    def __init__(self, brand, color):
        self.brand = brand    # 成员变量
        self.color = color    # 成员变量

    def start_engine(self):   # 成员方法
        print(f"The {self.color} {self.brand} car is starting the engine.")

    def accelerate(self, speed):   # 成员方法
        print(f"The {self.color} {self.brand} car is accelerating to {speed} mph.")

# 创建Car对象
my_car = Car("Toyota", "Red")

# 访问成员变量
print(f"My car is a {my_car.color} {my_car.brand} car.")

# 调用成员方法
my_car.start_engine()
my_car.accelerate(60)

输出结果:

My car is a Red Toyota car.
The Red Toyota car is starting the engine.
The Red Toyota car is accelerating to 60 mph.

在上述示例中,Car类具有成员变量 brandcolor,用于存储汽车的品牌和颜色信息。同时,Car类定义了成员方法 start_engine()accelerate(speed),用于启动发动机和加速汽车。通过创建Car对象并访问成员变量和调用成员方法,可以实现对汽车对象的操作和控制。

3. 类和对象

在面向对象编程中,类是用来描述现实世界的事物的模板或蓝图。类定义了事物的属性和行为,并提供了创建对象的机制。通过类,我们可以将现实世界的事物抽象成程序中的对象,从而更好地理解和处理问题。

类中的属性表示事物的特征或状态,例如一个人的姓名、年龄和性别等。属性存储了对象的数据,并描述了对象的特性。类中的行为则表示事物的动作或操作,例如一个人可以走路、说话和吃饭等。行为定义了对象可以执行的操作,并描述了对象的行为方式。

通过类的定义,我们可以创建具体的对象,这些对象具有类定义的属性和行为。对象是类的实例,表示现实世界中的具体个体或实体。每个对象都有自己独立的属性值,并可以通过调用类定义的方法来执行特定的操作。

举个例子来说明:

假设有一个类定义叫做Person,它用来描述人的特征和行为。这个类可能有属性如nameagegender,表示一个人的姓名、年龄和性别。而行为可能包括方法如walk()talk()eat(),表示一个人可以走路、说话和吃饭等动作。

通过创建Person类的对象,例如person1person2,我们可以给每个对象设置不同的属性值,如person1.name = "Alice"person2.name = "Bob",并调用对象的方法来执行相应的行为,如person1.walk()person2.talk()

这样,我们可以通过类和对象的组合,完美地描述现实世界的事物,并在程序中进行模拟和操作。通过面向对象编程的思想和方式,我们可以更好地理解和处理问题,并设计出更加模块化、可扩展和可维护的程序。

类只是一种程序内的“设计图纸”,需要基于图纸生产实体(对象),才能正常工作这种套路,称之为:面向对象编程

4.利用构造方法向成员变量赋值

在面向对象编程中,构造方法(Constructor)用于在创建对象时初始化对象的成员变量。构造方法的任务是为对象的成员变量赋予初始值,以确保对象在创建时具有正确的状态。

在Python中,构造方法使用特殊的方法名__init__来定义,它在对象创建时自动调用。构造方法需要在类中进行定义,并通过self参数来引用正在创建的对象。在构造方法内部,可以使用self来访问和初始化对象的成员变量。

下面是一个示例,演示如何使用构造方法向成员变量赋值:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def display(self):
        print(f"Name: {self.name}, Age: {self.age}")

# 创建Person对象时会自动调用构造方法进行初始化
person1 = Person("Alice", 25)
person2 = Person("Bob", 30)

# 调用对象的成员方法
person1.display()
person2.display()

在上述示例中,Person类定义了构造方法__init__,接受nameage作为参数,并使用self.nameself.age来初始化对象的成员变量。通过创建Person对象并传递相应的参数,构造方法会自动为对象的成员变量赋值。
使用构造方法的好处是可以在创建对象时方便地为成员变量赋值,确保对象在创建时具有正确的初始状态。
下面对比一个使用构造方法和一个不使用构造方法的例子,以突出构造方法的优点。

使用构造方法的例子:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def display(self):
        print(f"Name: {self.name}, Age: {self.age}")

person = Person("Alice", 25)
person.display()

不使用构造方法的例子:

class Person:
    def set_name(self, name):
        self.name = name

    def set_age(self, age):
        self.age = age

    def display(self):
        print(f"Name: {self.name}, Age: {self.age}")

person = Person()
person.set_name("Alice")
person.set_age(25)
person.display()

在使用构造方法的例子中,通过在类中定义__init__方法,我们可以在创建对象时直接传递初始化参数,并在构造方法内部将这些参数赋值给成员变量。这样,在创建对象时就可以一步到位地完成对象的初始化,代码更加简洁明了。

而在不使用构造方法的例子中,我们需要单独为每个成员变量编写一个设置方法,并在创建对象后分别调用这些方法来赋值。这种方式需要多个步骤,代码更加繁琐,容易出错。

因此,使用构造方法可以提供更便捷的方式来初始化对象的成员变量,使代码更加简洁和可读。同时,构造方法也可以在创建对象时执行其他必要的初始化操作,提高了代码的可维护性和可扩展性。

注意,构造方法可以有其他参数和逻辑,具体的实现根据需求而定。构造方法的目的是在对象创建时进行初始化,以确保对象在创建时具有合适的初始状态。

在 Python 中,一些特殊的方法被称为魔术方法(Magic Methods)或特殊方法(Special Methods)。这些方法以双下划线(__)开头和结尾,如__init__方法就是其中之一。

魔术方法在类定义中具有特殊的含义和功能,它们被自动调用,而不需要我们显式地调用它们。这些方法可以用于实现类的特定行为、操作符重载、上下文管理等功能。

一些常见的魔术方法包括:

  • __init__: 构造方法,在创建对象时自动调用,用于对象的初始化。
  • __str__: 字符串表示方法,返回对象的字符串表示。
  • __repr__: 对象表示方法,返回对象的可打印表示,常用于调试。
  • __len__: 长度方法,返回对象的长度。
  • __getitem__: 索引访问方法,用于通过索引获取对象的元素。
  • __setitem__: 索引赋值方法,用于通过索引设置对象的元素。
  • __delitem__: 索引删除方法,用于通过索引删除对象的元素。
  • __iter__: 迭代方法,返回一个可迭代对象。
  • __next__: 迭代下一个元素的方法,配合__iter__使用。
  • 等等。
    举例
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def __str__(self):
        return f"Rectangle(width={self.width}, height={self.height})"
    
    def __repr__(self):
        return f"Rectangle(width={self.width}, height={self.height})"
    
    def __len__(self):
        return self.width * self.height
    
    def __getitem__(self, index):
        if index == 0:
            return self.width
        elif index == 1:
            return self.height
        else:
            raise IndexError("Invalid index")
    
    def __setitem__(self, index, value):
        if index == 0:
            self.width = value
        elif index == 1:
            self.height = value
        else:
            raise IndexError("Invalid index")
    
    def __delitem__(self, index):
        raise NotImplementedError("Deletion not supported")
    
    def __iter__(self):
        self.current = 0
        return self
    
    def __next__(self):
        if self.current >= 2:
            raise StopIteration
        elif self.current == 0:
            value = self.width
        else:
            value = self.height
        self.current += 1
        return value

# 创建一个Rectangle对象
rect = Rectangle(10, 5)

# 调用__str__方法
print(str(rect))  # 输出: Rectangle(width=10, height=5)

# 调用__repr__方法
print(repr(rect))  # 输出: Rectangle(width=10, height=5)

# 调用__len__方法
print(len(rect))  # 输出: 50

# 调用__getitem__方法
print(rect[0])  # 输出: 10
print(rect[1])  # 输出: 5

# 调用__setitem__方法
rect[0] = 20
rect[1] = 8
print(rect.width)  # 输出: 20
print(rect.height)  # 输出: 8

# 调用__delitem__方法
try:
    del rect[0]  # 抛出NotImplementedError: Deletion not supported
except NotImplementedError as e:
    print(str(e))  # 输出: Deletion not supported

# 迭代矩形对象
for value in rect:
    print(value)  # 输出: 20 8

这些魔术方法使得我们可以更灵活地定义类的行为,并与 Python 的内置功能和语法进行交互。通过合理使用这些魔术方法,我们可以使类更加符合预期的行为,并提供更好的用户体验。

5.面向对象三大特性

5.1封装

封装是面向对象编程的一个重要概念,它指的是将数据和对数据的操作封装在一个对象中,以实现数据的隐藏和保护。通过封装,对象的内部细节对外部是不可见的,外部只能通过对象提供的公共接口来访问和操作对象的数据。

在Python中,可以通过使用命名约定来实现对成员的封装,其中以下划线(_)开头的成员被视为私有成员,不应该直接访问。私有成员在类的外部是不可见的,只能在类的内部访问。

下面是一个示例代码:

class Person:
    def __init__(self, name, age):
        self._name = name
        self._age = age

    # 使用类提供的公共接口访问私有属性
    def display_info(self):
        print(f"Name: {self._name}")
        print(f"Age: {self._age}")

    # 使用类提供的公共接口修改私有属性
    def change_date(self, change_name, change_age):
        self._name = change_name
        self._age = change_age

    def _internal_method(self):
        # 只在类的内部使用的方法
        pass


# 创建对象
person = Person("John", 25)

# 访问公共方法和属性
person.display_info()#Name: John   Age: 25

# 尝试访问私有成员(不推荐)
print(person._name)  # 输出:John
print(person._age)  # 输出:25

# 在类的外部修改私有成员(不推荐)
person._name = "Alice"
person._age = 30

# 再次调用公共方法
person.display_info()#Name: Alice   Age: 30

# 调用共有方法修改私有成员
person.change_date("hello", 21)
# 再次调用公共方法
person.display_info()#Name: hello   Age: 21

在上面的示例中,_name_age被定义为私有成员,外部不应该直接访问。尽管在Python中可以通过公共接口person._name的方式访问私有成员,通过类提供的公共接口修改私有成员:如change_date ,但这是一种约定,开发者应该尽量遵循封装的原则,只通过公共接口来访问和操作对象的数据。这样可以提高代码的可维护性和可扩展性,避免对对象的内部实现进行依赖。

5.2继承

单继承
下面是一个使用单继承的例子,其中子类 Student 继承自父类 Person

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def display_info(self):
        print(f"Name: {self.name}")
        print(f"Age: {self.age}")


class Student(Person):
    def __init__(self, name, age, student_id):
        super().__init__(name, age)
        self.student_id = student_id

    def display_info(self):
        super().display_info()
        print(f"Student ID: {self.student_id}")


# 创建父类对象
person = Person("John", 25)
person.display_info()

# 创建子类对象
student = Student("Alice", 20, "12345")
student.display_info()

在这个例子中,Person 是父类,Student 是子类。子类 Student 继承了父类 Person 的属性和方法。通过调用父类的 __init__ 方法来初始化父类的属性,同时在子类的构造方法中添加了子类特有的属性 student_id

子类还重写了父类的 display_info 方法,并通过 super().display_info() 调用了父类的 display_info 方法,以保留父类的功能。在子类的 display_info 方法中,首先打印父类的信息,然后添加了子类特有的信息。

通过继承,子类 Student 获得了父类 Person 的属性和方法,并且可以扩展和修改这些继承的功能,实现了代码的重用和扩展。

多继承
多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级。 即:先继承的保留,后继承的被覆盖
下面是一个使用多继承的例子,其中子类 Child 继承自两个父类 Parent1Parent2,并且有一个同名的成员方法 display_info

class Parent1:
    def display_info(self):
        print("This is Parent1's display_info method.")


class Parent2:
    def display_info(self):
        print("This is Parent2's display_info method.")


class Child(Parent1, Parent2):
    pass


# 创建子类对象
child = Child()
child.display_info()#输出This is Parent1's display_info method.

在这个例子中,Parent1Parent2 是两个父类,Child 是子类。子类 Child 继承了两个父类的属性和方法。

由于两个父类都有同名的成员方法 display_info,在子类中调用 child.display_info() 时,按照继承顺序(从左到右)为优先级,所以会调用 Parent1display_info 方法。

多继承的优先级规则可以通过继承顺序灵活地控制成员的调用顺序,从而实现灵活的代码组合和功能扩展。

复写

子类可以对继承自父类的成员属性和成员方法进行复写(重写)。通过在子类中重新定义同名的属性或方法,可以改变其行为或实现自定义的逻辑。

以下是一个示例,展示了子类如何重写父类的成员方法:

class Parent:
    def greet(self):
        print("Hello, I am the parent class.")


class Child(Parent):
    def greet(self):
        print("Hello, I am the child class.")


# 创建子类对象
child = Child()
child.greet()

在这个例子中,父类 Parent 中定义了成员方法 greet,子类 Child 继承了该方法并进行了重写。当调用子类对象的 greet 方法时,会执行子类中重新定义的方法,而不是父类中的方法。输出结果为 “Hello, I am the child class.”

通过重写父类的属性或方法,子类可以定制自己独特的行为,实现对继承的灵活扩展和个性化定制。

当子类复写了父类的成员属性或成员方法后,类对象调用成员时会优先调用子类中复写的新成员。

如果需要在子类中调用被复写的父类成员,可以使用两种方式:

方式1:通过父类名进行调用

  • 对于成员变量,使用 父类名.成员变量 进行访问。
  • 对于成员方法,使用 父类名.成员方法(self) 进行调用,注意需要传递 self 参数。

方式2:使用 super() 调用父类成员

  • 对于成员变量,使用 super().成员变量 进行访问。
  • 对于成员方法,使用 super().成员方法() 进行调用。

以下是一个示例,展示了如何在子类中调用被复写的父类成员:

class Parent:
    def __init__(self):
        self.member_variable = "Parent's variable"

    def member_method(self):
        print("Parent's method")


class Child(Parent):
    def __init__(self):
        super().__init__()  # 调用父类的构造方法
        self.member_variable = "Child's variable"  # 复写父类的成员变量

    def member_method(self):
        super().member_method()  # 调用父类的成员方法
        print("Child's method")


# 创建子类对象
child = Child()

# 调用子类成员
print(child.member_variable)  # 输出:Child's variable
child.member_method()  # 输出:
# Parent's method
# Child's method

# 调用父类成员
print(Parent.member_variable)  # 输出:Parent's variable
Parent.member_method(child)  # 输出:
# Parent's method

在这个例子中,子类 Child 复写了父类 Parent 的成员属性和成员方法。通过方式1和方式2,可以分别调用子类和父类的成员。

需要注意的是,对于成员方法的调用,使用 super().member_method() 时不需要显式传递 self 参数,因为 super() 已经自动处理了继承链和参数传递。

多态
多态是面向对象编程的重要特性,它允许使用不同的对象来执行相同的操作,但根据对象的类型,可以得到不同的结果。

理解多态可以通过以下方式:

  1. 抽象行为:将行为抽象为接口或基类,并定义相应的方法。这个行为可以被多个不同的对象实现。

  2. 继承和实现:不同的对象通过继承或实现相同的接口或基类,具有相同的方法名和参数。

  3. 调用方式:使用统一的方式调用这些对象的方法,无需关心具体对象的类型,只关心调用的方法和参数。

  4. 多态效果:根据对象的具体类型,在运行时动态地选择调用哪个对象的方法,从而得到不同的结果。

多态常常应用于继承关系中。

通过在函数或方法的形参中声明接收父类对象,然后实际传入父类的子类对象进行工作,实现了多态的效果。

这种方式的好处是可以通过父类的定义声明,对一组相关的对象进行统一的处理,而无需关心具体是哪个子类对象。这样可以提高代码的灵活性、可扩展性和可维护性。

举个例子:

假设有一个动物园的管理系统,有一个函数 feed_animal,接收一个 Animal 类型的参数,并执行喂食的操作。

class Animal:
    def feed(self):
        pass


class Dog(Animal):
    def feed(self):
        print("给狗狗喂食!")


class Cat(Animal):
    def feed(self):
        print("给猫咪喂食!")


def feed_animal(Animal):
    Animal.feed()


# 创建不同的动物对象
dog = Dog()
cat = Cat()

# 调用喂食函数,传入不同的动物对象
feed_animal(dog)  # 输出:给狗狗喂食!
feed_animal(cat)  # 输出:给猫咪喂食!

在这个例子中,定义了一个抽象的 Animal 基类和具体的子类 DogCat,它们都有自己的 feed 方法。

通过函数 feed_animal 的形参声明为 Animal 类型,并在实际调用时传入具体的子类对象,实现了多态的效果。无论传入的是 Dog 对象还是 Cat 对象,都可以正常执行对应的喂食操作。

通过多态的应用,我们可以在统一的函数接口下,以父类对象的形式来定义和处理一组相关的对象,而实际工作由子类对象来完成,达到了同一行为、不同状态的目的。

抽象类
抽象类(Abstract Class)是一种特殊的类,它不能被实例化,只能被继承。

抽象类主要用于定义一组共同的接口或行为,而不关注具体的实现细节。它可以包含抽象方法(Abstract Method),即只有方法声明而没有具体实现的方法,子类必须实现这些抽象方法。

抽象类可以通过abc模块中的ABC类和abstractmethod装饰器来定义。

使用抽象类可以达到以下目的:

  1. 定义一组共同的接口,确保子类实现了这些接口。
  2. 强制子类实现指定的方法。
  3. 提供一种机制来标识某个类为抽象类,不能被实例化。

举个例子:

from abc import ABC, abstractmethod

# 定义抽象类
class Animal(ABC):
    @abstractmethod
    def make_sound(self):
        pass

# 继承抽象类并实现抽象方法
class Dog(Animal):
    def make_sound(self):
        print("汪汪汪!")

class Cat(Animal):
    def make_sound(self):
        print("喵喵喵!")

# 尝试实例化抽象类(会抛出TypeError异常)
animal = Animal()

# 创建子类对象
dog = Dog()
cat = Cat()

# 调用抽象方法
dog.make_sound()  # 输出:汪汪汪!
cat.make_sound()  # 输出:喵喵喵!

在这个例子中,定义了一个抽象类 Animal,它包含一个抽象方法 make_sound。子类 DogCat 继承自 Animal 并实现了 make_sound 方法。

注意,尝试实例化抽象类 Animal 会抛出 TypeError 异常,因为抽象类不能被实例化。抽象类的作用是定义一组接口,子类必须实现这些接口。

通过使用抽象类,可以确保子类实现了指定的接口或方法,提高了代码的可靠性和可维护性。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值