欢迎光临
我们一直在努力

别再死记类和对象了!Python面向对象编程其实就这么简单(小白必看)(五)

目录

一. 为什么要有面向对象?

二. 类和对象:图纸与实物的关系

2.1 给对象添加属性

2.2 self到底是什么?

2.3 给对象添加方法

三. 类属性和实例属性

四. 访问限制:有些东西不能随便改

4.1 Python中的私有属性

4.2 那我怎么查看余额?

4.3 那怎么修改余额?

4.4 为什么不直接改?非要绕一圈?

4.5 私有属性真的完全访问不到吗?

4.6 小结

五. 继承:别重复造轮子

5.1 super()是什么?

5.2 为什么需要super()?

5.3 super()就是找爸爸帮忙

5.4 可以不用super()吗?

5.5 super()最常见的使用场景

5.6 小结

六. 多态:同一个命令,不同执行结果

6.1 多态真正厉害的地方

6.2 用生活中的例子理解多态

6.3 为什么需要多态?

6.4 Python中的多态更自由

6.5 小结

七. 获取对象信息:看看对象到底是谁

7.1 type():查看对象类型

7.2 isinstance():判断对象是不是某个类

7.3 dir():查看对象拥有的全部能力

7.4 小结

八. 魔法方法:让对象变得更聪明

8.1 为什么需要魔法方法?

8.2 str():自定义对象输出内容

8.3 len():让对象支持len()

8.4 魔法方法为什么叫魔法方法?

8.5 小结

总结:什么是面向对象?


学 Python 学到这里,很多同学都会遇到一个让人头大的知识点:

面向对象编程(Object Oriented Programming,简称 OOP)

光是名字就已经劝退一半人了。

再看到什么:

  • 类(Class)

  • 对象(Object)

  • 继承(Inheritance)

  • 多态(Polymorphism)

不少人当场进入懵圈状态。

实际上,面向对象并没有想象中那么复杂。

如果用一句话来概括:

面向对象就是用代码描述现实世界中的事物。

今天我们就用大白话把 Python 面向对象编程讲明白。

一. 为什么要有面向对象?

假设现在要记录一个学生的信息:

name = "张三"
age = 18
score = 95

第二个学生:

name2 = "李四"
age2 = 20
score2 = 88

第三个学生:

name3 = "王五"
age3 = 19
score3 = 92

刚开始还好。

如果有100个学生呢?

你的代码会变成大型灾难现场。

所以程序员想到一个办法:

能不能把“学生”这个概念单独抽出来?

于是就有了:

类(Class)

二. 类和对象:图纸与实物的关系

类可以理解成:

图纸

对象可以理解成:

根据图纸生产出来的实物

例如:

class Student:
pass

这里就创建了一个学生类。

然后根据这个类创建对象:

s1 = Student()
s2 = Student()

这时候:

Student

是图纸。

而:

s1
s2

是真正的学生对象。

就像:

汽车设计图

宝马
奔驰
奥迪

图纸不是车。

车是根据图纸造出来的。

2.1 给对象添加属性

现实中的学生肯定有自己的信息:

  • 姓名

  • 年龄

  • 成绩

那么对象也需要保存这些数据。

Python 提供了一个特殊方法:

__init__()

很多新手第一次看到这个名字的时候都会怀疑:

程序员是不是按键盘的时候手抽筋了?

其实它叫:

构造方法

创建对象时自动执行。

例如:

class Student:

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

创建对象:

s1 = Student("张三", 18)

print(s1.name)
print(s1.age)

输出:

张三
18

2.2 self到底是什么?

这是面向对象里最容易把小白绕晕的东西。

其实很简单。

self

表示:

当前对象自己

例如:

s1 = Student("张三",18)

这里:

self

指的就是:

s1

如果:

s2 = Student("李四",20)

那么:

self

指的就是:

s2

你可以把 self 理解成:

“我自己”

就不会觉得奇怪了。

2.3 给对象添加方法

对象不仅有属性。

还可以有行为。

