首页 >> 大全

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

2023-06-22 大全 1033 作者:考证青年

写在前面的话:本章内容为本人学习大神“”的“全栈”博客后,整理总结所得。如果想要深入学习可以看看大神的博客。链接如下:

一.数据类型详解 1.字符串 1.1 定义及格式

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]) # 获取字符串的最后一个元素

运行结果如下:

用len()函数。

name='xiaomingasoiffdh'
print(len(name))

用s*20即可将字符串s打印20编。

用in来判断一个字符串是否在另一个字符串中,如用a in b来判断a是否包含于b。如果包含则返回True,否则返回Flase

用max()和min()函数。

用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()去掉字符串左右两边的空格;

()去掉字符串左边的空格;

()去掉字符串右边的空格。

s=' I love Python '
print(s.strip())
print(s.lstrip())
print(s.rstrip())

upper()全部大写;

lower()全部小写;

()首字母大写;

()判断是否大写;

()判断是否小写。

例如:

s='I love Python'
print(s.upper())
print(s.capitialize())
print(s.islower())

2.列表 2.1 定义及格式

列表是中最基本也是最常用的数据结构之一,列表中的每一个元素被分配一个数字作为索引(即下标),用来表示该元素在列表内所排的位置,第1个元素索引是0,第2个索引是1,依次类推。

列表是一个有序可重复的集合。

names=["李大钊","陈独秀","蔡元培","陈佩斯"]
#注意:比C语言中的数组功能更强大,列表中的元素可以为不同类型
list1=[10,"人",2.5,True]

2.2列表的创建

创建列表:

2.3访问列表内的元素

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.删除值 2.6 列表组合

直接将两个列表相加,是列表的拼接,不是对应位置相加。

l1 = [1,2,3]
l2 = [4,5,6]
l3 = l1 + l2
print(l3)

打印:

[1, 2, 3, 4, 5, 6]

也可用

