【两万字由浅及深】python-类与对象

python类与对象

首先,你点开这篇文章,先看目录!!!

文章目录

  • python类与对象
    • 初认识:什么是类?为什么要使用类?
    • 《》第一部分:类,对象,成员,构造函数,self
      • 1.定义一个基本类
      • 2.类创建,对象创建,成员变量,成员函数,构造函数,self使用
      • 3.基于以上内容的实际训练
    • 《》第二部分:由浅到深重新学习第一部分
      • 1.重新学习__init__构造函数
      • 2.重新学习成员函数
      • 3.重新学习成员变量
    • 《》第三部分:析构函数
      • 1.析构函数讲解
      • 2.内存知识:对象=对象
    • 《》第四部分:公有、私有、保护属性和方法
      • 1.公有属性和方法
      • 2.私有属性和方法
      • 3.保护属性和方法
      • 4.总结
    • 《》第五部分:继承
      • 1.定义基类
      • 2.定义派生类
      • 3.创建对象
      • 4.方法重写
      • 5.super()函数
      • 6.多继承
      • 继承实战
    • 《》第六部分:多态性
      • 1.继承和方法重写
      • 2.鸭子类型(Duck Typing)
    • 《》第七部分:静态
      • 1.静态方法和静态属性
      • 2.非静态成员函数访问静态方法和属性
      • 3.静态属性的修改
    • 《》第八部分:魔术方法
      • 1.小目录
      • 2.相应的代码样例
    • 后记

初认识:什么是类?为什么要使用类?

当编写程序时,类是一种非常有用的工具,用于组织和封装相关的数据和功能。类提供了一种抽象的方式来描述现实世界中的对象和概念。

为什么要使用类呢?下面是一些主要的原因:

  1. 封装数据:类允许将相关的数据和功能组合在一起。通过将数据封装在类的内部,可以防止外部代码直接访问和修改数据。这提供了更好的数据安全性和保护,可以防止不正确的使用和更改数据。

  2. 模块化和重用:类的使用可以将程序分解为更小、更易于管理的模块。每个类可以专注于一个特定的任务或责任,并且可以在程序的其他部分进行重用。这种模块化和重用性有助于提高代码的可读性、可维护性和扩展性。

  3. 继承和多态:继承是面向对象编程中的一个重要概念,它允许创建一个类(子类)从另一个类(父类)继承属性和方法。通过继承,子类可以重用父类的代码,并且还可以添加或修改自己的行为。多态性是指在继承关系中,可以使用父类类型的变量来引用子类的对象,从而实现同一个方法具有不同的行为。

  4. 代码组织和可读性:类提供了一种将相关的数据和功能组织在一起的方式。这种组织使得代码更易于阅读和理解,因为相关的代码被放置在同一个地方。通过命名良好的类和方法,可以更清楚地表达代码的意图和功能。

在类的定义中,使用关键字class来声明一个类,并习惯使用大写字母开头的名称来命名类。类中可以定义属性(数据)和方法(功能)。属性是类的状态或特征,而方法是类的行为或操作,回顾前面的文章,我们用列表来举例,我们定义了一个列表叫做list1,这里list1就是一个对象,是列表类实例化出来的一个对象,而list1对象就可以使用列表类里面的方法,例如list1.append()这里的append就是一个方法。希望能够读者能够理解号方法和内置函数的区别。

《》第一部分:类,对象,成员,构造函数,self

跟着这个节奏走就对了,有些人学了C++的类看其他的会很晕的

1.定义一个基本类

这里会涉及到没有讲述的知识点,没关系我们后面再讨论…

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

    def say_hello(self):
        print(f"Hello, my name is {self.name} and I'm {self.age} years old.")

再来看类的实例化(差不多就是使用一个类,先不深究什么是类的实例化))

person1 = Person("Alice", 25)
person1.say_hello()  # 输出: Hello, my name is Alice and I'm 25 years old.

在类的定义中,使用关键字class来声明一个类,并习惯使用大写字母开头的名称来命名类。类中可以定义属性(数据)和方法(功能)。属性是类的状态或特征,而方法是类的行为或操作。在这个例子中我们定义了一个Person类,self表示类的实例本身,而nameage是传递给__init__方法的参数。通过self.nameself.age,我们将传递的参数值赋给了类的属性。这里先不深究__init__方法是什么,也先不深究为什么就赋予类的属性了,这段代码只是让你了解类的大概。下面一口气讲讲类的使用的20% 。

2.类创建,对象创建,成员变量,成员函数,构造函数,self使用

这些内容将会让你对python类有一定的了解,并且可以利用这些知识点去写写自己喜欢的小项目。

  1. 类的创建:

在Python中,使用class关键字创建类。类的命名应遵循一定的命名规范,通常使用首字母大写的驼峰命名法。

class MyClass:
    pass
  1. 对象的创建:

对象是类的实例化,通过调用类来创建对象。通过类名后跟括号,可以创建一个新的对象。

my_obj = MyClass()
  1. 成员变量:

成员变量是类中定义的变量,用于存储对象的状态。每个对象都有自己的一组成员变量。成员变量可以在类中任何地方定义,但通常在构造函数中初始化。

