面向对象编程入门
1. 编程范式概述
在编程领域,主要存在两种编程范式:过程式编程和面向对象编程。
1.1 过程式编程
过程式编程就像一份食谱,它以函数和代码块的形式提供一系列步骤,这些步骤按顺序执行以完成任务。在这种编程方式中,程序围绕函数来构建。
1.2 面向对象编程
面向对象编程则提供了一种组织程序的方式,它将属性和行为捆绑到单个对象中。例如,一个对象可以代表一个人,具有姓名、年龄、地址等属性,以及行走、说话、呼吸和跑步等行为;或者代表一封电子邮件,具有收件人列表、主题、正文等属性,以及添加附件和发送等行为。简而言之,面向对象编程是一种对具体的现实事物(如汽车)以及事物之间的关系(如公司和员工、学生和教师等)进行建模的方法。
需要注意的是,Python 是一种多范式编程语言,你可以选择最适合当前问题的范式,在一个程序中混合使用不同的范式,并且随着程序的发展从一种范式切换到另一种范式。
2. 类和实例
2.1 类
类用于创建新的用户自定义数据结构,它包含关于某个事物的任意信息。以动物为例,我们可以创建一个
Animal()
类来跟踪动物的属性,如姓名和年龄。
类只是提供结构,它是一个蓝图,规定了某个事物应该如何定义,但本身并不提供任何实际内容。例如,
Animal()
类可能规定定义一个动物需要姓名和年龄,但它不会具体说明某个特定动物的姓名或年龄是什么。可以将类看作是关于如何定义某个事物的一种概念。
2.2 实例
类是蓝图,而实例是具有实际值的类的副本,它是属于某个特定类的实际对象。例如,一只名叫 Roger 的八岁狗就是
Animal()
类的一个实例。
也可以把类比作一张表格或问卷,它定义了所需的信息。当你填写完表格后,你填写的具体副本就是该类的一个实例,它包含了与你相关的实际信息。你可以填写多份表格来创建多个不同的实例,但如果没有表格作为指导,你就会不知道需要哪些信息。因此,在创建对象的单个实例之前,我们必须先通过定义类来明确所需的信息。
3. 定义类
3.1 基本语法
定义一个类很简单,示例代码如下:
class Dog(object):
pass
这里,我们使用
class
关键字来表明正在创建一个类,然后添加类的名称(使用驼峰命名法,首字母大写),最后在括号中添加要继承的类(关于继承将在后面详细介绍)。
pass
关键字通常用作占位符,允许我们运行这段代码而不抛出错误。
3.2 实例属性
所有类都会创建对象,而所有对象都包含称为属性的特征(在开头段落中称为属性)。我们使用
__init__()
方法来初始化对象的初始属性,为它们赋予默认值(或状态)。这个方法至少需要一个参数以及
self
变量,
self
变量指的是对象本身(例如
Dog
)。示例代码如下:
class Dog(object):
# Initializer / Instance Attributes
def __init__(self, name, age):
self.name = name
self.age = age
在
Dog()
类中,每只狗都有特定的姓名和年龄,这在实际创建不同的狗时显然是很重要的信息。需要注意的是,类只是用于定义狗,而不是实际创建具有特定姓名和年龄的单个狗的实例。
self
变量也是类的一个实例。由于类的实例具有不同的值,我们本可以写成
Dog.name = name
而不是
self.name = name
。但并非所有的狗都有相同的姓名,我们需要能够为不同的实例分配不同的值。因此,需要使用特殊的
self
变量来跟踪每个类的单个实例。
另外,你永远不需要手动调用
__init__()
方法,当你创建一个新的
Dog
实例时,它会自动被调用。
3.3 类属性
实例属性是每个对象特有的,而类属性对于所有实例都是相同的。在
Dog()
类中,我们可以添加一个类属性
species
,示例代码如下:
class Dog(object):
# Class Attribute
species = 'mammal'
# Initializer / Instance Attributes
def __init__(self, name, age):
self.name = name
self.age = age
这样,虽然每只狗都有独特的姓名和年龄,但每只狗都是哺乳动物。
4. 实例化
实例化是创建类的新的、唯一实例的专业术语。以下是一些示例:
>>> class Dog(object):
... pass
...
>>> Dog()
<__main__.Dog object at 0x1004ccc50>
>>> Dog()
<__main__.Dog object at 0x1004ccc90>
>>> a = Dog()
>>> b = Dog()
>>> a == b
False
我们首先定义了一个新的
Dog()
类,然后创建了两只新的狗,分别分配给不同的对象。要创建类的实例,只需使用类名,后面跟上括号。为了证明每个实例实际上是不同的,我们又实例化了两只狗,将它们分别分配给变量,然后测试这些变量是否相等。
下面是一个稍微复杂一些的示例:
class Dog(object):
# Class Attribute
species = 'mammal'
# Initializer / Instance Attributes
def __init__(self, name, age):
self.name = name
self.age = age
# Instantiate the Dog object
philo = Dog("Philo", 5)
mikey = Dog("Mikey", 6)
# Access the instance attributes
print("{} is {} and {} is {}.".format(
philo.name, philo.age, mikey.name, mikey.age))
# Is Philo a mammal?
if philo.species == "mammal":
print("{0} is a {1}!".format(philo.name, philo.species))
将上述代码保存为
dog_class.py
并运行,你会看到如下输出:
Philo is 5 and Mikey is 6.
Philo is a mammal!
在这个例子中,我们创建了
Dog()
类的一个新实例,并将其分配给变量
philo
。然后我们传递了两个参数
"Philo"
和
5
,分别代表这只狗的姓名和年龄。这些属性被传递给
__init__
方法,每当你创建一个新实例时,该方法就会被调用,将姓名和年龄附加到对象上。你可能会疑惑为什么我们不需要传递
self
参数,这是 Python 的特性;当你创建类的新实例时,Python 会自动确定
self
是什么(在这个例子中是
Dog
),并将其传递给
__init__
方法。
4.1 实例化练习
使用相同的
Dog()
类,实例化三只年龄不同的新狗。然后编写一个名为
get_biggest_number()
的函数,该函数接受任意数量的年龄(
*args
)并返回最大的年龄。最后输出最老的狗的年龄,格式如下:
The oldest dog is 7 years old.
将此文件保存为
oldest_dog.py
。
5. 实例方法
实例方法定义在类内部,用于获取实例的内容,也可以用于对对象的属性执行操作。和
__init__
方法一样,实例方法的第一个参数总是
self
。示例代码如下:
class Dog(object):
# Class Attribute
species = 'mammal'
# Initializer / Instance Attributes
def __init__(self, name, age):
self.name = name
self.age = age
# instance method
def description(self):
return "{} is {} years old".format(self.name, self.age)
# instance method
def speak(self, sound):
return "{} says {}".format(self.name, sound)
# Instantiate the Dog object
mikey = Dog("Mikey", 6)
# call our instance methods
print(mikey.description())
print(mikey.speak("Gruff Gruff"))
将上述代码保存为
dog_instance_methods.py
并运行,输出如下:
Mikey is 6 years old
Mikey says Gruff Gruff
在
speak()
方法中,我们定义了狗的行为。你还可以为狗分配其他行为,回顾前面的段落可以看到其他对象的一些示例行为。
5.1 修改属性
你可以根据某些行为更改属性的值,示例如下:
>>> class Email(object):
... is_sent = False
... def send_email(self):
... self.is_sent = True
...
>>> my_email = Email()
>>> my_email.is_sent
False
>>> my_email.send_email()
>>> my_email.is_sent
True
在这个例子中,我们添加了一个发送电子邮件的方法,该方法将
is_sent
变量更新为
True
。
6. 继承
6.1 继承的概念
继承是一个类继承另一个类的属性和方法的过程。新形成的类称为子类,子类所继承的类称为父类。需要注意的是,子类可以覆盖或扩展父类的功能(如属性和行为)。也就是说,子类继承了父类的所有属性和行为,但也可以指定不同的行为。最基本的类是
object
,通常所有其他类都将其作为父类进行继承。
6.2 狗公园示例
假设我们在一个狗公园,有多个
Dog
对象在进行狗的行为,每个对象都有不同的属性。通俗地说,就是有些狗在跑步,有些在伸展,有些只是在看着其他狗。此外,每只狗都有主人给它起的名字,而且每只狗都会随着时间变老。
除了这些,我们还可以通过狗的品种来区分不同的狗,示例代码如下:
>>> class Dog(object):
... def __init__(self, breed):
... self.breed = breed
...
>>> spencer = Dog("German Shepard")
>>> spencer.breed
'German Shepard'
>>> sara = Dog("Boston Terrier")
>>> sara.breed
'Boston Terrier'
不同品种的狗有稍微不同的行为。为了考虑这些差异,我们可以为每个品种创建单独的类,这些类是父类
Dog
的子类。
6.3 子类扩展父类功能
创建一个名为
dog_inheritance.py
的新文件,代码如下:
# Parent class
class Dog(object):
# Class attribute
species = 'mammal'
# Initializer / Instance attributes
def __init__(self, name, age):
self.name = name
self.age = age
# instance method
def description(self):
return "{} is {} years old".format(self.name, self.age)
# instance method
def speak(self, sound):
return "{} says {}".format(self.name, sound)
# child class (inherits from Dog class)
class RussellTerrier(Dog):
def run(self, speed):
return "{} runs {}".format(self.name, speed)
# child class (inherits from Dog class)
class Bulldog(Dog):
def run(self, speed):
return "{} runs {}".format(self.name, speed)
# child classes inherit attributes and
# behaviors from the parent class
jim = Bulldog("Jim", 12)
print(jim.description())
# child classes have specific attributes
# and behaviors as well
print(jim.run("slowly"))
在运行这个程序之前,先大声朗读注释以帮助理解发生了什么,然后尝试预测预期的输出。运行程序后,你应该会看到如下输出:
Jim is 12 years old
Jim runs slowly
虽然我们没有添加任何特殊的属性或方法来区分
RussellTerrier
和
Bulldog
,但由于它们现在是两个不同的类,我们可以为它们定义不同的类属性,以定义它们各自的速度。
6.4 父类和子类的关系判断
可以使用
isinstance()
函数来确定一个实例是否也是某个父类的实例。保存以下代码为
dog_isinstance.py
:
# Parent class
class Dog(object):
# Class attribute
species = 'mammal'
# Initializer / Instance attributes
def __init__(self, name, age):
self.name = name
self.age = age
# instance method
def description(self):
return "{} is {} years old".format(self.name, self.age)
# instance method
def speak(self, sound):
return "{} says {}".format(self.name, sound)
# child class (inherits from Dog() class)
class RussellTerrier(Dog):
def run(self, speed):
return "{} runs {}".format(self.name, speed)
# child class (inherits from Dog() class)
class Bulldog(Dog):
def run(self, speed):
return "{} runs {}".format(self.name, speed)
# child classes inherit attributes and
# behaviors from the parent class
jim = Bulldog("Jim", 12)
print(jim.description())
# child classes have specific attributes
# and behaviors as well
print(jim.run("slowly"))
# is jim an instance of Dog()?
print(isinstance(jim, Dog))
# is julie an instance of Dog()?
julie = Dog("Julie", 100)
print(isinstance(julie, Dog))
# is johnny walker an instance of Bulldog()
johnnywalker = RussellTerrier("Johnny Walker", 4)
print(isinstance(johnnywalker, Bulldog))
# is julie and instance of jim?
print(isinstance(julie, jim))
输出如下:
('Jim', 12)
Jim runs slowly
True
True
False
Traceback (most recent call last):
File "dog_isinstance.py", line 50, in <module>
print isinstance(julie, jim)
TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types
这表明
jim
和
julie
都是
Dog()
类的实例,而
johnnywalker
不是
Bulldog()
类的实例。最后我们测试
julie
是否是
jim
的实例,这是不可能的,因为
jim
是一个类的实例而不是类本身,所以会出现
TypeError
。
6.5 子类覆盖父类功能
子类还可以覆盖父类的属性和行为,示例如下:
>>> class Dog(object):
... species = 'mammal'
...
>>> class SomeBreed(Dog):
... pass
...
>>> class SomeOtherBreed(Dog):
... species = 'reptile'
...
>>> frank = SomeBreed()
>>> frank.species
'mammal'
>>> beans = SomeOtherBreed()
>>> beans.species
'reptile'
SomeBreed()
类从父类继承了
species
属性,而
SomeOtherBreed()
类覆盖了
species
属性,将其设置为
reptile
。
6.6 继承练习
-
创建一个
Pet()类,用于保存狗的实例;这个类与Dog()类完全独立,即Dog()类不继承自Pet()类。然后将三个狗的实例分配给Pet()类。从以下代码开始,将文件保存为pet_class.py,输出应该如下所示:I have 3 dogs. Tom is 6. Mike is 7. Larry is 9. And they're all mammals, of course.
# Parent class
class Dog(object):
# Class attribute
species = 'mammal'
# Initializer / Instance attributes
def __init__(self, name, age):
self.name = name
self.age = age
# instance method
def description(self):
return "{} is {} years old".format(self.name, self.age)
# instance method
def speak(self, sound):
return "{} says {}".format(self.name, sound)
# child class (inherits from Dog() class)
class RussellTerrier(Dog):
def run(self, speed):
return "{} runs {}".format(self.name, speed)
# child class (inherits from Dog() class)
class Bulldog(Dog):
def run(self, speed):
return "{} runs {}".format(self.name, speed)
-
在同一个文件中,为
Dog()类添加一个类属性is_hungry = True。然后添加一个名为eat()的方法,该方法在被调用时将is_hungry的值更改为False。找出喂养每只狗的最佳方法,然后如果所有狗都饿了,输出My dogs are hungry.;如果所有狗都不饿,输出My dogs are not hungry.最终输出应该如下所示:I have 3 dogs. Tom is 6. Mike is 7. Larry is 9. And they're all mammals, of course. My dogs are not hungry. -
接下来,为
Pet()类和Dog()类都添加一个walk()方法,这样当你在Pet()类上调用该方法时,分配给Pet()类的每个狗实例都会walk()。将此保存为dog_walking.py。这稍微有点难度,首先按照speak()方法的方式实现该方法。对于Pet()类中的方法,你需要遍历狗的列表,然后调用该方法本身。输出应该如下所示:
Tom is walking!
Mike is walking!
Larry is walking!
7. 总结
以下是关于面向对象编程的一些关键概念总结:
| 概念 | 描述 |
| ---- | ---- |
| 类 | 用于创建新的用户自定义数据结构,是一个蓝图,规定了事物的定义方式,但不包含实际内容 |
| 实例 | 具有实际值的类的副本,是属于某个特定类的实际对象 |
| 实例属性 | 每个对象特有的属性,通过
__init__
方法初始化 |
| 类属性 | 对于所有实例都相同的属性 |
| 实例方法 | 定义在类内部,用于获取实例内容或对属性执行操作,第一个参数总是
self
|
| 继承 | 一个类继承另一个类的属性和方法,子类可以扩展或覆盖父类的功能 |
下面是一个简单的面向对象编程流程的 mermaid 流程图:
graph TD;
A[定义类] --> B[创建实例];
B --> C[访问属性和方法];
C --> D[修改属性];
A --> E[子类继承父类];
E --> F[子类扩展或覆盖父类功能];
通过学习面向对象编程,我们可以更高效地组织和管理代码,提高代码的可维护性和可扩展性。希望这些内容能帮助你更好地理解和应用面向对象编程。
8. 理解检查作业
8.1 问题解答
以下是对一些面向对象编程相关问题的解答:
1.
什么是类?
类是用于创建新的用户自定义数据结构的蓝图,它规定了某个事物应该如何定义,但本身不包含实际内容。例如,
Dog
类规定了狗应该有姓名和年龄等属性,但不会具体说明某只狗的姓名和年龄是什么。
2.
什么是实例?
实例是具有实际值的类的副本,是属于某个特定类的实际对象。比如,一只名叫 Roger 的八岁狗就是
Dog
类的一个实例。
3.
类和实例之间的关系是什么?
类是创建实例的模板,实例是类的具体实现。类定义了对象的属性和方法,而实例则是这些属性和方法的具体体现,每个实例都有自己的属性值。
4.
Python 中定义新类的语法是什么?
使用
class
关键字,后面跟上类名(采用驼峰命名法,首字母大写),再在括号中指定要继承的类(通常为
object
),示例如下:
class Dog(object):
pass
-
类名的拼写约定是什么?
类名通常使用驼峰命名法,即每个单词的首字母大写,其余字母小写,例如Dog、Animal等。 -
如何实例化一个类,即创建类的实例?
使用类名后面跟上括号的方式来实例化类,例如:
dog = Dog()
如果类的
__init__
方法需要参数,则在括号中传入相应的参数,如:
dog = Dog("Philo", 5)
-
如何访问类实例的属性和行为?
使用点号(.)来访问类实例的属性和方法,例如:
dog = Dog("Philo", 5)
print(dog.name) # 访问属性
print(dog.speak("Woof")) # 调用方法
-
什么是方法?
方法是定义在类内部的函数,用于实现对象的行为。方法可以访问和修改对象的属性,并且可以执行特定的操作。例如,Dog类中的speak方法就是一个实例方法。 -
self的作用是什么?
self是 Python 中一个特殊的参数,它代表类的实例本身。在类的方法中,使用self可以访问和修改实例的属性。当调用实例方法时,Python 会自动将实例作为self参数传递给方法。 -
__init__方法的作用是什么?
__init__方法是一个特殊的实例方法,也称为构造方法。它在创建类的实例时自动调用,用于初始化实例的属性。通过__init__方法,可以为实例的属性赋予初始值。 -
继承如何帮助防止代码重复?
继承允许一个类(子类)继承另一个类(父类)的属性和方法。子类可以复用父类的代码,避免了重复编写相同的代码。例如,如果多个类都有一些共同的属性和方法,可以将这些共同的部分放在一个父类中,然后让其他类继承这个父类,这样就减少了代码的重复。 -
子类可以覆盖父类的属性吗?
可以。子类可以覆盖父类的属性和方法,以实现不同的行为。当子类中定义了与父类相同名称的属性或方法时,子类的属性或方法会覆盖父类的属性或方法。例如,在前面的例子中,SomeOtherBreed类覆盖了Dog类的species属性。
8.2 农场建模作业
在这个作业中,我们要创建一个简化的农场模型。以下是实现步骤:
1.
设计思路
- 我们需要至少四个类:一个父类
Animal
和至少三个继承自
Animal
的子类。
- 每个类应该有一些属性和至少一个方法,以体现动物的行为。
- 利用继承来避免代码重复。
2.
代码实现
# 父类 Animal
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age
def description(self):
return f"{self.name} is {self.age} years old."
def sleep(self):
return f"{self.name} is sleeping."
# 子类 Cow
class Cow(Animal):
def __init__(self, name, age):
super().__init__(name, age)
def moo(self):
return f"{self.name} says Moo!"
# 子类 Chicken
class Chicken(Animal):
def __init__(self, name, age):
super().__init__(name, age)
def cluck(self):
return f"{self.name} says Cluck!"
# 子类 Horse
class Horse(Animal):
def __init__(self, name, age):
super().__init__(name, age)
def neigh(self):
return f"{self.name} says Neigh!"
# 创建动物实例
cow = Cow("Daisy", 3)
chicken = Chicken("Betty", 1)
horse = Horse("Spirit", 5)
# 输出动物信息和行为
print(cow.description())
print(cow.moo())
print(chicken.description())
print(chicken.cluck())
print(horse.description())
print(horse.neigh())
-
代码解释
-
Animal类是父类,包含了所有动物共有的属性(姓名和年龄)和方法(描述和睡觉)。 -
Cow、Chicken和Horse类是子类,它们继承了Animal类的属性和方法,并分别添加了自己独特的方法(moo、cluck和neigh)。 - 通过创建这些类的实例,我们可以输出动物的信息和它们的行为。
-
以下是一个农场动物类继承关系的 mermaid 流程图:
graph TD;
A[Animal] --> B[Cow];
A --> C[Chicken];
A --> D[Horse];
9. 总结与展望
9.1 总结
面向对象编程是一种强大的编程范式,它通过类和对象的概念,将数据和操作封装在一起,提高了代码的可维护性和可扩展性。在本文中,我们学习了类、实例、属性、方法、继承等面向对象编程的核心概念,并通过多个示例和练习加深了对这些概念的理解。
9.2 展望
掌握面向对象编程后,我们可以更好地应对复杂的编程任务。在实际开发中,我们可以根据需求设计合理的类和继承关系,避免代码重复,提高开发效率。同时,面向对象编程也为我们学习更高级的编程技术,如设计模式、框架开发等打下了坚实的基础。希望大家在今后的编程实践中,能够灵活运用面向对象编程的思想,编写出更加优秀的代码。
超级会员免费看
1478

被折叠的 条评论
为什么被折叠?