l3 = l1.(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

很显然,()函数对列表进行了改变,不再是原来的列表。

(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']

2.15 列表的内置方法 (1)()

添加一个元素到末尾

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)()

在列表末尾一次性追加另一个序列中的多个值(用新序列扩展原来的列表)

lis = ['a','b','c','d','e']
lis.extend(['f','g'])
print(lis)

打印出

['a', 'b', 'c', 'd', 'e', 'f', 'g']

(4)()

根据索引的位置添加元素

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 可变对象与不可变对象

中,不可变类型包括:

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 = [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,是一种新的数据结构,也可以叫映射()。

字典的作用:

存储对象的容器。

列表存储数据性能很好,但是查询数据性能很差;

字典中每一个元素都有唯一的名字,通过这个名字可以快速查找到指定的元素,这个唯一的名字称为键(key),通过key可以查找到值(value),所以字典也称为键值对(key-value),每个字典可以有多个键值对,每个键值对称为一项。

字典中键和值的类型:

值可以是任意对象;

键可以是任意不可变的对象,包括int、str、bool、tuple等。

4.2 创建字典 (1)用{}

语法:{key1:,key2:,...}。

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

如果键不存在会报错,即:

d = {'name':'Tom','age':'20','gender':'male'}
print(d['hello'])

打印

KeyError: 'hello'
1

为避免抛出异常,可以用get()方法

语法:get([key,])。

根据键来获取字典中的值,也可以指定一个默认值,作为第二个参数,这样当获取不到键的时候会返回默认值。

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'}

()函数

可以向字典当中添加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

()方法

将其他字典中的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}

删除 浅复制(浅拷贝)

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.()

返回一个序列,保存字典的值。

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 = 1while i < 10:j = 1while j <= i:print("%d * %d = %d\t"%(j,i,i*j),end=" ")j += 1i += 1print("")
# 函数的调用,只有调用函数,函数中的代码才会被执行
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 + breturn c
print(sum_a(5,8))
调用时,按照位置将实参与形参一一对应。5对应a,8对应b

3.2关键字参数

def sum_a(a,b):"""求和函数"""c = a + breturn c
print(sum_a(b=5,a=8))
调用函数时,使用关键字将实参与形参一一对应。这样参入参数时的位置可以颠倒

3.3默认值参数

def sum_a(a,b=3):"""求和函数"""c = a + breturn c
print(sum_a(1))
在定义形参时,可以给参数设置一个默认值。注意,默认值参数必须在最后面。sum_a(a=3,b)这样是错误的写法。当实参没有给默认值参数传值时,会使用默认值

3.4形参的*args,**

*args表示元组传参,**表示字典传参

def sum_a(*args):c = argsreturn c
print(sum_a(1,2))
结果:(1, 2)

def sum_a(**kwargs):"""求和函数"""c = kwargsreturn c
print(sum_a(a=1,b=2,c=3))
结果:{'a': 1, 'b': 2, 'c': 3}

一般形参设置为(*args,**)后,可以接受任何形式的实参

4.函数的返回值

有时候并不需要对结果进行打印,而是进行一些其他的处理,这时候就需要返回值。

返回值就是函数返回的结果。

返回值可以直接使用,也可以通过一个变量来接收函数返回值的结果。

def fn():return 100
r= fn()
print(r)

打印100。

后面可以跟任意的对象,甚至是一个函数。

def fn():def fn2():print('Python')return fn2
r= fn()
print(r)

打印.fn2 at >,即返回了一个函数。

又如

def fn():def fn2():print('Python')return fn2r= fn()
print(r)

打印

.fn2 at >

如果仅仅写一个(后边不加其他值)或者不写,相当于 None。

def fn2():return
r = fn2()
print(r)

打印None。

在函数中,后面的代码都不会执行,一旦执行函数自动结束。

def fn3():print('Hello')returnprint('ABC')
fn3()

打印Hello,没有打印出ABC。

和break的比较:

def fn4():for i in range(5):if i == 3:#break用于退出当前循环breakprint(i)print('Loop ends')
fn4()

打印

0
1
2
Loop ends
def fn4():for i in range(5):if i == 3:#return用来结束函数returnprint(i)print('Loop ends')
fn4()

打印

0
1
2
123

没有打印出Loop ends。

计算求和返回结果并运用:

def fn(*nums):#定义变量保存结果result = 0#便利元组,将元组中的元素累加for n in nums:result += nreturn resultr = 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 = 200print(a)
test()
#这种写法是允许的,打印出的结果为“200”,访问的为离的近的

如果需要在函数内部修改全局变量,则需要使用关键字,来声明变量。

a = 20
def fn2():#声明函数内部使用a是全局变量,此时再修改a时,就是修改全局变量。global aa = 10print('Inside fn2(),a =',a)
fn2()
print('Outside fn2(),a =',a)

打印

Inside fn2(),a = 10
Outside fn2(),a = 10

8.命名空间

命名空间实际上就是一个字典,专门用来存储变量的字典。

函数()用来获取当前作用域的命名空间,返回的是一个字典,把所有的变量存到该字典中。

如果在全局作用域中,调用()则获取全局作用域命名空间,如果在函数作用域中调用()则获取函数命名空间。

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 = 10s = locals()print(s)
fn4()

打印{'a': 10}

def fn4():a = 10s = locals()#可以通过操作s来操作函数的命名空间,但是不建议这么做!!s['b'] = 20print(s)
fn4()

打印{'a': 10, 'b': 20}

9.递归函数

在函数中不是调用其他而是调用自身,这种调用方式,称为递归调用

9.1 递归基本概念和使用

练习:尝试求出10的阶乘10!。

用循环:

result = 1
for i in range(1,11):result *= i
print(result)

打印。

要求:创建一个函数求任意数的阶乘:

def factorial(n):result = 1for i in range(1,n+1):result *= ireturn result
print(factorial(10))

打印。

递归:

即递归式函数;

递归简单说就是自己调用自己,递归式函数就是在函数中调用自己。

递归时解决问题的一种方式,思想是将大的问题拆分成小的问题,直到不能再拆分。

递归式函数的两个条件:

1.基线条件:

问题可以分解成最小的问题,当满足基线条件时,递归不再执行。

2.递归条件:

将问题继续分解的条件。

def recursion(n):#基线条件if n == 1:return 1#递归条件return n * recursion(n - 1)
print(recursion(10))

打印

9.2 递归练习

def exponentiation(n,i):#n为底数,i为指数#基线条件,指数i为1if 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))

即运用自带的幂运算符。

回文字符串:即字符串从前往后看和从后往前看是一样的,如、abcba。

方法:先检查第一个字符和最后一个字符是否一致,如果不一致不是回文字符串,如果一致,则看剩余部分是不是回文串。

def palindrome(s):#基线条件,如果字符串长度小于2,则这个字符串是回文串if len(s) < 2:return Trueelif 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

11.()函数

内置函数(),参数中传入可迭代的结构,即(,),可以从序列中过滤出符合条件的元素,保存到一个新的序列中。

参数:传递实现过滤功能的函数;

参数:需要过滤的序列。

返回值:过滤后的新序列。

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', '', '']。

还可以根据类型转换后的值进行比较:

l = ['34',554,'67',897,436]
l.sort(key=int)
print(l)

打印['34', '67', 436, 554, 897]

14.()函数:

返回一个新的列表。

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 Truenew_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())

打印.inner at >

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 + bprint('End...')return rdef multiply(a,b):# 求任意两个数的积print('Start...')r = a * bprint('End...')return rr = add(1,2)
print(r)

打印

Start...
End...
3

这是添加了提示开始和结束的输出,但是如果函数较多,会出现一些问题:

def fn():print('In fn()')def fn2():print('Start...')fn()print('End...')fn2()

打印

Start...
In fn()
End...

又如

def add(a,b):#求任意两个数的和r = a + breturn rdef new_add(a,b):print('Start...')r = add(a,b)print('End...')return rr = 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>

扩展后:

def fn():print('In fn()')def start_end():# 用来对其他函数进行扩展,使其他函数可以在执行前打印开始和结束# 创建一个函数def new_func():print('Start...')fn()print('End...')#返回函数return new_funcf = start_end()
f()

打印

Start...
In fn()
End...

但是是固定的、不能改变,失去了意义,所以可以在函数参数中传入被扩展的函数。

def fn():print('In fn()')def start_end(func):#用来对其他函数进行扩展,使其他函数可以在执行前打印开始和结束#创建一个函数def new_func():print('Start...')func()print('End...')#返回函数return new_funcf = start_end(fn)
f()

打印

Start...
In fn()
End...

当被扩展的函数有参数时,

def add(a,b):#求任意两个数的和r = a + breturn rdef start_end(func):#用来对其他函数进行扩展,使其他函数可以在执行前打印开始和结束#创建一个函数def new_func(a,b):print('Start...')result = func(a,b)print('End...')return result#返回函数return new_funcf = start_end(add)
r = f(1,2)
print(r)

打印

Start...
End...
3

需要对任何函数进行扩展时,

def add(a,b):#求任意两个数的和r = a + breturn rdef 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... 

总结:像上面的(func)这类的函数即称之为装饰器。

通过装饰器可以在不修改原来函数的基础之上来对函数进行扩展,在开发过程中,都是通过对装饰器来扩展函数的功能。

三.面向对象 1.面向对象简介 1.1 面向对象基本概念

面向对象OOP,即 。

什么是对象:

对象就是内存中存储指定数据的一块区域。

实际上对象就是一个容器,专门用来存数据。

程序运行的通俗解释:

代码存在硬盘,CPU处理代码,CPU和硬盘之间有内存,解释器将代码交给内存,CPU再从内存读取。

1.2 面向对象的结构 id(标识)

用来标识对象的唯一性,每个对象都有唯一的id,每个id指向一个内存地址值。

id由解释器生成,其实就是对象的内存地址。

type(类型)

类型决定了对象有哪些功能。

可以通过type()函数来查看对象的类型。

value(值)

值就是对象中存储的具体数据,分为:

1.3从面向过程到面向对象

所谓面向对象,简单理解就是语言中所有的操作都是通过对象来进行的。

面向过程 → 面向对象:

面向过程的典型代表是C语言,是早期语言的特点,是将一件事分成一个个步骤,并用函数分别实现。

面向对象是一种思考问题的方式,是一种思想,将事物变得简单化。

对面向对象的理解:

(1)可以让复杂事务变得简单化,人由执行者过渡到指挥者;

(2)具备封装、继承和多态。

举例——孩子吃瓜:

(1)妈妈穿衣服穿鞋出门

(2)妈妈骑电动车

(3)妈妈到超市门口停好电动车

(4)妈妈买瓜

(5)妈妈结账

(6)妈妈骑电动车回家

(7)到家孩子吃瓜

这是一个典型的面向过程的例子,在现实中没问题,但是在程序中会出现问题,在复用、修改时会出现很多问题。

面向过程的思想:

将一个功能分解成一个一个的小步骤。

面向对象就是让孩子吃瓜,而不管具体怎么实现,细节在对象内部。

2.类(class)的简介

目前所用到的对象都是内置的对象,如int、float、str等。

类简单理解就是一张图纸,在程序中我们需要根据类来创建对象。

a = int(10)
print(a)

打印10。

定义一个类:

自己定义类时,类名要以大写开头。

语法:

class 类名([父类]):代码块

class MyClass():pass
print(MyClass)

打印

class MyClass():pass
#用MyClass创建了一个对象mc,mc是MyClass的实例
mc = MyClass()
print(mc,type(mc))
12345

打印

解释:用创建了一个对象mc,mc是的实例。

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

()用来检查一个对象是否是一个类的实例。

3.对象的基本介绍 3.1 对象的创建流程

类也是一个对象,类是一个用来创建对象的对象。

class MyClass():pass
print(id(MyClass),type(MyClass))

打印28

显然,类是一个type类型的对象。

创建类的实例即对象时,实例并没有值,是空的,所对应的类也是没有值的,也是空类。

可以向对象中添加变量,对象中的变量称为属性。

class MyClass():pass
mc = MyClass()
mc.name = 'Tom'
print(mc.name)

打印Tom

即属性是添加到实例中,而不是到类中,如print(.name)会报错。

3.2 对象的定义

类和对象都是对现实生活中事物或程序内容的抽象。

实际上,所有的事物都是由两部分组成的:

※数据(属性)

※行为(方法)

在类的代码中,可以定义变量和函数;

在类中定义的变量将会成为所有实例的公共属性,所有实例都可以访问这些变量。

class Person():a = 10b = 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()

便会报错:: '' has no 'name'

在上例中,对于这个类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

可以说,每创建一个实例对象,特殊方法就执行一次。

对象的创建流程:

p1 = Person()

(1)创建了一个变量;

(2)在内存中创建了一个新的对象;

(3)执行类中的代码块的代码,并且只在类中执行一次;

(4)()方法执行。

即如类的定义中除特殊方法还有别的代码语句,则其他语句先执行,如,

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

()方法会在对象创建以后立即执行,向新创建的对象初始化属性,

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

为了解决之前出现过的问题,可以在()方法中添加参数,通过self向新建的对象初始化对象。

class Person:def __init__(self,name):self.name = namedef speak(self):print('Hello,I\'m %s'%self.name)p1 = Person()
p1.speak()

初始化时未添加name,会报错

: () 1 : 'name'

所以必须在初始化时即添加,

class Person:def __init__(self,name):self.name = namedef 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 = nameself.color = colordef 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 = nameself.color = colordef 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 = named = Dog('erha')
d.name = 'kaisa'
print(d.name)

打印kaisa

此例中name属性即未被隐藏,会被任意修改。

如何修改对象中的一个属性:

将对象的属性名修改为外部不知道的名字。

class Dog:def  __init__(self,name):self.hidden_name = namedef 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 = namedef speak(self):print('Hello,I\'m %s'%self.hidden_name)d = Dog('erha')
d.hidden_name = 'kaisa'
d.speak()

打印Hello,I'm kaisa,这又不是封装,成了最初的形式。

如何获取(修改)对象当中的属性:

通过提供()和()方法来访问和修改属性。

class Dog:def  __init__(self,name):self.hidden_name = namedef speak(self):print('Hello,I\'m %s'%self.hidden_name)def get_name(self):#获取对象的属性return self.hidden_namedef set_name(self,name):#修改对象属性self.hidden_name = named = Dog('erha')
print(d.get_name())
d.set_name('kaisa')
print(d.get_name())
d.speak()

打印

erha
kaisa
Hello,I'm kaisa

使用封装确实在一定程度上增加了程序的复杂性,