class MyClass:
    def __init__(self):  # 详见第五点
        self.my_variable = 10  # 详见第六点

my_obj = MyClass()
print(my_obj.my_variable)  # 输出: 10
  1. 成员函数:

成员函数是类中定义的函数,用于执行特定的操作。它们可以访问和操作对象的成员变量。成员函数在类中定义,必须在调用对象上使用点号访问。

class MyClass:
    def __init__(self):
        self.my_variable = 10
    
    def my_function(self):
        print("Hello from my_function!")

my_obj = MyClass()
my_obj.my_function()  # 输出: Hello from my_function!
  1. 构造函数:

构造函数是一个特殊的成员函数,在创建对象时自动调用。它用于初始化对象的成员变量。在Python中,构造函数名始终是__init__。构造函数可以接受参数,用于在创建对象时初始化成员变量。

class MyClass:
    def __init__(self, value):
        self.my_variable = value
    
    def my_function(self):
        print("Hello from my_function!")

my_obj = MyClass(20)
print(my_obj.my_variable)  # 输出: 20
  1. self使用:

在类中定义成员变量和成员函数时,需要使用self关键字引用当前对象。self表示对当前对象的引用,通过它可以访问对象的成员变量和成员函数,以及定义一个类属性,例如下面的self.my_variable=value

class MyClass:
    def __init__(self, value):
        self.my_variable = value

    def my_function(self):
        print("Value:", self.my_variable)
        self.my_print()  # 这里的调用是一定要家self的

    def my_print(self):
        print("我通过self调用了my_print()")


my_obj = MyClass(30)
my_obj.my_function()  # 输出: Value: 30\n我通过self调用了my_print()\n

注意事项:

  • 类名和对象名应遵循命名规范,以增加代码的可读性。
  • 在构造函数中初始化成员变量时,使用self来引用(定义)当前对象的成员变量。
  • 调用成员函数时,使用对象名后跟点号来访问该函数。
  • self是约定俗成的命名,实际上可以使用任何名称。但是在实际开发中,强烈建议使用self来表示对当前对象的引用。

3.基于以上内容的实际训练

编写一个简单的学生成绩录入系统,要求如下:

  • 运用以上的知识点
  • 通过类的构造函数来对学生的成绩进行初始化
class Student:
    def __init__(self, name, chinese_score, math_score, english_score):
        self.name = name
        self.chinese_score = float(chinese_score)
        self.math_score = float(math_score)
        self.english_score = float(english_score)
        self.total_score = 0.0

    def calculate_total_score(self):
        self.total_score = self.chinese_score + self.math_score + self.english_score

    def display_scores(self):
        print(f"姓名:{self.name}")
        print(f"语文成绩:{self.chinese_score}")
        print(f"数学成绩:{self.math_score}")
        print(f"英语成绩:{self.english_score}")
        print(f"总分:{self.total_score}")


# 创建学生对象并录入成绩
student1 = Student("张三", 89.5, 92.0, 87.5)
student1.calculate_total_score()

student2 = Student("李四", 78.0, 85.5, 90.0)
student2.calculate_total_score()

# 显示学生信息及总分
student1.display_scores()
print("------------")
student2.display_scores()
姓名:张三
语文成绩:89.5
数学成绩:92.0
英语成绩:87.5
总分:269.0
------------
姓名:李四
语文成绩:78.0
数学成绩:85.5
英语成绩:90.0
总分:253.5

《》第二部分:由浅到深重新学习第一部分

1.重新学习__init__构造函数

在Python中,当你实例化一个类并创建对象时,会调用该类的构造函数(也称为初始化方法或__init__方法)。构造函数是一个特殊的方法,用于初始化对象的属性和执行必要的设置。构造函数可以接受参数,这些参数在实例化对象时被传递。

构造函数的参数数量在类定义时确定,并且通常至少包含一个参数,即self,它引用正在创建的对象实例本身。

以下是几种常见的构造函数参数情况:

  1. 无参数构造函数:
    如果类定义中的构造函数没有任何参数(除了self),则在实例化对象时不需要传递任何参数。例如:

    class MyClass:
        def __init__(self):
            # 构造函数的代码
    
    obj = MyClass()  # 不需要传递任何参数
    
  2. 带有固定数量参数的构造函数:
    构造函数可以接受固定数量的参数,这些参数在实例化对象时需要传递。例如:

    class MyClass:
        def __init__(self, param1, param2):
            # 构造函数的代码
    
    obj = MyClass(arg1, arg2)  # 传递参数arg1和arg2
    
  3. 带有可选参数的构造函数:
    构造函数可以定义一些参数为可选参数,这意味着在实例化对象时可以选择性地传递这些参数。可以使用默认值来指定可选参数的初始值。例如:

    class MyClass:
        def __init__(self, param1, param2=None):
            # 构造函数的代码
    
    obj1 = MyClass(arg1)      # 只传递必需的参数arg1,param2使用默认值None
    obj2 = MyClass(arg1, arg2)  # 传递参数arg1和arg2,param2的值为传递的arg2的值
    

