学习笔记:《零基础入门学习Python》(小甲虫)
0前言
Python3不完全兼容Python2的语法。
Python官方网址:https://www.python.org/
] #号起到注释的作用。
Spyder(Scientific PYthon Development EnviRonment)是一个强大的交互式 Python 语言开发环境,提供高级的代码编辑、交互测试、调试等特性,支持包括 Windows、Linux 和 OS X 系统。
Spyder查看Python内置函数(BIF,Built-in Functions)列表:
import builtins
print(dir(builtins))
1成为高手前必须知道的一些基础知识
1.1变量
变量不需要先声明;
在使用变量前,需要对其先赋值;
变量名可以包括字母、数字、下划线,但不能以数字开头;
区分大小写;
1.2字符串
双引号或单引号内的东西:"Python I love you" 或 ‘Python I love you’。
如果字符串中需要出现单引号或双引号,此时需要使用转义符号(\)对出现的引号进行转义:
>>>'Let \'s go'
"Let's go"
如果字符串中需要多个转义时,使用转义符号会使代码变得混乱,此时可以使用原始字符串,在字符串前边加一个英文字母r即可:
>>>string =r 'C:\now'
string
'C:\\now'
print(string)
C:\now
注意:无论是否原始字符串,都不能以反斜杠作为结尾(放在末尾表示该字符串还没有结束,换行继续的意思);
如果希望得到一个跨越多行的字符串,只需要使用三种引号字符串("""内容""")就可以;
注意:编程中使用的标点符号都是英文的。
1.3数据类型
(1)整型
整数,Python3的整型已经与长整型进行了无缝结合,长度不受限制,如果说非要有个限制,那只限于计算机的虚拟内存总数。所以用Python3很容易进行大数运算。
(2)浮点型
小数,Python区分整型和浮点型的唯一方式,就是看有没有小数点。
浮点型采用E记法,即科学计数法,表示特别大或特别小的数:
>>>a = 0.000000000025
>>>a
2.5e - 11(大E和小e都可以)。
(3)布尔类型
实际上是特殊的整型,True相当于1,False相当于0。
(4)类型转换
int()将一个字符串或浮点数转换为一个整数:
>>>a = '520'
>>>b = int(a)
>>>a,b
('520', 520)
>>>c = 5.99
>>>d = int(c)
>>>c, d
(5.99, 5) #注意:浮点数转为整数,Python采取截断处理,将小数点后数据直接砍掉
(5)获得关于类型的消息
type()告诉变量的类型:
>>>type('520')
str
isinstance()确定变量的类型是否与给定的一致,有2个参数,第1个是待确定类型的数据,第2个是指定一个数据类型,一致返回True,不一致返回False:
>>>a = '520'
>>>isinstance(a, str)
True
1.4常用操作符
1. 算术操作符
+ - × / 加减乘除:
>>> a = 10
>>> b = a / 8
1.25 #返回一个浮点型的精确数值
// floor除法:
>>> a = 10
>>> b = a // 8
>>> 1 #返回比商小的最大整型
% 取余:
** 幂:
>>> 10 ** 2
100
2. 比较操作符
< <= > >= == !=
3. 逻辑操作符
and or not
4. 优先级
幂运算 > 正负号 > 算术操作符 > 比较操作符 > 逻辑操作符
注意:幂运算操作符比其左侧的一元操作符优先级高,比其右侧的一元操作符运算级低。
>>> -3 ** 2
-9
>>> 3 ** -2
0.1111111111111111
2分支和循环
2.1if ......else....
if 条件:
内容
elif 条件:
内容
elif 条件:
内容
else:
内容
2.2条件表达式(三元操作符)
使用此类表达式,你可以使用一条语句来完成以下的条件判断和赋值操作:
if x<y:
small = x
else:
small = y;
三元操作符语法:
a = x if 条件 else y
表示当条件为True时,a赋值为x,否则赋值为y。
所以上面的例子可以改写为:small = x if x<y else y
2.3断言(assert)
assert关键字称为“断言”,当关键字后面的条件为假时,程序自动崩溃并抛出AssertionError的异常。
>>>assert 3>4
Traceback (most recent call last):
File "<ipython-input-18-020d57da1a31>", line 1, in <module>
assert 3>4
AssertionError
当我们在测试程序时就很好用,因为与其让错误的条件导致程序今后莫名其妙地崩溃,不如在错误条件出现的一瞬间实现“自我毁灭”。一般来说,可以用它在程序中置入检查点,当需要确保程序中的某个条件一定为真才能让程序正常工作时,assert就非常有用。
2.4while循环语句
while 条件:
循环体
2.5for循环语句
Python的for循环跟C语言相比更加智能和强大,主要表现在它会自动调用迭代器的next()方法,会自动捕获StopIteration异常并结束循环:
>>>favourite = "FishC"
>>>for each in favourite:
print(each, end=' ')
FishC
for循环常与range()函数搭配。
range(start , stop, step):
(1)range(5):将第一个参数默认设置为0,生成从0到5的数字(包含0但不包含5:0,1,2,3,4)
(2)range(2,9):2,3,4,5,6,7,8
(3)range(1,10,2):1,3,5,7,9
2.6break语句和continue语句
break语句的作用是终止当前循环,跳出循环体;
coninue语句是终止本轮循环并开始下一轮循环。
3列表:一个打了激素的数组
3.1创建列表
创建列表和创建普通变量一样,用中括号括起一堆数据就可以了,数据之间用逗号隔开,这样一个普通的列表就创建成功:
>>>number = [1,2,3,4,5]
我们说列表是打了激素的数组,它可以创建一个鱼龙混杂的列表:
>>>mix = [1, "小甲鱼", 3.14, [1,2,3]]
该列表有整型、字符串、浮点型数据,甚至还可以包含另一个列表。
如果不知道要往列表里添加什么数据时,可以创建一个空列表:>>>empty = []。
3.2向列表添加元素
(1)append():向列表末尾添加单个元素
>>>number = [1,2,3,4,5]
>>>number.append(6)
>>>print(number)
[1,2,3,4,5,6]
(2)extend():向列表末尾添加多个元素
>>>number = [1,2,3,4,5]
>>>number.extend([7,8])
>>>print(number)
[1,2,3,4,5,7,8]
(3)insert():在列表的任意位置插入单个元素
>>>number = [1,2,3,4,5]
>>>number.insert(0,0) #注意列表的下标从0开始
>>>print(number)
[0,1,2,3,4,5]
3.3从列表中获取元素
跟数组一样,可以通过元素的索引值从列表中获取单个元素,注意列表的索引值是从0开始:
>>>name = ["wangyi","liuer","lisan","zhaosi"]
>>>print(name[0])
>>>print(name[1])
wangyi
liuer
元素位置互调:
>>>name[1],name[3] = name[3],name[1]
>>>print(name)
['wangyi', 'zhaosi', 'lisan', 'liuer']
对于复合列表:
>>>mix = [1, "xiaojiayu", 3.14, [1,2,3]]
>>>print(mix[3][0])
1
3.4从列表删除元素
(1)remove():你并不需要这个元素在列表中的具体位置,只需要知道该元素存在列表中就可以了。如果要删除的东西根本不在列表中,程序就会报错:
>>>name = ["wangyi","liuer","lisan","zhaosi"]
>>>name.remove("liuer")
>>>print(name)
['wangyi', 'lisan', 'zhaosi']
(2)del():remove()方法并不能指定删除某个位置的元素,这时就要用del实现:
>>>name = ["wangyi","liuer","lisan","zhaosi"]
>>>del name[1]
>>>print(name)
['wangyi', 'lisan', 'zhaosi']
删除整个列表:>>>del name
(3)pop():“弹出”元素
>>>name = ["wangyi","liuer","lisan","zhaosi"]
>>>name.pop()
>>>name.pop()
>>>print(name)
['wangyi', 'liuer']
pop()默认弹出列表中最后一个元素。但可以加上索引值,弹出对应的元素:
>>>name = ["wangyi","liuer","lisan","zhaosi"]
>>>name.pop(2)
>>>print(name)
['wangyi', 'liuer', 'zhaosi']
3.5列表分片(slice)
利用索引值,每次可以从列表中获取一个元素,如果需要一次性获取多个元素,有没有办法实现?利用列表分片可以实现:
>>>name = ["wangyi","liuer","lisan","zhaosi"]
>>>print(name[0:2])
['wangyi', 'liuer']
即用一个冒号隔开两个索引值,左边是开始位置,右边是结束位置,注意,结束位置上的元素是不包含的。
利用列表分片,得到一个原来列表的拷贝,原来的列表并没有改变。
列表分片简写:
>>>name = ["wangyi","liuer","lisan","zhaosi"]
>>>print(name[:2])
>>>print(name[1:])
>>>print(name[:])
['wangyi', 'liuer']
['liuer', 'lisan', 'zhaosi']
['wangyi', 'liuer', 'lisan', 'zhaosi']
列表分片的进阶玩法:
分片操作实际上可以接收第三个参数,其代表步长,默认为1。若步长改为2:
>>>list = [1,2,3,4,5,6,7,8,9]
>>>print(list[0:9:2])
[1, 3, 5, 7, 9]
若改为-1:
>>>list = [1,2,3,4,5,6,7,8,9]
>>>print(list[::-1]) #相当于复制一个反转的列表
[9, 8, 7, 6, 5, 4, 3, 2, 1]
3.6列表运算
(1)比较大小
>>>list1 = [123, 456]
>>>list2 = [234, 123]
>>>print(list1 > list2)
>>>list3 = ['abc']
>>>list4 = ['bcd']
>>>print(list3 > list4)
False
False
当列表包含多个元素时,默认是从第一个元素比较,只要有一个PK赢了,就算整个列表赢了。字符串比较也是同样(字符串比较的时第一个字符对应的ASCII码值的大小)。
(2)拼接
用加号+拼接:
>>>list1 = [123, 456]
>>>list2 = [234, 123]
>>>list3 = list1 + list2
>>>print(list3)
[123, 456, 234, 123]
(3)复制自身
用乘号*复制自身若干次:
>>>list1 = [123, 456]
>>>print(list1 * 3)
[123, 456, 123, 456, 123, 456]
3.7列表的小伙伴们
>>>dir(list)
['__add__',
'__class__',
'__contains__',
'__delattr__',
'__delitem__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__getitem__',
'__gt__',
'__hash__',
'__iadd__',
'__imul__',
'__init__',
'__init_subclass__',
'__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']
其中,extend,insert等方法已介绍,在此再补充几个常用的方法:
(1)count():计算它的参数在列表中出现的次数
>>>list1 = [1,1,2,3,4]
>>>print(list1.count(1))
2
(2)index():返回它的参数在列表中出现的位置
>>>list1 = [1,1,2,3,4]
>>>print(list1.index(1))
0
可以看到,返回的时第一个目标1在list1中的位置,怎么查找第二个1的位置呢?
>>>list1 = [1,1,2,3,4]
>>>start = list1.index(1) + 1
>>>stop = len(list1)
>>>print(list1.index(1,start,stop))
1
(3)reverse():将列表翻转
>>>list1 = [1,1,2,3,4]
>>>list1.reverse()
>>>print(list1)
[4, 3, 2, 1, 1]
(4)sort():对列表成员排序,默认从小到大排序
>>>list1 = [8,9,3,5,2,6,10,1,0]
>>>list1.sort()
>>>print(list1)
>>>list1.sort(reverse=True)
>>>print(list1)
[0, 1, 2, 3, 5, 6, 8, 9, 10]
[10, 9, 8, 6, 5, 3, 2, 1, 0]
4元组:戴上了枷锁的列表
元组和列表最大的区别就是你可以任意修改列表中的元素,可以任意插入或删除一个元素,而对元组是不行的,元组是不可改变的(像字符串一样)。
4.1创建和访问一个元组
>>>tuple1 = (1,2,3,4,5,6,7,8)
>>>print(tuple1)
>>>print(tuple1[1])
>>>print(tuple1[5:])
(1, 2, 3, 4, 5, 6, 7, 8)
2
(6, 7, 8)
也使用分片的方式来复制一个元组:
>>>tuple1 = (1,2,3,4,5,6,7,8)
>>>tuple2 = tuple1(:)
创建一个空元组:
>>>temp = ()
创建的元组中仅有一个元素:
>>>temp = (1,) #逗号不能省略
4.2更新和删除元组
直接在同一个元组上更新是不可行的,但可以通过拷贝现有的元组片段构造一个新的元组的方式解决:
>>>tuple1 = (1,2,4)
>>>tuple1 = tuple1[:2] + (3,) + tuple1[2:]
>>>print(tuple1)
(1, 2, 3, 4)
可以用del去删除整个元组,但很少使用,因为Python的回收机制会在这个元组不再被使用时自动删除。
5字符串
在C语言中,字符和字符串是两个不同的概念。但是Python并没有字符这个类型,在Python看来所谓字符,就是长度为1的字符串。访问字符串其中一个字符:
>>>str1 = "I love fishc.com"
>>>print(str1[5])
>>>print(str1[:6])
e
I love
5.1字符串内置方法
可以通过>>> dir(str)来查看。下面介绍几个常用的方法:
(1)casefold():将字符串所有字符变为小写
>>>str1 = "FishC"
>>>print(str1.casefold())
fishc
(2)count():查找sub子字符串出现的次数
>>>str1 = "AbcABCabCabcABCabc"
>>>print(str1.count('ab',0,15))
2
(3)find()和index():查找某个字符串在该字符串中的位置
>>>str1 = "I love fishc.com"
>>>print(str1.find("fishc"))
>>>print(str1.find("good"))
>>>print(str1.index("fishc"))
>>>print(str1.index("good"))
7
-1
7
Traceback (most recent call last):
File "<ipython-input-65-d5f76c5e96d6>", line 1, in <module>
runfile('/home/liuyu/.config/spyder-py3/temp.py', wdir='/home/liuyu/.config/spyder-py3')
File "/home/liuyu/anaconda3/lib/python3.7/site-packages/spyder_kernels/customize/spydercustomize.py", line 668, in runfile
execfile(filename, namespace)
File "/home/liuyu/anaconda3/lib/python3.7/site-packages/spyder_kernels/customize/spydercustomize.py", line 108, in execfile
exec(compile(f.read(), filename, 'exec'), namespace)
File "/home/liuyu/.config/spyder-py3/temp.py", line 12, in <module>
print(str1.index("good"))
ValueError: substring not found
(4)join():以字符串作为分隔符,插入到sub字符串中所有的字符之间
>>>print('x'.join("Test"))
>>>print('_'.join("FishC"))
Txexsxt
F_i_s_h_C
程序员喜欢用join()来连接字符串,因为当使用连接符号+去拼接大量的字符串时是非常低效率的,因为涉及内存复制和垃圾回收操作。所以对于大量的字符串拼接来说,使用join()方法的效率要高一些。
>>>print(' '.join(['I','love','fishc.com']))
I love fishc.com
5.2字符串格式化
format():待补充
6序列
列表、元组和字符串之间有很多共同点:
(1)都可以通过索引得到每一个元素
(2)默认索引值总是从0开始(当然灵活的Python还支持负数索引)
(3)可以通过分片的方法得到一个范围内的元素的集合
(4)有很多共同的操作符(重复操作符、拼接操作符、成员关系操作符)
我们把它统称为序列。
下面介绍一些关于序列的常用BIF(内建方法):
(1)list([iterable])
(2)tuple([iterable])
(3)str(obj)
(4)len(sub)
(5)max()
(6)min()
(7)sum()
(8)sorted()
(9)reversed()
(10)enumerate(iterable)
(11)zip()
7函数
7.1创建和调用函数
在Python中创建一个函数用def关键字,调用时直接写出函数名加上小括号部分即可:
>>>def myFirstFuction():
print("这是我创建的第一个函数!")
>>>myFirstFuction()
这是我创建的第一个函数
7.2函数的参数
参数使得函数可以实现个性化,多个参数用逗号隔开:
>>>def myFirstFuction(name):
print(name + "是美女")
>>>myFirstFuction("我")
>>>myFirstFuction("你")
>>>def add(num1, num2):
print(num1 + num2)
>>>add(1,2)
我是美女
你是美女
3
(1)形参和实参
形参:函数创建和定义过程中小括号里的参数。
实参:函数在被调用时传递进来的参数。
(2)关键字参数
普通的参数叫位置参数,通常在调用一个函数时,粗心的程序员很容易会搞乱位置参数的顺序。有了关键字参数,就可以解决该问题:
>>>def saySomething(name, world):
print(name + '->' + world )
>>>saySomething("小甲鱼", "让编程改变世界")
>>>saySomething(world="让编程改变世界", name="小甲鱼")
小甲鱼->让编程改变世界
小甲鱼->让编程改变世界
关键词参数其实就是在传入实参时指定形参的变量名。
(3)默认参数
在定义时赋予了默认值的参数:
>>>def saySomething(name = "小甲鱼", world = "让编程改变世界"):
print(name + '->' + world )
>>>saySomething()
小甲鱼->让编程改变世界
(4)收集参数/可变参数
发明该机制的原因是函数的作者有时也不知道这个函数到底需要多少个参数,这时,仅需要在参数前边加上星号(*)即可:
>>>def test(*params):
print("有%d个参数" % len(params))
print("第二个参数是:", params[1])
>>>test('F','i','s','h','C')
有5个参数
第二个参数是: i
如果在收集参数后还需要指定其它参数,在调用函数时就应该使用关键参数来指定:
>>>def test2(*params, extra):
print("收集参数是:",params)
print("位置参数是:",extra)
>>>test2(1,2,3,4,5,6,7,extra=8)
收集参数是: (1, 2, 3, 4, 5, 6, 7)
位置参数是: 8
(5)函数返回值
函数需要返回值时,只需要使用关键字return,后面跟着指定要返回的值:
>>>def add(num1, num2):
return num1+num2
>>>print(add(1, 2))
3
7.3内嵌函数和闭包
略
7.4lamda表达式
Python允许使用lamda关键字来创建匿名函数。
语法:基本语法是冒号(:)左边放原函数的参数,可以有多个参数,用逗号(,)隔开即可。lambda语句实际上是返回一个函数对象,如果要对它进行使用,只需要进行简单的赋值操作即可。
# 普通函数
>>>def add(x,y):
return x+y
>>>print(add(3,4))
# lambda表达式
>>>g = lambda x,y:x+y
>>>print(g(3,4))
7
7
7.5介绍两个BIF:filter()和map()
略
7.6递归
略
8字典:当索引不好用时
字典包括:键(key)和值(value)。另外,字典在有些地方称为哈希(hash),有些地方称为关系数组。字典的践必须独一无二,而值可以取任何数据类型,但必须是不可变的(如字符串、数和元组)。
8.1创建和访问字典
使用dict函数创建:
dict0 = dict() # 传一个空字典
print('dict0:', dict0)
dict1 = dict({'three': 3, 'four': 4}) # 传一个字典
print('dict1:', dict1)
dict2 = dict(five=5, six=6) # 传关键字
print('dict2:', dict2)
dict3 = dict([('seven', 7), ('eight', 8)]) # 传一个包含一个或多个元祖的列表
print('dict3:', dict3)
dict5 = dict(zip(['eleven', 'twelve'], [11, 12])) # 传一个zip()函数
print('dict5:', dict5)
dict0: {}
dict1: {'four': 4, 'three': 3}
dict2: {'five': 5, 'six': 6}
dict3: {'seven': 7, 'eight': 8}
dict5: {'twelve': 12, 'eleven': 11}
8.2各种内置方法
字典是Python中唯一的映射类型,字典不是序列。如果在序列中试图为一个不存在的位置赋值时,会报错;但是如果在字典中,会自动创建相应的键并添加对应的值进去。
(1)fromkeys()
用于创建并返回一个新字典。
(2)keys()、values()、items()
keys()用于返回字典中的键,values()用于返回字典中所有的值,items()返回字典中所有的键值对:
>>>dict1 = {}
>>>dict1 = dict1.fromkeys(range(32), "赞")
>>>print(dict1.keys())
>>>print(dict1.values())
>>>print(dict1.items())
dict_keys([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31])
dict_values(['赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞', '赞'])
dict_items([(0, '赞'), (1, '赞'), (2, '赞'), (3, '赞'), (4, '赞'), (5, '赞'), (6, '赞'), (7, '赞'), (8, '赞'), (9, '赞'), (10, '赞'), (11, '赞'), (12, '赞'), (13, '赞'), (14, '赞'), (15, '赞'), (16, '赞'), (17, '赞'), (18, '赞'), (19, '赞'), (20, '赞'), (21, '赞'), (22, '赞'), (23, '赞'), (24, '赞'), (25, '赞'), (26, '赞'), (27, '赞'), (28, '赞'), (29, '赞'), (30, '赞'), (31, '赞')])
(3)get()
提供了更宽松的方式去访问字典项,当键不存在时,get()方法并不会报错,只是默默返回一个none,表示啥也没找到:
>>>dict1 = {}
>>>dict1 = dict1.fromkeys(range(32), "赞")
>>>print(dict1.get(31))
>>>print(dict1.get(32))
赞
None
如果希望找不到数据时返回指定的值,那么可以在第二个参数设置对应的默认返回值:
>>>dict1 = {}
>>>dict1 = dict1.fromkeys(range(32), "赞")
>>>print(dict1.get(31))
>>>print(dict1.get(32,"木有"))
赞
木有
如果需要清空一个字典,则使用clear()方法:
>>>dict1 = {}
>>>dict1 = dict1.fromkeys(range(32), "赞")
>>>dict1.clear()
>>>print(dict1)
{}
有的人可能会使用变量名赋值为一个空字典的方法来清空字典,这样做存在一定的弊端。
(4)setdefault()
和get()方法类似,但是它在字典中找不到相应的键时会自动添加:
>>>a = {1:"one", 2:"two", 3:"three", 4:"four"}
>>>print(a.setdefault(3))
>>>a.setdefault(5)
>>>print(a)
three
{1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: None}
(5)copy()
复制字典:
>>>a = {1:"one", 2:"two", 3:"three"}
>>>b = a.copy()
>>>print(a)
>>>print(b)
>>>print(id(a))
>>>print(id(b))
{1: 'one', 2: 'two', 3: 'three'}
{1: 'one', 2: 'two', 3: 'three'}
1752517929864
1752517929936
(6)pop()和popitem()
pop()是给定键弹出对应的值,而popitem()弹出一个项:
>>>a = {1:"one", 2:"two", 3:"three", 4:"four"}
>>>a.pop(2)
>>>print(a)
>>>a.popitem()
>>>print(a)
{1: 'one', 3: 'three', 4: 'four'}
{1: 'one', 3: 'three'}
(7) update()
更新字典:
>>>a = {1:"one", 2:"two", 3:"three", 4:"five"}
>>>a.update({4:"four"})
>>>print(a)
{1: 'one', 2: 'two', 3: 'three', 4: 'four'}
9 集合:在我的世界里,你就是唯一
集合和字典长得很像:
>>>num1 = {}
>>>print(type(num1))
>>>num2 = {1,2,3,4,5}
>>>print(type(num2))
<class 'dict'>
<class 'set'>
在Python3中,如果用大括号括起一堆数字但没有体现映射关系,那么认为是集合。
集合起的作用:唯一。
>>>num = {1,2,3,4,5,4,3,2,1}
>>>print(num)
{1, 2, 3, 4, 5}
集合会自动帮我们将重复的数据清理掉。注意:集合无序,即你不能试图去索引集合中的某一个元素:
>>>num = {1,2,3,4,5}
>>>print(num[2])
Traceback (most recent call last):
File "D:/Project/untitled1/test.py", line 2, in <module>
print(num[2])
TypeError: 'set' object does not support indexing
9.1 创建集合
法1:直接将一堆元素用大括号({})括起来。
法2:使用set()。
>>>set1 = {1,2,3,2}
>>>set2 = set([1,2,3,2])
>>>print(set1 == set2)
True
若要求去除列表[1,2,3,4,5,5,3,1,0]中重复的元素,可以这样写:
>>>list1 = [1,2,3,4,5,5,3,1,0]
>>>list1 = list(set(list1))
>>>print(list1)
[0, 1, 2, 3, 4, 5]
注意:由于集合无序,这样转换之后不能保证原来的list的顺序。
9.2 访问集合
由于集合中元素无序,所以不能向序列那样用下标来访问,但是可以使用迭代将集合中的数据一个个读取出来。
>>>set1 = {1,2,3,4,5,4,3,2,1,0}
>>>for each in set1:
>>> print(each, end=' ')
0 1 2 3 4 5
使用add()方法可以为集合添加元素,使用remove()方法可以删除集合中已知的元素:
>>>set1 = {1,2,3,4,5,4,3,2,1,0}
>>>print(set1)
>>>set1.add(6)
>>>print(set1)
>>>set1.remove(0)
>>>print(set1)
{0, 1, 2, 3, 4, 5}
{0, 1, 2, 3, 4, 5, 6}
{1, 2, 3, 4, 5, 6}
9.3 不可变集合
有时希望集合中元素具有稳定性,即像元组一样不能随意增加或删除集合中的元素,那么我们可以定义不可变集合,使用frozenset()函数:
>>>set1 = frozenset({1,2,3,4,5,4,3,2,1,0})
>>>set1.add(6)
Traceback (most recent call last):
File "D:/Project/untitled1/test.py", line 3, in <module>
set1.add(6)
AttributeError: 'frozenset' object has no attribute 'add'
10 类和对象
10.1 对象=属性+方法
如果将“乌龟”写成代码,将会是:
class Turtle:
# Python中的类名约定以大写字母开头,函数名用小写字母开头,更容易区分
# 特征的描述称为属性,在代码层面来看其实就是变量
color = 'green'
weight = 10
legs = 4
mouth = '大嘴'
#方法其实就是函数,通过调用这些函数来完成某些工作
def climb(self):
print("我正在很努力地向前爬...")
def run(self):
print("我正在飞快地向前跑...")
def sleep(self):
print("困了,睡了,晚安!")
以上代码定义了对象的特征(属性)和行为(方法),但还不是一个完整对象,将定义的这些称为类(Class)。需要用类创建一个真正的对象,这个对象叫作这个类的一个实例(Instance),也叫实例对象(Instance Objects)。
创建一个对象:
>>>tt = Turtle()
调用对象里的方法:
>>>tt.climb()
>>>tt.run()
我正在很努力地向前爬...
我正在飞快地向前跑...
10.2 self是什么
Python的self其实就是C++的this指针。
由同一个类可以生成无数对象,当一个对象的方法被调用时,对象会将自身的引用作为第一个参数传给该方法,那么Python就知道需要操作哪个对象的方法了:
>>>class Ball:
def setName(self,name):
self.name = name
def myName(self):
print("我叫%s" %self.name)
>>>a = Ball()
>>>a.setName("小虎队")
>>>b = Ball()
>>>b.setName("TFBoys")
>>>a.myName()
>>>b.myName()
我叫小虎队
我叫TFBoys
10.3 __init__()构造方法
类似于C++中的构造函数,只要实例化一个对象,该方法就会在对象被创建时自动调用。
>>>class Potato:
def __init__(self, name):
self.name = name
def myName(self):
print("我叫%s" %self.name)
>>>p = Potato("土豆")
>>>p.myName()
我叫土豆
10.4 公有和私有
一般面向对象的编程语言都会区分公有和私有的数据类型,像C++用关键字public和private,但在Python中并无类似关键字来修饰。默认Python的对象属性和方法都是公开的,可以直接通过点操作符(.)进行访问。
为实现类似私有变量的特征,Python内部采用一种叫name mangling(名字改编)的技术,只要在变量名或函数名前加上“__”两个下划线,则变量或函数就会成为私有的:
>>>class Person:
__name = "小甲鱼"
>>>p = Person()
>>>p.__name
Traceback (most recent call last):
File "D:/Project/untitled1/test.py", line 4, in <module>
p.__name
AttributeError: 'Person' object has no attribute '__name'
这样在外部将变量名隐藏,理论上如果要访问,就需要从内部进行:
>>>class Person:
def __init__(self, name):
self.__name = name
def getName(self):
return self.__name
>>>p = Person("小甲鱼")
>>>print(p.getName())
小甲鱼
注意:Python目前的私有机制其实是伪私有,在外部你使用“_类名__变量名”即可访问双下横线开头的私有变量了:
>>>class Person:
def __init__(self, name):
self.__name = name
def getName(self):
return self.__name
>>>p = Person("小甲鱼")
>>>print(p._Person__name)
小甲鱼
10.5 继承
对鱼类细分:金鱼(Goldfish)、鲤鱼(Carp)、三文鱼(Salmon)和鲨鱼(Shark)。
继承:
import random as r
class Fish:
def __init__(self):
self.x = r.randint(0,10)
self.y = r.randint(0, 10)
def move(self):
#这里主要演示类的继承机制,就不考虑检查场景边界和移动方向的问题
#假设所有鱼都是一路向西游
self.x -= 1
print("我的位置是:", self.x, self.y)
class Goldfish(Fish):
pass
class Carp(Fish):
pass
class Salmon(Fish):
pass
#上面几个都是食物,食物不需要有个性,所以直接继承Fsih类的全部属性和方法即可
#下面定义鲨鱼类,这个是吃货,除了继承Fish类的属性和方法,还要添加一个吃的方法
class Shark(Fish):
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry == True:
print("我要吃")
self.hungry = False
else:
print("我太撑")
fish = Fish()
fish.move()
goldfish = Goldfish()
goldfish.move()
goldfish.move()
goldfish.move()
shark = Shark()
shark.eat()
shark.eat()
shark.move()
-----------------------------------------------------------------------------------------
我的位置是: 0 7
我的位置是: 2 6
我的位置是: 1 6
我的位置是: 0 6
我要吃
我太撑
shark.move()
File "D:/Project/untitled1/test.py", line 10, in move
self.x -= 1
AttributeError: 'Shark' object has no attribute 'x'
继承语法:
class 类名 (被继承的类)
被继承的类称为基类、父类或超类;继承者称为子类,一个子类可以继承它的父类的任何属性和方法。
奇怪!同样是继承于Fish类,为什么金鱼(goldfish)可以移动,而鲨鱼(shark)一移动就报错呢?
因为Shark对象没有x属性。原因:在Shark类中,重写了魔法方法__init__,但新的__init__方法里边没有初始化鲨鱼的x坐标和y坐标,因此调用move方法会报错。解决此问题的两个方法:
(1)调用未绑定的父类方法
class Shark(Fish):
def __init__(self):
Fish.__init__(self)
self.hungry = True
def eat(self):
if self.hungry == True:
print("我要吃")
self.hungry = False
else:
print("我太撑")
(2)使用super函数
class Shark(Fish):
def __init__(self):
super().__init__()
self.hungry = True
def eat(self):
if self.hungry == True:
print("我要吃")
self.hungry = False
else:
print("我太撑")
不需要明确给出任何基类的名字,super会自动找出所有基类以及对应的方法。这也意味着如果需要改变类继承关系,只要改变class语句里的父类即可,不必在大量代码中去修改所有被继承的方法。
10.6 多重继承
class 类名 (父类1, 父类2, 父类3,……)
多重继承易造成代码混乱,尽量避免使用。
10.7 组合
学习了继承和多重继承的概念,但听到大牛们说不到必要时不使用多重继承。哎呀,这可让大家烦死了,就像上回我们有了乌龟类、鱼类,现在要求定义一个类,叫水池,水池里要有乌龟和鱼。用多重继承就显得很奇怪,因为水池和乌龟,鱼是不同物种,那要怎样才能把它们组合成一个水池的类呢?
在Python里很简单,直接把需要的类放进去实例化就可以了,这叫组合:
class Turtle:
def __init__(self,x):
self.num = x
class Fish:
def __init__(self,x):
self.num = x
class Pool:
def __init__(self,x,y):
self.turtle = Turtle(x)
self.fish = Fish(y)
def print_num(self):
print("水池里共有乌龟%d只,小鱼%d条!" %(self.turtle.num, self.fish.num))
pool = Pool(1,10)
pool.print_num()
水池里共有乌龟1只,小鱼10条!
10.8 一些相关的BIF
下面介绍与类和对象相关的一些BIF(内置函数)。
(1)issubclass(class, classinfo)
* 如果class是classinfo的一个子类,则返回True,否则返回False;
* classinfo可以是类对象组成的元组,只要class是其中任何一个候选类的子类,则返回True;
* 一个类被认为是其自身的子类;
* 在其他情况下,会抛出一个TypeError异常。
(2)isinstance(object, classinfo)
* 如果object是classinfo的实例对象,则返回True,否则返回False;
* 如果object是classinfo子类的一个实例,也返回True;
* 如果第一个参数不是对象,永远返回False;
* classinfo可以是类对象组成的元组,只要object是其中任何一个候选对象的实例,则返回True;
* 如果第2个参数不是类或由类对象组成的元组,会抛出TypeError异常。
(3)
hasattr(object, name):测试一个对象里是否有指定的属性;
getattr(object, name [, default]):返回对象指定的属性值,若指定的属性值不存在,则返回default的值;若未设置default,则抛出ArttributeError异常;
setattr(object, name, value):设置对象中制定属性的值,若指定属性值不存在,则会新建属性并赋值。
delattr(object, name):删除对象中指定的属性,若属性不存在,则抛出ArttributeError异常。
class C:
def __init__(self, x=0):
self.x = x
c1 = C()
print(hasattr(c1, 'x')) #注意,属性名要用引号括起来
print(getattr(c1, 'x'))
setattr(c1, 'y', 'FishC')
print(getattr(c1, 'y'))
delattr(c1,'y')
True
0
FishC
11 模块
11.1 模块就是程序
容器:例如列表、元组、字符串、字典等,这些是对数据的封装;
函数:对语句的封装;
类:对方法和属性的封装,也就是对函数和数据的封装;
模块:就是程序。平时写的任何代码,保存的每一个.py结尾的文件,都是一个独立的模块。
11.2 命名空间
在Python中,每个模块都会维护一个独立的命名空间,我们应该将模块名加上,才能够使用模块中的函数。
11.3 导入模块
下面介绍几种导入模块的方法:
(1)import模块名
在调用模块中函数时,需要加上模块的命名空间:
# p13_1.py
def c2f(cel):
fah = cel*1.8+32
return fah
def f2c(fah):
cel = (fash-32)/1.8
return cel
# 再写一个文件导入刚才的模块
#p13_2.py
import p13_1
print("32摄氏度=%.2f华氏度" %p13_1.c2f(32))
print("99华氏度=%.2f摄氏度" %p13_1.f2c(99))
(2)from 模块名 import 函数名
该法会直接将模块的命名空间覆盖进来,所以调用时不需加上命名空间:
# p13_1.py
def c2f(cel):
fah = cel*1.8+32
return fah
def f2c(fah):
cel = (fash-32)/1.8
return cel
#p13_3.py
from p13_1 import c2f,f2c
print("32摄氏度=%.2f华氏度" %c2f(32))
print("99华氏度=%.2f摄氏度" %f2c(99))
还可以使用通配符星号(*)来导入模块中所有的命名空间,但是强烈要求不要使用该法,因为这样会使得命名空间的优势荡然无存,一不小心还会陷入名字混乱的局面:
from p13_1 import *
(3)import 模块名 as 新名字
推荐该法。给导入的命名空间替换一个新名字:
# p13_1.py
def c2f(cel):
fah = cel*1.8+32
return fah
def f2c(fah):
cel = (fash-32)/1.8
return cel
#p13_4.py
import p13_1 as tc
print("32摄氏度=%.2f华氏度" %tc.c2f(32))
print("99华氏度=%.2f摄氏度" %tc.f2c(99))
11.4 __name__ = '__main__'
# 1.py
def c2f(cel):
fah = cel*1.8+32
return fah
def f2c(fah):
cel = (fash-32)/1.8
return cel
def test():
print("测试,0摄氏度=%.2f华氏度" % c2f(0))
print("测试,0华氏度=%.2f摄氏度" % f2c(0))
if __name__ == '__main__' #确保只有单独运行1.py时才会执行test()函数
test()
11.5 搜索路径
Python模块的导入需要一个路径搜索的过程。而这个搜索路径,就是一组目录,可以通过sys模块中的path变量显示出来:
>>>import sys
>>>print(sys.path)
['D:\\Project\\untitled1', 'D:\\Project\\untitled1', 'D:\\Anaconda\\envs\\GameServer\\python36.zip', 'D:\\Anaconda\\envs\\GameServer\\DLLs', 'D:\\Anaconda\\envs\\GameServer\\lib', 'D:\\Anaconda\\envs\\GameServer', 'D:\\Anaconda\\envs\\GameServer\\lib\\site-packages']
site=packages目录是最佳的目录选择。
将自定义模块所在的位置添加到搜索路径中:
sys.path.append("路径")
11.6 包
在实际的开发中,一个大型系统有成千上万的Python模块,单单用模块定义功能显然不够,如果都放在一起显然不好管理并且有命名冲突的可能,因此出现了包。就是将模块分门别类存放于不同文件夹中,然后把 各个文件夹的位置告诉Python。
创建一个包的操作:
(1)创建一个文件夹,用于存放相关的模块,文件夹名字即包的名字;
(2)在文件夹中创建一个__init__.py的模块文件,内容可为空;
(3)将相关模块放入文件夹。
接下来就是在程序中导入包的模块(包名.模块名):
# 1.py
def c2f(cel):
fah = cel*1.8+32
return fah
def f2c(fah):
cel = (fash-32)/1.8
return cel
# 2.py
#将1.py放在文件夹M中
import M.1 as tc
print("32摄氏度=%.2f华氏度" %tc.c2f(32))
print("99华氏度=%.2f摄氏度" %tc.f2c(99))