跟着我学Python进阶篇:02.面向对象(上)

news2024/12/25 10:31:24

往期文章

跟着我学Python基础篇:01.初露端倪
跟着我学Python基础篇:02.数字与字符串编程
跟着我学Python基础篇:03.选择结构
跟着我学Python基础篇:04.循环
跟着我学Python基础篇:05.函数
跟着我学Python基础篇:06.列表
跟着我学Python基础篇:07.文本
跟着我学Python基础篇:08.集合和字典


跟着我学Python进阶篇:01.试用Python完成一些简单问题


目录

  • 往期文章
  • 1. 面向对象的概念
  • 2. 类和对象
  • 3. 类的定义和使用
    • 3.1 类的定义
    • 3.2 创建对象
    • 3.3 新式类和经典类
  • 4. 属性和方法
    • 4.1 类属性
    • 4.2 对象属性
    • 4.3 构造方法和析构方法
    • 4.4 对象方法
    • 4.5 类方法
    • 4.6 静态方法
    • 4.7 保护对象的私有属性
    • 4.7 更便捷地访问私有属性——使用@property

1. 面向对象的概念

面向对象把解决问题的事物提取为多个对象,而建立对象的目的不是为了完成一个个步骤,而是为了描述某个事物在解决整个问题的过程中所发生的行为。面向对象是一个更大的封装。

2. 类和对象

类是对某类事物的抽象描述,而对象是现实中该类事物的一个个个体,类用于描述多个对象的共同特征,它是对象的定义。对象是类的具体实例,对象是根据类创建的,一个类可以对应多个对象,但一个对象只能对应一个类。

3. 类的定义和使用

3.1 类的定义

class 类名(父类):
    属性名=属性值
    def()方法名:
    方法体

使用class关键字来定义类,class后是类的名称,如果类继承自某一个父类,则将父类写在括号中。
在类的定义体中,使用给属性赋值的方法定义属性,使用def关键字定义类的方法,类的方法定义与函数相似。

以下代码定义了一个dog类:

class Dog(Object):
    legs=4
    def description(self,dog_name):
        print("我的名字是s%" % dog_name)

这段代码定义了一个名为dog的类,并通过赋值方法定义了一个属性,名为legs,然后使用关键字定义了一个方法,名为description。
在类中定义的方法带一个默认的参数self,指向对象本身,这点与函数的定义格式有所不同。

3.2 创建对象

一般创建对象的格式如下:
对象名=类名()
可以看出,Python创建对象与其他开发语言不同,不需要使用常用的关键字new,直接使用类名加括号就可以创建了。
我们使用Dog类创建一个对象my_dog
my_dog=Dog()

3.3 新式类和经典类

在Python2.x及以前的版本,继承自任意自定义类型都成为新式类,能获得所有新式类的特性,相反地,不继承自任何内置类型的自定义类,都称经典类。
在Python3以后,新式类和经典类的区别已经不存在了,在以后的版本里,所有内置类型都继承自object,即所有类都是新式类,object是所有类的父类,如果在定义类型时没有指定父类,那么默认该类型继承自object类。

4. 属性和方法

属性用于描述事物的特征,比如颜色大小数量等,类的属性存储了各类数据,可以通过点语法来访问,类的方法定义了类的行为特征,包括这个类的各种操作。

4.1 类属性

属性包括类属性和对象属性,其中类属性定义在类的内部、方法的外部,它由该类的所有对象共同拥有。
类属性的值可以通过类和对象来访问,但是只能通过类修改,包括直接使用类属性修改,或者在类方法中修改。
比如上面定义的Dog类:

class Dog(object):
    legs=4
one_dog=Dog()
another_dog=Dog()
another_dog.legs=3
print(another_dog.legs)
print(one_dog.legs)
print(Dog.legs)

在这里插入图片描述
在这段代码中,我们首先定义了一个Dog类,它包含一个属性legs,并赋值为4,然后创建两个对象,one_dog和another_dog,代码another_legs.legs=3修改了属性的值,通过anotherdog访问时属性值已经改为3了,但是通过onedog和Dog访问属性值依旧为4,这说明什么呢?

  • 通过onedog和Dog访问属性值依旧为4,说明对象和类都可以访问类属性的值,也说明类属性并没有被anotherdog修改,即对象不能修改类属性的值。
  • another_legs.legs=3的含义是为another_dog对象添加一个新的对象属性,它覆盖了同名的类属性,所以通过another_legs.legs对象访问返回3.