在实例化对象时,你需要根据类定义中构造函数的参数数量和要求来传递相应数量和类型的参数。否则,在实例化对象时可能会引发TypeError异常。

2.重新学习成员函数

在Python中,类中定义的函数称为成员函数(也称为方法)。成员函数可以访问和操作类的属性,并提供了类的行为和功能。

  1. 成员函数的定义:
    成员函数在类的内部通过定义普通函数来实现。在函数定义中,第一个参数通常命名为self,它引用调用该函数的对象实例本身。通过self,成员函数可以访问类的属性和其他成员函数。

  2. 成员函数的调用:
    成员函数通过对象实例来调用,使用点号(.)运算符将对象实例和函数名称连接起来。例如,如果obj是类的对象实例,成员函数的调用形式为obj.function_name()

  3. 访问类的属性:
    成员函数可以访问类的属性,包括实例变量和类变量。可以使用self前缀来访问实例变量,使用类名前缀(或self.__class__)来访问类变量。

  4. 修改类的属性:
    成员函数可以修改类的属性的值。对于实例变量,可以使用self前缀来引用和修改它们。对于类变量,可以使用类名前缀来引用和修改它们。

  5. 返回值:
    成员函数可以返回一个值,可以使用return语句来指定要返回的值。调用成员函数时,可以将其返回值赋给一个变量或直接使用。

  6. 访问方法:
    在Python中,成员函数可以在类的内部和外部进行访问。以下是成员函数的几种访问方法:

①在类内部访问成员函数:
在类的内部,可以直接通过函数名来访问成员函数。在成员函数内部,可以通过self关键字引用当前对象实例,从而访问其他成员函数和类的属性。例如:

class MyClass:
    def member_func(self):
        # 在类内部访问成员函数
        self.another_func()  # 访问其他成员函数
   
    def another_func(self):
        # 成员函数的实现
        pass

②在类外部访问成员函数:
在类的外部,可以通过类的对象实例来访问成员函数。使用对象实例和点号(.)运算符来调用成员函数。例如:

obj = MyClass()     # 创建类的对象实例
obj.member_func()   # 在类外部访问成员函数

3.重新学习成员变量

成员变量(也称为实例变量)是类中定义的变量,用于存储对象的状态和数据。成员变量可以在类的任何成员函数内部以及类的外部进行访问。

  1. 在类内部访问成员变量:
    在类的内部,可以通过self关键字引用当前对象实例,从而访问成员变量。使用点号(.)运算符将self和成员变量名称连接起来即可。
  2. 在类外部访问成员变量:
    在类的外部,可以通过对象实例来访问成员变量。使用对象实例和点号(.)运算符来引用成员变量。
  3. 类变量的访问:
    类变量是属于类而不是特定对象实例的变量,可以通过类名直接访问。使用类名和点号(.)运算符来引用类变量。
class MyClass:
    def __init__(self):
        # 在类内部访问成员变量
        self.my_variable = 10

    def another_func(self):
        # 在成员函数内部访问成员变量
        print(self.my_variable)

obj = MyClass()       # 创建类的对象实例
obj.another_func()   # 10
obj.my_variable = 20  # 在类外部访问和修改成员变量
obj.another_func()   # 20
print(f"{obj.my_variable}")   # 20

class MyClass:
    class_variable = 30

print(MyClass.class_variable)  # 在类外部访问类变量

需要注意的是,如果在类的方法中使用了与成员变量同名的局部变量,局部变量将会覆盖成员变量。为了避免混淆,建议在类内部使用self前缀来引用成员变量,以明确表示使用的是成员变量而不是局部变量。

《》第三部分:析构函数

1.析构函数讲解

析构函数(Destructor)是在对象被销毁(内存被释放)时自动调用的特殊方法。它用于执行一些清理操作,例如释放资源、关闭文件等。在Python中,析构函数被称为__del__方法。

以下是有关析构函数的知识点:

  1. 析构函数的命名为__del__,它位于类中,并在对象内存被释放时自动调用。
  2. 析构函数没有参数,除了self(指向当前对象的引用)。
  3. 析构函数通常用于释放对象所占用的资源,例如关闭文件、释放网络连接等。
  4. 当对象的所有引用都被删除或超出作用域时,析构函数被自动调用。
  5. 可以使用del关键字手动删除对象的内存,从而触发析构函数的调用。
  6. 析构函数的调用顺序与对象的创建顺序相反。即最后创建的对象的析构函数最先被调用。

下面是一个简单的示例代码,演示了如何定义和使用析构函数:

class MyClass:
    def __init__(self, name):
        self.name = name

    def __del__(self):
        print(f"Destructor called for {self.name}")

# 创建对象
obj1 = MyClass("Object 1")
obj2 = MyClass("Object 2")

# 删除对象引用
del obj1
del obj2

# 输出:
# Destructor called for Object 2
# Destructor called for Object 1

在上面的示例中,MyClass类具有一个析构函数__del__,它在对象被销毁时打印一条消息。创建了两个对象obj1obj2,然后手动删除了这些对象的引用。当对象被删除时,析构函数被自动调用。当然当程序运行完成时也会调用析构函数,我们可以通过del来控制释放的先后。

