写在前面的话:本章内容为本人学习大神“cutercorley”的“python全栈”博客后,整理总结所得。如果想要深入学习可以看看大神的博客。链接如下:https://blog.csdn.net/cufeecr/category_9441490.html🙃
一.数据类型详解
1.字符串
1.1 定义及格式
- 字符串就是由字符、数字、下划线组成的一串字符,如’Hello’、“World”。
- 字符串用单引号或双引号包含,但是单双引号必须成对使用,不能混用,并且相同引号之间不能嵌套。
- 可由
type()
函数来检测字符串的类型。
a = 'hello world'
b = "hello world"
c = "hello 'world'"
d = 'hello "world"'
1.2 字符串常用操作
- 下标索引
就是指的编号,类似超市储物柜的编号,通过编号就能找到对应的东西。
a = 'hello world'
print(a[0]) # 获取字符串的第一个元素
print(a[1]) # 获取字符串的第二个元素
print(a[-1]) # 获取字符串的最后一个元素
运行结果如下:
- 切片
获取字符串的某一范围内的内容
格式:[起始:结束:步长] 区间位置:左闭右开
运行结果如下:name='xiaomingasoiffdh' print(name[0:3]) # 获取字符串下标为0,1,2的内容。 print(name[2:]) #从下标2(包含下标2)开始取到最后 print(name[:3]) #从头开始取,直到索引值为3的位置(不包括该元素) print(name[2:-1]) #从索引值为2的位置开始取,直到倒数第一个(不包括倒数第一个) print(name[0::2]) #从头开始取直到最后,每两个切出一个 print(name[::-1]) #倒序的内容输出 print(name[5:0:-1]) #倒序切出索引5到索引值1中间的数据
- 字符串长度
用len()
函数。
name='xiaomingasoiffdh'
print(len(name))
- 字符串运算
用s*20
即可将字符串s打印20编。
- 字符串包含
用in来判断一个字符串是否在另一个字符串中,如用a in b
来判断a是否包含于b。如果包含则返回True,否则返回Flase
- 求最大值和最小值
用max()
和min()
函数。
- 求字符在ASCII表中的十进制数值
用ord()
函数,如print(ord('A'))
。
- 分割字符串
用split()
函数,返回列表。
如
name='my_name_is_haha'
a = name.split('_') # 表示以'_'来分割字符串,返回分割后的列表
print(a)
运行结果如下:
- 拼接字符串
用join()
方法,如'_'.join(s)
。
a = ['my', 'name', 'is', 'haha']
s = '_'.join(a) # 使用'_'来将列表中的元素拼接为字符串
print(s)
运行结果如下:
- 去掉空格
strip()
去掉字符串左右两边的空格;
lstrip()
去掉字符串左边的空格;
rstrip()
去掉字符串右边的空格。
如
s=' I love Python '
print(s.strip())
print(s.lstrip())
print(s.rstrip())
- 字符串大小写
upper()
全部大写;
lower()
全部小写;
capitalize()
首字母大写;
isupper()
判断是否大写;
islower()
判断是否小写。
例如:
s='I love Python'
print(s.upper())
print(s.capitialize())
print(s.islower())
-
查找字符串中是否包含特定内容
str1.find(str)-----检查str是否包含在str1中,如果是返回开始的索引值,否则返回-1str1 ="good good study,day day up" index = str1.find("stu") print(index)
-
index
跟find()方法一样,只不过目标字符串如果不在要查找的字符串中会报一个异常
-
查看特定内容在字符串中出现的次数
str1.count(str,start,end)-----返回str在str1中(start和end范围内)出现的次数,start和end可以不指定。默认为0,-1。返回str在目标字符串中start-end之间出现的次数 str1="good good study,day day up" print(str1.count("good"))
-
将字符串中的某内容替换为其他内容
# 把str1中指定的字符串"good",用“222”进行替换,最多替换2次 str1="good good study,day day up" print(str1.count("good")) str2 = str1.replace("good","222",2) print(str2)
-
检查字符串是否以指定字符串开头
# 检查字符串是否以指定字符串开头, # 是则返回True,否则返回False str1 ="good good study,day day up" res = str1.startswith('good') print(res)
-
检查字符串是否以指定字符串结尾
# 检查字符串是否以指定字符串结尾,是则返回True,否则返回False str1 ="good good study,day day up" res = str1.endswith('up') print(res)
-
字符串左对齐
# 返回一个原字符串左对齐,并使用空格填充至长度width的新字符 str1 = "hello" print(str1.ljust(10))
-
字符串右对齐
# 返回一个原字符串右对齐,并使用空格填充至长度width的新字符 str1 = "hello" print(str1.rjust(10))
-
字符串居中对齐
# 返回一个原字符串居中对齐,并使用空格填充至长度width的新字符 str1 = "hello" print(str1.center(10))
-
rfind
类似于find()函数,不过是从右边开始查找
-
rindex
类似与index()函数,不过是从右边开始查找
-
partition
# 把目标字符串分割成str前,str以及str后三部分,得到一个tuple(元组) str1="nihaoma" str1.partition("hao") # 结果为:('ni', 'hao', 'ma')
-
判断目标字符串中是否所有的字符都为字母
# 判断目标字符串中是否所有的字符都为字母,返回True,或者False str1 = "123Hello" print(str1.isalpha())
-
判断目标字符串中是否所有的字符都为数字
# 判断目标字符串中是否所有的字符都为数字,返回True或者False str1 = "123Hello" print(str1.isdigit())
-
判断目标字符串中是否所有的字符都为数字或者字母
# 如果字符串中都是字母或者数字则返回True,否则返回False str1="abc123" print(str1.isalnum())
-
判断目标字符串中是否所有的字符都为空格
# 如果字符串中只包含空格,则返回True,否则返回False str1="abc123" print(str1.isspace())
2.列表
2.1 定义及格式
列表是Python中最基本也是最常用的数据结构之一,列表中的每一个元素被分配一个数字作为索引(即下标),用来表示该元素在列表内所排的位置,第1个元素索引是0,第2个索引是1,依次类推。
列表是一个有序可重复的集合。
- 语法格式:
names=["李大钊","陈独秀","蔡元培","陈佩斯"]
#注意:比C语言中的数组功能更强大,列表中的元素可以为不同类型
list1=[10,"人",2.5,True]
2.2列表的创建
创建列表:
-
方式一:用函数方式
l = list(iterable) # iterable表示一个可迭代的对象
可迭代:可以用for循环遍历即可迭代;
参数中传入可迭代的类型如字符串、列表、元组、字典、集合等。
在PyCharm可按CTRL键并将鼠标光标直到函数上查看该函数的Python官方说明。 -
方式二:用方括号[]
l = [1,2,3,4,5] print(l)
2.3访问列表内的元素
- 列表从0开始为它的每一个元素顺序创建下标索引,直到总长度减1。要访问它的某一个元素,以方括号加下标值的方式即可。
- 注意要确保索引不越界,一旦访问的索引超过范围,会抛出异常,一定要记得最后一个元素的索引是
len(list)-1
. - 索引必须为整型,其他类型会报错。
如
lst = [1,2,3]
num = lst[1]
print(num)
会打印出2。
2.4 修改元素的值
lst = [1,2,3,4,5]
lst[1] = 'hello'
print(lst)
打印结果如下:
[1, 'hello', 3, 4, 5]
注意:不能赋值给列表中不存在的索引。
2.5.删除值
-
方式一:用
del
,根据索引删除值del lst[0] # 删除索引为0的内容
-
方式二:根据值进行删除
lst.remove(3) # 删除内容3
删除值为3的这个元素
-
方式三:删除并返回删除的值
value = lst.pop() print(value)
该方法有返回值,为删除的元素的值;
可以传参数,如无参数默认弹出最后一个元素,有则删除指定索引的元素。
如传参数为:value = lst.pop(2) # 删除并返回索引为2的值 print(value)
即删除第3个元素。
2.6 列表组合
直接将两个列表相加,是列表的拼接,不是对应位置相加。
l1 = [1,2,3]
l2 = [4,5,6]
l3 = l1 + l2
print(l3)
打印:
[1, 2, 3, 4, 5, 6]
也可用
l3 = l1.__add__(l2)
,此为魔法方法,为面向对象的方法。
2.7列表的乘法
l4 = l1 * 3
相当于
l4 = l1.__mul__(3)
是将列表l1重复3次,成为一个列表;
两个列表不能直接相乘,否则会报错。
2.8判断元素是否在列表中
用关键词in
:
print(2 in l1)
2.9迭代列表中的每个元素
for i in l1:
print(i)
2.10 列表长度
l1 = [1,2,3]
print(len(l1))
相当于
print(l1.__len__())
2.11 最大值与最小值
l1 = [1,2,3]
print(max(l1),min(l1))
2.12 类型转换
lst = list('str')
1
属于强制类型转换
print(type(lst))
1
type()
函数可以查看变量的类型。
2.13 列表排序
(1)反转
lst = [1,3,2,4]
lst.reverse()
print(lst)
打印出
[4, 2, 3, 1]
1
很显然,reverse()
函数对列表进行了改变,不再是原来的列表。
(2)排序
lst.sort()
为默认升序
lst.sort(reverse=True)
1
为降序
排序只能对元素为同种类型的值进行排序,不能对不同类型的值进行排序,要么全为数值型,要么均为字符串。
2.14 切片
切片即为对序列进行截取,选取序列中的某一段。
语法为:
list[start : end : step]
step为步长,区间为左闭右开。
lis = ['a','b','c','d','e']
print(lis[1:3])
打印
['b', 'c']
- 省略end取到末尾,省略start从头开始到当前,省略所有复制列表。
- 步长为负数时反方向取,即从尾到头取。
2.15 列表的内置方法
(1)append()
添加一个元素到末尾
lis = ['a','b','c','d','e']
lis.append('e')
print(lis)
打印
['a', 'b', 'c', 'd', 'e', 'e']
如参数为列表,则列表作为一个元素添加到之前的列表末尾。
(2)count()
对传入的元素进行计数
lis = ['a','b','c','d','e']
print(lis.count('a'))
结果为1。
(3)extend()
在列表末尾一次性追加另一个序列中的多个值(用新序列扩展原来的列表)
lis = ['a','b','c','d','e']
lis.extend(['f','g'])
print(lis)
打印出
['a', 'b', 'c', 'd', 'e', 'f', 'g']
(4)insert()
根据索引的位置添加元素
lis = ['a','b','c','d','e']
lis.insert(2,'g')
print(lis)
打印出
['a', 'b', 'g', 'c', 'd', 'e']
(5)copy()
浅复制
lis = ['a','b','c','d','e']
lst = lis.copy()
print(lst)
相当于lst = lis[:]
(6)clear()
清空列表
3.元组
3.1 元组概念
Python的元组与列表类似,不同之处在于元组的元素不能修改,元组使用“()”
列表使用"[]"
3.2 元组操作
- 访问
t=(1,2,3)
print(t[0])
修改或者删除元素的话会报错:
t[0]= 10
删除报错:
t.pop()
但是可以变向进行删除
tuple->list(进行删除操作)->tuple
3.3 可变对象与不可变对象
Python中,不可变类型包括:
int 、float、 字符串str 、元组tuple、bool。
可变类型包括:
列表list、集合set、字典dict。
每个对象在内存中保存了3个数据:
id(标识)
type(类型)
value(值)
举例:
列表a = [1,2,3]
,在内存中的一定区域,会分别保存一个对象的id(假设为0x111)、type(class list)、value([1,2,3])。
对序列中的元素进行改变即对值进行改变。
- 改对象:
如a[0] = 10,则a变为[10,2,3]
这个操作是通过变量修改对象里面的值,不会改变变量指向的操作。 - 改变量:
如a = [4,5,6]
,即a发生了改变,假设id变为0x211,此时为id(0x211)、type(class list)、value([4,5,6])
对变量重新赋值,会改变变量所指向的对象。
修改前:
a = [1,2,3]
print(a,id(a))
打印
[1, 2, 3] 1843535938056
改对象后:
a[0] = 10
print(a,id(a))
打印
[10, 2, 3] 1843535938056
显然,此时a对象未发生改变。
改变量后:
a = [4,5,6]
print(a,id(a))
打印
[4, 5, 6] 1384044909640
显然,此时a对象发生改变。
将a赋给新变量b:
a = [1,2,3]
b = a
b[0] = 10
print('a:',a,id(a))
print('b:',b,id(b))
打印
a: [10, 2, 3] 1963325084232
b: [10, 2, 3] 1963325084232
显示,a和b的值和id完全一致。
解释:
生成a时,a对应的值为id(假设为0x111)、type(class list)、value([1,2,3]),
将a赋给b时,其实只是将b指向了a对应的内存对象的内存地址,并未分配新的内存空间给b,a和b共用一个内存地址,所以a和b打印出来的结果一致。
a = [1,2,3]
b = a
b = [10,2,3]
print('a:',a,id(a))
print('b:',b,id(b))
打印
a: [1, 2, 3] 2260109775432
b: [10, 2, 3] 2260109775944
此时b和a不一致,因为b是通过重新赋值得到的新列表,系统会重新分配一个内存地址,所以id会不同。
3.4 ==和is
==
和!=
比较的是对象的值是否相等;
is
和is not
比较的是对象的id是否相等。
a = [1,2,3]
b = [1,2,3]
print(a == b)
print(a is b)
打印
True
False
!=和is not的用法类似。
4.字典
4.1.定义
字典即dict,是一种新的数据结构,也可以叫映射(mapping)。
字典的作用:
存储对象的容器。
列表存储数据性能很好,但是查询数据性能很差;
字典中每一个元素都有唯一的名字,通过这个名字可以快速查找到指定的元素,这个唯一的名字称为键(key),通过key可以查找到值(value),所以字典也称为键值对(key-value),每个字典可以有多个键值对,每个键值对称为一项。
字典中键和值的类型:
值可以是任意对象;
键可以是任意不可变的对象,包括int、str、bool、tuple等。
4.2 创建字典
(1)用{}
语法:{key1:value1,key2:value2,...}
。
d = {'name':'Tom','age':'20','gender':'male'}
print(d,type(d))
打印
{'name': 'Tom', 'age': '20', 'gender': 'male'} <class 'dict'>
字典的键不能重复,如有重复,则后边的会覆盖前边的。
如
d = {'name':'Tom','age':'20','gender':'male','name':'Jerry'}
print(d,type(d))
打印
{'name': 'Jerry', 'age': '20', 'gender': 'male'} <class 'dict'>
即前边的Tom被覆盖。
字典还可以跨行写,如
d = {
'name':'Tom',
'age':'20',
'gender':'male'
}
print(d,type(d))
输出与之前相同。
(2)用dict()函数
方式一:
d = dict(name='Tom',age=20,gender='male')
print(d)
打印
{'name': 'Tom', 'age': 20, 'gender': 'male'}
方式二:
d = dict([('name','Tom'),('age',20)])
print(d,type(d))
打印
{'name': 'Tom', 'age': 20} <class 'dict'>
解释:
dict()
函数可以将一个包双值子序列转化为字典。
双值序列:即序列中只有两个值,如[3,4]
、('name','hello)
等。
子序列:如果序列中的元素也是序列,称这个元素为子序列,如[(1,2)]
即为子序列。
4.3 根据键来获取值
d = {
'name':'Tom',
'age':'20',
'gender':'male'
}
print(d['name'],d['age'],d['gender'])
打印
Tom 20 male
4.4 字典的常见用法
len()
获取字典的长度即字典中键值对的个数。
d = dict([('name','Tom'),('age',20)])
print(len(d))
结果为2
。
in、not in
检查字典中是否含有或不含有指定的键。
d = dict([('name','Tom'),('age',20)])
print('hello' in d)
打印
False
获取字典里面的值
语法:d[key]
。
d = dict([('name','Tom'),('age',20)])
print(d['name'])
打印
Tom
如将键赋值给一个变量,则通过变量访问时不需要引号,如
d = {'name':'Tom','age':'20','gender':'male'}
b = 'name'
print(d[b])
打印
Tom
如果键不存在会报错,即KeyError:
d = {'name':'Tom','age':'20','gender':'male'}
print(d['hello'])
打印
KeyError: 'hello'
1
为避免抛出异常,可以用get()
方法
语法:get([key,default])
。
根据键来获取字典中的值,也可以指定一个默认值,作为第二个参数,这样当获取不到键的时候会返回默认值。
如
d = {'name':'Tom','age':'20','gender':'male'}
print(d.get('hello','no such key'))
打印
no such key
修改字典
语法:d[key] = value
。
如果存在则覆盖,不存在则添加。
d = {'name':'Tom','age':'20','gender':'male'}
d['name'] = 'Jerry'
d['phone'] = '010-11111111'
print(d)
打印
{'name': 'Jerry', 'age': '20', 'gender': 'male', 'phone': '010-11111111'}
setdefault()函数
可以向字典当中添加key-value:
如果key已经存在于字典中,则返回key对应的值,不会对字典做任何操作;
如果key不存在,则向字典中添加这个key,并设置value。
key存在时:
d = {'name':'Tom','age':'20','gender':'male'}
result = d.setdefault('name','Jerry')
print(d,result)
打印
{'name': 'Tom', 'age': '20', 'gender': 'male'} Tom
1
key不存在时:
d = {'name':'Tom','age':'20','gender':'male'}
result = d.setdefault('phone','010-11111111')
print(d,'\n',result)
打印
{'name': 'Tom', 'age': '20', 'gender': 'male', 'phone': '010-11111111'}
010-11111111
update()方法
将其他字典中的key-value添加到当前的字典中
d1 = {'a':1,'b':2,'c':3}
d2 = {'d':4,'e':5,'f':6}
d1.update(d2)
print(d1)
打印
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}
如存在相同的键,则覆盖:
d1 = {'a':1,'b':2,'c':3}
d2 = {'b':4,'e':5,'f':6}
d1.update(d2)
print(d1)
打印
{'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6}
删除
-
方式一:
del
删除字典中的key和valued = {'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6} del d['a'] del d['b'] print(d)
打印
{'c': 3, 'e': 5, 'f': 6}
-
方式二:
d.popitem()
随机删除字典中的一个键值对,一般情况会删除最后一个键值对。
删除之后会将key-value作为返回值,返回值为元组。d = {'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6} result = d.popitem() print('result =',result) print(d)
打印
result = ('f', 6) {'a': 1, 'b': 4, 'c': 3, 'e': 5}
-
方式三:
d.pop([,default])
根据key删除字典中的键值对,返回的是被删除的value值d = {'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6} result = d.pop('b') print('result =',result) print(d)
打印
result = 4 {'a': 1, 'c': 3, 'e': 5, 'f': 6}
如被删除的键不存在,则报错。
d = {'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6} result = d.pop('z') print('result =',result) print(d)
抛出异常:
KeyError: 'z'
可以通过在pop中添加第二个参数指定默认值,如果指定,当删除不存在的键时,会返回默认值。d = {'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6} result = d.pop('z','no such key') print('result =',result) print(d)
打印
result = no such key {'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6}
-
方式四:
d.clear()
用来清空字典。d = {'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6} d.clear() print(d)
结果为:
{}
。
浅复制(浅拷贝)
copy()
:用于将字典浅复制,即创建已有字典的副本。
d = {'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6}
d2 = d
print(d,id(d))
print(d2,id(d2))
打印
{'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6} 1997496449288
{'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6} 1997496449288
d和d2指向同一个对象。
当d中的值改变时,d2中的值也随之改变。
d = {'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6}
d2 = d
d['a'] = 7
print(d,id(d))
print(d2,id(d2))
打印
{'a': 7, 'b': 4, 'c': 3, 'e': 5, 'f': 6} 2121296378120
{'a': 7, 'b': 4, 'c': 3, 'e': 5, 'f': 6} 2121296378120
用copy()
方法:
d = {'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6}
d2 = d.copy()
print(d,id(d))
print(d2,id(d2))
打印
{'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6} 3102906251528
{'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6} 3102906251608
d和d2指向不同对象。
当d中的值改变时,d2中的值不会改变。
d = {'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6}
d2 = d.copy()
d['a'] = 7
print(d,id(d))
print(d2,id(d2))
打印
{'a': 7, 'b': 4, 'c': 3, 'e': 5, 'f': 6} 2430767003912
{'a': 1, 'b': 4, 'c': 3, 'e': 5, 'f': 6} 2430767003992
当字典的值为字典时,
d = {'a': {'name':'Tom','age':'20','gender':'male'}, 'b': 4, 'c': 3, 'e': 5, 'f': 6}
d2 = d.copy()
print(d,id(d))
print(d2,id(d2))
打印
{'a': {'name': 'Tom', 'age': '20', 'gender': 'male'}, 'b': 4, 'c': 3, 'e': 5, 'f': 6} 2220628113672
{'a': {'name': 'Tom', 'age': '20', 'gender': 'male'}, 'b': 4, 'c': 3, 'e': 5, 'f': 6} 2220628114392
当d中的值的值改变时,如
d = {'a': {'name':'Tom','age':'20','gender':'male'}, 'b': 4, 'c': 3, 'e': 5, 'f': 6}
d2 = d.copy()
d['a']['name'] = 'Jerry'
print(d,id(d))
print(d2,id(d2))
打印
{'a': {'name': 'Jerry', 'age': '20', 'gender': 'male'}, 'b': 4, 'c': 3, 'e': 5, 'f': 6} 2104435669336
{'a': {'name': 'Jerry', 'age': '20', 'gender': 'male'}, 'b': 4, 'c': 3, 'e': 5, 'f': 6} 2104435670056
复制:即创建已有对象的副本,则原对象改变,副本应该不发生改变,这才是正常的复制。
修改d时,d中的’a’下的’name’的值也发生了改变,此即浅复制,只会复制字典本身,字典中还有字典不会被复制。
总结:浅复制只会复制字典本身,如果字典中还有字典,是不会被复制的。
4.5 遍历字典
keys()
该方法会返回字典所有的键。
d = {'name':'Tom','age':'20','gender':'male'}
for key in d.keys():
print(d[key])
打印
Tom
20
male
2.values()
返回一个序列,保存字典的值。
d = {'name':'Tom','age':'20','gender':'male'}
for value in d.values():
print(value)
打印
Tom
20
male
3.items()
返回字典中所有的项,会返回一个序列,序列中包含双值子序列,双值分别为字典中的key和value。
d = {'name':'Tom','age':'20','gender':'male'}
for key,value in d.items():
print(key,'=',value)
打印
name = Tom
age = 20
gender = male
二.函数
1.函数介绍
- 函数概念
一般情况下,某段代码需要反复使用多次,
而且这段代码又具备特定的功能,
我们会把这段代码组织成为单独的功能模块,
这个功能模块就可以叫做函数
-
函数的定义和调用
- 函数的定义(函数可以重复调用,不能重复定义)
语法格式: def 函数名(): 代码块...
- 函数的调用
调用格式: 函数名()
-
练习 封装函数打印99乘法表:
# 函数的定义
def show99():
i = 1
while i < 10:
j = 1
while j <= i:
print("%d * %d = %d\t"%(j,i,i*j),end=" ")
j += 1
i += 1
print("")
# 函数的调用,只有调用函数,函数中的代码才会被执行
show99()
运行结果如下:
2.函数的参数上
- 定义带有参数的函数
语法格式:
def 方法名(形参...):
代码块(带代码中,形参可以被理解为定义好的一个变量)
- 带参数的函数的调用
方法名(实际参数)
注意:参数的个数要匹配
- 练习 :
def add_three(name,age):
print(f"my name is {name}, I am {age} years old")
add_three('zhangsan',18)
运行结果如下:
3.函数的参数下
3.1位置参数
def sum_a(a,b):
"""求和函数"""
c = a + b
return c
print(sum_a(5,8))
调用时,按照位置将实参与形参一一对应。5对应a,8对应b
3.2关键字参数
def sum_a(a,b):
"""求和函数"""
c = a + b
return c
print(sum_a(b=5,a=8))
调用函数时,使用关键字将实参与形参一一对应。这样参入参数时的位置可以颠倒
3.3默认值参数
def sum_a(a,b=3):
"""求和函数"""
c = a + b
return c
print(sum_a(1))
在定义形参时,可以给参数设置一个默认值。注意,默认值参数必须在最后面。sum_a(a=3,b)这样是错误的写法。当实参没有给默认值参数传值时,会使用默认值
3.4形参的*args,**kwargs
*args表示元组传参,**kwargs表示字典传参
def sum_a(*args):
c = args
return c
print(sum_a(1,2))
结果:(1, 2)
def sum_a(**kwargs):
"""求和函数"""
c = kwargs
return c
print(sum_a(a=1,b=2,c=3))
结果:{'a': 1, 'b': 2, 'c': 3}
一般形参设置为(*args,**kwargs)后,可以接受任何形式的实参
4.函数的返回值
有时候并不需要对结果进行打印,而是进行一些其他的处理,这时候就需要返回值。
返回值就是函数返回的结果。
返回值可以直接使用,也可以通过一个变量来接收函数返回值的结果。
如
def fn():
return 100
r= fn()
print(r)
打印100
。
return后面可以跟任意的对象,甚至是一个函数。
如
def fn():
def fn2():
print('Python')
return fn2
r= fn()
print(r)
打印<function fn.<locals>.fn2 at 0x0000022AF4782EE8>
,即返回了一个函数。
又如
def fn():
def fn2():
print('Python')
return fn2
r= fn()
print(r)
打印
Python <function fn.<locals>.fn2 at 0x000001ECEE8A2EE8>
如果仅仅写一个return(后边不加其他值)或者不写return,相当于return None
。
如
def fn2():
return
r = fn2()
print(r)
打印None
。
在函数中,return后面的代码都不会执行,return一旦执行函数自动结束。
def fn3():
print('Hello')
return
print('ABC')
fn3()
打印Hello
,没有打印出ABC。
return
和break
的比较:
def fn4():
for i in range(5):
if i == 3:
#break用于退出当前循环
break
print(i)
print('Loop ends')
fn4()
打印
0
1
2
Loop ends
def fn4():
for i in range(5):
if i == 3:
#return用来结束函数
return
print(i)
print('Loop ends')
fn4()
打印
0
1
2
123
没有打印出Loop ends
。
计算求和返回结果并运用:
def fn(*nums):
#定义变量保存结果
result = 0
#便利元组,将元组中的元素累加
for n in nums:
result += n
return result
r = fn(1,2,3,4)
print(r+6)
打印16
def fn5():
return 10
print(fn5)
print(fn5())
打印出
<function fn5 at 0x000002997A442F78>
10
12
print(fn5)
是函数对象,实际上是在打印函数对象;
print(fn5())
是在调用函数,实际上是在打印fn5()
函数的返回值。
5.四种函数的类型
- 无参数,无返回值
- 无参数,有返回值
- 有参数,无返回值
- 有参数,有返回值
6.局部变量
- 概念
在函数内部定义,用来存储临时数据的变量,称为临时变量,形参也是局部变量
- 注意
-
在函数内部定义,作用域为函数内部
-
不同函数中可以存在同名变量,互不干涉,没有关系
-
7.全局变量
-
概念
- 既能在其中一个函数中使用,又能在其他函数中使用的变量,成为全局变量
#例: a = 100 def test1(): print(a) def test2(): print(a)
-
全局变量与局部变量同名问题
- 可以重名
a = 100 def test(): a = 200 print(a) test() #这种写法是允许的,打印出的结果为“200”,访问的为离的近的
如果需要在函数内部修改全局变量,则需要使用global
关键字,来声明变量。
a = 20
def fn2():
#声明函数内部使用a是全局变量,此时再修改a时,就是修改全局变量。
global a
a = 10
print('Inside fn2(),a =',a)
fn2()
print('Outside fn2(),a =',a)
打印
Inside fn2(),a = 10
Outside fn2(),a = 10
8.命名空间
命名空间实际上就是一个字典,专门用来存储变量的字典。
函数locals()
用来获取当前作用域的命名空间,返回的是一个字典,把所有的变量存到该字典中。
如果在全局作用域中,调用locals()
则获取全局作用域命名空间,如果在函数作用域中调用locals()
则获取函数命名空间。
s = locals()
print(s)
打印
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000016F7E71CB88>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'Demo.py', '__cached__': None, 's': {...}}
又如
a = 20
s = locals()
print(s)
print(s['a'])
打印
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000024D0E2DCB88>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'Demo.py', '__cached__': None, 'a': 20, 's': {...}}
20
向字典中添加一个键值对,即增加一个变量。
a = 20
s = locals()
print(s)
s['c'] = 200
print(s)
打印
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000022917BECB88>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'Demo.py', '__cached__': None, 'a': 20, 's': {...}}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000022917BECB88>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'Demo.py', '__cached__': None, 'a': 20, 's': {...}, 'c': 200}
在函数中定义变量:
def fn4():
a = 10
s = locals()
print(s)
fn4()
打印{'a': 10}
def fn4():
a = 10
s = locals()
#可以通过操作s来操作函数的命名空间,但是不建议这么做!!
s['b'] = 20
print(s)
fn4()
打印{'a': 10, 'b': 20}
9.递归函数
- 递归概念
在函数中不是调用其他而是调用自身,这种调用方式,称为递归调用
9.1 递归基本概念和使用
练习:尝试求出10的阶乘10!
。
用循环:
result = 1
for i in range(1,11):
result *= i
print(result)
打印3628800
。
要求:创建一个函数求任意数的阶乘:
def factorial(n):
result = 1
for i in range(1,n+1):
result *= i
return result
print(factorial(10))
打印3628800
。
递归:
即递归式函数;
递归简单说就是自己调用自己,递归式函数就是在函数中调用自己。
递归时解决问题的一种方式,思想是将大的问题拆分成小的问题,直到不能再拆分。
递归式函数的两个条件:
1.基线条件:
问题可以分解成最小的问题,当满足基线条件时,递归不再执行。
2.递归条件:
将问题继续分解的条件。
def recursion(n):
#基线条件
if n == 1:
return 1
#递归条件
return n * recursion(n - 1)
print(recursion(10))
打印3628800
9.2 递归练习
- 创建一个函数,来为任意数作任意次幂运算
def exponentiation(n,i):
#n为底数,i为指数
#基线条件,指数i为1
if i == 1:
return n
#递归条件
return n * exponentiation(n,i - 1)
print(exponentiation(2,4))
打印16
。
当然,还有更简单的方法
def exponentiation(n,i):
return n ** i
print(exponentiation(2,4))
即运用Python自带的幂运算符。
- 创建一个函数,用来检查任意的字符串是否为回文字符串,如果是返回True,不是,返回False
回文字符串:即字符串从前往后看和从后往前看是一样的,如123321、abcba。
方法:先检查第一个字符和最后一个字符是否一致,如果不一致不是回文字符串,如果一致,则看剩余部分是不是回文串。
def palindrome(s):
#基线条件,如果字符串长度小于2,则这个字符串是回文串
if len(s) < 2:
return True
elif s[0] != s[-1]:
return False
#递归条件
return palindrome(s[1:-1])
print(palindrome('Python'))
打印False
。
10.匿名函数
- 概念
用lambda关键字创建的简单的没有函数名的函数,这种函数不用def声明
- 语法
语法格式:
lambda 参数... : 表达式
sum = lambda args1,args2: args1+args2
print(sum(1,2))
#结果为:3
- 注意事项:
- 在匿名函数中,不能有return
- 可以有0个、1个、或者多个参数
- 表达式计算后只能返回一个值
11.filter()函数
内置函数filter()
,参数中传入可迭代的结构,即filter(function,iterable)
,可以从序列中过滤出符合条件的元素,保存到一个新的序列中。
参数function:传递实现过滤功能的函数;
参数iterable:需要过滤的序列。
返回值:过滤后的新序列。
l = [1,2,3,4,5,6,7,8,9,10]
def tri(n):
if n % 3 == 0:
return True
print(list(filter(tri,l)))
打印出[3, 6, 9]
。
12.map()函数:
可以对可迭代对象中的所有元素做指定操作,然后将其添加到一个新的可迭代对象并返回。
匿名函数一般都是作为参数使用,其他地方一般不用。
l = [1,2,3,4,5,6,7,8,9,10]
r = map(lambda i:i+1,l)
print(list(r))
打印[2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
13.sort()方法:
该方法用来对列表中的元素进行排序,直接默认比较列表中元素的大小。
在该方法中可以接受一个关键字参数,即一个函数作为参数。
l = [34,554,67,897,436]
l.sort()
print(l)
打印[34, 67, 436, 554, 897]
同时可传递参数来对字符串长度进行比较。
l = ['ssd','adsd','frfrrffr','frfrsfrfs','rd']
l.sort(key=len)
print(l)
打印['rd', 'ssd', 'adsd', 'frfrrffr', 'frfrsfrfs']
。
还可以根据类型转换后的值进行比较:
l = ['34',554,'67',897,436]
l.sort(key=int)
print(l)
打印['34', '67', 436, 554, 897]
14.sorted()函数:
返回一个新的列表。
l = ['34',554,'67',897,436]
l2 = sorted(l,key=int)
print(l)
print(l2)
打印
['34', 554, '67', 897, 436]
['34', '67', 436, 554, 897]
15.高级函数
特点:
1.接受一个或多个函数作为参数;
2.将函数作为返回值。
满足任意一个特点即为高级函数。
练习:定义一个函数,将指定列表中的所有偶数,保存到新的列表中返回。
l = [1,2,3,4,5,6,7,8,9,10]
def evenlist(lst):
new_list = []
for item in lst:
#判断item的奇偶
if item % 2 == 0:
new_list.append(item)
return new_list
print(evenlist(l))
打印[2, 4, 6, 8, 10]
用函数判断奇偶时:
l = [1,2,3,4,5,6,7,8,9,10]
def evenlist(lst):
def even(i):
if i % 2 == 0:
return True
new_list = []
for item in lst:
#判断item的奇偶
if even(item):
new_list.append(item)
return new_list
print(evenlist(l))
结果相同。
可在外函数中同时添加多个内部函数,但是这不灵活的,需要将每个函数都写出来。
当使用函数作为参数时,实际上是将相应函数的代码传递进了目标函数。
l = [1,2,3,4,5,6,7,8,9,10]
def even(i):
if i % 2 == 0:
return True
def tri(i):
if i % 3 == 0:
return True
def evenlist(func,lst):
new_list = []
for item in lst:
#判断item的奇偶
if func(item):
new_list.append(item)
return new_list
print(evenlist(even,l))
print(evenlist(tri,l))
即为高级函数,打印
[2, 4, 6, 8, 10]
[3, 6, 9]
16.闭包
闭包将函数作为返回值返回,也是一种高级函数。
def fn():
#函数内部再定义一个函数
def inner():
print('In fn2()')
#将inner()函数作为返回值返回
return inner
print(fn())
打印<function fn.<locals>.inner at 0x000001E69ABC2EE8>
def fn():
#函数内部再定义一个函数
def inner():
print('In fn2()')
#将inner()函数作为返回值返回
return inner
r = fn()
r()
打印In fn2()
。
解释:r其实就是fn()
返回的函数inner对象,再调用,就会打印,所以这个函数总是能返回fn()
函数内部的变量,如下:
def fn():
a = 10
#函数内部再定义一个函数
def inner():
print('In fn2()')
print(a)
#将inner()函数作为返回值返回
return inner
r = fn()
r()
打印
In fn2()
10
r是调用fn()
后返回的函数,是在fn()
内部定义的,并不是全局函数,所以这个函数总是能访问到fn()
函数内部的变量。
闭包的好处:
通过闭包可以创建一些只有当前函数可以访问到的对象(可以将一些私有的数据藏到闭包中)。
应用:
nums = []
def average(n):
nums.append(n)
return sum(nums)/len(nums)
print(average(10))
print(average(20))
print(average(30))
打印
10.0
15.0
20.0
不好的地方:nums是全局变量,会被任意访问到,可能会出现被修改、覆盖,从而导致本身的nums改变导致错误。
所以形成闭包:
def op_avg():
nums = []
def average(n):
nums.append(n)
return sum(nums)/len(nums)
return average
avg = op_avg()
print(avg(10))
print(avg(20))
print(avg(30))
结果与上面相同。
总结:
形成闭包的条件:
1.函数嵌套;
2.将内部函数作为返回值返回;
3.内部函数必须使用外部函数的变量。
闭包可以确保数据的安全。
17.装饰器
17.1 装饰器的出现背景
def add(a,b):
#求任意两个数的和
print('Start...')
r = a + b
print('End...')
return r
def multiply(a,b):
# 求任意两个数的积
print('Start...')
r = a * b
print('End...')
return r
r = add(1,2)
print(r)
打印
Start...
End...
3
这是添加了提示开始和结束的输出,但是如果函数较多,会出现一些问题:
- 如果要修改的函数很多,修改起来很麻烦;
- 不方便后期维护;
- 会违反开闭原则(OCP)。
开闭原则:在开发的时候,要求可以开发对程序的扩展,但是要关闭对程序的修改。
def fn():
print('In fn()')
def fn2():
print('Start...')
fn()
print('End...')
fn2()
打印
Start...
In fn()
End...
又如
def add(a,b):
#求任意两个数的和
r = a + b
return r
def new_add(a,b):
print('Start...')
r = add(a,b)
print('End...')
return r
r = new_add(1,2)
print(r)
打印
Start...
End...
3
很显然,会出现一定问题,每扩展一个函数会定义一个新的函数,很麻烦。
17.2 装饰器的使用
def start_end():
#用来对其他函数进行扩展,使其他函数可以在执行前打印开始和结束
#创建一个函数
def new_func():
pass
#返回函数
return new_func
f = start_end()
f2 = start_end()
print(f)
print(f2)
打印
<function start_end.<locals>.new_func at 0x0000018368DE2EE8>
<function start_end.<locals>.new_func at 0x0000018368DFB1F8>
扩展new_func后:
def fn():
print('In fn()')
def start_end():
# 用来对其他函数进行扩展,使其他函数可以在执行前打印开始和结束
# 创建一个函数
def new_func():
print('Start...')
fn()
print('End...')
#返回函数
return new_func
f = start_end()
f()
打印
Start...
In fn()
End...
但是new_func是固定的、不能改变,失去了意义,所以可以在函数参数中传入被扩展的函数。
def fn():
print('In fn()')
def start_end(func):
#用来对其他函数进行扩展,使其他函数可以在执行前打印开始和结束
#创建一个函数
def new_func():
print('Start...')
func()
print('End...')
#返回函数
return new_func
f = start_end(fn)
f()
打印
Start...
In fn()
End...
当被扩展的函数有参数时,
def add(a,b):
#求任意两个数的和
r = a + b
return r
def start_end(func):
#用来对其他函数进行扩展,使其他函数可以在执行前打印开始和结束
#创建一个函数
def new_func(a,b):
print('Start...')
result = func(a,b)
print('End...')
return result
#返回函数
return new_func
f = start_end(add)
r = f(1,2)
print(r)
打印
Start...
End...
3
需要对任何函数进行扩展时,
def add(a,b):
#求任意两个数的和
r = a + b
return r
def fn():
print('In fn()')
def start_end(func):
#用来对其他函数进行扩展,使其他函数可以在执行前打印开始和结束
#创建一个函数
def new_func(*args,**kwargs): #*args接收所有位置参数,**kwargs接收所有关键字参数
print('Start...')
result = func(*args,**kwargs)
print('End...')
return result
#返回函数
return new_func
f = start_end(add)
f2 = start_end(fn)
r = f(1,2)
print(r)
f2()
打印
Start...
End...
3
Start...
In fn()
End...
总结:像上面的start_end(func)
这类的函数即称之为装饰器。
通过装饰器可以在不修改原来函数的基础之上来对函数进行扩展,在开发过程中,都是通过对装饰器来扩展函数的功能。
三.面向对象
1.面向对象简介
1.1 面向对象基本概念
面向对象OOP,即Object Oriented Programming。
什么是对象:
对象就是内存中存储指定数据的一块区域。
实际上对象就是一个容器,专门用来存数据。
程序运行的通俗解释:
代码存在硬盘,CPU处理代码,CPU和硬盘之间有内存,解释器将代码交给内存,CPU再从内存读取。
1.2 面向对象的结构
id(标识)
用来标识对象的唯一性,每个对象都有唯一的id,每个id指向一个内存地址值。
id由解释器生成,其实就是对象的内存地址。
type(类型)
类型决定了对象有哪些功能。
可以通过type()
函数来查看对象的类型。
value(值)
值就是对象中存储的具体数据,分为:
- 可变对象:
列表list、集合set、字典dict。 - 不可变对象:
数值类型int、float,字符串str,元组tuple,布尔型bool。
1.3从面向过程到面向对象
所谓面向对象,简单理解就是语言中所有的操作都是通过对象来进行的。
面向过程 → 面向对象:
面向过程的典型代表是C语言,是早期语言的特点,是将一件事分成一个个步骤,并用函数分别实现。
面向对象是一种思考问题的方式,是一种思想,将事物变得简单化。
对面向对象的理解:
(1)可以让复杂事务变得简单化,人由执行者过渡到指挥者;
(2)具备封装、继承和多态。
举例——孩子吃瓜:
(1)妈妈穿衣服穿鞋出门
(2)妈妈骑电动车
(3)妈妈到超市门口停好电动车
(4)妈妈买瓜
(5)妈妈结账
(6)妈妈骑电动车回家
(7)到家孩子吃瓜
这是一个典型的面向过程的例子,在现实中没问题,但是在程序中会出现问题,在复用、修改时会出现很多问题。
面向过程的思想:
将一个功能分解成一个一个的小步骤。
- 优点
比较符合人的思维,编写起来比较容易; - 缺点
这种编程方式只适用于一个功能,我们要实现别的功能的时候,往往需要编写新的代码,复用性较低。
面向对象就是让孩子吃瓜,而不管具体怎么实现,细节在对象内部。
- 优点
容易维护、方便复用; - 缺点
不符合人的思维,编写时比较麻烦。
2.类(class)的简介
目前所用到的对象都是Python内置的对象,如int、float、str等。
类简单理解就是一张图纸,在程序中我们需要根据类来创建对象。
a = int(10)
print(a)
打印10
。
定义一个类:
自己定义类时,类名要以大写开头。
语法:
class 类名([父类]):
代码块
如
class MyClass():
pass
print(MyClass)
打印<class '__main__.MyClass'>
class MyClass():
pass
#用MyClass创建了一个对象mc,mc是MyClass的实例
mc = MyClass()
print(mc,type(mc))
12345
打印<__main__.MyClass object at 0x000001C9EB32AA88> <class '__main__.MyClass'>
解释:用MyClass创建了一个对象mc,mc是MyClass的实例。
class MyClass():
pass
#用MyClass创建了一个对象mc,mc是MyClass的实例
mc = MyClass()
mc2 = MyClass()
mc3 = MyClass()
mc4 = MyClass()
#用来检查一个对象是否是一个类的实例
result = isinstance(mc,MyClass)
result2 = isinstance(mc2,int)
result3 = isinstance(mc3,type(mc))
result4 = isinstance(mc4,type(mc4))
print(result,result2,result3,result4)
打印True False True True
isinstance()
用来检查一个对象是否是一个类的实例。
3.对象的基本介绍
3.1 对象的创建流程
类也是一个对象,类是一个用来创建对象的对象。
class MyClass():
pass
print(id(MyClass),type(MyClass))
打印1720742711128 <class 'type'>
显然,类是一个type类型的对象。
创建类的实例即对象时,实例并没有值,是空的,所对应的类也是没有值的,也是空类。
可以向对象中添加变量,对象中的变量称为属性。
class MyClass():
pass
mc = MyClass()
mc.name = 'Tom'
print(mc.name)
打印Tom
即属性是添加到实例中,而不是到类中,如print(MyClass.name)
会报错。
3.2 对象的定义
类和对象都是对现实生活中事物或程序内容的抽象。
实际上,所有的事物都是由两部分组成的:
※数据(属性)
※行为(方法)
在类的代码中,可以定义变量和函数;
在类中定义的变量将会成为所有实例的公共属性,所有实例都可以访问这些变量。
如
class Person():
a = 10
b = 20
#创建Person的实例
p1 = Person()
p2 = Person()
print(p1.a,p2.b)
打印10 20
加入有意义的属性:
class Person():
name = 'Tom'
#创建Person的实例
p1 = Person()
p2 = Person()
print(p1.name)
打印Tom
在类中可以定义函数,类中的函数称为方法。
这些方法通过该类的实例都可以访问。
class Person():
name = 'Tom'
def speak(self):
print('Hello')
#创建Person的实例
p1 = Person()
p2 = Person()
print(p1.name)
p2.speak()
打印
Tom
Hello
如果是函数,有几个形参,就传几个实参;
如果是方法,默认传递一个参数,即类中的方法至少要定义一个形参,如上面speak()
的self
参数。这个self参数,在我们调用对象方法时,并不需要我们去传递。系统会自动默认传递
4.属性和方法
属性和方法的查找流程:
调用一个对象的属性和方法时,解析器会在当前对象中寻找是否有该属性和方法,如果有,直接返回;
如果没有,去当前对象的类对象中寻找,如果有,则返回类对象中的属性值和方法,如果没有,则报错。
class Person():
name = 'Tom'
def speak(self):
print('Hello')
#创建Person的实例
p1 = Person()
p2 = Person()
p1.name = 'John'
print(p1.name,p2.name)
打印John Tom
。
类对象和实例对象中都可以保存属性和方法:
如果属性和方法是所有类的实例共享的,则应该保存到类对象中;
如果属性和方法是某个实例独有的,则应该保存到实例对象中;
一般,属性保存到实例对象中,方法保存到类对象方法中。
探究self
:
class Person():
name = ''
def speak(self,name):
print(self)
#创建Person的实例
p1 = Person()
p2 = Person()
p1.name = 'John'
p2.name = 'Tom'
p1.speak(p1.name)
p2.speak(p2.name)
打印
<__main__.Person object at 0x0000020D3D325188>
<__main__.Person object at 0x0000020D3D3251C8>
即默认传递的参数,就是调用方法的对象本身,明明为self
。
所以,要想实现对象的方法分别调用自己的属性,可以如下:
class Person():
name = ''
def speak(self,name):
print('Hello, I\'m %s'%name)
#创建Person的实例
p1 = Person()
p2 = Person()
p1.name = 'John'
p2.name = 'Tom'
p1.speak(p1.name)
p2.speak(p2.name)
打印
Hello, I'm John
Hello, I'm Tom
另一种方法:
class Person():
name = ''
def speak(self):
print('Hello, I\'m %s'%self.name)
#创建Person的实例
p1 = Person()
p2 = Person()
p1.name = 'John'
p2.name = 'Tom'
p1.speak()
p2.speak()
效果与前一种方法是一样的。
5. 特殊方法
class Person:
name = 'Tom'
def speak(self):
print('Hello,I\'m %s'%self.name)
p1 = Person()
p2 = Person()
p2.speak()
打印Hello,I'm Tom
类中没有定义属性时,手动添加属性
class Person:
def speak(self):
print('Hello,I\'m %s'%self.name)
p1 = Person()
p2 = Person()
#手动向对象添加属性
p1.name = 'Tom'
p2.name = 'Jerry'
p1.speak()
p2.speak()
打印
Hello,I'm Tom
Hello,I'm Jerry
如未手动添加属性时,
class Person:
def speak(self):
print('Hello,I\'m %s'%self.name)
p1 = Person()
p2 = Person()
p1.speak()
p2.speak()
便会报错:AttributeError: 'Person' object has no attribute 'name'
在上例中,对于Person这个类name属性是必须的,并且对于每一个对象name属性的值是不一样的,将name属性手动添加,很容易出错。
5.1 特殊方法:
即魔术方法,都是以__开头、__结尾的方法。
特殊方法不需要我们调用,会在特殊的时候自己调用。
学习特殊方法:
※特殊方法什么时候调用;
※特殊方法有什么作用。
class Person:
def __init__(self):
print('hello')
def speak(self):
print('Hello,I\'m %s'%self.name)
p1 = Person()
p1.__init__()
打印
hello
hello
hello打印了两次,因为类的实例创建时,会自动调用了一次,再手动调用了一次,所以调用了两次。
class Person:
def __init__(self):
print('hello')
def speak(self):
print('Hello,I\'m %s'%self.name)
p1 = Person()
p2 = Person()
p3 = Person()
p4 = Person()
打印
hello
hello
hello
hello
可以说,每创建一个实例对象,特殊方法__init__就执行一次。
对象的创建流程:
p1 = Person()
(1)创建了一个变量;
(2)在内存中创建了一个新的对象;
(3)执行类中的代码块的代码,并且只在类中执行一次;
(4)__init__()
方法执行。
即如类的定义中除特殊方法还有别的代码语句,则其他语句先执行,如,
class Person:
print('In class Person')
def __init__(self):
print('hello')
def speak(self):
print('Hello,I\'m %s'%self.name)
p1 = Person()
p2 = Person()
p3 = Person()
p4 = Person()
打印
In class Person
hello
hello
hello
hello
__init__()
方法会在对象创建以后立即执行,向新创建的对象初始化属性,
class Person:
def __init__(self):
print(self)
def speak(self):
print('Hello,I\'m %s'%self.name)
p1 = Person()
p2 = Person()
p3 = Person()
p4 = Person()
打印
<__main__.Person object at 0x000002E1EEC36708>
<__main__.Person object at 0x000002E1EEC36EC8>
<__main__.Person object at 0x000002E1EEC36F08>
<__main__.Person object at 0x000002E1EEC36F48>
即打印self即打印出对象,self代表对象本身。
class Person:
def __init__(self):
self.name = 'Tom'
def speak(self):
print('Hello,I\'m %s'%self.name)
p1 = Person()
p1.speak()
打印Hello,I'm Tom
为了解决之前出现过的问题,可以在__init__()
方法中添加参数,通过self向新建的对象初始化对象。
class Person:
def __init__(self,name):
self.name = name
def speak(self):
print('Hello,I\'m %s'%self.name)
p1 = Person()
p1.speak()
初始化时未添加name,会报错
TypeError: __init__() missing 1 required positional argument: 'name'
所以必须在初始化时即添加,
class Person:
def __init__(self,name):
self.name = name
def speak(self):
print('Hello,I\'m %s'%self.name)
p1 = Person('Tom')
print(p1.name)
p2 = Person('Jerry')
print(p2.name)
p1.speak()
p2.speak()
打印
Tom
Jerry
Hello,I'm Tom
Hello,I'm Jerry
6.类的基本结构:
class 类名([父类]):
公共属性
#对象的初始化方法
__init__(self,...):
...
#其他的方法
def method(self,...):
...
要求:尝试定义一个车的类。
class Car():
'''属性:name、color
方法:run()、didi()'''
def __init__(self,name,color):
self.name = name
self.color = color
def run(self):
print('Car in running...')
def didi(self):
print('%s didi'%self.name)
c = Car('daben','white')
print(c.name,c.color)
c.run()
c.didi()
打印
daben white
Car in running...
daben didi
定义对象时,希望对象能发挥一些作用,应该保证数据的安全性和有效性,
class Car():
'''属性:name、color
方法:run()、didi()'''
def __init__(self,name,color):
self.name = name
self.color = color
def run(self):
print('Car in running...')
def didi(self):
print('%s didi'%self.name)
c = Car('daben','white')
#修改属性
c.name = 'aodi'
print(c.name,c.color)
c.run()
c.didi()
结果就会改变
aodi white
Car in running...
aodi didi
要增加数据的安全性,要做到:
1.属性不能随意修改,有确实要改的需要才改,没有真实的需要才改;
2.属性不能改为任意的值,要保证有效。
7.封装
封装是面向对象的三大特性之一(另外两个是继承、多态)。
封装指的是隐藏对象中一些不希望被外部访问到的属性和方法。
class Dog:
def __init__(self,name):
self.name = name
d = Dog('erha')
d.name = 'kaisa'
print(d.name)
打印kaisa
此例中name属性即未被隐藏,会被任意修改。
如何修改对象中的一个属性:
将对象的属性名修改为外部不知道的名字。
class Dog:
def __init__(self,name):
self.hidden_name = name
def speak(self):
print('Hello,I\'m %s'%self.hidden_name)
d = Dog('erha')
d.name = 'kaisa'
d.speak()
打印Hello,I'm erha
此时不能修改,要想修改,
class Dog:
def __init__(self,name):
self.hidden_name = name
def speak(self):
print('Hello,I\'m %s'%self.hidden_name)
d = Dog('erha')
d.hidden_name = 'kaisa'
d.speak()
打印Hello,I'm kaisa
,这又不是封装,成了最初的形式。
如何获取(修改)对象当中的属性:
通过提供getter()和setter()方法来访问和修改属性。
class Dog:
def __init__(self,name):
self.hidden_name = name
def speak(self):
print('Hello,I\'m %s'%self.hidden_name)
def get_name(self):
#获取对象的属性
return self.hidden_name
def set_name(self,name):
#修改对象属性
self.hidden_name = name
d = Dog('erha')
print(d.get_name())
d.set_name('kaisa')
print(d.get_name())
d.speak()
打印
erha
kaisa
Hello,I'm kaisa
使用封装确实在一定程度上增加了程序的复杂性,
但是它确保了数据的安全性:
1.隐藏了属性名,使调用者无法任意修改对象中的属性。
2.增加了getter和setter方法,可以很好地控制属性是否是可读的:
如果希望属性是可读的,则可以直接去掉setter方法;
如果希望属性不能被完结访问,则可以直接去掉getter方法。
3.使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的。
增加了程序的安全性。
4.可以在读取属性和设置属性的方法中做一些其他的操作。
class Dog:
def __init__(self,name,age):
self.hidden_name = name
self.hidden_age = age
def speak(self):
print('Hello,I\'m %s'%self.hidden_name)
def get_name(self):
#获取对象的属性
return self.hidden_name
def set_name(self,name):
#修改对象属性
self.hidden_name = name
def get_age(self):
#获取对象的属性
return self.hidden_age
def set_age(self,age):
#修改对象属性
self.hidden_age = age
d = Dog('erha',5)
print(d.get_age())
d.set_age(-5)
print(d.get_age())
打印
5
-5
显然,把狗的年龄设为负值程序是没报错的,但是是没有实际意义的,所以我们可以增加对数据的判断。
class Dog:
def __init__(self,name,age):
self.hidden_name = name
self.hidden_age = age
def speak(self):
print('Hello,I\'m %s'%self.hidden_name)
def get_name(self):
#获取对象的属性
return self.hidden_name
def set_name(self,name):
#修改对象属性
self.hidden_name = name
def get_age(self):
#获取对象的属性
return self.hidden_age
def set_age(self,age):
#修改对象属性
#判断再处理
if age > 0:
self.hidden_age = age
d = Dog('erha',5)
print(d.get_age())
d.set_age(-5)
print(d.get_age())
打印
5
5
8.封装进阶
class Person():
def __init__(self,name):
self.hidden_name = name
def get_name(self):
return self.hidden_name
def set_name(self,name):
self.hidden_name = name
p = Person('Tom')
p.set_name('Jerry')
print(p.get_name())
打印Jerry
,即属性值会改变。hidden_name改为__name
class Person():
def __init__(self,name):
self.__name = name
def get_name(self):
return self.__name
def set_name(self,name):
self.__name = name
p = Person('Tom')
print(p.get_name())
打印Tom
class Person():
def __init__(self,name):
self.__name = name
def get_name(self):
return self.__name
def set_name(self,name):
self.__name = name
p = Person('Tom')
print(p.__name)
会报错AttributeError: 'Person' object has no attribute '__name'
class Person():
def __init__(self,name):
self.__name = name
def get_name(self):
return self.__name
def set_name(self,name):
self.__name = name
p = Person('Tom')
p.__name = 'Jerry'
print(p.get_name())
打印还是Tom
,没有被修改。
总结:可以为对象的属性使用双下划线开头,__xxx
。
双下划线开头的属性是类的隐藏属性,隐藏属性只能在类的内部访问,无法通过外部访问。
隐藏属性是Python自动为属性改了一个名字,实际改的名字是:类名_属性名:
__name -> _Person__name
class Person():
def __init__(self,name):
self.__name = name
def get_name(self):
return self.__name
def set_name(self,name):
self.__name = name
p = Person('Tom')
print(p.get_name())
p._Person__name = 'Jerry'
print(p.get_name())
打印
Tom
Jerry
即_Person__name
又可以对属性进行修改。
在写代码的时候,可以用一个下划线,既容易书写,外部能访问,且不易被修改。
一般情况下:使用_开头的属性都是私有属性,没有特殊情况不要修改。
9.装饰器@property
用来将get()方法转化为对象的属性。
添加property装饰器以后,就可以像调用属性一样调用方法。
class Person():
def __init__(self,name):
self._name = name
def name(self):
return self._name
p = Person('Tom')
print(p.name())
打印Tom
用了property
装饰器以后,
class Person():
def __init__(self,name):
self._name = name
@property
def name(self):
print('The method is run')
return self._name
p = Person('Tom')
print(p.name)
输出结果为:
The method is run
Tom
加入setter后,
class Person():
def __init__(self,name):
self._name = name
@property
def name(self):
print('The method is run')
return self._name
@name.setter
def name(self,name):
self._name = name
p = Person('Tom')
print(p.name)
p.name = 'Jerry'
print(p.name)
打印
The method is run
Tom
The method is run
Jerry
10.继承
10.1 引入
class Doctor():
name = ''
age = ''
def treat(self):
print('treat a patient')
class soldier():
name = ''
age = ''
def protect(self):
print('Protect the people')
以上定义了两个类,但是又重复的属性name、age,如果每定义一个类都要分别定义这两个属性,很重复冗余,所以可以抽象出一个类来封装它们的公共属性和方法,即它们的父类这些类都继承
自它们的父类。
如
class Person():
name = ''
age = ''
10.2 继承的特点:
1.提高了代码的复用性;
2.让类与类之间产生联系,有了这个联系,才有了多态。
继承是面向对象的三大特性之一,其他两个特性是封装、多态。
定义一个动物类
class Animal:
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
a = Animal()
a.run()
再定义一个狗类,
class Dog:
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
这样定义狗类,有大量重复性代码,表现较差,我们应该使狗类继承自动物类,继承动物类的属性和方法。
class Dog(Animal):
pass
d = Dog()
d.run()
d.sleep()
打印
Running...
Sleeping...
在定义类时可以在类名+括号,括号内的指定的是当前类的父类(超类、基类,super)。
class Dog(Animal):
def Housekeep(self):
print('Housekeeping...')
d = Dog()
d.run()
d.sleep()
d.Housekeep()
r1 = isinstance(d,Dog)
r2 = isinstance(d,Animal)
print(r1,r2)
打印
Running...
Sleeping...
Housekeeping...
True True
显然,d既是Dog的实例,也是Animal的实例,所以Dog也继承自了Animal。
如果在创建类的时候省略了父类,则默认父类是object;
object是所有类的父类,所有类都继承自object。
issubclass()
检查一个类是否是另一个类的子类。
class Animal:
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
class Dog(Animal):
def Housekeep(self):
print('Housekeeping')
r1 = issubclass(Dog,Animal)
r2 = issubclass(Animal,object)
print(r1,r2)
证明了所有类都是object的子类,所有对象都是object的实例。
10.3 方法的重写
如果在子类中有和父类重名的方法,通过子类的实例去调用方法时,会调用子类的方法而不是父类的方法,这个特点称为方法的重写(覆盖,override)。
如,
class Animal:
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
class Dog(Animal):
def run(self):
print('Dog is running...')
def sleep(self):
print('Dog is sleeping...')
def Housekeep(self):
print('Dog is housekeeping')
d = Dog()
d.run()
打印Dog is running...
下边再详细说明,
class A(object):
def test(self):
print('A...')
class B(A):
pass
class C(B):
pass
c = C()
c.test()
打印A...
。
类B中加入方法test()后,
class A(object):
def test(self):
print('A...')
class B(A):
def test(self):
print('B...')
class C(B):
pass
c = C()
c.test()
打印B...
类C中加入方法test()后,
class A(object):
def test(self):
print('A...')
class B(A):
def test(self):
print('B...')
class C(B):
def test(self):
print('C...')
c = C()
c.test()
打印C...
。
当我们调用一个对象时,会有先去当前对象寻找是否有该方法,如果有直接调用;如果没有,则取当前对象的父类中寻找,如果有,直接调用父类中的方法,如果没有,则去父类的父类寻找,如果有直接调用,以此类推,直到找到object,如果依然没有则报错。
10.4 super()
父类中所有的方法都会被子类继承,包括特殊方法,如__init__()
等,也可以重写特殊方法。
class Animal:
def __init__(self,name):
self._name = name
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class Dog(Animal):
def run(self):
print('Dog is running...')
def sleep(self):
print('Dog is sleeping...')
def Housekeep(self):
print('Dog is housekeeping')
d = Dog()
d.run()
会抛出异常,TypeError: __init__() missing 1 required positional argument: 'name'
因为初始化没有传入参数,因为父类初始化要传入name这个参数,子类也必须传入。
传入参数后,
class Animal:
def __init__(self,name):
self._name = name
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class Dog(Animal):
def run(self):
print('Dog is running...')
def sleep(self):
print('Dog is sleeping...')
def Housekeep(self):
print('Dog is housekeeping')
d = Dog('erha')
print(d.name)
打印erha
调用setter方法修改属性,
class Animal:
def __init__(self,name):
self._name = name
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class Dog(Animal):
def run(self):
print('Dog is running...')
def sleep(self):
print('Dog is sleeping...')
def Housekeep(self):
print('Dog is housekeeping')
d = Dog('erha')
d.name = 'demu'
print(d.name)
打印demu
。
如果需要在子类中定义新的属性时,即要扩展属性时,
class Animal:
def __init__(self,name):
self._name = name
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class Dog(Animal):
def __init__(self,name,age):
self._name = name
self._age = age
def run(self):
print('Dog is running...')
def sleep(self):
print('Dog is sleeping...')
def Housekeep(self):
print('Dog is housekeeping')
@property
def age(self):
return self._age
@age.setter
def age(self, age):
self._age = age
d = Dog('erha',10)
print(d.name)
print(d.age)
打印
erha
10
12
可以直接调用父类中的__init__()
来初始化父类中的属性,
class Animal:
def __init__(self,name):
self._name = name
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class Dog(Animal):
def __init__(self,name,age):
Animal.__init__(self,name)
self._age = age
def run(self):
print('Dog is running...')
def sleep(self):
print('Dog is sleeping...')
def Housekeep(self):
print('Dog is housekeeping')
@property
def age(self):
return self._age
@age.setter
def age(self, age):
self._age = age
d = Dog('erha',10)
print(d.name)
print(d.age)
即Dog类中的初始化方法中,name属性通过Animal的初始化调用获取,可以将父类的多个属性传给子类,但是继承的父类是固定的,这是需要用super()动态地继承父类属性。
super()可以用来获取当前类的父类,并且通过super()返回的对象,调用父类方法时不需要传入self。
class Animal:
def __init__(self,name):
self._name = name
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class Dog(Animal):
def __init__(self,name,age):
super().__init__(name)
self._age = age
def run(self):
print('Dog is running...')
def sleep(self):
print('Dog is sleeping...')
def Housekeep(self):
print('Dog is housekeeping')
@property
def age(self):
return self._age
@age.setter
def age(self, age):
self._age = age
d = Dog('erha',10)
print(d.name)
print(d.age)
执行结果与之前相同。
10.5 多重继承
语法:类名.bases
可以用来获取当前类的所有直接父类。
class A(object):
def test(self):
print('A...')
class B(A):
def test2(self):
print('B...')
class C(B):
pass
print(C.__bases__)
print(B.__bases__)
print(A.__bases__)
打印
(<class '__main__.B'>,)
(<class '__main__.A'>,)
(<class 'object'>,)
在Python中支持多重继承,即可以为一个类同时指定多个父类。
class A(object):
def test(self):
print('A...')
class B(object):
def test2(self):
print('B...')
class C(A,B):
pass
print(C.__bases__)
打印(<class '__main__.A'>, <class '__main__.B'>)
。
class A(object):
def test(self):
print('A...')
class B(object):
def test2(self):
print('B...')
class C(A,B):
pass
c = C()
c.test()
c.test2()
打印
A...
B...
12
显然,子类可以同时继承多个父类的方法。
但是一般在实际中没有特殊情况,尽量避免使用多重继承,因为多重继承会让代码过于复杂;
如果多个父类中有同名的方法,则会在第一个父类中寻找,找不到再继续往下寻找…
如,
class A(object):
def test(self):
print('A...test')
class B(object):
def test(self):
print('B...test')
def test2(self):
print('B...')
class C(A,B):
pass
c = C()
c.test()
会打印A...test
如果A中没有test()方法,
class A(object):
pass
class B(object):
def test(self):
print('B...test')
def test2(self):
print('B...')
class C(A,B):
pass
c = C()
c.test()
会打印B...test
11.多态
多态是面向对象的三大特征之一。
多态字面理解即多种形态,在面向对象中指一个对象可以以不同的形态呈现。
class A(object):
def __init__(self,name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class B(object):
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
def speak(obj):
print('Hello,%s' % obj.name)
a = A('Tom')
b = B('Jerry')
speak(a)
speak(b)
打印
Hello,Tom
Hello,Jerry
定义一个新类C,但是没有name属性时,
class A(object):
def __init__(self,name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class B(object):
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
class C(object):
pass
def speak(obj):
print('Hello,%s' % obj.name)
a = A('Tom')
b = B('Jerry')
c= C()
speak(a)
speak(b)
speak(c)
会抛出异常,AttributeError: 'C' object has no attribute 'name'
,
增加类型检查,
class A(object):
def __init__(self,name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class B(object):
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
class C(object):
pass
def speak(obj):
print('Hello,%s' % obj.name)
def test_speak(obj):
'''类型检查'''
if isinstance(obj,A):
print('Hello,%s' % obj.name)
a = A('Tom')
b = B('Jerry')
c= C()
test_speak(a)
test_speak(b)
test_speak(c)
打印Hello,Tom
。
在test_speak()
这个函数中做了类型检查,也就是obj是A类型的对象的时候,才可以正常执行,其他类型的对象无法使用该函数。
这个函数其实就是违反了多态。
违反了多态的函数,只适合于一种类型的对象,无法适用于其他类型的对象,导致函数的适用性非常差。
多态使面向对象的编程更加具有灵活性
。
l = [1,2,3]
s = 'python'
print(len(l),len(s))
如上,len()函数既可以得到list的长度,又可以获取str的长度,
len()就可以检查不同对象类型的长度就是面向对象的特征之一——多态。
len()的底层实现:
之所以len()函数能获取长度,是因为这些对象中有一个特殊方法__len__()方法;
换句话说,只要对象中有__len__()方法,就可以通过len()方法来获取对象的长度。
class B(object):
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
b = B('Jerry')
print(len(b))
会报错TypeError: object of type 'B' has no len()
。
在B中加入__len__()
方法后,
class B(object):
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
def __len__(self):
return 1
b = B('Jerry')
print(len(b))
会打印1
面向对象的三大特征:
1.封装:确保对象中的数据更安全;
2.继承:保证对象的可扩展性;
3.多态:保证了程序的灵活性。
12.类中的属性和方法
类属性是直接在类中定义的属性,类属性可以通过类或类的实例访问;
通过实例对象添加的属性属于实例属性。
class A(object):
#类属性
count = 0
a = A()
print(A.count)
print(a.count)
打印
0
0
修改实例的属性,
class A(object):
#类属性
count = 0
a = A()
a.count = 5
print('In class,',A.count)
print('In instance,',a.count)
打印
In class, 0
In instance, 5
修改类中的属性,
class A(object):
#类属性
count = 0
a = A()
A.count = 5
print('In class,',A.count)
print('In instance,',a.count)
打印
In class, 5
In instance, 5
可总结出:类属性只能通过类对象来修改,无法通过实例对象来修改;
class A(object):
def __init__(self):
#实例属性
self.name = 'Tom'
a = A()
print(a.name)
打印Tom
在初始化中定义的属性为实例属性,只能通过实例属性来访问和修改,类对象无法访问和修改。
如,
class A(object):
def __init__(self):
#实例属性
self.name = 'Tom'
a = A()
print(A.name)
报错AttributeError: type object 'A' has no attribute 'name'
在类中定义的以self为第一个参数的方法都是实例方法;
实力方法在调用时,Python会将调用对象作为self传入;
class A(object):
def __init__(self):
#实例属性
self.name = 'Tom'
def test(self):
#实例方法
print('in test')
a = A()
a.test()
打印in test
类对象调用时,
class A(object):
def __init__(self):
#实例属性
self.name = 'Tom'
def test(self):
#实例方法
print('in test')
a = A()
A.test()
报错TypeError: test() missing 1 required positional argument: 'self'
但是通过类对象调用时,传入实例对象多为方法的参数不会报错。
即通过类对象调用方法时,不会自动穿self,必须手动传self。
class A(object):
def __init__(self):
#实例属性
self.name = 'Tom'
def test(self):
#实例方法
print('in test')
a = A()
A.test(a)
输出结果相同。
即a.test()
等价于A.test(a)
。
可以通过classmethod装饰器来调用类方法;
类方法的第一个参数是cls,也会被自动传递,cls就是当前的类对象。
class A(object):
count = 0
def __init__(self):
#实例属性
self.name = 'Tom'
def test(self):
#实例方法
print('in test')
@classmethod
def test2(cls):
#实例方法
print('in test2',cls)
a = A()
A.test2()
打印in test2 <class '__main__.A'>
调用类属性,
class A(object):
count = 0
def __init__(self):
#实例属性
self.name = 'Tom'
def test(self):
#实例方法
print('in test')
@classmethod
def test2(cls):
#实例方法
print('in test2',cls)
print(cls.count)
a = A()
A.test2()
打印
in test2 <class '__main__.A'>
0
通过实例调用类方法,
class A(object):
count = 0
def __init__(self):
#实例属性
self.name = 'Tom'
def test(self):
#实例方法
print('in test')
@classmethod
def test2(cls):
#实例方法
print('in test2',cls)
print(cls.count)
a = A()
a.test2()
结果相同。
可总结:类方法可以通过类对象调用,也可通过实例方法调用。
A.test2()
等价于a.test2()
。
静态方法:
用staticmethod装饰器来调用类方法
class A(object):
count = 0
def __init__(self):
#实例属性
self.name = 'Tom'
def test(self):
#实例方法
print('in test')
@classmethod
def test2(cls):
#实例方法
print('in test2',cls)
print(cls.count)
@staticmethod
def test3():
print('in test3')
a = A()
A.test3()
a.test3()
打印
in test3
in test3
静态方法,基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数;
静态方法一般是工具方法,和当前类无关。