比如学生可以介绍自己:

class Student:

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

def introduce(self):
print("大家好,我叫",self.name)

使用:

s1 = Student("张三")

s1.introduce()

输出:

大家好,我叫 张三

这里的:

introduce()

就是对象的方法。

三. 类属性和实例属性

先看一个例子:

class Student:

school = "清华大学"

这里的:

school

叫做类属性。

所有对象共享。

s1 = Student()
s2 = Student()

print(s1.school)
print(s2.school)

输出:

清华大学
清华大学

而:

self.name

属于实例属性。

每个对象都有自己的值。

s1 = Student("张三")
s2 = Student("李四")

此时:

s1.name

s2.name

完全独立。

简单记:

  • 类属性:大家共用

  • 实例属性:自己独有

四. 访问限制:有些东西不能随便改

先来看一个例子。

假设你正在开发一个银行系统。

每个用户都有自己的余额:

class Bank:

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

创建一个用户:

user = Bank(1000)

print(user.money)

输出:

1000

看起来没什么问题。

但是问题来了。

因为这个属性是公开的,所以任何人都能修改:

user.money = 100000000

print(user.money)

输出:

100000000

看到这里银行经理已经开始心脏疼了。

因为如果用户都能这样改余额:

user.money = 999999999999

那银行第二天就可以宣布破产了。

现实世界中也是一样。

比如:

  • 银行余额
  • 游戏角色等级
  • 考试成绩
  • 员工工资

这些数据都不能让别人随便修改。

所以面向对象提供了一个重要思想:

重要的数据要藏起来。

4.1 Python中的私有属性

在属性前面加两个下划线:

class Bank:

def __init__(self, money):
self.__money = money

这里的:

__money

就叫私有属性。

创建对象:

user = Bank(1000)

如果直接访问:

print(user.__money)

会报错:

AttributeError

因为 Python 会告诉你:

这是对象内部的数据,不允许直接访问。

4.2 那我怎么查看余额?

总不能连自己的钱都不知道吧?

所以通常会提供一个专门的方法:

class Bank:

def __init__(self, money):
self.__money = money

def get_money(self):
return self.__money

调用:

user = Bank(1000)

print(user.get_money())

输出:

1000

这样就能安全地获取余额。

4.3 那怎么修改余额?

同样不能直接改:

user.__money = 5000

而是通过专门的方法:

class Bank:

def __init__(self, money):
self.__money = money

def set_money(self, money):
self.__money = money

调用:

user.set_money(5000)

这样余额就被修改了。

4.4 为什么不直接改?非要绕一圈?

很多小白学到这里都会问:

直接写 user.money = 5000 不香吗?

确实香。

但是有风险。

例如银行规定:

余额不能是负数。

如果直接修改:

user.money = -1000000

程序根本拦不住。

但如果通过:

set_money()

就可以加判断:

class Bank:

def __init__(self, money):
self.__money = money

def set_money(self, money):

if money >= 0:
self.__money = money
else:
print("余额不能小于0")

测试:

user.set_money(-100)

输出:

余额不能小于0

这样数据就安全多了。

4.5 私有属性真的完全访问不到吗?

这里顺便提一句。

很多新手会发现:

print(user._Bank__money)

居然还能访问。

然后觉得:

Python骗我!

其实 Python 的私有属性更像是:

不建议访问,而不是绝对禁止访问。

属于一种程序员之间的默契:

这是我的内部数据

你可以强行看

但最好别看

正常开发中:

get_xxx()
set_xxx()

才是推荐方式。

4.6 小结

访问限制的核心思想其实很简单:

不让外部直接操作重要数据。

而是通过专门的方法进行访问和修改。

这样做的好处是:

  • 数据更安全
  • 更容易检查错误
  • 更符合现实业务逻辑

说白了就是:

不是所有东西都应该让别人直接碰。

就像银行卡密码一样。

你当然知道它存在。

但银行绝不会把它贴在ATM机上。 😏

五. 继承:别重复造轮子

假设有一个动物类:

class Animal:

def eat(self):
print("吃东西")

狗会吃东西:

class Dog(Animal):
pass

猫也会吃东西:

class Cat(Animal):
pass

这样:

dog = Dog()
dog.eat()

就可以直接使用父类的方法。

这就是继承。

继承的核心思想:

能复用就复用。

程序员最讨厌重复代码。

因为复制粘贴一时爽。

后期维护火葬场。

5.1 super()是什么?

学习继承时,经常会看到这样一段代码:

class Animal:

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

然后创建一个狗类:

class Dog(Animal):

def __init__(self, name):
super().__init__(name)

这里最神秘的部分就是:

super()

很多新手第一次看到它的时候都会想:

这是什么超人(Superman)函数吗?

其实它的作用很简单:

帮我们调用父类的方法。

5.2 为什么需要super()?

先看一个例子。

假设动物都有名字。

父类:

class Animal:

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

狗继承动物:

class Dog(Animal):

def __init__(self, name):
pass

创建对象:

dog = Dog("旺财")

看起来没问题。

但是当你访问:

print(dog.name)

就会报错。

因为:

class Dog(Animal):

def __init__(self, name):
pass

重写了父类的 __init__()。

此时程序执行流程变成:

创建Dog对象

执行Dog自己的__init__()

父类__init__()没有执行

name根本没有创建

所以:

dog.name

找不到。

5.3 super()就是找爸爸帮忙

解决方法:

class Dog(Animal):

def __init__(self, name):
super().__init__(name)

再次创建:

dog = Dog("旺财")

执行流程:

创建Dog对象

执行Dog自己的__init__()

super()

执行Animal的__init__()

创建self.name

对象初始化完成

此时:

print(dog.name)

输出:

旺财

正常了。

5.4 可以不用super()吗?

当然可以。

例如:

class Dog(Animal):

def __init__(self, name):
Animal.__init__(self, name)

效果一样。

输出也一样。

很多新手会问:

那我直接写这个不就行了吗?

确实可以。

但是当继承关系复杂之后:

Animal

Dog

GoldenDog

或者:

Animal

Dog

PoliceDog

如果全部写死:

Animal.__init__()

后面维护会越来越麻烦。

而:

super()

会自动找到当前类对应的父类。

代码更灵活。

所以实际开发中几乎都使用:

super()

5.5 super()最常见的使用场景

最常见的情况就是:

子类想增加自己的属性,同时保留父类的属性。

例如:

class Animal:

def __init__(self, name):
self.name = name
class Dog(Animal):

def __init__(self, name, age):

super().__init__(name)

self.age = age

创建对象:

dog = Dog("旺财", 3)

此时:

print(dog.name)
print(dog.age)

输出:

旺财
3

如果不用:

super().__init__(name)

那么:

self.name

根本不会创建。

一个生活中的理解方式

假设父类是:

Animal

负责提供:

名字
年龄
体重

这些基础信息。

而子类:

Dog

除了这些基础信息之外,还想增加:

品种

这时候没必要把父类代码全部复制一遍。

直接:

super()

相当于说:

爸,这部分你先帮我处理。

我再补充自己的内容。

所以:

super().__init__()

你完全可以理解成:

先让父类干活。

子类再接着干活。

5.6 小结

super() 最核心的作用就是:

调用父类的方法。

最常见写法:

super().__init__()

作用:

  • 继承父类初始化逻辑
  • 避免重复代码
  • 让代码更容易维护

对于初学者来说,可以先记住一句话:

super() 就是子类向父类“借能力”。

自己没有的功能,先找父类帮忙处理,然后再完成自己的工作。 😏

六. 多态:同一个命令,不同执行结果

前面我们学过继承。

例如:

class Animal:

def run(self):
print("动物在跑")

狗继承动物:

class Dog(Animal):

def run(self):
print("小狗在跑")

猫继承动物:

class Cat(Animal):

def run(self):
print("小猫在跑")

创建对象:

dog = Dog()
cat = Cat()

执行:

dog.run()
cat.run()

输出:

小狗在跑
小猫在跑

虽然调用的都是:

run()

但不同对象执行出了不同结果。

这就是多态最基本的体现。

6.1 多态真正厉害的地方

刚才的例子其实还不够明显。

因为我们知道:

dog

就是狗。

cat

就是猫。

下面来看一个更经典的例子。

定义一个函数:

def animal_run(animal):
animal.run()

注意这里。

函数根本不关心传进来的是什么。

它只负责:

animal.run()

调用即可。

传入狗对象:

animal_run(Dog())

输出:

小狗在跑

传入猫对象:

animal_run(Cat())

输出:

小猫在跑

你会发现:

同样的函数:

animal_run()

完全不用修改。

仅仅因为传入的对象不同。

结果就不同。

这才是多态最核心的思想。

6.2 用生活中的例子理解多态

假设有这样一个按钮:

播放

你点击按钮:

play()

如果播放的是音乐:

开始播放歌曲

如果播放的是电影:

开始播放电影

如果播放的是游戏:

启动游戏

你按的始终是同一个按钮。

调用的始终是:

play()

但不同对象执行出了不同结果。

这就是多态。

6.3 为什么需要多态?

假设没有多态。

你可能需要写:

if isinstance(animal, Dog):
animal.run()

elif isinstance(animal, Cat):
animal.run()

elif isinstance(animal, Pig):
animal.run()

elif isinstance(animal, Bird):
animal.run()

每增加一种动物。

代码就得修改一次。

程序员看了血压都会升高。

有了多态之后:

def animal_run(animal):
animal.run()

无论以后新增:

Dog
Cat
Pig
Bird
Tiger

甚至:

SuperDog

这个函数都不用改。

因为它只关心一件事:

你有没有 run() 方法。

至于你到底是什么动物。

它不关心。

6.4 Python中的多态更自由

很多编程语言要求:

必须继承同一个父类

才能实现多态。

而 Python 更随意。

例如:

class Car:

def run(self):
print("汽车在跑")

调用:

animal_run(Car())

输出:

汽车在跑

居然也能正常运行。

因为 Python 更看重:

你能不能做这件事。

而不是:

你到底是什么身份。

这种思想也经常被称为:

鸭子类型(Duck Typing)

意思是:

走起来像鸭子
叫起来像鸭子

那我就把你当鸭子。

6.5 小结

多态的核心不是:

子类重写父类方法。

而是:

同一个接口,不同对象表现出不同的行为。

例如:

run()

对于:

Dog
Cat
Car

都可以调用。

但执行结果各不相同。

这就是多态。

简单记一句:

继承解决代码复用,多态解决代码扩展。

当以后增加新功能时,我们往往只需要新增类,而不用修改原来的代码。

这也是面向对象编程最大的魅力之一。

七. 获取对象信息:看看对象到底是谁

写程序的时候,经常会遇到这样一种情况:

这个对象到底是什么类型?

或者:

这个对象有哪些属性和方法?

这时候 Python 提供了一些非常实用的工具。

它们就像对象的身份证和档案袋一样。

能够帮助我们快速了解一个对象的信息。

7.1 type():查看对象类型

例如:

class Student:
pass

s1 = Student()

print(type(s1))

输出:

<class '__main__.Student'>

说明:

s1

是一个 Student 类创建出来的对象。

不仅对象能查看。

数字、字符串、列表也能查看。

print(type(100))
print(type("hello"))
print(type([1,2,3]))

输出:

<class 'int'>
<class 'str'>
<class 'list'>

简单理解:

type()就是查看对象属于什么类型。

相当于查看身份证上的职业信息。

7.2 isinstance():判断对象是不是某个类

有时候我们不仅想知道类型。

还想判断:

这个对象是不是某个类创建出来的?

例如:

class Student:
pass

s1 = Student()

print(isinstance(s1, Student))

输出:

True

如果换成:

print(isinstance(100, Student))

输出:

False

继承场景下尤其有用。

class Animal:
pass

class Dog(Animal):
pass

创建对象:

dog = Dog()

判断:

print(isinstance(dog, Dog))
print(isinstance(dog, Animal))

输出:

True
True

因为:

Dog 本身也是 Animal 的一种。

7.3 dir():查看对象拥有的全部能力

假设你拿到一个对象。

完全不知道里面有什么。

这时候:

print(dir(s1))

就能查看对象所有属性和方法。

输出会很多:

['__class__',
'__dict__',
'__init__',
'__str__',

]

第一次看到可能会觉得:

这是什么天书?

其实不用慌。

它的作用就是告诉你:

这个对象有哪些功能。

很多时候忘记方法名了。

直接:

dir(对象)

看看就知道了。

7.4 小结

这三个工具是最常用的对象信息查看工具:

函数作用
type() 查看对象类型
isinstance() 判断对象是否属于某个类
dir() 查看对象所有属性和方法

以后调试程序的时候会经常用到。

相当于给对象做一次体检。

八. 魔法方法:让对象变得更聪明

前面我们已经接触过:

__init__()

其实 Python 中还有很多类似的方法。

它们都有一个特点:

前后两个下划线

例如:

__init__
__str__
__len__

这种方法通常被称为:

魔法方法(Magic Method)

或者:

特殊方法(Special Method)

它们能够让对象拥有一些特殊能力。

8.1 为什么需要魔法方法?

来看一个例子。

创建学生类:

class Student:

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

创建对象:

s1 = Student("张三")

print(s1)

输出:

<__main__.Student object at 0x123456>

很多小白第一次看到都会想:

这打印的是地址还是乱码?

实际上这是对象在内存中的默认表示方式。

对于人类来说并不友好。

8.2 str():自定义对象输出内容

如果希望打印得更好看一点:

class Student:

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

def __str__(self):
return f"Student:{self.name}"

再次打印:

print(s1)

输出:

Student:张三

是不是舒服多了?

以后只要执行:

print(对象)

Python就会自动调用:

__str__()

方法。

8.3 len():让对象支持len()

我们知道:

len("hello")

输出:

5

那么自己的对象能不能也支持呢?

当然可以。

class Student:

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

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

使用:

s1 = Student("张三")

print(len(s1))

输出:

2

这里 Python 自动调用了:

__len__()

方法。

8.4 魔法方法为什么叫魔法方法?

因为很多时候:

你明明没有主动调用它。

它却自动执行了。

例如:

创建对象:

Student("张三")

自动调用:

__init__()

打印对象:

print(s1)

自动调用:

__str__()

计算长度:

len(s1)

自动调用:

__len__()

看起来就像会魔法一样。

所以大家习惯称它们为:

魔法方法。

8.5 小结

魔法方法本质上就是:

Python预留好的一些特殊方法。

通过重写这些方法,我们可以让自己的对象拥有更多能力。

例如:

魔法方法作用
init() 创建对象时自动执行
str() 打印对象时自动执行
len() 调用len()时自动执行

对于初学者来说,记住最常见的:

__init__()
__str__()

就足够了。

以后随着学习深入,你还会接触更多有趣的魔法方法。

而这些魔法方法,也是 Python 面向对象编程中非常有特色的一部分。 😏

总结:什么是面向对象?

很多教程喜欢把面向对象总结成:

  • 封装

  • 继承

  • 多态

这些没错。

但对于初学者来说,更简单的理解方式是:

用类描述事物,用对象表示具体事物。

例如:

类:学生

对象:
张三
李四
王五

通过属性描述特征:

姓名
年龄
成绩

通过方法描述行为:

学习
考试
自我介绍

这样代码会更加清晰、更容易维护。

当你的程序越来越大时,面向对象的优势就会越来越明显。

毕竟管理代码和管理房间是一个道理。

东西少的时候随便放。

东西多了以后,没有分类迟早会乱成一锅粥。

赞(0)
未经允许不得转载:171主机测评 » 别再死记类和对象了!Python面向对象编程其实就这么简单(小白必看)(五)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址