但是它确保了数据的安全性:

1.隐藏了属性名,使调用者无法任意修改对象中的属性。

2.增加了和方法,可以很好地控制属性是否是可读的:

如果希望属性是可读的,则可以直接去掉方法;

如果希望属性不能被完结访问,则可以直接去掉方法。

3.使用方法设置属性,可以增加数据的验证,确保数据的值是正确的。

增加了程序的安全性。

4.可以在读取属性和设置属性的方法中做一些其他的操作。

class Dog:def  __init__(self,name,age):self.hidden_name = nameself.hidden_age = agedef speak(self):print('Hello,I\'m %s'%self.hidden_name)def get_name(self):#获取对象的属性return self.hidden_namedef set_name(self,name):#修改对象属性self.hidden_name = namedef get_age(self):#获取对象的属性return self.hidden_agedef set_age(self,age):#修改对象属性self.hidden_age = aged = 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 = nameself.hidden_age = agedef speak(self):print('Hello,I\'m %s'%self.hidden_name)def get_name(self):#获取对象的属性return self.hidden_namedef set_name(self,name):#修改对象属性self.hidden_name = namedef get_age(self):#获取对象的属性return self.hidden_agedef set_age(self,age):#修改对象属性#判断再处理if age > 0:self.hidden_age = aged = 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 = namedef get_name(self):return self.hidden_namedef set_name(self,name):self.hidden_name = namep = Person('Tom')
p.set_name('Jerry')
print(p.get_name())

打印Jerry,即属性值会改变。改为

class Person():def __init__(self,name):self.__name = namedef get_name(self):return self.__namedef set_name(self,name):self.__name = namep = Person('Tom')
print(p.get_name())

打印Tom

class Person():def __init__(self,name):self.__name = namedef get_name(self):return self.__namedef set_name(self,name):self.__name = namep = Person('Tom')
print(p.__name)

会报错: '' has no ''

class Person():def __init__(self,name):self.__name = namedef get_name(self):return self.__namedef set_name(self,name):self.__name = namep = Person('Tom')
p.__name = 'Jerry'
print(p.get_name())

打印还是Tom,没有被修改。

总结:可以为对象的属性使用双下划线开头,__xxx。

双下划线开头的属性是类的隐藏属性,隐藏属性只能在类的内部访问,无法通过外部访问。

隐藏属性是自动为属性改了一个名字,实际改的名字是:类名_属性名:

->

class Person():def __init__(self,name):self.__name = namedef get_name(self):return self.__namedef set_name(self,name):self.__name = namep = Person('Tom')
print(p.get_name())
p._Person__name = 'Jerry'
print(p.get_name())

打印

Tom
Jerry

即又可以对属性进行修改。

在写代码的时候,可以用一个下划线,既容易书写,外部能访问,且不易被修改。

一般情况下:使用_开头的属性都是私有属性,没有特殊情况不要修改。

9.装饰器@

用来将get()方法转化为对象的属性。

添加装饰器以后,就可以像调用属性一样调用方法。

class Person():def __init__(self,name):self._name = namedef name(self):return self._namep = Person('Tom')
print(p.name())

打印Tom

用了装饰器以后,

class Person():def __init__(self,name):self._name = name@propertydef name(self):print('The method is run')return self._namep = Person('Tom')
print(p.name)

输出结果为:

The method is run
Tom

加入后,

class Person():def __init__(self,name):self._name = name@propertydef name(self):print('The method is run')return self._name@name.setterdef name(self,name):self._name = namep = 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):passd = 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的实例,也是的实例,所以Dog也继承自了。

如果在创建类的时候省略了父类,则默认父类是;

是所有类的父类,所有类都继承自。

()检查一个类是否是另一个类的子类。

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)

证明了所有类都是的子类,所有对象都是的实例。

10.3 方法的重写

如果在子类中有和父类重名的方法,通过子类的实例去调用方法时,会调用子类的方法而不是父类的方法,这个特点称为方法的重写(覆盖,)。

如,

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 ...

下边再详细说明,

class A(object):def test(self):print('A...')class B(A):passclass C(B):passc = 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):passc = 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...。

