Published in:2024-10-24 |

类和对象

一、面向过程和面向对象

1
2
3
4
5
6
7
1. 编程: 特定的语法+数据结构+算法组成一段代码告诉计算机如何执行一个任务的过程(程序:数据结构+算法)

2. 编程范式:条条大路通罗马,实现一个任务可以有很多种不同的方式(范式有很多种),不同的编程范式本质上来说就是对各种不同类型的任务采取的不同解决问题的思路

3. 编程范式分类: 两种编程的方式
- 面向过程的编程:C语言
- 面向对象的编程:C++、Java、Python
1、面向过程编程
  • 实现一个任务是自上而下的设计方式,程序从上到下一步步执行,从头到尾来解决问题
  • 获取用户输入的数据、对数据进行运算并做出某种决策、在屏幕显示计算结果
  • 根据业务逻辑从上到下垒代码

函数式编程:在面向过程的编程中,也可以使用函数式编程 – 将某个功能模块的代码封装到一个函数中,之后便可以重复地去调用这个函数。

2、面向对象编程 OOP
  • 对数据和函数进行分类和封装,让开发‘更快更好更强….’ – 更高层次的一种封装
  • 面向对象编程,更抽象、封装性更好

需求(产品经理):计算坐标系中两点间距离

1
2
3
4
5
6
7
8
9
10
# 1.面向过程: 一步步进行,过程化
1. 输入数据
x1,y1 = int(input('x1:')),int(input('y1:'))
x2,y2 = int(input('x2:')),int(input('y2:'))

2. 运算、计算
distance = ((x1-x2)**2+(y1-y2)**2)**0.5

3. 输出结果
print(f'({x1},{y1})和({x2},{y2})之间的距离为:{distance}')
1
2
3
4
5
6
7
8
9
10
11
# 2.函数式编程:模块化、封装化  
def distance(**kwargs):
x1 = kwargs.get('x1')
y1 = kwargs.get('y1')
x2 = kwargs.get('x2')
y2 = kwargs.get('y2')
return ((x1-x2)**2+(y1-y2)**2)**0.5

x1,y1 = int(input('x1:')),int(input('y1:'))
x2,y2 = int(input('x2:')),int(input('y2:'))
print(distance(x1=x1,y1=y1,x2=x2,y2=y2))
1
2
3
4
5
6
7
8
9
10
11
12
13
# 3.面向对象编程:更高级、更抽象的一种模块化和封装

class Point:
def __init__(self,a,b):
self.x = a
self.y = b

def distance(self,p):
return ((self.x-p.x)**2+(self.y-p.y)**2)**0.5

p1 = Point(0,0)
p2 = Point(1,1)
print(p1.distance(p2))

二、类和对象

1
2
3
4
5
1. 面向对象的设计思想是从自然界中来的(从现实世界中获取的),因为在自然界中,类(Class)和实例(Instance)的概念是很自然的

2. Class(类型)是一种抽象的概念,比如我们定义一个Class - Person,是指人这个概念. 人是一个抽象的概念,不能具化。而实例指的就是一个一个的具体的人,比如:张三、李四、王五他们是具体某一个人,他们的类型是“人”。
人是张三、李四、王五的类型
而张三、李四、王五是一个个具体的实例
1、类到底是什么
1
2
3
4
5
6
1. 类是对客观世界中具有相同属性和行为的一组对象的抽象
人(类): 一类对象:张三、李四、王五
人:姓名、年龄、生日(属性).. 吃饭、睡觉(行为)等

类:人、狗、坐标中的点、帐户
对象:张三、旺财、(1,1)、620....
2、对象又是什么
1
2
3
4
5
1. 现实世界中客观存在的任何一个事务都可以看成是一个"对象",或者说,现实世界就是由千千万万个对象组成.  而这些对象是可以进行一个类别的划分.  

2. 对象可以有形的,如一座房子、一个人、一辆车、一个学生等.它也可以是无形,如:银行帐户、一次旅行等

3. 对象指类的一个个具体的实例(存在).
3、类和对象的关系
1
2
# 类和对象的关系总结为:类是对象的抽象类型,而对象则是类的具体实例
人是张三的类型,而张三是人的具体实例