2.内存知识:对象=对象

下面来看这一段代码:

class Ab:
    def __init__(self):
        self.it="调用了printf()"
    def __del__(self):
        print("内存释放")
    def printf(self):
        print(f"{self.it}")
a=Ab()
b=a
del b
a.printf()

我们可以看到delprintf()方法之前,那么是不是先输出"内存释放"再输出"调用了printf()"呢?我们回到上面的知识点第四点和第五点,是要释放对象的内存的时候才会调用析构函数,而对象B=对象A实际上是A和B对象共用一段内存。
在这段代码中,a和b是两个变量,但它们指向的是同一个对象即引用的是同一段内存。当你调用del b时,它会删除变量b,也就是这个名字,但不会立即释放对象的内存,因为对象仍然有一个引用a在使用它。这也就是上面第四点的知识。

《》第四部分:公有、私有、保护属性和方法

公有、私有和保护属性和方法的使用可以帮助开发人员控制对类成员的访问权限,提高代码的可维护性和安全性。通过隐藏实现细节和限制访问,可以减少意外修改和不必要的依赖,并提供更好的封装性和抽象性。学过C++的人会着急地学python类地公有私有和保护,其实在python中私有和保护的使用频率不大,而且还有挺多不同。

1.公有属性和方法

公有属性和方法是指在类中以常规命名方式定义的成员,可以被类的实例、类的方法以及类外的代码访问,上面的内容全都是公有属性和方法。下面是相关知识点的解释和示例代码:

  1. 公有属性(Public Attributes):

    • 定义:在类中使用普通的命名方式定义的属性。
    • 访问:可以通过类的实例、类的方法以及类外的代码直接访问。

    示例代码:

    class MyClass:
        def __init__(self):
            self.attribute = 10  # 公有属性
    
    obj = MyClass()
    print(obj.attribute)  # 通过实例访问公有属性
    
  2. 公有方法(Public Methods):

    • 定义:在类中使用普通的命名方式定义的方法。
    • 访问:可以通过类的实例、类的方法以及类外的代码直接调用。

    示例代码:

    class MyClass:
        def public_method(self):
            print("This is a public method")
    
    obj = MyClass()
    obj.public_method()  # 通过实例调用公有方法
    

通过上述示例代码,可以看到类中的公有属性和方法可以被类的实例直接访问和调用。同时,也可以在类外部通过实例访问公有属性和调用公有方法。公有属性和方法并没有严格的访问控制限制,因此可以在任何地方都可以直接访问和调用。这也意味着需要在编码过程中保持良好的封装性和适当的访问约定,以便维护代码的可维护性和可读性。

2.私有属性和方法

私有属性和方法是以双下划线(__)开头但没有以双下划线结尾的命名方式定义的成员。它们被认为是类的内部实现细节,建议不直接从外部访问。然而,在Python中,私有成员实际上只是进行了一种名称修饰,被称为名称修饰(name mangling),通过在名称前添加一个下划线和类名来实现。这样的命名方式提醒用户将其视为私有成员,但仍然可以从外部访问。以下是相关知识点的解释和示例代码:

  1. 私有属性(Private Attributes):

    • 定义:以双下划线(__)开头但没有以双下划线结尾的命名方式定义的属性。
    • 访问:私有属性在类的内部可以直接访问,但在类外部访问时需要通过名称修饰的方式进行访问,即 _类名__属性名

    示例代码:

    class MyClass:
        def __init__(self):
            self.__attribute = 10  # 私有属性
    
    obj = MyClass()
    # print(obj.__attribute)  # 错误!无法直接访问私有属性
    print(obj._MyClass__attribute)  # 通过名称修饰的方式访问私有属性
    
  2. 私有方法(Private Methods):

    • 定义:以双下划线(__)开头但没有以双下划线结尾的命名方式定义的方法。
    • 访问:私有方法在类的内部可以直接调用,但在类外部调用时需要通过名称修饰的方式进行调用,即 _类名__方法名()

    示例代码:

    class MyClass:
        def __private_method(self):
            print("This is a private method")
    
    obj = MyClass()
    # obj.__private_method()  # 错误!无法直接调用私有方法
    obj._MyClass__private_method()  # 通过名称修饰的方式调用私有方法
    

需要注意的是,尽管通过名称修饰的方式可以从外部访问和调用私有属性和方法,但这样做是不被推荐的,因为它违背了私有成员的意图和设计目的。私有成员被认为是类的内部实现细节,应该尽量避免直接从外部进行访问,以维持封装性和数据的一致性。

3.保护属性和方法

以下是关于保护属性和方法的全部知识点,以及相应的代码示例:

  1. 保护属性和方法以单下划线(_)开头的命名方式定义,如_attribute_method()
  2. 它们被视为内部使用或受保护的成员,意味着它们不应该被类外部直接访问。
  3. 在类的实例、类的方法以及类外的代码中,都可以访问保护属性和方法。
  4. 保护属性和方法不会受到名称修饰(name mangling)的影响,它们的名称保持不变,不添加类名作为前缀。