如果使用类名对类属性进行修改,是什么样子呢?

class Dog(object):
    legs=4
one_dog=Dog()
print(one_dog.legs)
print(Dog.legs)
Dog.legs=3
print(Dog.legs)
print(one_dog.legs)

在这里插入图片描述
通过结果我们可以看出,使用类名成功修改了类属性的值。

4.2 对象属性

对象属性是描述对象特征的数据属性,对象属性的设置方法很灵活,可以在类定义的方法里添加,也可以在调用实例的代码里添加。
(1)在方法中添加对象属性
如果在类定义的对象方法里添加对象属性,这个属性之一在调用该方法后才能使用,否则会引起错误。
对象属性只能由对象调用,不能由类调用。
如果不调用方法,则在方法里定义的对象属性都无法访问,而其他对象创建的对象属性归创建它的对象所有,本地对象也无法访问。
(2)在调用时添加对象属性
在调用对象时,也可以动态地给对象添加属性。例如:

class Dog(object):
    legs=4
one_dog=Dog()
one_dog.color="红色"
print("狗狗的颜色是%s"%one_dog.color)

在这里插入图片描述
(3)删除对象属性
对象属性可以删除,这是Python特有的语法。例如:

class Dog(object):
    legs=4
one_dog=Dog()
one_dog.color="红色"
print("狗狗的颜色是%s"%one_dog.color)
del one_dog.color
print("狗狗的颜色是%s"%one_dog.color)

在这里插入图片描述

4.3 构造方法和析构方法

在Python中,构造方法和析构方法是类中的两种特殊方法,用于对象的初始化和资源的释放。构造方法在对象创建时调用,而析构方法在对象被销毁时(通常是在程序结束时或对象被垃圾回收时)调用。以下是它们的基本使用方式:

  1. 构造方法(__init__):

    • 构造方法是在对象创建时自动调用的特殊方法。
    • 它用于执行对象的初始化操作,设置对象的属性等。
    • 构造方法的名称是__init__,它接受self作为第一个参数,后面可以包含其他参数。
    • 示例:
    class MyClass:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    # 创建对象时,构造方法被调用
    obj = MyClass("John", 25)
    
  2. 析构方法(__del__):

    • 析构方法是在对象被销毁时自动调用的特殊方法。
    • 它用于执行一些清理操作,释放对象占用的资源等。
    • 析构方法的名称是__del__,它接受self作为第一个参数。
    • 注意:析构方法的调用时机不是确定的,依赖于Python的垃圾回收机制。
    • 示例:
    class MyClass:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __del__(self):
            print(f"Object {self.name} is being destroyed")
    
    # 创建对象
    obj = MyClass("John", 25)
    
    # 手动删除对象,触发析构方法
    del obj
    

在实际应用中,通常更推荐使用上下文管理器(with语句)来确保资源的正确释放,而不是依赖析构方法。这是因为析构方法的调用时机不是完全可控的,而上下文管理器可以更明确地在需要时进行资源管理。

4.4 对象方法

对象方法是在类中定义的,以关键字self作为第一个参数的方法。对象方法的self参数代表调用这个方法的对象本身(在其他语言中可能使用this)。在方法调用时,可以不用传递这个参数,系统会自动将调用方法的对象作为self参数传入。
在对象方法里可以使用self关键字定义和访问对象属性,同名的对象属性会覆盖类属性。
下面的代码定义了一个类Employee,它有一个对象方法work_for_one_year().

class Employee(object):
    level=3
    def work_for_one_year(self):
        self.level=4
tom=Employee()
print(tom.level)
tom.work_for_one_year()
print(tom.level)

在这里插入图片描述
在方法中定义的对象属性覆盖了同名类属性。

对象可以直接调用对象方法,但是类不可以直接调用对象方法,但是类在调用时传入类或者该类对象也可以调用对象方法。
Python是一个非常灵活的语言。

4.5 类方法

类方法是使用修饰器@classmethod修饰的方法,他的第一个参数代表类本身,按照惯例,以cls作为第一个参数,在调用时,不需要传递这个参数,系统会自动调用它的类当作参数传入。
类方法还有一个用途就是对类属性进行修改,我们知道,在对象方法中无法给类属性赋值,这样的语句会被视为定义同名的对象属性。
下面的代码演示分别使用对象方法和类方法试图修改类属性:

class Apple(object):
    count=0
    def add_one(self):
        self.count=1
    @classmethod
    def add_two(cls):
        cls.count=2

apple=Apple()
apple.add_one()
print(Apple.count)
Apple.add_two()
print(Apple.count)

在这里插入图片描述
从结果可以看出,调用对象方法addone后,类属性的值count没有改变。因为对象代码中self.count=1实际上创建了一个同名的对象属性,并没有修改属性值,而调用addtwo类方法后,类属性的值就被修改为2.

那么谁可以调用类方法呢?Python是一个很灵活的语言,虽然叫做类方法,但是对象和类都可以调用。当对象调用类方法时,系统自动将对象所属的类作为第一个默认参数传入。我们使用一个例子来测试一下:

class ExampleClass(object):
    @classmethod
    def class_method(cls):
        print("---类方法---")
        print(cls)

example=ExampleClass()
example.class_method()
ExampleClass.class_method()

在这里插入图片描述
从这个例子可以看出,对象和类都可以调用类方法,而且从打印结果可知,传入的第一个参数都是类。

4.6 静态方法

静态方法是使用修饰器@staticmethod进行修饰的方法,它不需要传入默认参数,所以与类没有很强的联系。由于没有传入默认的参数,所以在静态方法中不能直接访问类属性和方法,需要通过类名方法名和属性名来访问。

那么对象和类可以调用动态方法吗?我们进行一个测试:

class ExampleClass(object):
    @staticmethod
    def static_method():
        print("---静态方法---")

example=ExampleClass()
example.static_method()
ExampleClass.static_method()

在这里插入图片描述
从测试结果可以看出,类和方法都可以调用静态方法。
至此,我们学习了对象方法,类方法和静态方法,我们通过下表对他们的具体特征进行归纳:

方法第一个参数修饰器类和对象访问
实例方法self对象可以直接访问,类需要传入参数
类方法cls@classmethod都可以直接访问
静态方法@staticmethod都可以直接访问

在Python中,有一种私有机制用于限制对类的属性和方法的直接访问。这通过在属性名或方法名前面添加一个或两个下划线来实现。这种机制被称为名称修饰(Name Mangling)。

  1. 单下划线 _

    • 以单下划线开头的属性或方法被视为“受保护”的,表示它们不应该在类外部直接访问,但并没有强制限制。
    • 这只是一种约定,用于告诉其他程序员“请不要直接访问这个属性或方法”。
    class MyClass:
        def __init__(self):
            self._protected_variable = 42
    
    obj = MyClass()
    print(obj._protected_variable)  # 可以访问,但是被视为受保护的
    
  2. 双下划线 __

    • 以双下划线开头的属性或方法会进行名称修饰,变成 _类名__属性名_类名__方法名 的形式。
    • 这种方式使得属性或方法在类的外部几乎无法直接访问,但在类内部是可以的。
    class MyClass:
        def __init__(self):
            self.__private_variable = 42
    
        def __private_method(self):
            print("This is a private method.")
    
    obj = MyClass()
    # 在类的外部访问双下划线开头的属性和方法会进行名称修饰
    print(obj._MyClass__private_variable)
    obj._MyClass__private_method()
    

请注意,虽然可以通过名称修饰来实现某种程度上的私有性,但在Python中并没有真正的私有性。这是因为在Python中,“我们都是成年人(We are all consenting adults here)”的理念,强制执行私有性可能会破坏灵活性和开发者的创造力。因此,开发者被信任以负责使用和维护这些规定。

4.7 保护对象的私有属性

在开发中,我们有时会不希望对象的私有属性被外界随意访问和修改的情况,我们希望外界对属性的操作在我们的控制范围之内,此时一般的处理方式是:

  • 将属性定义为私有
  • 添加一个公有方法,用于属性操作

现在我们将类Girl修改一下,使用方法来修改它的私有属性_age:

class Girl(object):
    def __init__(self,age):
        self._age=age
    def get_age(self):
        is_stranger=False
        if is_stranger:
            return 0
        else:
            return self._age
    def set_age(self,age):
        if 110>age>0:
            self._age=age
        else:
            print("年龄不正确")
lily=Girl(25)
lily.set_age(18)
print(lily.get_age())