问题:在编程世界中,先有类,还是先有对象

答:先有类,才有对象

三、如何进行面向对象的编程

1、定义类

一个类应该包含两部分:属性(变量)和行为(方法 - 函数)

1
2
3
4
5
6
7
8
9
10
# 定义一个人‘类’ 
class Person: # 类名:大驼峰 HelloWorld
name = 'Mr_lee'
age = '18'

def eat(self):
print('是人就得吃饭')

def sleep(self):
print('人和猪都得睡觉')
2、定义对象
1
2
3
4
5
6
7
8
9
p1 = Person() # p1就是一个对象,此时这个对象就拥有类中所有的属性和方法
p2 = Person() # p2是另外一个对象,当然它也拥有类中所有的属性和方法

print(p1.name) # 获取p1这个对象的名字
print(p2.name) # 获取p2这个对象的名字eat()

p1.eat()
p2.eat()
# 虽然p1和p2都有相同的属性和行为,但它们是独立的个体存在,相互不会有影响

四、类属性和实例属性

1
2
3
1. 在上面的案例中,所有人的姓名和年龄都是一样的(属性的值是一样)

2. 所有对象的所有的属性值都一样(类属性),对于姓名和年龄它是不合理的。
1、类属性
  • 类内部定义的变量,不在任何的方法中
  • 所有的对象都可以共用
  • 可以使用类名.属性名访问,也可以使用对象名.属性名访问
  • 一旦修改了类属性的值,所有的对象去访问时都会变. 为什么?
1
2
3
1. 每一个对象,都会在内存中占用一块空间,对象占用的空间实际上是它的属性占用的空间

2. 如果是类属性,在整个类中只有一块空间,所有的对象都共享这块空间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Student:
teacher_name = 'Mr_lee'

def print_teacher(self):
print(f"我是老师是:{self.teacher_name}")

s1 = Student() # 构造了一个学生对象
s2 = Student() # 又构造了一个学生对象

# 这两个学生对象都会拥有teacher_name,但是在内存空间中不会给s1和s2都开一块空间来存储teacher_name,而是在整个类中,只有一块空间存储着teacher_name,所有的学生对象都去共用这一块空间

print(s1.teacher_name) # Mr_lee
print(s2.teacher_name) # Mr_lee

Student.teacher_name = 'Dr.lee'
print(s1.teacher_name) # Dr.lee
print(s2.teacher_name) # Dr.lee

# s1.teacher_name = 'xxx' 不可这样去修改类属性的值

注意

  • 修改类属性值时,要使用 类名.类属性名 = 值

  • 如果使用对象名.类属性名=值,实际上添加了一个实例属性

2、实例属性

当声明学生对象时,每一个学生都应该拥有自己的姓名、年龄等属性,这些属性在内存中应该独立存在,而不应该被共享。

1
2
3
4
5
6
7
8
class Student:
name = 'Tom' # 类属性
age = 18

s1 = Student()
s2 = Student() # s1和s2的姓名和年龄都会一样

# 当去修改了name和age时,s1和s2的name、age会一起被修改

此时,name和age应该被声明为实例属性。(属于某一个对象,而不是被所有对象共享)

  • 如何声明实例属性
    • 在类中的方法中进行声明,基本形式:self.xx = xx ,如果直接在方法中声明为xx=xx,则这它是普通的局部变量
    • 实例属性的生命周期是和对象的生命周期绑定
    • 某一个对象的实例属性发生了改变,不会影响另外的对象
    • 实例属性要使用** 对象名.属性名 **去访问
1
2
3
4
5
6
7
8
9
10
11
12
13
class Student:
teacher_name = 'Mr_lee' # 类属性

def init_name(self, n):
self.name = n

def output(self):
print(f'我的名字是{self.name},我的老师是{self.teacher_name}')


s1 = Student()
s1.init_name('Tom') # 在使用实例属性之前 一定要先初始化
s1.output()

五、self参数和__init__方法

1、self参数
1
2
3
4
5
1. 类中的实例方法 ,在定义时,都有一个默认的参数self (约定俗成)

2. self是向方法的调用者,当某个对象在调用实例方法时,默认会将对象作为实参传递给方法,作为第一个参数,所以self就是指向对象本身,谁调用该方法self就指向谁