以下是一个简单的代码示例来说明保护属性和方法的使用:

class MyClass:
    def __init__(self):
        self._protected_attribute = 42   # 保护属性

    def _protected_method(self):        # 保护方法
        print("This is a protected method.")

    def public_method(self):
        self._protected_method()        # 在类的方法中访问保护方法
        print("This is a public method accessing a protected attribute:", self._protected_attribute)


# 在类外部访问保护属性和方法
my_obj = MyClass()
print("Accessing protected attribute from outside the class:", my_obj._protected_attribute)
my_obj._protected_method()

# 在类的方法中访问保护属性和方法
my_obj.public_method()

在上面的示例中,_protected_attribute_protected_method()都被定义为保护属性和方法。在类外部,可以通过实例对象的名称直接访问保护属性和方法。在类的方法内部,也可以直接访问保护属性和方法。请注意,按照约定,我们应该避免直接访问保护属性和方法。

4.总结

在使用方法上,公有属性和方法、私有属性和方法以及保护属性和方法之间存在一些差别。以下是它们的一些特点:

  1. 公有属性和方法:

    • 可以通过类的实例、类的方法以及类外的代码直接访问。
    • 通常用于提供公共的接口和功能。
    • 命名方式常规,不以双下划线或单下划线开头。
  2. 私有属性和方法:

    • 以双下划线(__)开头,但没有以双下划线结尾。
    • 被认为是类的内部实现细节,建议不直接访问。
    • 在名称上进行了一种名称修饰(name mangling),使其在类外部更难以直接访问。
    • 可以通过类的实例、类的方法以及类外的代码间接访问,但不建议直接访问。
    • 常用于实现类的内部功能或辅助方法。
  3. 保护属性和方法:

    • 以单下划线(_)开头。
    • 被视为内部使用或受保护的成员,不建议直接从类外部访问。
    • 不进行名称修饰,可以通过类的实例、类的方法以及类外的代码直接访问。
    • 常用于限制对特定成员的直接访问,以维护封装性。

需要注意的是,这些属性和方法的差异主要体现在命名和约定上,并没有强制的访问控制机制。在使用这些属性和方法时,开发者应当遵循相应的命名约定,以维护良好的代码风格和封装性,避免直接访问私有成员或保护成员。

《》第五部分:继承

继承是面向对象编程中的一个重要概念,它允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。继承的主要用途是代码复用和建立类之间的层次关系。

下面是继承的一些主要用途:

  1. 代码复用:通过继承,子类可以直接获得父类的属性和方法,而无需重复编写相同的代码。这提高了代码的可维护性和可扩展性,因为当需要修改或添加功能时,只需要在父类中进行修改,而不需要修改所有的子类。

  2. 扩展功能:子类可以通过继承来扩展或修改父类的功能。子类可以添加新的属性和方法,也可以重写父类的方法以改变其行为。这样可以根据具体的需求在父类的基础上进行定制,从而实现更多的功能。

  3. 多态性:继承是实现多态性的基础之一。多态性是指同一个方法可以根据对象的不同类型表现出不同的行为。通过继承,可以定义一个通用的方法在父类中,然后在子类中根据具体的需求进行重写,从而实现多态的效果。

  4. 维护和管理:继承可以帮助我们更好地组织和管理代码。通过将相关的类组织成一个继承层次结构,可以更清晰地表达类之间的关系,使代码更易于理解和维护。
    当涉及到类继承时,Python中的对象可以通过继承从另一个类(称为基类或父类)获得属性和方法。这种机制允许创建具有相似功能但又有所不同的类(称为派生类或子类)。以下是有关Python类继承的关键知识点:

1.定义基类

定义一个基类,它包含一些共享的属性和方法。基类是用于派生其他类的模板。

class BaseClass:
    def __init__(self, x):
        self.x = x

    def base_method(self):
        print("Base method called.")

    def shared_method(self):
        print("Shared method called.")

2.定义派生类

使用基类作为参数,定义派生类。派生类继承了基类的属性和方法,并可以添加自己的属性和方法。

class DerivedClass(BaseClass):
    def __init__(self, x, y):
        super().__init__(x)
        self.y = y

    def derived_method(self):
        print("Derived method called.")

3.创建对象

使用派生类创建对象。对象可以访问派生类和基类中的方法和属性。

obj = DerivedClass(10, 20)
obj.derived_method()  # 调用派生类方法
obj.base_method()     # 调用基类方法

4.方法重写

派生类可以重写(覆盖)基类的方法,以满足自己的需求。通过在派生类中重新定义具有相同名称的方法来实现重写。

class DerivedClass(BaseClass):
    # ...

    def shared_method(self):
        print("Derived class override.")

5.super()函数

用于在派生类中调用基类的方法。通过使用super()函数,可以在派生类中访问基类中被重写的方法。

class DerivedClass(BaseClass):
    # ...

    def shared_method(self):
        super().shared_method()  # 调用基类的方法
        print("Derived class override.")

6.多继承