当我们调用一个对象时,会有先去当前对象寻找是否有该方法,如果有直接调用;如果没有,则取当前对象的父类中寻找,如果有,直接调用父类中的方法,如果没有,则去父类的父类寻找,如果有直接调用,以此类推,直到找到,如果依然没有则报错。

10.4 super()

父类中所有的方法都会被子类继承,包括特殊方法,如()等,也可以重写特殊方法。

class Animal:def __init__(self,name):self._name = namedef run(self):print('Running...')def sleep(self):print('Sleeping...')@propertydef name(self):return self._name@name.setterdef name(self,name):self._name = nameclass 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()

会抛出异常,: () 1 : 'name'

因为初始化没有传入参数,因为父类初始化要传入name这个参数,子类也必须传入。

传入参数后,

class Animal:def __init__(self,name):self._name = namedef run(self):print('Running...')def sleep(self):print('Sleeping...')@propertydef name(self):return self._name@name.setterdef name(self,name):self._name = nameclass 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

调用方法修改属性,

class Animal:def __init__(self,name):self._name = namedef run(self):print('Running...')def sleep(self):print('Sleeping...')@propertydef name(self):return self._name@name.setterdef name(self,name):self._name = nameclass 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 = namedef run(self):print('Running...')def sleep(self):print('Sleeping...')@propertydef name(self):return self._name@name.setterdef name(self,name):self._name = nameclass Dog(Animal):def __init__(self,name,age):self._name = nameself._age = agedef run(self):print('Dog is running...')def sleep(self):print('Dog is sleeping...')def Housekeep(self):print('Dog is housekeeping')@propertydef age(self):return self._age@age.setterdef age(self, age):self._age = aged = Dog('erha',10)
print(d.name)
print(d.age)

打印

erha
10
12

可以直接调用父类中的()来初始化父类中的属性,

class Animal:def __init__(self,name):self._name = namedef run(self):print('Running...')def sleep(self):print('Sleeping...')@propertydef name(self):return self._name@name.setterdef name(self,name):self._name = nameclass Dog(Animal):def __init__(self,name,age):Animal.__init__(self,name)self._age = agedef run(self):print('Dog is running...')def sleep(self):print('Dog is sleeping...')def Housekeep(self):print('Dog is housekeeping')@propertydef age(self):return self._age@age.setterdef age(self, age):self._age = aged = Dog('erha',10)
print(d.name)
print(d.age)

即Dog类中的初始化方法中,name属性通过的初始化调用获取,可以将父类的多个属性传给子类,但是继承的父类是固定的,这是需要用super()动态地继承父类属性。

super()可以用来获取当前类的父类,并且通过super()返回的对象,调用父类方法时不需要传入self。

class Animal:def __init__(self,name):self._name = namedef run(self):print('Running...')def sleep(self):print('Sleeping...')@propertydef name(self):return self._name@name.setterdef name(self,name):self._name = nameclass Dog(Animal):def __init__(self,name,age):super().__init__(name)self._age = agedef run(self):print('Dog is running...')def sleep(self):print('Dog is sleeping...')def Housekeep(self):print('Dog is housekeeping')@propertydef age(self):return self._age@age.setterdef age(self, age):self._age = aged = 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):passprint(C.__bases__)
print(B.__bases__)
print(A.__bases__)

打印

(<class '__main__.B'>,)
(<class '__main__.A'>,)
(<class 'object'>,)

在中支持多重继承,即可以为一个类同时指定多个父类。

class A(object):def test(self):print('A...')class B(object):def test2(self):print('B...')class C(A,B):passprint(C.__bases__)

打印(, )。

class A(object):def test(self):print('A...')class B(object):def test2(self):print('B...')class C(A,B):passc = 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):passc = C()
c.test()

会打印A...test

如果A中没有test()方法,

class A(object):passclass B(object):def test(self):print('B...test')def test2(self):print('B...')class C(A,B):passc = C()
c.test()

会打印B...test

11.多态

多态是面向对象的三大特征之一。

多态字面理解即多种形态,在面向对象中指一个对象可以以不同的形态呈现。