在这里插入图片描述

4.7 更便捷地访问私有属性——使用@property

@property装饰器可以把方法变成属性调用。
是的,@property 装饰器是用于将一个方法变成只读属性的一种方式。它允许你像访问属性一样调用方法,而不需要添加额外的括号。

在一个类中,如果你有一个方法返回某个值,并且你希望在使用该方法时像访问属性一样,而不是使用括号调用方法,可以使用 @property 装饰器。

以下是一个简单的例子:

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

    @property
    def radius(self):
        return self._radius

    @property
    def diameter(self):
        return self._radius * 2

    @property
    def area(self):
        return 3.14 * self._radius ** 2

# 创建一个 Circle 实例
my_circle = Circle(radius=5)

# 使用 @property 装饰器后,可以像访问属性一样访问方法
print(my_circle.radius)   # 输出: 5
print(my_circle.diameter) # 输出: 10
print(my_circle.area)     # 输出: 78.5

在这个例子中,radiusdiameterarea 方法都被用 @property 装饰器修饰,因此可以直接访问,而不需要在后面添加括号。这提高了代码的可读性,并使其更像是访问对象的属性而不是调用方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1372571.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机体系结构----寄存器重命名/Tomasulo算法

前情提要 计分板算法可看我写的博文计算机体系结构----计分板(scoreboard)算法 Tomasulo算法的核心是寄存器重命名(register renaming);通过寄存器重命名,可彻底消除WAR/WAW冲突,计分板算法中…

2024--Django平台开发-Django知识点(五)

day05 django知识点 今日概要: 中间件 【使用】【源码】cookie 【使用】【源码 - Django底层请求本质】session【使用】【源码 - 数据库请求周期中间件】 1.中间件 1.1 使用 编写类,在类型定义:process_request、process_view、process_…

C++重新认知:inline关键字

一、为什么要使用inline关键字 在C中.,为了解决频繁调用小函数造成大量消耗栈空间的问题,引进了inline关键字,inline修饰的函数为内敛函数(频繁大量的使用栈空间会造成栈溢出)。 在这段代码中,每次执行for…

RT-Thread:SPI万能驱动 SFUD 驱动Flash W25Q64,通过 STM32CubeMX 配置 STM32 SPI 驱动

关键词:SFUD,FLASH,W25Q64,W25Q128,STM32F407 说明:RT-Thread 系统 使用 SPI万能驱动 SFUD 驱动 Flash W25Q64,通过 STM32CubeMX 配置 STM32 SPI 驱动。 提示:SFUD添加后的存储位置 1.打开RT-Thread Sett…

【obj To 3DTiles 格式转换】 可以自定义经纬高、属性表等参数 (一)

目录 0 引言1 3DTiles数据2 objTo3DTiles2.1 工具的安装2.1.1 拓展:Node.js 和 npm 2.2 工具的使用2.2.1 输出成瓦片数据2.2.2 输出带有坐标参数的瓦片数据 3 查看3DTiles数据 🙋‍♂️ 作者:海码007📜 专栏:Cesiumfor…

强化学习9——免模型预测算法介绍(蒙特卡洛方法和时步差分方法)

对于大部分情况来说,环境是未知的,也就是说状态转移概率未知,对于这种情况的算法称为免模型预测算法。免模型算法与环境不断交互学习,但是需要大量的运算。 蒙特卡洛方法 蒙特卡罗方法通过重复随机抽选,之后运用统计…

条款21:必须返回对象时,别妄想返回其引用