Python允许一个类从多个基类继承属性和方法。在类定义中列出多个基类,以逗号分隔。

class DerivedClass(BaseClass1, BaseClass2):
    # ...

继承实战

本节运用了以上的知识点(除了多继承)写了一个继承的代码,这里不做解释

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

    def eat(self):
        print(f"{self.name} is eating.")

    def sleep(self):
        print(f"{self.name} is sleeping.")


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

    def study(self):
        print(f"{self.name} is studying in grade {self.grade}.")

    def eat(self):
        print(f"{self.name} is eating snacks.")  # 重写基类的eat()方法


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

    def teach(self):
        print(f"{self.name} is teaching {self.subject}.")

    def eat(self):
        print(f"{self.name} is having lunch.")  # 重写基类的eat()方法


# 创建一个学生的对象并调用方法
student = Student("Alice", 15, 10)
student.eat()      # 输出: Alice is eating snacks. (调用重写后的方法)
student.sleep()    # 输出: Alice is sleeping.
student.study()    # 输出: Alice is studying in grade 10.

# 创建一个教师的对象并调用方法
teacher = Teacher("Mr. Smith", 35, "Math")
teacher.eat()      # 输出: Mr. Smith is having lunch. (调用重写后的方法)
teacher.sleep()    # 输出: Mr. Smith is sleeping.
teacher.teach()    # 输出: Mr. Smith is teaching Math.

“学生"类和"教师"类分别重写了基类"人"的eat()方法。“学生"类重写后的方法打印"eating snacks”,而"教师"类重写后的方法打印"having lunch”。通过创建相应的对象并调用eat()方法,我们可以看到不同类的重写方法被正确地调用。
这个例子展示了类继承中重写方法的概念,允许派生类根据自己的需求对基类方法进行个性化定制。

《》第六部分:多态性

多态性(Polymorphism)是面向对象编程的一个重要概念,它允许不同的对象对相同的消息(方法调用)作出不同的响应。这意味着可以通过相同的接口调用不同的对象的方法,从而实现灵活而可扩展的代码设计。在Python中,多态性是通过动态绑定和方法重写来实现的。当一个对象被调用时,解释器会根据对象的实际类型来确定应该调用哪个方法。下面是关于Python多态性的一些知识点、代码示例和过程原理分析:

1.继承和方法重写

多态性通常与继承和方法重写一起使用。子类可以继承父类的方法,并且在需要时可以重写这些方法以实现自己的行为。通过方法重写,不同的子类可以为相同的方法提供不同的实现。

代码示例:

class Animal:
    def make_sound(self):
        pass

class Dog(Animal):
    def make_sound(self):
        print("Woof!")

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

animals = [Dog(), Cat()]
for animal in animals:
    animal.make_sound()

输出结果:

Woof!
Meow!

过程原理分析:

  • 定义了一个基类 Animal,其中有一个空的方法 make_sound()。
  • 定义了两个子类 Dog 和 Cat,它们分别重写了 make_sound() 方法,并提供了不同的实现。
  • 创建了一个包含 Dog 和 Cat 对象的列表 animals。
  • 使用 for 循环遍历列表 animals,对每个 animal 调用 make_sound() 方法。
  • 运行时会根据对象的实际类型来确定应该调用哪个方法,从而产生了不同的输出。

2.鸭子类型(Duck Typing)

Python中还存在另一种形式的多态性,称为鸭子类型。鸭子类型关注的是对象的行为,而不是对象的类型。只要对象具有特定的方法或属性,它就可以被视为具有某种类型,即使它并没有显式地继承自该类型。

代码示例:

class Duck:
    def quack(self):
        print("Quack!")

class Person:
    def quack(self):
        print("I'm quacking like a duck!")

def make_quack(duck_like):
    duck_like.quack()

duck = Duck()
person = Person()

make_quack(duck)     # 输出: Quack!
make_quack(person)   # 输出: I'm quacking like a duck!

过程原理分析:

  • 定义了一个 Duck 类和一个 Person 类,它们都有一个 quack() 方法。
  • 定义了一个函数 make_quack(),它接受一个 duck_like 参数,并调用其 quack() 方法。
  • 创建了一个 Duck 对象 duck 和一个 Person 对象 person。
  • 分别将 duck 和 person 对象作为参数传递给 make_quack() 函数。
  • 尽管 duck 和 person 类型不同,但它们都具有 quack() 方法,因此可以被视为具有 “鸭子” 的行为,make_quack() 函数可以正常调用。

注意事项:

  • 多态性是面向对象编程中的一种高级特性,合理使用可以提高代码的灵活性和可扩展性。
  • 在使用多态性时,需要确保所有相关的类都遵循共同的接口或基类,以便方法调用可以统一处理。
  • 多态性的运行时决定性意味着需要注意确保对象的实际类型与预期一致,否则可能会导致意外的结果。
  • 在设计和编写代码时,需要仔细考虑多态性的应用场景,并选择合适的抽象和继承关系来实现所需的行为多样性。

《》第七部分:静态

在Python中,我们可以使用静态方法和静态属性来定义与类相关但与实例无关的方法和属性。静态方法是类的一部分,而不是实例的一部分。静态属性是与类关联的属性,而不是与实例关联的属性。