3. s1.init_name('Tom') self指向s1 或 self就是s1
2、__init__方法
1
2
3
4
5
6
7
8
9
10
class Student:
teacher_name = 'Mr_lee' # 类属性

def init_name(self, n):
self.name = n

s1 = Student()
print(s1.name) #直接访问出错'Student' object has no attribute 'name'

# 每次声明完对象后,都需要手动调用init_name方法来初始化实例属性,不方便

期望,当声明完对象时,有一个方法可以自动被调用,而不需要程序员手动调用,在这个方法来完成实例属性的初始化

1
2
3
4
5
6
7
8
9
10
11
12
class Student:
teacher_name = 'Mr_lee' # 类属性

# 该方法会在实例化对象时自动调用,参数是在实例化对象时传递
def __init__(self, n):
self.name = n

s1 = Student('Tom') # 1. 构造了s1对象 2. 初始化了name属性
print(s1.name)

s2 = Student('Jack')
print(s2.name)
  • 作用:初始化实例属性,__init__方法是自动调用

  • __init__方法可以加参数,因此在实例化对象从外界传递数据给实例属性

  • __init__方法不能定义返回值,它应该返回None

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 坐标系中的点
class Point:
def __init__(self,x,y):
self.x = x
self.y = y

def output(self):
print(f'({self.x},{self.y})')

p = Point(0,0) # 注意这里要传递两个位置参数
p.output()

p2 = Point(y=2,x=1)
p2.output() # (1,2)

六、组合类

把一个类的对象,作为另一个类的成员(属性)

1
2
3
4
5
6
7
8
9
10
11
12
# 定义一个圆类
class Circle:
def __init__(self,x,y,r):
self.x = x
self.y = y
self.radius = r

def print_circle(self):
print(self.x,self.y,self.radius)

c = Circle(1,2,3)
c.print_circle()

修改成组合类形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Point:
def __init__(self,x,y):
self.x = x
self.y = y

# p1.distance(p2)
def distance(self,p):
return ((self.x-p.x)**2+(self.y-p.y)**2)**0.5

def output(self):
print(f'({self.x},{self.y})')

class Circle:
def __init__(self,x,y,r):
self.radius = r
self.center = Point(x,y) # center是一个Point类型的对象

def output(self):
#self.center.output() # 调用的是Point类中output方法
print(self.radius,self.center.x,self.center.y)


def distance(self,c):
dis = self.center.distance(c.center)
return dis

c1 = Circle(1,2,3)
c1.output()

print(c1.distance(c2))

七、访问限制

权限:公有、保护、私有

  • 面向对象的三大特性:封装、继承、多态
  • 封装:
    • 将对象的属性和方法进行有机的结合,形成一个整体(包装起来)
    • 将不想暴露在外界的成员进行私有化,外界无法直接访问,只能在类中提供公有的接口(公有方法)来供外界去访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Student:
def __init__(self,n,a):
self.__name = n # self.__名字 = 值 表示私有属性
self.__age = a


def output(self):
print(self.__name,self.__age)


s1 = Student('Mr_lee',18)
s1.output()

print(s1.__name) # AttributeError: 'Student' object has no attribute '__name'
  • 公有:属性名前没有__,在类内部及类外都可以直接通过对象名.属性名访问
  • 保护:_名字
  • 私有(伪私有):属性名前加__名字,此时可以在类内部通过 self.__属性名来访问,但不可以在类外通过对象名来进行访问
    • 语法:双下划线+名字 私有属性
    • 范围:只能在本类中访问
    • 访问:通过get、set等公有方法来访问和修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Student:
def __init__(self,n,a):
self.__name = n
self.__age = a

def get_name(self):
return self.__name

def set_name(self,n):
self.__name = n

def output(self):
print(self.__name,self.__age)


s1 = Student('Mr_lee',18)
s1.output()

print(s1.get_name()) # 通过公有的方法来访问私有属性
s1.set_name('Jack') # 公有方法来修改私有属性

根本原因:在外界访问不到__name私有属性,是因为Python对于__name声明的属性进行了重命名操作。_类名__属性名

1
print(s1._Student__name) # 不建议这么用
Prev:
Next: