在本章中,我们将讨论 Python 中的数据类型和面向对象编程(OOP)。我们将讨论 Python 中的数据类型,包括列表、字典、元组和集合。我们还将讨论 OOP,它的必要性,以及如何为基于 Raspberry Pi 的项目(例如,使用 OOP 控制家用电器)编写 Python 面向对象代码。我们将讨论在 Raspberry Pi Zero 项目中使用 OOP。
在 Python 中,列表是一种数据类型(其文档可在此处获得,https://docs.python.org/3.4/tutorial/datastructures.html# ),可用于按顺序存储元素。
The topics discussed in this chapter can be difficult to grasp unless used in practice. Any example that is represented using this notation: >>>
indicates that it could be tested using the Python interpreter.
列表可以由字符串、对象(本章将详细讨论)或数字等组成。例如,以下是列表的示例:
>>> sequence = [1, 2, 3, 4, 5, 6]
>>> example_list = ['apple', 'orange', 1.0, 2.0, 3]
在前面的一组示例中,sequence
列表由1
和6
之间的数字组成,example_list
列表由字符串、整数和浮点数组成。列表用方括号([]
表示。可以将项目添加到以逗号分隔的列表中:
>>> type(sequence)
<class 'list'>
由于列表是元素的有序序列,因此可以通过使用for
循环遍历列表元素来获取列表元素,如下所示:
for item in sequence:
print("The number is ", item)
输出如下:
The number is 1
The number is 2
The number is 3
The number is 4
The number is 5
The number is 6
因为 Python 的循环可以遍历一系列元素,所以它获取每个元素并将其分配给item
。此项打印在控制台上。
在 Python 中,可以使用dir()
方法检索数据类型的属性。例如,sequence
列表可用的属性可以按如下方式检索:
>>> dir(sequence)
['__add__', '__class__', '__contains__', '__delattr__',
'__delitem__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__getitem__',
'__gt__', '__hash__', '__iadd__', '__imul__', '__init__',
'__iter__', '__le__', '__len__', '__lt__', '__mul__',
'__ne__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__reversed__', '__rmul__', '__setattr__',
'__setitem__', '__sizeof__', '__str__', '__subclasshook__',
'append', 'clear', 'copy', 'count', 'extend', 'index',
'insert', 'pop', 'remove', 'reverse', 'sort']
这些属性允许对列表执行不同的操作。让我们详细讨论每个属性。
可以使用append()
方法添加元素:
>>> sequence.append(7)
>>> sequence
[1, 2, 3, 4, 5, 6, 7]
remove()
方法查找元素的第一个实例(传递了一个参数)并将其从列表中删除。让我们考虑下面的例子:
>>> sequence = [1, 1, 2, 3, 4, 7, 5, 6, 7]
>>> sequence.remove(7)
>>> sequence
[1, 1, 2, 3, 4, 5, 6, 7]
>>> sequence.remove(1)
>>> sequence
[1, 2, 3, 4, 5, 6, 7]
>>> sequence.remove(1)
>>> sequence
[2, 3, 4, 5, 6, 7]
index()
方法返回元素在列表中的位置:
>>> index_list = [1, 2, 3, 4, 5, 6, 7]
>>> index_list.index(5)
4
在本例中,该方法返回元素5
的索引。由于 Python 使用基于零的索引,即索引从 0 开始计数,因此元素5
的索引为4
:
random_list = [2, 2, 4, 5, 5, 5, 6, 7, 7, 8]
>>> random_list.index(5)
3
在本例中,该方法返回元素的第一个实例的位置。元件5
位于第三个位置。
pop()
方法允许从指定位置移除元素并返回:
>>> index_list = [1, 2, 3, 4, 5, 6, 7]
>>> index_list.pop(3)
4
>>> index_list
[1, 2, 3, 5, 6, 7]
在本例中,index_list
列表由1
和7
之间的数字组成。当通过传递索引位置(3)
作为参数弹出第三个元素时,数字4
将从列表中删除并返回。
如果没有为索引位置提供参数,则弹出并返回最后一个元素:
>>> index_list.pop()
7
>>> index_list
[1, 2, 3, 5, 6]
在本例中,最后一个元素(7)
被弹出并返回。
count()
方法返回元素在列表中出现的次数。例如,元素在列表中出现两次:random_list
。
>>> random_list = [2, 9, 8, 4, 3, 2, 1, 7] >>> random_list.count(2) 2
insert()
方法允许在列表中的特定位置添加元素。例如,让我们考虑下面的例子:
>>> day_of_week = ['Monday', 'Tuesday', 'Thursday',
'Friday', 'Saturday']
在列表中,Wednesday
缺失。它需要定位在位置 2 的Tuesday
和Thursday
之间(Python 使用零基索引,即元素的位置/索引被计为 0、1、2 等)。可以使用 insert 添加,如下所示:
>>> day_of_week.insert(2, 'Wednesday')
>>> day_of_week
['Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday', 'Saturday']
在前面的列表中,Sunday
缺失。使用列表的insert
属性将其插入到正确的位置。
可以使用extend()
方法将两个列表组合在一起。day_of_week
和sequence
列表可以组合如下:
>>> day_of_week.extend(sequence)
>>> day_of_week
['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
'Saturday', 1, 2, 3, 4, 5, 6]
列表也可以按如下方式组合:
>>> [1, 2, 3] + [4, 5, 6]
[1, 2, 3, 4, 5, 6]
也可以将列表作为元素添加到另一个列表中:
sequence.insert(6, [1, 2, 3])
>>> sequence
[1, 2, 3, 4, 5, 6, [1, 2, 3]]
可以使用clear()
方法删除列表中的所有元素:
>>> sequence.clear()
>>> sequence
[]
可以使用sort()
方法对列表中的元素进行排序:
random_list = [8, 7, 5, 2, 2, 5, 7, 5, 6, 4]
>>> random_list.sort()
>>> random_list
[2, 2, 4, 5, 5, 5, 6, 7, 7, 8]
当列表由字符串集合组成时,它们将按字母顺序排序:
>>> day_of_week = ['Monday', 'Tuesday', 'Thursday',
'Friday', 'Saturday']
>>> day_of_week.sort()
>>> day_of_week
['Friday', 'Monday', 'Saturday', 'Thursday', 'Tuesday']
reverse()
方法允许颠倒列表元素的顺序:
>>> random_list = [8, 7, 5, 2, 2, 5, 7, 5, 6, 4]
>>> random_list.reverse()
>>> random_list
[4, 6, 5, 7, 5, 2, 2, 5, 7, 8]
copy()
方法允许创建列表的副本:
>>> copy_list = random_list.copy()
>>> copy_list
[4, 6, 5, 7, 5, 2, 2, 5, 7, 8]
可以通过指定list_name[i]
元素的索引位置来访问列表元素。例如,random_list
列表的第 0 个列表元素可以按如下方式访问:
>>> random_list = [4, 6, 5, 7, 5, 2, 2, 5, 7, 8]
>>> random_list[0]4>>> random_list[3]7
可以访问指定索引之间的元素。例如,可以检索索引 2 和 4 之间的所有元素:
>>> random_list[2:5]
[5, 7, 5]
清单的前六个要素可按如下方式查阅:
>>> random_list[:6]
[4, 6, 5, 7, 5, 2]
列表中的元素可以按以下相反顺序打印:
>>> random_list[::-1]
[8, 7, 5, 2, 2, 5, 7, 5, 6, 4]
列表中的第二个元素可以按如下方式提取:
>>> random_list[::2]
[4, 5, 5, 2, 7]
在跳过前两个元素后,还可以在第二个元素之后获取每一个第二个元素:
>>> random_list[2::2]
[5, 5, 2, 7]
可以使用in
关键字检查值是否为列表的成员。例如:
>>> random_list = [2, 1, 0, 8, 3, 1, 10, 9, 5, 4]
在此列表中,我们可以检查编号6
是否为成员:
>>> 6 in random_list
False
>>> 4 in random_list
True
本练习由两部分组成。在第一部分中,我们将回顾如何构建一个包含 10 个介于0
和10
之间的随机数的列表。第二部分是对读者的挑战。执行以下步骤:
random_list
的空列表。可以按如下方式创建空列表: random_list = []
random
模块(https://docs.python.org/3/library/random.html 生成随机数。为了在0
和10
之间生成随机数,我们将使用random
模块中的randint()
方法: random_number = random.randint(0,10)
for
循环重复10
次: for index in range(0,10):
random_number = random.randint(0, 10)
random_list.append(random_number)
print("The items in random_list are ")
print(random_list)
The items in random_list are
[2, 1, 0, 8, 3, 1, 10, 9, 5, 4]
我们讨论了生成随机数列表。下一步是进行用户输入,我们要求用户猜测介于0
和10
之间的数字。如果号码是列表中的一个成员,则会将消息Your guess is correct
打印到屏幕上,否则会打印消息Sorry! Your guess is incorrect
。我们把第二部分留给读者作为一个挑战。开始使用本章提供的可下载的list_generator.py
代码示例。
字典(https://docs.python.org/3.4/tutorial/datastructures.html#dictionaries 是一种数据类型,是键和值对的无序集合。字典中的每个键都有一个关联的值。字典的一个例子是:
>>> my_dict = {1: "Hello", 2: "World"}
>>> my_dict
{1: 'Hello', 2: 'World'}
使用大括号{}
创建字典。在创建时,新成员将以以下格式添加到字典中:key: value
(如上例所示)。在前面的示例中,1
和2
是键,'Hello'
和'World'
是相关值。添加到字典中的每个值都需要有一个关联的键。
字典中的元素没有顺序,即无法按添加顺序检索元素。可以通过迭代键来检索字典的值。让我们考虑下面的例子:
>>> my_dict = {1: "Hello", 2: "World", 3: "I", 4: "am",
5: "excited", 6: "to", 7: "learn", 8: "Python" }
有几种方法可以打印字典的键或值:
>>> for key in my_dict: ...
print(my_dict[value])
... Hello World I
am excited to learn Python
在前面的示例中,我们迭代字典的键,并使用键my_dict[key]
检索值。也可以使用字典提供的values()
方法检索值:
>>> for value in my_dict.values(): ...
print(value) ... Hello World I am excited to learn Python
字典的键可以是整数、字符串或元组。字典的键必须是唯一的,并且是不可变的,即创建后不能修改键。无法创建密钥的副本。如果向现有键添加新值,则最新值将存储在字典中。让我们考虑下面的例子:
>>> my_dict[9] = 'test' >>> my_dict {1: 'Hello', 2: 'World', 3: 'I', 4: 'am', 5: 'excited',
6: 'to', 7: 'learn', 8: 'Python', 9: 'test'}
9
的副本: >>> my_dict[9] = 'programming' >>> my_dict {1: 'Hello', 2: 'World', 3: 'I', 4: 'am', 5: 'excited',
6: 'to', 7: 'learn', 8: 'Python', 9: 'programming'}
>>> my_dict = {1: "Hello", 2: "World", 3: "I", 4: "am",
"values": [1, 2, 3,4, 5], "test": {"1": 1, "2": 2} }
字典在解析 CSV 文件和将每一行与唯一键关联等场景中非常有用。字典还用于对 JSON 数据进行编码和解码
元组(发音类似于two ple或tuh ple)是一种不可变的数据类型,由逗号排序和分隔。可以按如下方式创建元组:
>>> my_tuple = 1, 2, 3, 4, 5
>>> my_tuple (1, 2, 3, 4, 5)
由于元组是不可变的,因此无法修改给定索引处的值:
>>> my_tuple[1] = 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
元组可以由数字、字符串或列表组成。由于列表是可变的,如果列表是元组的成员,则可以对其进行修改。例如:
>>> my_tuple = 1, 2, 3, 4, [1, 2, 4, 5]
>>> my_tuple[4][2] = 3
>>> my_tuple
(1, 2, 3, 4, [1, 2, 3, 5])
元组在无法修改值的情况下特别有用。元组还用于从函数返回值。让我们考虑下面的例子:
>>> for value in my_dict.items(): ...
print(value)
...
(1, 'Hello') (2, 'World') (3, 'I') (4, 'am') ('test', {'1': 1, '2': 2}) ('values', [1, 2, 3, 4, 5])
在前面的示例中,items()
方法返回元组列表。
一套(https://docs.python.org/3/tutorial/datastructures.html#sets 是一个无序的不可变元素集合,没有重复条目。可以按如下方式创建一个集合:
>>> my_set = set([1, 2, 3, 4, 5]) >>> my_set {1, 2, 3, 4, 5}
现在,让我们在此集合中添加一个重复列表:
>>> my_set.update([1, 2, 3, 4, 5]) >>> my_set {1, 2, 3, 4, 5}
集合可避免重复条目并保存唯一条目。可以将单个元素添加到集合中,如下所示:
>>> my_set = set([1, 2, 3, 4, 5]) >>> my_set.add(6)
>>> my_set
{1, 2, 3, 4, 5, 6}
集合用于测试元素在不同集合之间的成员关系。有不同的方法与成员资格测试相关。我们建议使用集合上的文档了解每种方法(运行help(my_set)
以找到可用于成员资格测试的不同方法)。
OOP 是一个有助于简化代码和简化应用程序开发的概念。它在重用代码时特别有用。面向对象的代码允许对使用通信接口的传感器重用代码。例如,可以使用面向对象的代码将配备 UART 端口的所有传感器分组在一起。
OOP 的一个例子是GPIO 零库(https://www.raspberrypi.org/blog/gpio-zero-a-friendly-python-api-for-physical-computing/ 前面章节中使用的。事实上,在 Python 中,一切都是一个对象。
面向对象的代码在项目中与其他人协作时特别有用。例如,您可以使用 Python 中的面向对象代码实现传感器驱动程序,并记录其用法。这使其他开发人员能够开发应用程序,而不必关注传感器接口背后的细节。OOP 为简化应用程序开发的应用程序提供了模块化。我们将回顾本章中演示 OOP 优势的一个示例。在本章中,我们将利用 OOP 为我们的项目带来模块化。
让我们开始吧!
让我们回顾一下第 10 章、算术运算、循环和闪烁灯光(input_test.py
中的身份证示例。我们讨论了如何编写一个简单的程序来捕获和打印属于学生的信息。学生的联系信息可以按如下方式检索和存储:
name = input("What is your name? ")
address = input("What is your address? ")
age = input("How old are you? ")
现在,考虑一个场景,其中 10 个学生的信息必须在程序执行过程中的任何时刻被保存和检索。我们需要为用于保存学生信息的变量制定一个术语。如果我们使用 30 个不同的变量来存储属于每个学生的信息,那将是一个混乱。这就是面向对象编程真正有用的地方。
让我们使用 OOP 重写此示例以简化问题。OOP 的第一步是为对象声明一个结构。这是通过定义一个类来完成的。该类确定对象的函数。让我们编写一个 Python 类来定义 student 对象的结构。
由于我们将保存学生信息,因此该类将被称为Student
。使用class
关键字定义一个类,如下所示:
class Student(object):
因此,定义了一个名为Student
的类。每当创建一个新对象时,Python 会在内部调用方法__init__()
(下划线表示 init 方法是一个神奇的方法,也就是说,它是一个在创建对象时由 Python 调用的函数)。
此方法在类中定义:
class Student(object):
"""A Python class to store student information"""
def __init__(self, name, address, age):
self.name = name
self.address = address
self.age = age
在本例中,__init__
方法的参数包括name
、age
和address
。这些参数称为属性。这些属性允许创建属于Student
类的唯一对象。因此,在本例中,当创建Student
类的实例时,属性name
、age
和address
是必需的参数。
让我们创建一个属于Student
类的对象(也称为实例):
student1 = Student("John Doe", "123 Main Street, Newark, CA", "29")
在本例中,我们创建了一个属于名为student1
的Student
类的对象,其中John Doe
(名称)、29
(年龄)和123 Main Street, Newark, CA
(地址)是创建对象所需的属性。当我们通过传递必要的参数(前面在Student
类的__init__()
方法中声明)来创建属于Student
类的对象时,会自动调用__init__()
方法来初始化该对象。初始化时,与student1
相关的信息存储在对象student1
下。
现在,属于student1
的信息可以按如下方式检索:
print(student1.name)
print(student1.age)
print(student1.address)
现在,让我们创建另一个名为student2
的对象:
student2 = Student("Jane Doe", "123 Main Street, San Jose, CA", "27")
我们创建了两个名为student1
和student2
的对象。每个对象的属性都可以通过student1.name
、student2.name
等方式访问。在没有面向对象编程的情况下,我们将不得不创建像student1_name
、student1_age
、student1_address
、student2_name
、student2_age
和student2_address
等变量。因此,OOP 支持模块化代码。
让我们在Student
类中添加一些方法来帮助检索学生的信息:
class Student(object):
"""A Python class to store student information"""
def __init__(self, name, age, address):
self.name = name
self.address = address
self.age = age
def return_name(self):
"""return student name"""
return self.name
def return_age(self):
"""return student age"""
return self.age
def return_address(self):
"""return student address"""
return self.address
在本例中,我们添加了三个方法,即分别返回属性name
、age
和address
的return_name()
、return_age()
和return_address()
。类的这些方法称为可调用属性。让我们回顾一个快速的示例,其中我们使用这些可调用属性打印对象的信息。
student1 = Student("John Doe", "29", "123 Main Street, Newark, CA")
print(student1.return_name())
print(student1.return_age())
print(student1.return_address())
到目前为止,我们讨论了检索学生信息的方法。让我们在类中包含一个方法,该方法可以更新属于学生的信息。现在,让我们向类中添加另一个方法,该方法允许学生更新地址:
def update_address(self, address):
"""update student address"""
self.address = address
return self.address
让我们比较一下更新地址前后student1
对象的地址:
print(student1.address())
print(student1.update_address("234 Main Street, Newark, CA"))
这将在屏幕上打印以下输出:
123 Main Street, Newark, CA
234 Main Street, Newark, CA
因此,我们编写了第一个面向对象的代码,演示了模块化代码的能力。前面的代码示例可作为student_info.py
与本章一起下载。
在面向对象的示例中,您可能注意到一个用三个双引号括起来的句子:
"""A Python class to store student information"""
这称为一个文档字符串。文档字符串用于记录有关类或方法的信息。文档字符串在尝试存储与方法或类的使用相关的信息时特别有用(这将在本章后面演示)。文档字符串还用于在文件开头存储与应用程序或代码示例相关的多行注释。Python 解释器忽略了文档字符串,它们旨在向其他程序员提供关于类的文档。
类似地,Python 解释器忽略任何以#
符号开头的单行注释。单行注释通常用于对代码块进行特定注释。包含结构良好的注释的实践使代码可读。
例如,下面的代码片段通知读者,0
和9
之间的随机数被生成并存储在变量rand_num
中:
# generate a random number between 0 and 9
rand_num = random.randrange(0,10)
相反,不提供上下文的注释会让正在审阅代码的人感到困惑:
# Todo: Fix this later
很可能在以后重新访问代码时无法回忆起需要修复的内容。
在我们的面向对象示例中,每个方法的第一个参数都有一个名为self
的参数。self
指正在使用的类的实例,self
关键字用作与该类实例交互的方法中的第一个参数。在上例中,self
表示对象student1
。这相当于初始化一个对象并按如下方式访问它:
Student(student1, "John Doe", "29", "123 Main Street, Newark, CA")
Student.return_address(student1)
在本例中,self
关键字简化了我们访问对象属性的方式。现在,让我们回顾一些例子,在这些例子中,我们使用了涉及 Raspberry Pi 的 OOP。
让我们编写一个 Python 类(tone_player.py
在下载中),它播放一个音乐音调,指示 Raspberry Pi 的启动已完成。在本节中,您需要一个 USB 声卡和一个连接到 Raspberry Pi USB 集线器的扬声器。
让我们给我们班打电话TonePlayer
。此类应能够控制扬声器音量,并在创建对象时播放作为参数传递的任何文件:
class TonePlayer(object):
"""A Python class to play boot-up complete tone"""
def __init__(self, file_name):
self.file_name = file_name
在这种情况下,TonePlayer
类必须播放的文件必须传递一个参数。例如:
tone_player = TonePlayer("/home/pi/tone.wav")
我们还需要能够设置音量水平,在该音量水平下必须播放音调。让我们添加一个方法来执行相同的操作:
def set_volume(self, value):
"""set tone sound volume"""
subprocess.Popen(["amixer", "set", "'PCM'", str(value)],
shell=False)
在set_volume
方法中,我们使用 Python 的subprocess
模块运行 Linux 系统命令来调整声音驱动音量。
这个类最基本的方法是play
命令。调用play
方法时,我们需要使用 Linux 的 aplay
命令播放音调声音:
def play(self):
"""play the wav file"""
subprocess.Popen(["aplay", self.file_name], shell=False)
总而言之:
import subprocess
class TonePlayer(object):
"""A Python class to play boot-up complete tone"""
def __init__(self, file_name):
self.file_name = file_name
def set_volume(self, value):
"""set tone sound volume"""
subprocess.Popen(["amixer", "set", "'PCM'", str(value)],
shell=False)
def play(self):
"""play the wav file"""
subprocess.Popen(["aplay", self.file_name], shell=False)
if __name__ == "__main__":
tone_player = TonePlayer("/home/pi/tone.wav")
tone_player.set_volume(75)
tone_player.play()
将TonePlayer
类保存到您的 Raspberry Pi(保存到名为tone_player.py
的文件中),并使用来自freesound(等来源的音调文件 https://www.freesound.org/people/zippi1/sounds/18872/ )。将其保存到您选择的位置,然后尝试运行代码。它应该以所需的音量播放音调声音!
现在,编辑/etc/rc.local
并在文件末尾添加以下行(就在exit 0
行之前):
python3 /home/pi/toneplayer.py
这应该会在 Pi 启动时播放一个音调!
让我们回顾另一个示例,其中我们使用 OOP 实现了一个简单的守护进程,该守护进程在一天中的指定时间打开/关闭灯光。为了能够在预定时间执行任务,我们将使用schedule
库(https://github.com/dbader/schedule 。它可以按如下方式安装:
sudo pip3 install schedule
让我们给我们班打电话LightScheduler
。它应该能够接受在给定时间打开/关闭灯的开始和结束时间。它还应提供覆盖功能,允许用户根据需要打开/关闭灯。假设该灯由动力开关 Tail II(控制 http://www.powerswitchtail.com/Pages/default.aspx )。其接口如下所示:
Raspberry Pi Zero interfaced to the PowerSwitch Tail II
以下是创建的LightSchedular
类:
class LightScheduler(object):
"""A Python class to turn on/off lights"""
def __init__(self, start_time, stop_time):
self.start_time = start_time
self.stop_time = stop_time
# lamp is connected to GPIO pin2.
self.lights = OutputDevice(2)
无论何时创建LightScheduler
实例,GPIO 引脚都会初始化以控制动力开关 Tail II。现在,让我们添加打开/关闭灯光的方法:
def init_schedule(self):
# set the schedule
schedule.every().day.at(self.start_time).do(self.on)
schedule.every().day.at(self.stop_time).do(self.off)
def on(self):
"""turn on lights"""
self.lights.on()
def off(self):
"""turn off lights"""
self.lights.off()
在init_schedule()
方法中,作为参数传递的开始和停止时间用于初始化schedule
以在指定时间打开/关闭灯。
综合起来,我们有:
import schedule
import time
from gpiozero import OutputDevice
class LightScheduler(object):
"""A Python class to turn on/off lights"""
def __init__(self, start_time, stop_time):
self.start_time = start_time
self.stop_time = stop_time
# lamp is connected to GPIO pin2.
self.lights = OutputDevice(2)
def init_schedule(self):
# set the schedule
schedule.every().day.at(self.start_time).do(self.on)
schedule.every().day.at(self.stop_time).do(self.off)
def on(self):
"""turn on lights"""
self.lights.on()
def off(self):
"""turn off lights"""
self.lights.off()
if __name__ == "__main__":
lamp = LightScheduler("18:30", "9:30")
lamp.on()
time.sleep(50)
lamp.off()
lamp.init_schedule()
while True:
schedule.run_pending()
time.sleep(1)
在前面的示例中,灯光计划在下午 6:30 打开,在上午 9:30 关闭。一旦作业计划完成,程序将进入无限循环,等待任务执行。这个例子可以通过在启动时执行文件作为守护进程运行(在/etc/rc.local
中添加一个名为light_scheduler.py
的行)。调度作业后,它将继续作为后台守护进程运行。
This is just a basic introduction to OOP and its applications (keeping the beginner in mind). Refer to this book's website for more examples on OOP.
在本章中,我们讨论了列表和 OOP 的优点。我们以 Raspberry Pi 为中心讨论了 OOP 示例。由于本书主要面向初学者,我们决定在讨论示例时坚持 OOP 的基础知识。有些高级方面超出了本书的范围。我们让读者使用本书网站上提供的其他示例来学习高级概念。