1.静态方法和静态属性

静态方法的知识点:

  1. 静态方法使用@staticmethod装饰器进行定义,该装饰器将方法标记为静态方法。
  2. 静态方法不需要传入实例作为第一个参数(通常是self)。它们与类关联,而不是与实例关联。
  3. 静态方法可以通过类或实例调用。
  4. 静态方法不能访问实例属性,也不能修改实例属性。

静态属性的知识点:

  1. 静态属性是与类关联的属性,而不是与实例关联的属性。
  2. 静态属性通常在类定义中直接声明,不需要使用装饰器进行修饰。
  3. 静态属性可以通过类或实例进行访问。
  4. 静态属性是类级别的,所有的实例共享相同的静态属性值。

下面是代码示例:

class MyClass:
    static_property = "Static Property"  # 静态属性

    def __init__(self, value):
        self.value = value

    @staticmethod
    def static_method():
        print("This is a static method.")

    def instance_method(self):
        print("This is an instance method.")

# 访问静态属性
print(MyClass.static_property)  # 输出: Static Property

# 实例化对象
obj = MyClass(10)

# 通过类调用静态方法
MyClass.static_method()  # 输出: This is a static method.

# 通过实例调用静态方法
obj.static_method()  # 输出: This is a static method.

# 通过实例调用实例方法
obj.instance_method()  # 输出: This is an instance method.

# 静态方法无法访问实例属性
# obj.static_method()  # 报错: TypeError: static_method() takes 0 positional arguments but 1 was given

在上面的示例中,static_property是一个静态属性,可以通过类或实例进行访问。static_method()是一个静态方法,可以通过类或实例调用。注意,尽管我们可以通过实例调用静态方法,但它们无法访问实例属性。

2.非静态成员函数访问静态方法和属性

当非静态成员函数(实例方法)需要访问静态方法和属性时,可以通过类名或实例来访问它们。下面是相关的知识点和代码示例:

  1. 非静态成员函数可以通过类名访问静态方法和属性。
  2. 非静态成员函数可以通过实例访问静态方法和属性。

以下是代码示例:

class MyClass:
    static_property = "Static Property"

    def __init__(self, value):
        self.value = value

    @staticmethod
    def static_method():
        print("This is a static method.")

    def instance_method(self):
        print("This is an instance method.")
        print("Accessing static property:", MyClass.static_property)
        MyClass.static_method()

# 实例化对象
obj = MyClass(10)

# 通过实例调用实例方法,访问静态方法和属性
obj.instance_method()
# 输出:
# This is an instance method.
# Accessing static property: Static Property
# This is a static method.

# 通过类名调用非静态成员函数,访问静态方法和属性
MyClass.instance_method(obj)
# 输出:
# This is an instance method.
# Accessing static property: Static Property
# This is a static method.

在上面的示例中,instance_method()是一个非静态成员函数(实例方法),它通过类名和实例分别访问了静态属性和静态方法。在方法内部,我们使用MyClass.static_property来访问静态属性,并使用MyClass.static_method()来调用静态方法。通过实例调用实例方法时,也可以使用self.static_propertyself.static_method()来访问静态属性和静态方法。

3.静态属性的修改

在Python中,静态属性的值可以通过类名直接修改。下面是关于修改静态属性的知识点和代码示例:

  1. 静态属性可以通过类名进行修改,使用赋值操作符(=)来更新其值。
  2. 通过类名修改静态属性后,所有实例共享相同的更新后的值。

以下是代码示例:

class MyClass:
    static_property = "Original Value"

    @staticmethod
    def static_method():
        print("Accessing static property:", MyClass.static_property)

# 访问原始静态属性值
print("Original static property value:", MyClass.static_property)  # 输出: Original Value

# 修改静态属性的值
MyClass.static_property = "Updated Value"

# 通过类名调用静态方法,打印更新后的静态属性值
MyClass.static_method()  # 输出: Accessing static property: Updated Value

# 实例化两个对象
obj1 = MyClass()
obj2 = MyClass()

# 通过实例访问静态属性,打印更新后的静态属性值
print("Instance 1 static property value:", obj1.static_property)  # 输出: Updated Value
print("Instance 2 static property value:", obj2.static_property)  # 输出: Updated Value

在上面的示例中,我们首先访问了原始的静态属性值,并打印出该值。然后,通过类名修改了静态属性的值,将其更新为"Updated Value"。接着,通过类名调用静态方法,以验证静态属性值的更新。最后,我们实例化了两个对象并通过实例访问静态属性,打印出更新后的静态属性值。可以看到,所有实例共享相同的静态属性值。

《》第八部分:魔术方法

在Python中,类的魔术方法(也称为特殊方法或双下划线方法)是一些以双下划线(__)开头和结尾的方法,它们用于在类中实现特定的行为和操作。这些方法在类的实例化、运算符重载、属性访问等方面起到重要作用。

1.小目录

  1. __init__(self, ...): 这是类的构造方法,用于在创建对象时进行初始化操作。例如,设置对象的属性或执行其他必要的操作。

  2. __str__(self): 当使用内置的str()函数或打印对象时,该方法被调用,返回对象的字符串表示。可以在方法中定义对象的自定义字符串表示形式。

  3. __repr__(self): 当使用内置的repr()函数时,该方法被调用,返回对象的“官方”字符串表示形式。通常应该是一个可以用于重建对象的表达式。

  4. __len__(self): 当使用内置的len()函数获取对象的长度时,该方法被调用。应该返回对象的长度。

  5. __getitem__(self, key): 当使用索引操作符[]获取对象的元素时,该方法被调用。可以用于实现自定义的索引行为。

  6. __setitem__(self, key, value): 当使用索引操作符[]设置对象的元素时,该方法被调用。可以用于实现自定义的赋值行为。

  7. __delitem__(self, key): 当使用del关键字删除对象的元素时,该方法被调用。可以用于实现自定义的删除行为。

  8. __iter__(self): 当对象需要迭代时,该方法被调用。应返回一个迭代器对象。

  9. __next__(self): 当迭代器对象需要返回下一个元素时,该方法被调用。通常与__iter__方法一起使用。

  10. __getattr__(self, name): 当访问对象中不存在的属性时,该方法被调用。可以用于实现动态属性访问或处理未定义的属性。

  11. __setattr__(self, name, value): 当设置对象属性时,该方法被调用。可以用于捕获属性赋值操作并执行额外的逻辑。

  12. __delattr__(self, name): 当删除对象属性时,该方法被调用。可以用于捕获属性删除操作并执行额外的逻辑。

2.相应的代码样例

当然,以下是各个类魔术方法的代码示例:

  1. __init__(self, ...)
class MyClass:
    def __init__(self, name):
        self.name = name

obj = MyClass("John")
print(obj.name)  # 输出: John
  1. __str__(self)
class MyClass:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return f"MyClass object with name: {self.name}"

obj = MyClass("John")
print(obj)  # 输出: MyClass object with name: John
  1. __repr__(self)
class MyClass:
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return f"MyClass('{self.name}')"

obj = MyClass("John")
print(repr(obj))  # 输出: MyClass('John')
  1. __len__(self)
class MyList:
    def __init__(self):
        self.items = []

    def __len__(self):
        return len(self.items)

my_list = MyList()
my_list.items = [1, 2, 3, 4, 5]
print(len(my_list))  # 输出: 5
  1. __getitem__(self, key)
class MyList:
    def __init__(self):
        self.items = []

    def __getitem__(self, index):
        return self.items[index]

my_list = MyList()
my_list.items = [1, 2, 3, 4, 5]
print(my_list[2])  # 输出: 3
  1. __setitem__(self, key, value)
class MyList:
    def __init__(self):
        self.items = []

    def __setitem__(self, index, value):
        self.items[index] = value

my_list = MyList()
my_list.items = [1, 2, 3, 4, 5]
my_list[2] = 10
print(my_list.items)  # 输出: [1, 2, 10, 4, 5]
  1. __delitem__(self, key)
class MyList:
    def __init__(self):
        self.items = []

    def __delitem__(self, index):
        del self.items[index]

my_list = MyList()
my_list.items = [1, 2, 3, 4, 5]
del my_list[2]
print(my_list.items)  # 输出: [1, 2, 4, 5]
  1. __iter__(self)
class MyList:
    def __init__(self):
        self.items = []

    def __iter__(self):
        return iter(self.items)

my_list = MyList()
my_list.items = [1, 2, 3, 4, 5]
for item in my_list:
    print(item)  # 输出: 1 2 3 4 5
  1. __next__(self)
class MyIterator:
    def __init__(self, limit):
        self.limit = limit
        self.current = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.current < self.limit:
            self.current += 1
            return self.current
        else:
            raise StopIteration

my_iterator

 = MyIterator(5)
for num in my_iterator:
    print(num)  # 输出: 1 2 3 4 5
  1. __getattr__(self, name)
class MyClass:
    def __getattr__(self, name):
        return f"Attribute '{name}' doesn't exist"

obj = MyClass()
print(obj.name)  # 输出: Attribute 'name' doesn't exist
  1. __setattr__(self, name, value)
class MyClass:
    def __setattr__(self, name, value):
        print(f"Setting attribute '{name}' to '{value}'")
        super().__setattr__(name, value)

obj = MyClass()
obj.name = "John"  # 输出: Setting attribute 'name' to 'John'
  1. __delattr__(self, name)
class MyClass:
    def __delattr__(self, name):
        print(f"Deleting attribute '{name}'")
        super().__delattr__(name)

obj = MyClass()
obj.name = "John"
del obj.name  # 输出: Deleting attribute 'name'

后记

我是学了C和C++之后过来的,可能在某些词语上的专业性不够好,并且本篇文章查阅了很多的资料包括书本和博客,虽然也修改过详细阐述过但是难免会有一些漏掉的,若有发现请读者指正。当然有其他重要的内容也可以留言,学无止境!

你可能感兴趣的:(python,python,数学建模,开发语言,numpy)