考虑一个表示有理数的类,其中包含一个计算两个有理数相乘的函数: class Rational { public:Rational(int numerator 0, int denominator 1) :n{ numerator }, d{ denominator }{} private:int n, d; // 分子和分母friend const Rational& operator*(const R…

vue知识-03

购物车案例 要实现的功能&#xff1a; 1、计算商品总价格 2、全选框和取消全选框 3、商品数量的增加和减少 <body> <div id"app"><div class"row"><div class"col-md-6 col-md-offset-3"><h1 class"text-center…

C++模板——(4)C++泛型编程与标准模板库简介

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 勤奋&#xff0c;机会&#xff0c;乐观…

苍穹外卖Day01——解决总结1中存在的问题

前序章节&#xff1a; 苍穹外卖Day01——总结1 解决总结1中存在的问题 1. 什么是JWT2. POJO、Entity、VO、DTO3. Nginx&#xff08;反向代理&#xff09;4. Data注解 1. 什么是JWT JWT&#xff08;JSON Web Token&#xff09;是一种用于在网络应用间传递信息的开放标准&#…

智慧校园电子班牌管理系统源码 Java Android原生

智慧校园电子班牌系统源码是一种智能化的教育管理解决方案&#xff0c;它可以在学校内实现信息共享、教学管理、学生管理、家校互通等各个方面的协调与配合&#xff0c;帮助教师、学生和家长更加高效地开展教学活动、管理学生、协同合作&#xff0c;从而推动学校教育水平的提高…

【高等数学之泰勒公式】

一、从零开始 1.1、泰勒中值定理1 什么是泰勒公式?我们先看看权威解读: 那么我们从古至今到底是如何创造出泰勒公式的呢? 由上图可知&#xff0c;任一无穷小数均可以表示成用一系列数字的求和而得出的结果&#xff0c;我们称之为“无穷算法”。 那么同理我们想对任一曲线来…

Ubuntu启动Xming报错:cannot open display: :1.0

Ubuntu启动Xming报错&#xff1a;cannot open display: :1.0 1、问题描述&#xff1a;2、问题解决&#xff1a;3、实践结果&#xff1a; 叮嘟&#xff01;这里是小啊呜的学习课程资料整理。好记性不如烂笔头&#xff0c;今天也是努力进步的一天。一起加油进阶吧&#xff01; 1、…

Android 事件分发介绍

文章目录 一、目的二、环境三、相关概念3.1 事件分发 四、详细设计4.1应用布局4.1.1 应用布局结构4.1.2 LayoutInspector 4.2 关键View&方法4.2.1 相关View4.2.2 相关方法4.2.3 View与方法关系 4.3 事件分发概念图4.3.1 事件分发类图4.3.2 事件分发模型图 4.4 Activity组件…

C#编程-描述异常

描述异常 异常是在程序执行期间出现的错误。异常情况发生在运算不能正常完成的时候。当程序中出现异常是,系统会抛出错误。错误通过异常处理过程被处理。 例如,System.IO.IOException异常在试图访问非法流对象时抛出。同样,如果分母是0,整数除法运算抛出System.DivideByZ…

02.构建和使用的大型语言模型(LLMs)阶段

我们为什么要建立自己的LLMs?LLM从头开始编码是了解其机制和局限性的绝佳练习。此外,它还为我们提供了必要的知识,可以保留或微调现有的开源LLM架构,以适应我们自己的特定领域的数据集或任务。 研究表明,在建模性能方面,定制(LLMs为特定任务或领域量身定制的)可以胜过…

前端项目构建打包生成Git信息文件

系列文章目录 TypeScript 从入门到进阶专栏 文章目录 系列文章目录前言一、前端项目构建打包生成Git信息文件作用二、步骤1.引入相关的npm包1.1. **fs** 包1.2. **child_process** 包1.3. **os** 包 (非必须 如果你想生成的文件信息中包含当前电脑信息则可用)1.4. **path** 包…

Qt QCheckBox复选按钮控件

文章目录 1 属性和方法1.1 文本1.2 三态1.3 自动排他1.4 信号和槽 2 实例2.1 布局2.2 代码实现 Qt中的复选按钮类是QCheckBox它和单选按钮很相似&#xff0c;单选按钮常用在“多选一”的场景&#xff0c;而复选按钮常用在"多选多"的场景比如喜欢的水果选项中&#xf…

Python从入门到网络爬虫(控制语句详解)

前言 做任何事情都要遵循一定的原则。例如&#xff0c;到图书馆去借书&#xff0c;就需要有借书证&#xff0c;并且借书证不能过期&#xff0c;这两个条件缺一不可。程序设计亦是如此&#xff0c;需要使用流程控制实现与用户的交流&#xff0c;并根据用户需求决定程序“做什么…

特征工程:图像数据不足时的处理办法

在机器学习中&#xff0c;绝大部分模型都需要大量的数据进行训练和学习&#xff08;包括有监督学习和无监督学习&#xff09;&#xff0c;然而在实际应用中经常会遇到训练数据不足的问题。比如图像分类&#xff0c;作为计算机视觉最基本的任务之一&#xff0c;其目标是将每幅图…