class A(object):def __init__(self,name):self._name = name@propertydef name(self):return self._name@name.setterdef name(self,name):self._name = nameclass B(object):def __init__(self, name):self._name = name@propertydef name(self):return self._name@name.setterdef name(self, name):self._name = namedef 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@propertydef name(self):return self._name@name.setterdef name(self,name):self._name = nameclass B(object):def __init__(self, name):self._name = name@propertydef name(self):return self._name@name.setterdef name(self, name):self._name = nameclass 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)

会抛出异常,: 'C' has no 'name',

增加类型检查,

class A(object):def __init__(self,name):self._name = name@propertydef name(self):return self._name@name.setterdef name(self,name):self._name = nameclass B(object):def __init__(self, name):self._name = name@propertydef name(self):return self._name@name.setterdef name(self, name):self._name = nameclass C(object):passdef 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。

在()这个函数中做了类型检查,也就是obj是A类型的对象的时候,才可以正常执行,其他类型的对象无法使用该函数。

这个函数其实就是违反了多态。

违反了多态的函数,只适合于一种类型的对象,无法适用于其他类型的对象,导致函数的适用性非常差。

多态使面向对象的编程更加具有灵活性。

l = [1,2,3]
s = 'python'
print(len(l),len(s))

如上,len()函数既可以得到list的长度,又可以获取str的长度,

len()就可以检查不同对象类型的长度就是面向对象的特征之一——多态。

len()的底层实现:

之所以len()函数能获取长度,是因为这些对象中有一个特殊方法()方法;

换句话说,只要对象中有()方法,就可以通过len()方法来获取对象的长度。

class B(object):def __init__(self, name):self._name = name@propertydef name(self):return self._name@name.setterdef name(self, name):self._name = nameb = B('Jerry')
print(len(b))

会报错: of type 'B' has no len()。

在B中加入()方法后,

class B(object):def __init__(self, name):self._name = name@propertydef name(self):return self._name@name.setterdef name(self, name):self._name = namedef __len__(self):return 1b = B('Jerry')
print(len(b))

会打印1

面向对象的三大特征:

1.封装:确保对象中的数据更安全;

2.继承:保证对象的可扩展性;

3.多态:保证了程序的灵活性。

12.类中的属性和方法

类属性是直接在类中定义的属性,类属性可以通过类或类的实例访问;

通过实例对象添加的属性属于实例属性。

class A(object):#类属性count = 0a = A()
print(A.count)
print(a.count)

打印

0
0

修改实例的属性,

class A(object):#类属性count = 0a = A()
a.count = 5
print('In class,',A.count)
print('In instance,',a.count)

打印

In class, 0
In instance, 5

修改类中的属性,

class A(object):#类属性count = 0a = 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)

报错: type 'A' has no 'name'

在类中定义的以self为第一个参数的方法都是实例方法;

实力方法在调用时,会将调用对象作为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()

报错: test() 1 : '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)。

可以通过装饰器来调用类方法;

类方法的第一个参数是cls,也会被自动传递,cls就是当前的类对象。

class A(object):count = 0def __init__(self):#实例属性self.name = 'Tom'def test(self):#实例方法print('in test')@classmethoddef test2(cls):#实例方法print('in test2',cls)a = A()
A.test2()

打印in test2

调用类属性,

class A(object):count = 0def __init__(self):#实例属性self.name = 'Tom'def test(self):#实例方法print('in test')@classmethoddef test2(cls):#实例方法print('in test2',cls)print(cls.count)a = A()
A.test2()

打印

in test2 <class '__main__.A'>
0

通过实例调用类方法,

class A(object):count = 0def __init__(self):#实例属性self.name = 'Tom'def test(self):#实例方法print('in test')@classmethoddef test2(cls):#实例方法print('in test2',cls)print(cls.count)a = A()
a.test2()

结果相同。

可总结:类方法可以通过类对象调用,也可通过实例方法调用。

A.test2()等价于a.test2()。

静态方法:

用装饰器来调用类方法

class A(object):count = 0def __init__(self):#实例属性self.name = 'Tom'def test(self):#实例方法print('in test')@classmethoddef test2(cls):#实例方法print('in test2',cls)print(cls.count)@staticmethoddef test3():print('in test3')a = A()
A.test3()
a.test3()

打印

in test3
in test3

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

静态方法一般是工具方法,和当前类无关。

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了