Files
fzu-product/2023旧版内容/3.编程思维体系构建/3.6.4.3阶段三:数据抽象.md

317 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 阶段三:数据抽象
数据抽象 (Data Abstraction)
::: warning 🐱 [可参考教程](https://zhuanlan.zhihu.com/p/343133774)
各位需要认真了解以下内容,他们是构建任何大厦的基石
:::
## Data Abstraction
数据抽象是一个伟大的概念,它允许程序员将代码以对象的形式进行看待,并且从更高的层面去审视问题。
简单来说,它可以帮助程序员不用具体去了解程序做了什么,而是更去重视它有什么用。
举个例子:你在开车时,如果要控制发动机的活塞怎么动,对你来说是否有些太过于困难了。因此将其抽象成了离合器,油门,刹车这些较为简单的操作。
## 组成
一个抽象的数据类型ADT由两个主要部分组成
- Constructors:架构抽象数据类型的主要函数
- Selectors:操作数据类型的各式方法
## 列表与元组
列表是可以存储多个元素的 Python 数据结构。每个元素可以是任何类型,甚至可以是另一个列表!
```python
>>> list_of_nums = [1, 2, 3, 4]
>>> list_of_bools = [True, True, False, False]
>>> nested_lists = [1, [2, 3], [4, [5]]]
```
其可以随意的增加删除或改动元素
元组何其差不多,但是最大的差距在于,元组是静态的,不可随意更改的,要想改动,必须重新开启一片内存空间
```python
tup = (1, 2, 3, 4)
new_tup = tup + (5, ) # 创建新的元组 new_tup并依次填充原元组的值
new _tup
(1, 2, 3, 4, 5)
l = [1, 2, 3, 4]
l.append(5) # 添加元素 5 到原列表的末尾
l
[1, 2, 3, 4, 5]
```
同时,你可以对列表和元组进行索引,甚至使用:进行切片操作,这部分内容在以后的任务会有体现
```python
>>> lst = [True, False, True, True, False]
>>> lst[1:4]
[False, True, True]
>>> lst[:3] # Start index defaults to 0
[True, False, True]
>>> lst[3:] # End index defaults to len(lst)
[True, False]
>>> lst[:] # Creates a copy of the whole list
[True, False, True, True, False]
```
```python
>>> lst = [6, 5, 4, 3, 2, 1]
>>> lst[0]
6
>>> lst[3]
3
```
::: warning 🤔 思考题:
列表和元组在性能上有什么差异呢?
他们对应的使用场景有哪些呢?
:::
## ;ltyi 字典与集合
字典是一系列由键key和值value配对组成的元素的集合在 Python3.7+,字典被确定为有序
相比于列表和元组,字典的性能更优,特别是对于查找、添加和删除操作,字典都能在常数时间复杂度内完成。
而集合和字典基本相同,唯一的区别,就是集合没有键和值的配对,是一系列无序的、唯一的元素组合。
```python
d1 = {'name': 'jason', 'age': 20, 'gender': 'male'}
d2 = dict({'name': 'jason', 'age': 20, 'gender': 'male'})
d3 = dict([('name', 'jason'), ('age', 20), ('gender', 'male')])
d4 = dict(name='jason', age=20, gender='male')
d1 == d2 == d3 ==d4
True
s1 = {1, 2, 3}
s2 = set([1, 2, 3])
s1 == s2
True
```
当然,除了创建和访问,字典和集合也同样支持增加、删除、更新等操作。
```python
d = {'name': 'jason', 'age': 20}
d['gender'] = 'male' # 增加元素对'gender': 'male'
d['dob'] = '1999-02-01' # 增加元素对'dob': '1999-02-01'
d
{'name': 'jason', 'age': 20, 'gender': 'male', 'dob': '1999-02-01'}
d['dob'] = '1998-01-01' # 更新键'dob'对应的值
d.pop('dob') # 删除键为'dob'的元素对
'1998-01-01'
d
{'name': 'jason', 'age': 20, 'gender': 'male'}
s = {1, 2, 3}
s.add(4) # 增加元素 4 到集合
s
{1, 2, 3, 4}
s.remove(4) # 从集合中删除元素 4
s
{1, 2, 3}
```
::: warning 🤔 思考题:
字典和集合分别是什么原理呢?
字典可以是一个列表吗?为什么?
:::
## 可变性
我们说如果一个对象可以由代码进行操作而改变那么我们称其具有可变性。
可变对象的示例包括列表和字典。不可变对象的示例包括元组和函数。
我们假定已经知道了如何使用 `==` 运算符来检查两个表达式的计算结果是否**相同**。
我们现在引入一个新的比较运算符 `is`,它检查两个表达式的计算结果是否**相同**。
```python
>>> 2 + 2 == 3 + 1
True
>>> 2 + 2 is 3 + 1
True
```
有什么不同呢?
```python
>>> large_num1 = 23333333333333333333
>>> large_num2 = 23333333333333333333
>>> large_num1 == large_num2
True
>>> large_num1 is large_num2
False
>>> lst1 = [1, 2, 3, 4]
>>> lst2 = [1, 2, 3, 4]
>>> lst1 == lst2
True
>>> lst1 is lst2
False
```
欸?为什么最后一个不一样了?
其实原因是,你创建的两个列表虽然内容相同,但是毕竟是两个不同的列表,其在内存空间上并不一样。
这在讨论可变性的时候非常重要,当我们改变一个数的值的时候这非常重要
```python
>>> lst1 = [1, 2, 3, 4]
>>> lst2 = lst1
>>> lst1.append(5)
>>> lst2
[1, 2, 3, 4, 5]
>>> lst1 is lst2
True
```
::: warning 🤔 思考题,你能否从指针的角度去理解可变性呢?
:::
## 任务
P7:9*9 乘法表
可能现在对你来说,构建像下图这样的 99 乘法表已经是非常容易的一件事了,可是如果我要求你使用 python 的列表推导式 (list comprehension),在两行以内完成呢?
![](https://cdn.xyxsw.site/boxcnccDSRQj5W3lZWEUkCOHz2b.png)
P8couple 情侣
实现函数 `couple`,它接受两个列表并返回一个列表,其中包含两个序列的第 i 个元素耦合在一起的列表。您可以假设两个序列的长度相同。
tipszip(list1,list2)
```python
def couple(lst1, lst2):
"""Return a list that contains lists with i-th elements of two sequences
coupled together.
>>> lst1 = [1, 2, 3]
>>> lst2 = [4, 5, 6]
>>> couple(lst1, lst2)
[[1, 4], [2, 5], [3, 6]]
>>> lst3 = ['c', 6]
>>> lst4 = ['s', '1']
>>> couple(lst3, lst4)
[['c', 's'], [6, '1']]
"""
assert len(lst1) == len(lst2)
"*** YOUR CODE HERE ***"
```
P 8.5:对城市数据抽象
假设我们有一个城市的抽象数据类型。城市有名称、纬度坐标和经度坐标。
这是我们构造城市的函数
make_city(name, lat, lon)
构建一个城市对象有经纬度和名字
- `get_name(city)`:返回城市名称
- `get_lat(city)`:返回城市的纬度
- `get_lon(city)`:返回城市的经度
```python
>>> nanjing = make_city('Nanjing', 31, 118)
>>> get_name(nanjing)
'Nanjing'
>>> get_lat(nanjing)
31
>>> beijing = make_city('Beijing', 39, 116)
>>> get_lon(beijing)
116
```
```python
def make_city(name, lat, lon):
"""
>>> city = make_city('Berkeley', 0, 1
>>> get_name(city)
'Berkeley
>>> get_lat(city)
0
>>> get_lon(city)
1
"""
return [name, lat, lon]
def get_name(city):
"""
>>> city = make_city('Berkeley', 0, 1)
>>> get_name(city)
'Berkeley'
"""
return city[0]
def get_lat(city):
"""
>>> city = make_city('Berkeley', 0, 1)
>>> get_lat(city)
0
"""
return city[1]
def get_lon(city):
"""
>>> city = make_city('Berkeley', 0, 1)
>>> get_lon(city)
1
"""
return city[2]
```
首先你试试求出两个地方的距离。
`(x2, y2)` 可以通过计算 的 sqrt 来找到 `(x1 - x2)**2 + (y1 - y2)**2``sqrt` 我们引用了
```python
from math import sqrt
def distance(city1, city2):
"""
>>> city1 = make_city('city1', 0, 1)
>>> city2 = make_city('city2', 0, 2)
>>> distance(city1, city2)
1.0
>>> city3 = make_city('city3', 6.5, 12)
>>> city4 = make_city('city4', 2.5, 15)
>>> distance(city3, city4)
5.0
"""
"*** YOUR CODE HERE ***"
```
实现 `closer_city` 一个函数,该函数接受一个纬度、经度和两个城市,并返回与提供的经纬度相对较近的城市名称。
```python
def closer_city(lat, lon, city1, city2):
"""
Returns the name of either city1 or city2, whichever is closest to
coordinate (lat, lon).
>>> berkeley = make_city('Berkeley', 37.87, 112.26)
>>> stanford = make_city('Stanford', 34.05, 118.25)
>>> closer_city(38.33, 121.44, berkeley, stanford)
'Stanford'
>>> bucharest = make_city('Bucharest', 44.43, 26.10)
>>> vienna = make_city('Vienna', 48.20, 16.37)
>>> closer_city(41.29, 174.78, bucharest, vienna)
'Bucharest'
"""
"*** YOUR CODE HERE ***"
```