从零开始学Python第08课:常用数据结构之列表-1
Posted 骆昊的技术专栏
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始学Python第08课:常用数据结构之列表-1相关的知识,希望对你有一定的参考价值。
在开始本节课的内容之前,我们先给大家一个编程任务,将一颗色子掷6000
次,统计每种点数出现的次数。这个任务对大家来说应该是非常简单的,我们可以用1
到6
均匀分布的随机数来模拟掷色子,然后用6
个变量分别记录每个点数出现的次数,相信通过前面的学习,大家都能比较顺利的写出下面的代码。
"""
将一颗色子掷6000次,统计每种点数出现的次数
Author: 骆昊
Version: 1.0
"""
import random
f1 = 0
f2 = 0
f3 = 0
f4 = 0
f5 = 0
f6 = 0
for _ in range(6000):
face = random.randrange(1, 7)
if face == 1:
f1 += 1
elif face == 2:
f2 += 1
elif face == 3:
f3 += 1
elif face == 4:
f4 += 1
elif face == 5:
f5 += 1
else:
f6 += 1
print(f'1点出现了f1次')
print(f'2点出现了f2次')
print(f'3点出现了f3次')
print(f'4点出现了f4次')
print(f'5点出现了f5次')
print(f'6点出现了f6次')
上面的代码非常有多么“丑陋”相信就不用我多说了。当然,更为可怕的是,如果我们要掷两颗或者掷更多的色子,然后统计每种点数出现的次数,那就需要定义更多的变量,写更多的分支结构,大家想想都会感到恶心。讲到这里,相信大家心中已经有一个疑问了:有没有办法用一个变量来保存多个数据,有没有办法用统一的代码对多个数据进行操作?答案是肯定的,在 Python 中我们可以通过容器类型的变量来保存和操作多个数据,我们首先为大家介绍列表(list
)这种新的数据类型。
创建列表
在 Python 中,列表是由一系元素按特定顺序构成的数据序列,这就意味着如果我们定义一个列表类型的变量,可以用它来保存多个数据。在 Python 中,可以使用[]
字面量语法来定义列表,列表中的多个元素用逗号进行分隔,代码如下所示。
items1 = [35, 12, 99, 68, 55, 35, 87]
items2 = ['Python', 'Java', 'Go', 'Kotlin']
items3 = [100, 12.3, 'Python', True]
print(items1) # [35, 12, 99, 68, 55, 35, 87]
print(items2) # ['Python', 'Java', 'Go', 'Kotlin']
print(items3) # [100, 12.3, 'Python', True]
说明:列表中可以有重复元素,例如
items1
中的35
;列表中可以有不同类型的元素,例如items3
中有int
类型、float
类型、str
类型和bool
类型的元素,但是我们通常并不建议将不同类型的元素放在同一个列表中,主要是操作起来极为不方便。
我们可以使用type
函数来查看变量的类型,有兴趣的小伙伴可以自行查看上面的变量items1
到底是什么类型。因为列表可以保存多个元素,它是一种容器型的数据类型,所以我们在给列表类型的变量起名字时,变量名通常用复数形式的单词。
除此以外,还可以通过 Python 内置的list
函数将其他序列变成列表。准确的说,list
并不是一个普通的函数,它是创建列表对象的构造器,后面的课程会为大家介绍对象和构造器这些概念。
items4 = list(range(1, 10))
items5 = list('hello')
print(items4) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(items5) # ['h', 'e', 'l', 'l', 'o']
说明:
range(1, 10)
会产生1
到9
的整数序列,给到list
构造器中,会创建出由1
到9
的整数构成的列表。字符串是字符构成的序列,上面的list('hello')
用字符串hello
的字符作为列表元素,创建了列表对象。
列表的运算
我们可以使用+
运算符实现两个列表的拼接,拼接运算会将两个列表中的元素连接起来放到一个列表中,代码如下所示。
items5 = [35, 12, 99, 45, 66]
items6 = [45, 58, 29]
items7 = ['Python', 'Java', 'javascript']
print(items5 + items6) # [35, 12, 99, 45, 66, 45, 58, 29]
print(items6 + items7) # [45, 58, 29, 'Python', 'Java', 'JavaScript']
items5 += items6
print(items5) # [35, 12, 99, 45, 66, 45, 58, 29]
我们可以使用*
运算符实现列表的重复运算,*
运算符会将列表元素重复指定的次数,我们在上面的代码中增加两行,如下所示。
print(items6 * 3) # [45, 58, 29, 45, 58, 29, 45, 58, 29]
print(items7 * 2) # ['Python', 'Java', 'JavaScript', 'Python', 'Java', 'JavaScript']
我们可以使用in
或not in
运算符判断一个元素在不在列表中,我们在上面的代码代码中再增加两行,如下所示。
print(29 in items6) # True
print(99 in items6) # False
print('C++' not in items7) # True
print('Python' not in items7) # False
由于列表中有多个元素,而且元素是按照特定顺序放在列表中的,所以当我们想操作列表中的某个元素时,可以使用[]
运算符,通过在[]
中指定元素的位置来访问该元素,这种运算称为索引运算。需要说明的是,[]
的元素位置可以是0
到N - 1
的整数,也可以是-1
到-N
的整数,分别称为正向索引和反向索引,其中N
代表列表元素的个数。对于正向索引,[0]
可以访问列表中的第一个元素,[N - 1]
可以访问最后一个元素;对于反向索引,[-1]
可以访问列表中的最后一个元素,[-N]
可以访问第一个元素,代码如下所示。
items8 = ['apple', 'waxberry', 'pitaya', 'peach', 'watermelon']
print(items8[0]) # apple
print(items8[2]) # pitaya
print(items8[4]) # watermelon
items8[2] = 'durian'
print(items8) # ['apple', 'waxberry', 'durian', 'peach', 'watermelon']
print(items8[-5]) # 'apple'
print(items8[-4]) # 'waxberry'
print(items8[-1]) # watermelon
items8[-4] = 'strawberry'
print(items8) # ['apple', 'strawberry', 'durian', 'peach', 'watermelon']
在使用索引运算的时候要避免出现索引越界的情况,对于上面的items8
,如果我们访问items8[5]
或items8[-6]
,就会引发IndexError
错误,导致程序崩溃,对应的错误信息是:list index out of range,翻译成中文就是“数组索引超出范围”。因为对于只有五个元素的列表items8
,有效的正向索引是0
到4
,有效的反向索引是-1
到-5
。
如果希望一次性访问列表中的多个元素,我们可以使用切片运算。切片运算是形如[start:end:stride]
的运算符,其中start
代表访问列表元素的起始位置,end
代表访问列表元素的终止位置(终止位置的元素无法访问),而stride
则代表了跨度,简单的说就是位置的增量,比如我们访问的第一个元素在start
位置,那么第二个元素就在start + stride
位置,当然start + stride
要小于end
。我们给上面的代码增加下面的语句,来使用切片运算符访问列表元素。
print(items8[1:3:1]) # ['strawberry', 'durian']
print(items8[0:3:1]) # ['apple', 'strawberry', 'durian']
print(items8[0:5:2]) # ['apple', 'durian', 'watermelon']
print(items8[-4:-2:1]) # ['strawberry', 'durian']
print(items8[-2:-6:-1]) # ['peach', 'durian', 'strawberry', 'apple']
提醒:大家可以看看上面代码中的最后一行,想一想当跨度为负数时,切片运算是如何访问元素的。
如果start
值等于0
,那么在使用切片运算符时可以将其省略;如果end
值等于N
,N
代表列表元素的个数,那么在使用切片运算符时可以将其省略;如果stride
值等于1
,那么在使用切片运算符时也可以将其省略。所以,下面的代码跟上面的代码作用完全相同。
print(items8[1:3]) # ['strawberry', 'durian']
print(items8[:3:1]) # ['apple', 'strawberry', 'durian']
print(items8[::2]) # ['apple', 'durian', 'watermelon']
print(items8[-4:-2]) # ['strawberry', 'durian']
print(items8[-2::-1]) # ['peach', 'durian', 'strawberry', 'apple']
事实上,我们还可以通过切片操作修改列表中的元素,例如我们给上面的代码再加上一行,大家可以看看这里的输出。
items8[1:3] = ['x', 'o']
print(items8) # ['apple', 'x', 'o', 'peach', 'watermelon']
两个列表还可以做关系运算,我们可以比较两个列表是否相等,也可以给两个列表比大小,代码如下所示。
nums1 = [1, 2, 3, 4]
nums2 = list(range(1, 5))
nums3 = [3, 2, 1]
print(nums1 == nums2) # True
print(nums1 != nums2) # False
print(nums1 <= nums3) # True
print(nums2 >= nums3) # False
说明:上面的
nums1
和nums2
对应元素完全相同,所以==
运算的结果是True
。nums2
和nums3
的比较,由于nums2
的第一个元素1
小于nums3
的第一个元素3
,所以nums2 >= nums3
比较的结果是False
。两个列表的关系运算在实际工作并不那么常用,如果实在不理解就下面放放吧,不用纠结。
元素的遍历
如果想逐个取出列表中的元素,可以使用for-in
循环的,有以下两种做法。
方法一:在循环结构中通过索引运算,遍历列表元素。
languages = ['Python', 'Java', 'C++', 'Kotlin']
for index in range(len(languages)):
print(languages[index])
输出:
Python
Java
C++
Kotlin
说明:上面的
len
函数可以获取列表元素的个数N
,而range(N)
则构成了从0
到N-1
的范围,刚好可以作为列表元素的索引。
方法二:直接对列表做循环,循环变量就是列表元素的代表。
languages = ['Python', 'Java', 'C++', 'Kotlin']
for language in languages:
print(language)
输出:
Python
Java
C++
Kotlin
总结
讲到这里,我们可以用列表的知识来重构上面“掷色子统计每种点数出现次数”的代码。
"""
将一颗色子掷6000次,统计每种点数出现的次数
Author: 骆昊
Version: 1.1
"""
import random
counters = [0] * 6
for _ in range(6000):
face = random.randrange(1, 7)
counters[face - 1] += 1
for face in range(1, 7):
print(f'face点出现了counters[face - 1]次')
上面的代码中,我们用counters
列表中的六个元素分别表示1
到6
点出现的次数,最开始的时候六个元素的值都是0
。接下来,我们用1
到6
均匀分布的随机数模拟掷色子,如果摇出1点,counters[0]
的值加1
,如果摇出2点,counters[1]
的值加1
,以此类推。大家感受一下,由于使用了列表类型加上循环结构,我们对数据的处理是批量性的,这就使得修改后的代码比之前的代码要简单优雅得多。
python之感知器-从零开始学深度学习
感知器-从零开始学深度学习
未来将是人工智能和大数据的时代,是各行各业使用人工智能在云上处理大数据的时代,深度学习将是新时代的一大利器,在此我将从零开始记录深度学习的学习历程。
我希望在学习过程中做到以下几点:
- 了解各种神经网络设计原理。
- 掌握各种深度学习算法的python编程实现。
- 运用深度学习解决实际问题。
让我们开始踏上深度度学习的征程。
一、感知器原型
想要了解“神经网络”,我们需要了解一种叫做“感知器”的⼈⼯神经元。感知器在 20 世纪五、六⼗年代由科学家 Frank Rosenblatt 发明,⼀个感知器接受⼏个输⼊,并产⽣⼀个输出。
下图是一个感知器:
⽰例中的感知器有三个输⼊x1、x2、x3(1*w0作为偏置,后面会讲到)。通常可以有更多或更少输⼊。 Rosenblatt 提议⼀个简单的规则来计算输出。他引⼊权重w1、w2、w3..表⽰相应输⼊对于输出重要性的实数(权重)。神经元的输出为0 或者 1,则由计算权重后的总和 ∑jwjxj∑jwjxj
⼩于或者⼤于⼀些阈值决定。和权重⼀样,
阈值是⼀个实数,⼀个神经元的参数。⽤更精确的代数形式:
这就是⼀个感知器所要做的所有事情!
而我们把阖值移动到不等式左边,并用感知器的偏置b=-threshold代替,用偏置而不用阖值。其中实现偏置的一种方法就是如前图所示在输入中引入一个偏置神经元x0=1,则b=x0*w0,那么感知器的规则可以重写为:
此时就可以使用阶跃函数来作为感知器的激励函数。
到此我们可以发现,一个感知器由以下几部分组成
-
接下去我们将会用一个例子来理解感知器的模型。
模型的建立是运用深度学习方法解决问题的基础。
二、感知器的运用
1、感知器实现逻辑运算
我们设计一个感知器,让它来实现and运算。程序员都知道,and是一个二元函数(带有两个参数和),下面是它的真值表:
x1x1 | x2x2 | yy |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
为了计算方便,我们用0表示false,用1表示true。
可以看到感知器本身是一个线性分类器,它通过求考虑了权重的各输入之和与阖值的大小关系,对事物进行分类。
所以任何线性分类或线性回归问题都可以用感知器来解决。前面的布尔运算可以看作是二分类问题,即给定一个输入,输出0(属于分类0)或1(属于分类1)。
如下面所示,and运算是一个线性分类问题,即可以用一条直线把分类0(false,红叉表示)和分类1(true,绿点表示)分开。
然而,感知器却不能实现异或运算,如下图所示,异或运算不是线性的,你无法用一条直线把分类0和分类1分开。
2、感知器的训练
三、python实现感知器
class Perceptron(object): def __init__(self, input_num, activator): \'\'\' 初始化感知器,设置输入参数的个数,以及激活函数。 激活函数的类型为double -> double \'\'\' self.activator = activator # 权重向量初始化为0 self.weights = [0.0 for _ in range(input_num)] # 偏置项初始化为0 self.bias = 0.0 def __str__(self): \'\'\' 打印学习到的权重、偏置项 \'\'\' return \'weights\\t:%s\\nbias\\t:%f\\n\' % (self.weights, self.bias) def predict(self, input_vec): \'\'\' 输入向量,输出感知器的计算结果 \'\'\' # 把input_vec[x1,x2,x3...]和weights[w1,w2,w3,...]打包在一起 # 变成[(x1,w1),(x2,w2),(x3,w3),...] # 然后利用map函数计算[x1*w1, x2*w2, x3*w3] # 最后利用reduce求和 return self.activator( reduce(lambda a, b: a + b, map(lambda (x, w): x * w, zip(input_vec, self.weights)) , 0.0) + self.bias) def train(self, input_vecs, labels, iteration, rate): \'\'\' 输入训练数据:一组向量、与每个向量对应的label;以及训练轮数、学习率 \'\'\' for i in range(iteration): self._one_iteration(input_vecs, labels, rate) def _one_iteration(self, input_vecs, labels, rate): \'\'\' 一次迭代,把所有的训练数据过一遍 \'\'\' # 把输入和输出打包在一起,成为样本的列表[(input_vec, label), ...] # 而每个训练样本是(input_vec, label) samples = zip(input_vecs, labels) # 对每个样本,按照感知器规则更新权重 for (input_vec, label) in samples: # 计算感知器在当前权重下的输出 output = self.predict(input_vec) # 更新权重 self._update_weights(input_vec, output, label, rate) def _update_weights(self, input_vec, output, label, rate): \'\'\' 按照感知器规则更新权重 \'\'\' # 把input_vec[x1,x2,x3,...]和weights[w1,w2,w3,...]打包在一起 # 变成[(x1,w1),(x2,w2),(x3,w3),...] # 然后利用感知器规则更新权重 delta = label - output self.weights = map( lambda (x, w): w + rate * delta * x, zip(input_vec, self.weights)) # 更新bias self.bias += rate * delta def f(x): \'\'\' 定义激活函数f \'\'\' return 1 if x > 0 else 0 def get_training_dataset(): \'\'\' 基于and真值表构建训练数据 \'\'\' # 构建训练数据 # 输入向量列表 input_vecs = [[1,1], [0,0], [1,0], [0,1]] # 期望的输出列表,注意要与输入一一对应 # [1,1] -> 1, [0,0] -> 0, [1,0] -> 0, [0,1] -> 0 labels = [1, 0, 0, 0] return input_vecs, labels def train_and_perceptron(): \'\'\' 使用and真值表训练感知器 \'\'\' # 创建感知器,输入参数个数为2(因为and是二元函数),激活函数为f p = Perceptron(2, f) # 训练,迭代10轮, 学习速率为0.1 input_vecs, labels = get_training_dataset() p.train(input_vecs, labels, 10, 0.1) #返回训练好的感知器 return p if __name__ == \'__main__\': # 训练and感知器 and_perception = train_and_perceptron() # 打印训练获得的权重 print and_perception # 测试 print \'1 and 1 = %d\' % and_perception.predict([1, 1]) print \'0 and 0 = %d\' % and_perception.predict([0, 0]) print \'1 and 0 = %d\' % and_perception.predict([1, 0]) print \'0 and 1 = %d\' % and_perception.predict([0, 1])
以上是关于从零开始学Python第08课:常用数据结构之列表-1的主要内容,如果未能解决你的问题,请参考以下文章
Python 大白从零开始 OpenCV 学习课-7. 空间域图像滤波