文章一览
- [一、面向过程编程 VS 面向对象编程](#一、面向过程编程 VS 面向对象编程)
-
- [1.1 基本概念](#1.1 基本概念)
- [1.2 结构和组织](#1.2 结构和组织)
- [1.3 数据安全](#1.3 数据安全)
- [1.4 代码重用](#1.4 代码重用)
- [1.5 维护性和扩展性](#1.5 维护性和扩展性)
- [1.6 编程范式](#1.6 编程范式)
- [二 、类和对象](#二 、类和对象)
-
- [2.1 创建类](#2.1 创建类)
- [2.2 init 函数](#2.2 init 函数)
-
- [init() 方法小结:](#init() 方法小结:)
- [2.3 类继承](#2.3 类继承)
-
- [2.3.1 继承概念](#2.3.1 继承概念)
- [2.3.2 super() 方法的使用](#2.3.2 super() 方法的使用)
- [2.4 object 类](#2.4 object 类)
一、面向过程编程 VS 面向对象编程
面向过程编程(Procedural Programming)和面向对象编程(Object-Oriented Programming, OOP)是两种不同的软件设计方法。各自有不同的特点和适用场景。
1.1 基本概念
- 面向过程编程:这种编程方式强调的是过程和步骤,即程序是一系列操作的集合,这些操作按照一定的顺序执行来完成任务。它通常围绕函数和数据结构来组织代码
- 面向对象编程:这种编程方式则是以对象为中心,对象是数据(属性)和操作数据的方法(行为)的封装体。OOP强调的是对象之间的交互来完成任务
1.2 结构和组织
- 面向过程:程序被组织成一系列函数调用,每个函数负责完成特定的任务。数据和函数是分离的
- 面向对象:程序由多个类组成,每个类可以包含数据成员和成员函数(方法)。类通过继承和组合的方式组织起来,形成一个层次结构
1.3 数据安全
- 面向过程:由于数据和处理数据的函数是分开的,因此保护数据不被意外修改较为困难
- 面向对象:提供了封装机制,可以限制对对象内部状态的访问,从而提高数据的安全性
1.4 代码重用
- 面向过程:代码重用主要是通过函数的形式实现,但这种方式可能需要更多的参数传递,导致接口复杂
- 面向对象:通过继承和多态等特性,可以很容易地扩展和重用现有代码,而不需要修改原来的代码
1.5 维护性和扩展性
- 面向过程:随着程序规模的增长,维护难度会增加,因为过程之间的依赖关系可能会变得非常复杂
- 面向对象:由于对象是独立的单元,因此更容易进行维护和扩展。面向对象的设计鼓励将复杂的系统分解为更小、更易于管理的部分
1.6 编程范式
- 面向过程:更适合解决那些可以通过清晰定义的过程或算法来描述的问题
- 面向对象:更适合于模拟现实世界中的实体及其交互,特别是在构建大型、复杂的应用程序时
面向过程是编年体,面向对象是纪传体。与"编年体"不同,"纪传体"侧重于描述人物生平或特定主题的历史,比如《史记》
面向对象编程关注的是对象之间的关系和交互,而不是单一的流程或步骤。通过对象之间的互动来构建应用程序的功能
每种编程范式都有其优势和局限性,选择哪种方式取决于具体的应用需求和个人偏好。在实际开发中,有时候也会结合使用这两种编程风格,以发挥各自的长处
二 、类和对象
Python 中,一切皆对象!
面向对象编程有 2 个非常重要的概念:类和对象
人以类聚,物以群分。类(Class)是具有相同属性(Attribute)和 行为(Method)事物的统称。类是抽象的
对象(Object) :是类(Class) 的具体实例
类就相当于制造飞机时的图纸,用它来进行创建的飞机就相当于对象
通过类定义数据类型的属性 (数据)和行为(方法),将行为和属性打包在一起
所谓方法 ,就是类里面的函数 ,属性 就是类里的变量
从一个类创建对象时,每个对象会共享 这个类的行为(类中定义的方法)和属性,但对象也会有自己的属性值(不共享)。
在描述一个真实对象(物体)时包括两个方面:
- 行为:可以做什么
- 属性(特征):是什么样的
结论:对象 = 属性(特征或数据)+ 方法(行为)
2.1 创建类
在python中,把具有相同属性和方法的对象归为一个类(class),内容包括:
- 类名:类似于函数名,创建类时需要一个类名
- 属性(Attribute):类里面用于描述所有对象共同特征的变量 或数据
- 方法(Method):类里面的函数 ,用来区别类外面的函数,也称为成员函数。 用来实现某些功能。比如打印出学生的名字和分数
要创建一个类需要使用关键词:class
类结构:
python
class 类名:
def __init__(self,parm1,parm2...)
self.parm1 = parm1
self.parm2 = parm2
......
def 成员函数1(self,parm1,parm2...):
......
def 成员函数2(self,parm1,parm2...):
......
def 成员函数n(self,parm1,parm2...): ......
python
# 创建一个学生类
class Student:
# 定义学生属性,初始化方法
def __init__(self, s_name, s_score):
self.name = s_name
self.score = s_score
# 定义打印学生信息的方法
def show(self):
print(f'Name: {self.name} Score: {self.score}')
和普通函数相比,在类中定义的函数第一个参数 永远是变量 self!调用时,不用传递该参数,只需要传递后面的参数即可,所谓的 self,可以理解为自己,就是对象自身的意思。
为什么每个类实例方法都有一个参数self,它到底有什么作用呢?
想要理解 self 有个最简单的方法,就是把 self 当做实例(对象)的身份证。换句话就是把属性的值绑定在实例对象上。
每个人可以凭借身份证去上大学、坐高铁、住酒店...(方法),而Python中的实例(对象)也可以凭着 self 去调用类的方法
如果前面没有 self ,python认为是给普通的变量赋值,不会把这个值看成是对象的属性值
类是抽象 的,不能直接使用。只有通过创建实例(对象)才能发挥它的功能,每个实例(对象)都是独一无二的,它可以调用类的方法、属性。
**例:**创建一个汽车类
python
#创建汽车类
class Car:
# 移动
def move(self):
print('车在奔跑')
# 拐弯
def left(self):
print('车在左拐')
# 创建一个红旗车对象HQ
HQ = Car()
HQ.move()
HQ.left()
HQ.color = '黑色'
HQ.wheelNum = 4 #轮子数量
print(HQ.color)
print(HQ.wheelNum)
HQ = Car()
,产生了一个Car的实例对象,此时也可以通过对象 HQ 来访问属性或者方法
第一次 使用 HQ.color = '黑色',表示给 HQ 这个对象添加属性 ,如果后面再次出现 HQ.color = xxx 表示对属性进行修改。
有没有办法能够在创建对象的时候,就顺便把对象的属性给设置了呢?
可以在创建实例的时候,把一些认为必须绑定的属性填写进去。
2.2 init 函数
python
#创建汽车类
class Car:
def __init__(self):
self.wheelNum = 4
self.color = '蓝色'
# 移动
def move(self):
print('车在奔跑')
HQ = Car()
HQ.move()
print(HQ.color)
print(HQ.wheelNum)
init (initialize)是类的构造(初始化)函数。用来完成一些默认的设定
注意 :"init"前后分别有两个下划线!!!
init () 这个方法会在创建对象时完成初始化初始化函数,创建 Car 对象后,在无调用 init() 的前提下,HQ 就默认拥有了 2 个属性 color 和 wheelNum,使用点号 . 来访问对象的属性。
既然在创建完对象后 init() 方法已经被默认的执行了,那么能否让对象在调用它的时候能传递一些参数呢?如果可以,那怎样传递呢?
python
#创建汽车类
class Car:
def __init__(self, WheelNum, Color):
self.wheelNum = WheelNum
self.color = Color
# 移动
def move(self):
print('车在奔跑')
# 创建一个红旗车和宝马车对象,HQ和BWM
HQ = Car(4, 'black')
BMW = Car(4, 'white')
print('红旗车颜色:',HQ.color)
print('宝马车颜色:',BMW.color)
init() 方法小结:
-
init()方法第一个参数永远是 self,表示创建的实例本身。默认有 1 个参数时,名字为 self,如果在创建对象时传递了 2 个实参,那么__init__(self)中除了 self 作为第一个形参外还需要 2 个形参
-
在 init() 方法内部,就可以把各种属性绑定到 self,因为 self 就指向创建的实例本身
-
在创建一个对象时默认被调用,不需要手动调用
-
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量 self,并且,调用时,不用传递该参数,python解释器会自动把当前的对象引用传递进去
python
# 创建一个狗类
class Dog:
# 定义狗的属性,初始化方法
def __init__(self, dog_name, dog_age, dog_color):
self.name = dog_name
self.age = dog_age
self.color = dog_color
def speak(self):
print ('汪'*self.age)
dog1 = Dog('jone',2,'白色')
dog1.speak()
创建一个矩形类
python
class rectangle:
def __init__(self,w,h):
self.w,self.h = w,h
def area(self):
return self.w * self.h
def perimeter(self):
return 2*(self.w + self.h)
w = eval(input('输入长方形边宽:'))
h = eval(input('输入长方形边长:'))
rect = rectangle(w,h)
print ('面积:',rect.area())
print ('周长:',rect.perimeter())
类的作用:就是将数据 (属性)和操作数据的函数 (方法)捆绑在一起,当作一个整体使用
2.3 类继承
面向对象的编程带来的主要好处之一是代码的重用,为了避免每个类都从头编写的重复劳动,实现各种重用的方法之一是通过继承机制
2.3.1 继承概念
继承是类与类的一种关系,是一种子类与父类的关系
用代码表示继承,语句是:
class A (B): A
------子类名;B:父类名
A 类继承了 B 类全部的属性和方法
python
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def eat(self):
print('吃海鲜去')
# 定义子类
class Student(Person):
def __init__(self,ID = 202999):
self.id = ID
def study(self):
print('Python语言')
s = Student() # 实例化子类
s.study() # 调用子类的方法
s.eat() # 调用父类方法
-
虽然Student类没有定义eat函数,但它继承了Person类,所以默认有eat函数
-
如果 Student 类有自己定义的 eat 函数,就会调用自己的
-
父类 Person 里构造函数 init 中的属性 name、age,子类 Student 能使用吗?
在创建子类实例的时候,会优先调用子类 的构造函数 init ,导致实例只有ID属性
一个办法是在子类 init 里,把name、age等属性全部写上,但造成重复写代码!
一个优雅的做法是:在子类下面,用 super 这个方法
2.3.2 super() 方法的使用
在面向对象编程中,super() 是一个非常有用的内置函数,它主要用于处理类的继承关系,特别是多重继承的情况
super() 主要用于调用父类的方法,这样可以避免显式地指定父类的名称,从而提高代码的可维护性和灵活性。
python
class Base:
def __init__(self):
print("Base class constructor")
# 继承 Base 类
class Derived (Base):
def __init__(self):
super().__init__() # 调用父类的构造函数
print("Derived class constructor")
# 创建 Derived 类的实例
d = Derived()
python
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def eat(self):
print('吃海鲜去')
# 定义子类
class Student(Person):
def __init__(self,name,age,ID):
super().__init__(name, age) #调用父类的构造函数
self.id = ID
print (f'{name}的年龄是{age},学号是{ID}')
def study(self):
print('Python语言')
s = Student('zhangsan',20,2024111) # 实例化子类
s.study() # 调用子类的方法
s.eat() # 调用父类方法
super 的设计目的是用来解决多重继承时父类的查找问题,所以在单重继承中用不用都没关系,但是使用 super 是一个好的习惯。一般在子类中需要调用父类的方法时才会这么用
python
class Animal:
def __init__(self, name):
self.name = name
def greet(self):
print ('Hello, it is %s.' % self.name)
class Dog(Animal):
def greet(self):
super().greet()
print ('WangWang...')
dog = Dog('dog')
dog.greet()
2.4 object 类
所有类都是 object 类的派生类,因而具有 object 类的各种属性和方法
python
class A:
def func(x):
pass
print (dir(A))
例题:
定义一个学生类,要求:
属性包括学生姓名、学号,以及C语言、Python语言和离散数学三科的成绩
能够设置学生某科目的成绩
能够打印出该学生的所有科目成绩
python
class Student:
def __init__(self, name, student_id):
self.name = name
self.student_id = student_id
self.grades = {'c语言': 0, 'Python语言': 0, '离散数学': 0}
def set_grade(self, course, grade):
for course in self.grades:
self.grades[course] = grade
def print_grades(self):
print(f'学生: {self.name} 学号: {self.student_id} 的成绩为: ')
for course in self.grades:
print(f'{course}: {self.grades[course]} 分')
# 学生类实例化
zhang = Student ("小张","2024000111")
zhang.set_grade ("Python语言", 90)
zhang.set_grade ("离散数学", 85)