python自动化(一)基础能力:3.python基础上之数据类型,函数,面向对象

写在前面的话:本章内容为本人学习大神“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中,如果是返回开始的索引值,否则返回-1

    str1 ="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

==!=比较的是对象的值是否相等
isis 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和value

    d = {'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。
returnbreak的比较:

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

静态方法,基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数;
静态方法一般是工具方法,和当前类无关。

在这里插入图片描述

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值