面向过程:
- 变量
- 程序的交互
- 基础数据类型
- 基本运算符
- python数据类型
- 数据类型的总结
- 流程控制
1.变量
变量就是用来存储数据的地址的名称
print() //输出
print(end=”;”) //以什么结束,默认换行。
input(”请输入姓名:”) //提示输入接收到的值是字符串
type() //查看数据类型
id() //查看内存地址
int() //将字符串转换成int类型的。
v.bir_length() //查看int型数字至少占用多少位
强类型与弱类型的区别:
强类型就是数字和字符串不相等。弱类型的是相等的。
编程语言分为:
动态类型和静态类型,是指数据类型可以变化例如a=10 ,a=”10”。
有整形(int),布尔型,字符串(str)加了引号的就是字符串类型。2.运算符:
算数运算符,赋值运算符,逻辑运算符,成员运算。逻辑运算:and,or,not。
and:true and 0 第一个为真直接输出第二个,第一个为假输出第一个
or: ture or 0 第一个为真直接输出第一个,第一个为假输出第二个。
not:只会返回ture或者false。成员运算:in not in
返回ture或者faulse。
3.条件控制
条件控制
if控制
分为单分支,双分支,和多分支。
单分支语法:
if(条件):
条件执行体
双分支语法:
if(条件):
条件执行体
else:
条件执行体
//双分支代表如果不满足第一种则执行第二种。两种情况一定会执行一个
多分支语法:
if(条件表达式):
条件执行体
elif(条件表达式:
条件执行体
elif(条件表达式):
条件执行体
[else]:
条件执行体
//逻辑是从上往下判断,一旦有符合条件的就执行。如果全部都不满足,且定义了else就会执行else里面的语句
if嵌套:
if(条件表达式):
执行语句
if(条件表达式):
执行语句
elif(条件表达式):
执行语句
else:
执行语句
//可以用多个if嵌套来解决问题。
a=8
if(a>5): //如果满足条件执行下面的语句,if语句也会执行.
print('大于5')
if(a>6): //因为是嵌套在if中的所以可以执行
print('dayu6')
if(a>7):
print('dayu7')
else:
print(a<5)
//输出结果是大于5,dayu6,dayu7.
小明成绩赢麻了案例:
score=int(input("请输入小明的成绩:"))
if(score<0 or score>100):
print("输入有误,请重新输入")
elif(score<60):
print("不及格")
elif(score<80):
print("良好")
else:
print("优秀,请继续保持!")
//其中对字符串的修改不建议用int()直接修改,而应该使用:
if score.isdigit()://用isdigit()判断是否属于数字
num= int(score) //再用int方法对数据进行处理。
//尽量让程序减少报错。
循环:
while语法:
while 条件表达式:
循环体
//条件表达式为faulse就退出循环。
计算1+2+3+…+1000案例:
a = 0
sum=0
while(a<=1000):
sum+=a
a=a+1
print(sum)
1000以内有多少个水仙花数案例:
ge=0
shiwei=0
baiwei=0
num=99
while(num<=998):
num = num + 1
baiwei=num//100
shiwei=num%100//10
ge=num%10
if(ge**3+shiwei**3+baiwei**3==num):
print(num)
//重点是求出各个位置的数
其他方法:用range来生成数字。
for bai in range(1,10): #百位
for shi in range(0,10): #十位
for ge in range(0,10): #个位
i=bai*100+shi*10+ge
if i==bai**3+shi**3+ge**3:
print(i)
五人钓鱼案例:
"""ABCDE五个人在某天夜里合伙去捕鱼,到第二天凌晨时期疲惫不堪
于是各自找地方睡觉
A第一个醒来,它将鱼分成5份,把多余的一条鱼扔掉拿走了自己的一份
BCDE依次醒来按照同样的方法分鱼
问他们一共补了多少条鱼
"""
fish=1
flag =True
while flag:
totla,enough=fish,True
i=5
while i :
if(totla%5==1):
totla=(totla-1)//5*4
else:
enough=False
i -= 1
fish=fish+1
if enough:
flag = 0
print(fish)
//计算顺序,括号的优先级最高,然后是乘方,才是乘除取模和整除。然后才是加减。然后是位运算符
猜数字游戏:
import (random) //引入模块
import random
num = random.randint(1,100)
i=3
print("一共有%s次机会" %i)
while i :
guess = input("请输入你要猜的数字")
if guess.isdigit():
guess=int(guess)
else:
print("输入有误,请重新输入")
guess = input("请输入你要猜的数字")
if guess>num:
print(f"猜大了你还剩 {i-1} 次机会")
i-=1
elif guess<num:
print(f"猜小了你还剩 {i-1} 次机会")
i-=1
elif guess==num:
print(f"猜对了,一共猜了{i-1}次")
break
//若要输出变量,可以用{}将变量括起来并在前面加f
random模块是随机数模块,里面的方法randint(1,100)可以生成一个1-100的随机数
2.程序的交互
输入:
name=input("请输入你的名字“)
print("你的名字是",name)
格式化输出:
name=input("请输入名字:")
age=input("请输入年龄:")
sex=input("请输入性别:")
info= '''
-------info of %s--------
姓名:%s
年龄:%s
性别:%s
''' %(name,name,age,sex)
print(info)
3.基础数据类型
整数型,bool值和字符串
字符串类型:在Python中,加了引号的字符都被认为是字符串!
字符串:
字符串操作:
a='abdcdefg'
print(a[1::1]) //切片
print(a[0]) //索引
常用方法:
.capitaliza() //首字母大写
.swapcase() //大小写翻转
.title() //每个单词首字母大写
.center(20,"*") //居中
.count(e,0,30) //从索引0-30字符元素出现的个数
print(f'你好{i}') //格式化输出
print('{}{}{}'.format('cxy',18,man))
.strip("*") //去除字符串结尾的*号
4.容器类型:
列表,字典,集合,元组
列表:可以混放,并且后期可以加数据。列表不可哈希。
列表的操作:
list1=['nihao','wojiao','cxy'] //创建列表,也可以创建空列表
增加:
.append('nihao') //增加到最后、不生成新列表
list1.append(555)
print(list1)
.extend(list2) //将两个列表合并,迭代的去增加到末尾
list1.extend(1,2,3)
print(list1)
.insert(0,'wobuhao') //按照索引位置添加
list.insert(7,111) //如果超出会添加到最后
删除:
.pop(1) // 按照位置删除,如没有数字则从最后一个开始删除
del list1[1:3] //按照位置去删,也可以切片删除、或者直接删除列表
.remove() //按照元素的值去删除,删除不存在的值会报错,字符串和数字不能一起删
.clear() // 清空列表
修改:
li[0]='aaa' //按照索引去修改
list1[2:3]=[3,'e'] //按照切片去修改
查询:
list[1] //按索引查
list[1:4] //按照切片查
for i in list:
print(i) //循环的查
元组:
字典:
字典操作:
dic1={'k1':'v1','k2':'v2'} //是以键值对的方式来定义的
增加:
dic1['li']=['1','2','3','4'] //字典的值可以为一个列表,字典和值
dic.setdefault('k','v') //如果原字典存在设定的键,则他不会更改或者覆盖,如果不存在则会创建
删除:
.pop('k1','查无此项') //根据键删除键值对,如果键不存在会报错,会返回删除键的值.若设置了后面的参数会返回设定的值
print(dic.pop(k2))
输出的是v2 //其他的不行
del dic['name'] //删除键对应的键值对,不存在会报错
.popitem() //随机删除键值对,将删除值以元组形式返回。从后往前删的
.clear() //清空字典
修改:
.update(dic2) //将两个字典的键值对添加覆盖(相同的覆盖,没有的添加)到dic2中。
dic2['k1']='nihao' //根据键修改值
查询:
valuve=dic['k2'] // 根据键来查,会报错
dic.get('abc','查无此项') //根据键来查,有默认选项,不会报错
其他操作:
for i in dic.item(): // 将键和值作为元组列出
for key,values in dic.item(): //将键和值以字符串类型的输出
for i in dic: //只迭代键
dic.keys() //列出所有键
dic.values() //列出所有值
集合set:
集合是无序的,不可重复的数据集合。能够对列表进行去重。通过set方法将列表变成集合
set1={1,2,5,6}
集合的增加:
.add() //添加到任意位置
.update() //迭代添加,必须是可迭代对象。若迭代的是字典则会添加键。列表是全部迭代
删除:
.remove() //删除特定的值
.pop() //随机删除一个
.clear() //清空集合
del set1 // 删除集合
集合的其他操作:
交集,并集,补集...
可以用.frozenset方法将集合变为不可变类型
模拟斗地主发牌案例:
需求:
地主:17张牌+3张底牌
玩家1:17张牌
玩家2:17张牌
每次都是随机,看牌的时候要排好序
5.文件操作:
对文件的操作:
第一种需要手动将文件关闭
f =open('db.txt','rw',encoding='utf-8')
data = f.readlines() //读出来的是一个列表,将所有的数据都读出来存储到一个列表中,包括换行符)
f.write('hello world')
f.closed() //关闭文件句柄
第二种是用with的方法:这种方法将文件托管给系统进行管理
with open('db.txt,'rw',encoding='utf-8') as f
data=f.readlines() //不用调用closed 手动关闭
文件编码:
如果不指定文件编码格式,则会按照系统的编码去操作文件,window是GBK,linux是utf-8
文件的打开模式:
r:只读,不可写,文件不存在报错。默认模式
w:只写,不可读,不存在就创建,存在就会覆盖内容
a:只追加写模式,不可读,不存在就创建,存在就追加。
注意:对于非文本类的文件,例如图片,视频。我们就需要加一个b表示以字节的方式操作。使用这种模式不需要考虑文本文件的字符编码。
rb,wb,ab。
+模式就是增加了一个功能。
r+,w+,a+。r+b,w+b,a+b
文件 操作的方法:
def close(self,*arg,**kwargs) //real signature unknown
关闭文件
pass
文件的修改:
文件的数据是存放于硬盘上的,因而只存在覆盖,不存在修改,具体的实现方法是
- 将硬盘存放的该文件的内容全部加载到内存,在内存中可以修改的,修改完后,再由内存覆盖到硬盘(word,vim等编辑器)
- 将硬盘存放的该文件的内容一行一行的读入内存,修改完毕后就写入新文件,最后用新文件覆盖旧文件
一种是直接全部读完,一种是一行一行的读。
直接赋值,深拷贝和浅拷贝:
- 直接赋值:就是对象的引用(别名)
- 浅拷贝:拷贝父对象,不会拷贝对象内部的子对象。例如父对象内部的子对象修改,拷贝后的文件也会跟着修改
- 深拷贝:拷贝父对象及其子对象。需要引用import copy模块。
实例:
浅拷贝:
a= {1: [1, 2, 3]}
b = a.copy()
print(a, b) //输出的是a和b输出的是一样的,这里用的copy()是浅拷贝函数
a[1].append(4)
print(a, b) //a,b还是输出的是一样的
# 这个案例中我们可以看到a和b其实还是同一个字典
深拷贝:
import copy //深拷贝要引入copy模块
a = {1: [1, 2, 3]}
b = copy.deepcopy(a) //深拷贝要函数deepcopy()
print(a, b)
a[1].append(4)
print(a,b) //这里输出的a,b不一样,a输出了追加的内容,而b输出的是拷贝时的内容。
//也就是说深拷贝和浅拷贝的差别是,浅拷贝复制的是父对象的地址,子对象的地址没有拷贝
而深拷贝拷贝了父对象和子对象的地址。
案例:
实现注册登陆功能.
6.函数
定义:
函数是组织号的,可重复使用的,用来实现单一,或相关联功能的代码段。
案例:
a ='hello world'
b='cxy'
def func1(a): //定义函数名,注意后面有冒号。形参的定义
len1=0
for i in a
len1 += 1
print(len1)
return len1 //返回一个len1
v1=func1(a) //将函数的返回值用一个变量去接收,可以对返回值进行操作
print(v1)
func1(b) //函数调用
return:是指函数执行完时返回一个值给调用的对象。return多个返回值,会被塞到一个元组中。
函数的参数:
形参和实参的传递:
按位置传,按关键字传参,混合传参。
注意:对于混合传参。位置参数必须在关键字传参的前面。
形参的类型:
- 默认参数
- 默认参数可以是一个可变数据类型,如列表,字典等
- 动态参数(不定长传参):args,*kwargs。接收所有的单个元素,并且返回一个元组的形式
命名空间和作用域:
命名空间一共分为三类:
全局,局部,内置
取值顺序:
在局部调用:局部命名空间,全局命名空间,内置命名空间
在全局调用:全局命名空间,内置命名空间
变量的作用域:
全局作用域:包含了内置命名空间,全局命名空间,在文件任何位置都能被引用,全局有效。
局部作用域:局部命名空间,只能在局部范围内生效
globals,locals可以查看调用地方的可用变量。
可以通过global改变作用范围:在函数局部声明范围可以在全局调用。对于可变数据类型,可以直接引用不用通过global
nonlocal
不能修改全局变量,在局部作用域中,对父级作用域(或更外层作用域非全局作用域)的变量进行引用,。引用的意思是可以修改值,不是复制。并且引用的那层从下此变量全部发生改变。
nonlocal的实例:
def add_b():
b = 1
def do_global():
b = 10
print(b)
def dd_nolocal():
nonlocal b # 应用了上一层的变量b
b = b + 20
print(b) # 发生了改变
dd_nolocal() # 调用函数,导致do_global的命名空间b也改变了
print(b)
do_global()
print(b)
add_b() # 最上面一层没有变化 //输出的值为10,30,30,1.
函数的嵌套和作用域链
函数的嵌套就是函数中可以嵌套另外一个函数。同样函数的调用会从局部空间到全局空间。
函数名的本质:函数名就是函数的内存地址。
- 函数名可以被引用(定义别名,赋值)
- 可以被当作容器类型的元素,也就是可以将函数名存放到列表,字典中。因为它的本质就是一个地址
- 可以当作函数的参数和返回值。函数的参数可以是一个函数
闭包
内部函数包含对外部作用域而非全局作用域变量的引用,该内部函数称为闭包函数。也就是说内部函数用到的变量能够从外部函数中获得,而不是全局变量中获得。能实现函数的自给自足。那么这个函数就称为闭包函数。
如何判断一个函数是否是闭包函数呢?
closure() 函数。如果返回值是cell则说明函数是闭包的。否则说明函数不是闭包的。
我的理解是。闭包函数能够对第一次从硬盘中读取到内存的数据进行多次引用。而不是每次都从硬盘中读取数据。也就是倘若睡眠3s。是一开始睡眠3s。之后不用睡眠,还是每一次都需要睡眠3s。
案例:
闭包函数:inner
def func():
name = 'aaron'
def inner():
print(name) //这里是对外部空间变量name的引用
print(inner.__closure__) //只能在inner内部判断是否是闭包函数。在全局空间中再执行这一个语句 的话就会报错。说没有inner函数。具体原因就是全局空间不能引用局 部空间的变量
return inner()
f=func
f()
from urllib.request import urlopen //从urllib.request模块中,引入urlopen方法
def func():
content = urlopen('http://myip.ipip.net').read()
def get_content():
return content
print(get_content.__closure__) //用到的变量content是外部函数func的变量,所以判断是闭包函数
return get_content
code = func() //因为执行了func()函数,return的是get_content函数的地址,所以code是地址
content =code() //调用了get_content()函数,返回的是content,也就是函数func()中从网页上读取到 的内容
print(content.decode('utf-8')) //用.decode('utf-8')解码。并输出
conten2 = code()
print(conten2.decode('utf-8'))
7.装饰器:
案例:
修复登陆的bug.加上登陆成功后可以点菜以及退出登陆功能.
点菜需要通过装饰器鉴定权限
什么是装饰器:
让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数.
本质上看来,装饰器也是一种函数,它的特点是返回值是一个函数对象,通过’@’符号引用装饰器加到别的函数上,来增加函数的功能. 是一种语法糖.所谓语法糖就是添加某种语法,这种语法对语言的功能并没有什么影响,但是更方便程序使用.
通过装饰器可以实现开放封闭原则一句话说来就是软件实体应该是可拓展的但是不可修改的.对于扩展是开放的对于修改时封闭的.
案例:
检查程序运行的时间
import time //引入一个时间模块
def timer(func): 定义装饰器(装饰器的形参必须是一个函数)若不是函数的话就不能将函数的功能加进来.
def inner(): //真正执行功能的地方
start = time.time() //程序开始运行的时间戳
func() //运行我们需要检查运行时间的程序
print(time.time()-start) //将运行之后的时间,和运行之前的时间作差就能得到运行时间
return inner //装饰器的返回值必须是一个函数对象
@timer //通过@参数器名的方式为函数添加装饰器
def func1(): //自己定义的函数
time.sleep(1)
print('in func1')
func1() //调用函数这里其实调用的是inner()函数 因为加了装饰器之后相当于执行的语句是 func1 = timer(func1) 将func1传到timer函数中,返回的是inner的地址,所以在加了装饰器执行相同的函数,在内部却执行了inner函数.这就是装饰器的本质,
装饰一个带参数的函数:
本质就是让inner()函数与被装饰的函数保持相同的形参.
案例:
装饰带一个形参的函数,当然也可以装饰带任意形参的函数通过(*args,**kwargs)来实现形参的传递.装饰器不用加参数.
import time
def timer(func):
def inner(a):
start = time.time()
func(a)
print(time.time()-start)
return inner
@timer
def func1(a):
time.sleep(1)
print(f'形参是:{a}')
func1('hello,world')
不是很重要的一个点:
当我们需要查看注释信息的时候,会发现注释信息不能正常显示,所以需要加上import wraps模块.引入一个wraps(func)装饰器.
带装饰器的参数:
带多个装饰器装饰一个函数:
def wrapper1 (func):
print('第一个装饰器')
def inner1():
print('第一个装饰器运行之前')
func()
print('第一个装饰器运行之后')
return inner1
def wrapper2 (func):
print('第二个装饰器')
def inner2():
print('第二个装饰器运行之前')
func()
print('第二个装饰器运行之后')
return inner2
@wrapper1 //后装饰的先执行 第二个装饰 2
@wrapper2 //装饰器一层一层的往上面装 第一个装饰 1
def func1():
print('hello,world')
func1()
运行结果:
第二个装饰器
第一个装饰器
第一个装饰器运行之前
第二个装饰器运行之前
hello,world
第二个装饰器运行之后
第一个装饰器运行之后
8.Python垃圾回收机制:
主要策略是:引用计数器为主,标注清除和分带回收为辅
那么什么是引用计数器,什么是标记清除和分带回收呢?
在python程序中,创建的任何对象都会放在refchain的双向链表中,也就是双向链表帮忙维护python中所有的对象.
总的来说,引用计数器就是指创建一个对象的时候,例如’hello,world’这个字符串,会在内存中开辟一个地址空间并存放到双向链表中,如果我们将它赋值给一个变量a.也就是a=’hello,world’ 那么这个字符串对象的引用计数器就加1.
当引用计数器变为0的时候就会被python自动回收.
但是有一种情况存在就是在双向链表中,两个容器类型中的元素会互相引用,也就是循环引用,如果我们删除容器类型的数据,但是实际上他们的计数器还是不为0.会一直存在空间中,所以就引入了标记清除和分带回收.
标记清除法:
【标记清除(Mark—Sweep)】算法是⼀种基于追踪回收(tracing GC)技术实现的垃圾回收 算法。它分为两个阶段:第⼀阶段是标记阶段,GC会把所有的『活动对象』打上标记,第⼆阶 段是把那些没有标记的对象『⾮活动对象』进⾏回收。那么GC⼜是如何判断哪些是活动对象哪 些是⾮活动对象的呢? 对象之间通过引⽤(指针)连在⼀起,构成⼀个有向图,对象构成这个有向图的节点,⽽引⽤关 系构成这个有向图的边。从根对象(root object)出发,沿着有向边遍历对象,可达的 (reachable)对象标记为活动对象,不可达的对象就是要被清除的⾮活动对象。根对象就是全 局变量、调⽤栈、寄存器 . 会将没有标记的对象清除.
图示: 其中45不可达被清除
分带回收:
标记清除法的效率不高,因为清除非活动对象的时候,会顺序扫描整个堆内存.为了解决这个问题提出了分带回收,
在python中会去再维护一个链表,这个链表会存放那些可能存在循环引用的对象.按照弱代假说的理论 ,越是年轻的对象通常死得越快,而老对象则很有可能存活的时间更长.因此会将第二个链表维护成3个环状双向的链表,0代,1代,2代.其中0代中对象的个数达到700个扫描一次,0代扫描10次1代扫描一次,1代扫描10次2代扫描一次
扫描的过程实际上也是检查是否存在循环引用,并将他们的引用计数器的值减0.对象的引用次数为0则会被回收.返回给操作系统分配.
缓存机制:
- 有缓存池
会缓存一些比较常用的对象例如-5-256会被缓存到池中,不用再创建
- free-list
有些对象的引用计数器为0的时候,也不会被删除,而是会收到一个叫free-list链表中缓存,但是free-list有一个最大值是80,只有当它满的时候,才会直接去销毁缓存的对象.
9.迭代器与生成器:
迭代器:
迭代:对同一个数据进行多次相同的操作
字符串,列表,元组,字典,集合都可以被for循环,说明它们都是迭代器,也就是里面的值可以被一一 的拆出来。
可迭代协议:如果满足可迭代协议可以转换成迭代器,如果不满足则不能转换。如何判断一个对象是否满足可迭代协议就看他有没有.iter()方法那么如何取数据呢。对象满足可迭代协议则说明有取数据的潜力,具体取的话需要用iter()方法,转换成迭代器通过netx()方法取数据
for循环就是基于迭代器协议提供了一个统一的可以遍历所有对象的方法,即在遍历之前,先调用对象的iter方法将其转换成一个迭代器,然后使用迭代协议取实习循环访问,这样所有对象就可以通过for寻魂来遍历了。
最重要的一点,转换成迭代器,在循环时,同一时刻在内存中值出现一条数据,极大限度的节省了内存
如何判断一个对象是否是可迭代的
from collections import Iterable
L1=[1,2,3,4]
print(isinstance(l,Iterable))
不推荐这种方法:
如何判断一个对象是否满足可迭代协议
l1=[1,2,3,4].__iter__() //有iter方法则说明满足可迭代协议,有取数据的潜力
print(l1) //说l1是itretor类型,如果没有iter()则是一个列表,加了就变成了迭代器
如何取值呢?
l1.__next__()
推荐这种方法:
l=iter([1,2,3,4,5,6]) //l是迭代器
print(netx(l)) //更直观
以及
for i in l1
print(i)
生成器:
本质上就是一种迭代器,但是区别是在迭代的中途能暂停。
不会一次性在内存中生成太多数据,而是能够被控制。
案例
def produce():
for i in range(1,1001)
yield f'生成了第{i}个包子' //关键字yield,就是生成器函数
return f'生成了第{i}个包子' //只会生成一个 ,遇到return就返回。
print(p1.__next__())
send
send在获取下一个值的效果和next基本一致
只是在获取下一个值的适合,给上一个yield的位置传递一个数据
使用send的注意事项
- 第一次使用生成器的适合要用netx获取下一个值
- 最后一个yield不能接收外部的值
案例::
def produce():
for i in range(1,1001):
typy1= yield f'生成了第{i}个包子'
print(f'{type1}类型的包子送到')
p1= produce()
print(netx(p1))
print(p1.send('肉包子')
输出:
生成了第1个包子
肉包子类型的包子送到
生成了第2个包子
10.列表推导式和生成器表达式
就是通过一些条件来推导出特定的列表。
l= [i for i in range(10)] //通过列表推导式生成了一个列表
print(l)
l1=[f'项目{i}' for i in range(10)]
print(l1)
l1=(i for i in range(10))
print(next(l1)) //将[]变成()就变成了生成器表达式
ret=sum(x for x in range(101))
print(ret)
- 列表解析与生成器表达式都是一种遍历的编程方式,只不过生成式表达器更节省内存?
列表推导式:
```python multiples= [i for i in rang(30) if i%3 is 0] //30以内所有能被3整除的数
//找到嵌套列表中名字含有两个及以上a的所有名字 fruits = [[‘peach’,’Lemon’,’Pear’,’avocado’,’cantaloupe’,’Banana’,’Grape’], [‘raisins’,’plum’,’apricot’,’nectarine’,’orange’,’papaya’]] print([name for lst in fruits for name in lst if name.count(‘a’) >= 2])
<a name="G7D1A"></a>
#### 字典推导式:
将一个字典的key和value对调
```python
将一个字典的key和value对调
dic1 = {'a':'1','b':'2'}
dic2 = {dic1[k]:k for k in dic1}
print(dic2)
合并大小写对应的value值,将k统一成小写
dic1={'a':'1','b':'2','A':'4','Y':'9'}
dic2={k.lower():dic1.get(k.lower(),0)+dic1.get(k.upper(),0) for k in dic.keys()}
print(dic2)
集合推导式:
总结:能通过推导式形成规定集合,列表,字典。
案例:
除法口诀表
过滤长度小于3的字符串列表,并将剩下的转换成大写字母
求(x,y)其中x是0-5之间的偶数,y是(0-5)之间的技术组成的元组列表
11.递归函数
递归的定义:在一个函数里再度这个函数本身。
简单案例:
案例:汉诺塔问题
案例:
汉诺塔
12.排序算法与二分查找算法:
1.冒泡排序
思想:将序列中大的排到后面。
实现方法:将第一个元素和后面的比较,较大的又和后面比较
#冒泡排序
def pop(list1):
a=len(list1)
for j in range(a):
for i in range(0,a-1):
if(list1[i]>list1[i+1]):
list1[i],list1[i+1] = list1[i+1] , list1[i]
print(list1)
li=[4,5,10,7,8,6]
pop(li)
2.选择排序
思想:找出最大或是最小的元素,存放到起始位置
再从剩余未排序的元素中,继续寻找最大(小)元素,然后放到已排序序列的末尾
实现方法:遍历整个序列,找出最小的放到起始位置。然后再遍历,依次寻找
#选择排序
def select_sort(list1):
a=len(list1)
for i in range(a-1):
min_index = i
for j in range(i+1,a):
if(list1[j]<list1[min_index]):
min_index = j
if min_index != i:
list1[i] ,list1[min_index] = list1[min_index] ,list1[i]
return list1
l1=[4,6,2,7,5]
l2 =select_sort(l1)
print(l2)
3.快速排序
快速排序,是指通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
步骤:
- 从数列中挑出一个元素作为基准,然后有两个指针分别是low和high
- 一般以第一个元素为基准,然后high指针往前,如果遇到了比基准小的就停止,遇到大的就继续走
- high指针停止后low指针就往前走,遇到比基准小的不管,遇到比基准大的就停止
- 并且low指针和high指针的元素互换后,high指针继续走,low指针继续走,当两个指针重合的位置就是基准元素的位置
- 这样划分下来基准左边都是比基准小的,基准右边都是比基准大的。继续此方法,对左边和右边的数据进行排序。
最后就可以得到一个按顺序排列的序列 ```python
快速排序
分析: 将第一个数字作为中间值 然后使用low和high两个游标分别向中间前进 当low遇到比中间值大的数字的时候就停止不动,等到high遇到比中间值小的时候,low和high互 换元素 互换元素之后,游标再继续前进,直到重合,当其重合的时候,也就是得到了中间值所在的位置 之后需要依次对中间值左右进行相同的操作 def quick_sort(alist, start, end): if start >= end:
return
mid=alist[start] low = start high = end while low < high:
while low< high and alist[high] >= mid: high-=1 alist[low] =alist[high] while low <high and alist[low] <mid: low +=1 alist[high] =alist[low]
alist[low] = mid quick_sort(alist,start,low-1) quick_sort(alist,low+1,end)
alist= [54,26,93,17,77,31,44,55,20] quick_sort(alist,0,len(alist)-1) print(alist)
<a name="mjg3I"></a>
#### 4.二分查找法
又称折半查找,优点是比较次数少,查找速度快,平均性能好,其缺点是对查找表的要求是有序表
```python
#二分查找法
def binary_search(alist, item):
if len(alist) == 0:
return False
else:
midpoint = len(alist)//2
if alist[midpoint]==item:
return True
else:
if item<alist[midpoint]:
return binary_search(alist[:midpoint],item)
else:
return binary_search(alist[midpoint+1:],item)
testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42,]
print(binary_search(testlist, 3))
print(binary_search(testlist, 13))
13.内置函数与匿名函数
查看Python的版本:
import sys
print(sys.version)
print(sys.version_info)
输出:
3.9.7 (tags/v3.9.7:1016ef3, Aug 30 2021, 20:19:38) [MSC v.1929 64 bit (AMD64)]
sys.version_info(major=3, minor=9, micro=7, releaselevel='final', serial=0)
//整理一下Python2 和Python3的区别。
内置函数
截至到python版本3.6.2,现在Python提供了68个内置函数
作用域相关
- locals:函数会以字典的类型返回当前位置的全部局部变量
-
其他相关
字符串类型代码的执行eval,exec,complie。
eval:执行字符串类型的代码,并返回最终结果
- exec:执行字符串类型的代码
- complie:将字符串类型的代码编译。
参数source:字符串。即需要动态执⾏的代码段。
- input() :接收一个标准输入数据,返回为string类型
print():打印输出
def print(self, *args, sep=' ', end='\n', file=None)
内存相关:
hash():获取一个对象的哈希值
-
文件相关:
-
模块相关:
-
调用相关:
-
查看内置属性:
dir(): 函数不带参数时,返回当前范围内的变量、⽅法和定义的类型列表;带参数时,返回参 数的属性、⽅法列表。如果参数包含⽅法 dir() ,该⽅法将被调⽤。如果参数不包 含 dir() ,该⽅法将最⼤限度地收集参数信息。
数字相关:
进制转换:bin(),oct(),hex()分别为2进制10进制和16进制。
数学运算:abs():绝对值 pow()计算x的y次方如果有z的话就对z取模 sum()对可迭代对象进行求和。min()返回最小值。max()返回最大值。
数据结构相关:
list()将可迭代对象转化成列表
- tuple()将可迭代对象转换为元组
- reversed()将一个序列反转想想如何才能反转序列
- slice():构造一个切片对象,用于列表的切片
- str()转换成字符串
- format():与具体的数据相关,用于计算各种小数精算
- encode(’utf-8’)decode(‘utf-8’):对对象进行编码
- bytearry():返回一个新字节数组,这个数组里的元素是可变的,并且每个元素的值发范围:0<=x<256
- memoryview:内存查看对象,是指对支持缓冲区协议的数据进行包装,再不需要复制对象基础上允许python代码访问
- ord:输入字符找该字符编码的位置
- chr:输入位置数字找出器对应的字符
- ascii:是ascill码中的返回该值,不是就返回/u
- repr:返回一个对象的string类型照妖镜
- len():返回一个对象中元素的个数
- sorted():对所有可迭代对象进行排序
- enumetate():用于将要给可遍历的数据对象组合为一个索引序列,同时列出数据和数据下表,一般再for循环当中。只拆一层
- zip():函数用于将可迭代的对象作为参数,将对象中的对应元素打包成一个元组,然后返回由这些元组组成的列表,如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同。
- filter():用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表
- map():会根据提供的函数对执行序列做映射。py3.x返回迭代器。
匿名函数:
匿名函数:为了解决那些功能很简单的需求而设计的一句话函数 ```python这段代码
def calc(n): return n ** n print(calc(10))
换成匿名函数
calc = lambda n: n ** n print(calc(10))
<a name="CS6kF"></a>
### 14.模块和包
<a name="F61xe"></a>
#### 模块概念:
<a name="HPnj2"></a>
##### 什么是模块:
1. 使用python编写的代码(.py文件)
1. 已被编译为共享库或dll的c或c++拓展
1. 包是一组模块的包
1. 使用c编写并链接到python解释器的内置模块
使用模块能实现代码和功能的复用
<a name="HkxWb"></a>
##### 我们如何通过模块复用代码和功能呢?
```python
#通过import math 导入math.py这个文件中的功能和代码。
import math
print(math.pi)
每个模块都是一个独立的名称空间,定义在这个模块的函数中,把这个模块的名称空间当作全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突。
总结:首次导入模块时会做三件事
- 为源文件创建新的名称空间,在模块中定义的函数和方法若是使用到来global时访问的就是这个名称空间
- 在新创建的命名空间中执行模块中包含的代码
- 创建模板名字来引用该命名空间
为模块起别名:
import my_module as mm
import sys,os,time,re //在一行导入多个模块
from…import
对⽐import my_module,会将源⽂件的名称空间’my_module’带到当前名称空间中,使⽤时必须是my_module.名字的⽅式 ⽽from 语句相当于import,也会创建新的名称空间,但是将my_module中的名字直接导⼊到当前的名称空间中,在当前名称空间中,直接使⽤名字就可以了
注意:不论是哪一种方法导入模块,私有方法都不能被导入。
模块的搜索路径:
查找顺序:内存中以及加载的模块然后是内置模块。然后是sys,path路径中包含的模块。
包的概念:
包就是⼀个包含有 init.py ⽂件的⽂件夹,所以其实我们创建包的⽬的就是为了⽤⽂件夹 将⽂件/模块组织起来 需要强调的是: 1. 在python3中,即使包下没有 init.py ⽂件,import 包仍然不会报错,⽽在python2 中,包下⼀定要有该⽂件,否则import 包报错 2. 创建包的⽬的不是为了运⾏,⽽是被导⼊使⽤,记住,包只是模块的⼀种形式⽽已,包的本质就是⼀种模块
为什么要使用包:
包的本质就是⼀个⽂件夹,那么⽂件夹唯⼀的功能就是将⽂件组织起来随着功能越写越多,我们⽆法将所以功能都放到⼀个⽂件中,于是我们使⽤模块去组织功能, ⽽随着模块越来越多,我们就需要⽤⽂件夹将模块⽂件组织起来,以此来提⾼程序的结构性和可维护性
注意事项:
1.关于包相关的导⼊语句也分为 import 和 from … import … 两种,但是⽆论哪种,⽆
论在什么位置,在导⼊时都必须遵循⼀个原则:凡是在导⼊时带点的,点的左边都必须是⼀
个包,否则⾮法。可以带有⼀连串的点,如 item.subitem.subsubitem ,但都必须遵循这个
原则。但对于导⼊后,在使⽤时就没有这种限制了,点的左边可以是包,模块,函数,类(它
们都可以⽤点的⽅式调⽤⾃⼰的属性)。
2. import导⼊⽂件时,产⽣名称空间中的名字来源于⽂件,import 包,产⽣的名称空间的名
字同样来源于⽂件,即包下的 init.py ,导⼊包本质就是在导⼊该⽂件
3. 包A和包B下有同名模块也不会冲突,如A.a与B.a来⾃俩个命名空间
绝对导入和相对导入:
绝对导⼊:以glance作为起始
相对导⼊:⽤.或者..的⽅式最为起始(只能在⼀个包中使⽤,不能⽤于不同⽬录内)
绝对导⼊: 以执⾏⽂件的sys.path为起始点开始导⼊,称之为绝对导⼊
- 优点: 执⾏⽂件与被导⼊的模块中都可以使⽤
- 缺点: 所有导⼊都是以sys.path为起始点,导⼊麻烦
相对导⼊: 参照当前所在⽂件的⽂件夹为起始开始查找,称之为相对导⼊
- 符号: .代表当前所在⽂件的⽂件加,..代表上⼀级⽂件夹,…代表上⼀级的上⼀级⽂件夹
- 优点: 导⼊更加简单
- 缺点: 只能在导⼊包中的模块时才能使⽤
注意:
相对导⼊只能⽤于包内部模块之间的相互导⼊,导⼊者与被导⼊者都必须存在于⼀个包内
试图在顶级包之外使⽤相对导⼊是错误的,⾔外之意,必须在顶级包内使⽤相对导⼊,每增加⼀
个.代表跳到上⼀级⽂件夹,⽽上⼀级不应该超出顶级包。
15.常用模块(不是很懂,明天重新听)
序列化模块:
目的:
序列化的目的是为了数据结构转换成字符串便于传输。
Json模块:
Json模块提供了四个功能,dumps,dump,loads,load
其中dumps和dump的区别是一个是
dumps实时从内存中取对象,将变量序列化。dump是从文件中将数据取出来将字符串序列化
lodas和load也是一样
只是一个将内存中的变量,反序列化,一个是从文件中将数据取出来反序列化。
import json //要调用json模块
list1=[1,23,4,5,66,7,78,9,10]
str_list1=json.dumps(list1) //序列化,写入到一个变量中
print(repr(str_list1))
list2 =json.loads(str_list1) //反序列化
print(repr(list2))
import json
list1={'k':'你好','k1':78}
with open('test2','w',encoding='utf-8') as f:
json.dump(list1,f,ensure_ascii=False) //写入到一个文件中,其中要传入一个文件句柄
案例:
import json
data = {'name':'陈松','sex':'female','age':88}
json_dic2 = json.dumps(data,sort_keys=True,indent=2,separators=(',',':'),ensure_ascii=False) //要想显示中文字符,则在dumps的后面要加上参数ensure_ascii=False因为ascill不支持中文。
print(json_dic2)
Pickle模块
和Json一样是序列化的模块,只不过保存的是二进制,并且只能在python中使用,因为它的特点是能保存python中比较复杂并且其他语言没有的结构,例如时间。
pickl模块提供了四个功能,dumps,dump,loads,load,不仅可以序列化字典,列表等,可以把python中的任意数据类型序列化(包括时间类型)
shelve模块
shelve也是序列化工具,比pickle用起来更简单一些。shelve只提供给我们一个open方法,是用key来访问的,使用起来和字典类似
import shelve
f = shelve.open('shelve_file')
f['key'] = {'int':10,'str':'hello','float':0.123}
f.close()
f1 = shelve.open('shelve_file')
ret = f1['key']
f1.close()
print(ret)
//有个注意的点,就是在文件未被close的时候,写入的数据是不能被持久化到硬盘中的,此时down机数据会丢失。
这个模块有个限制,它不支持多个应用同时往同一个DB进行写操作,所以当我们知道我们的应用如果只进行读操作,就可以让shelve通过只读方式打开DB
hashlib模块
用来计算md5或者是SHA1的散列值,一般用于后台对明文密码进行加密,这样能保证密码的安全性,具体做法可以加盐加时间序列。
import hashlib
jiami = hashlib.md5()
jiami.update('123456'.encode('utf-8'))
print((jiami.hexdigest()))
//加盐是指一般的数据可以被碰撞出来,但是加盐后密码不变,但是复杂性变高了,提高了安全性
jiami.update('123456'.join('wad').encode('utf-8'))
upx加壳,代码保护机制,为了保证程序不会被别人看到源码,
logging模块
这个模块的意思是程序在运行的时候,将错误信息记录成日志。
默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于warning级别的日志,这说明默认的日志级别设置为warning。默认的日志格式为日志级别:Logger名称:用户输出信息
collections模块
在内置数据类型的基础上,collections模块还提供了几个额外的数据类型,counter,deque,deaultdict,namedtuple和ordereddict。
1.namedtuple: ⽣成可以使⽤名字来访问元素内容的tuple
2. deque: 双端队列,可以快速的从另外⼀侧追加和推出对象
3. Counter: 计数器,主要⽤来计数
4. OrderedDict: 有序字典
5. defaultdict: 带有默认值的字典
时间有关的模块
常用方法:
import time
time.sleep(2) //(线程)推迟指定的运行时间,单位为秒
time.time() //获取当前的时间戳 时间戳就是从1970年1月1日8:00到现在的秒数
在python中,通常有这三种方式来表示时间
- 时间戳:通过time.time()调用,返回类型是float
- 元组:struct_time元组共有9个元素:(年,月,日,时,分,秒,一年中的第几周,一年中的第几天,是否是夏令时。)
- 格式化的时间字符串:’2022-4-21’
总结:
时间戳是计算机能够识别的时间,时间字符串是人能够看懂的时间,元组是用来操作时间的。
import time
print(time.time()) //显示时间戳
print(time.strftime('%Y-%m-%d %X')) //显示结构化时间
print(time.strftime('%Y-%m-%d %H-%M-%S'))
print(time.localtime()) //显示元组
输出:
1650511850.437261
2022-04-21 11:30:50
2022-04-21 11-30-50
time.struct_time(tm_year=2022, tm_mon=4, tm_mday=21, tm_hour=11, tm_min=30, tm_sec=50, tm_wday=3, tm_yday=111, tm_isdst=0)
几种时间格式之间的转换
time模块
datatime模块
Random模块
#随机模块
import random
print(random.random()) # 大于0且小于1之间的小数
print(random.uniform(1,3)) # 大于1小于3的小数
print(random.randint(1,5)) # 大于等于1且小于等于5之间的整数
print(random.randrange(1,10,2)) # 大于等于1且小于10之间的奇数
ret = random.choice([1,'23',[4,5]]) # 1或者23或者[4,5]
print(ret)
a,b = random.sample([1,'23',[4,5]],2) # 列表元素任意2个组合
print(a,b)
item = [1,3,5,7,9]
random.shuffle(item) # 打乱次序
print(item)
案例:生成随机验证码
import random
def v_code():
code = ''
for i in range(5):
num=random.randint(0,9)
alf=chr(random.randint(65,90))
add=random.choice([num,alf])
code="".join([code,str(add)])
return code
print(v_code())
OS模块
os模块是与操作系统交互的一个接口
当前执行这个python文件的工作目录相关的工作路径
文件夹相关
文件相关
操作系统差异相关
执行系统命令相关
path系列,和路径相关
SYS模块
sys模块是与python解释器交互的一个接口
RE模块
正则表达式:
用来匹配文本中的字符串
案例:
import re
print(re.findall('\w','大人123asdfg')) //返回的是一个列表
print(re.findall('a.*?b', 'ab a1b a*()b, aaaaaab')) //非贪婪
print(re.findall('a.*b', 'ab aab a*()b')) //贪婪模式
输出:
['大', '人', '1', '2', '3', 'a', 's', 'd', 'f', 'g']
['ab', 'a1b', 'a*()b', 'aaaaaab']
['ab aab a*()b']
# 分组:() 制定一个规则,将满足规则的结果匹配出来
print(re.findall('(.*?)_sb', 'cs_sb zhao_sb 日天_sb'))
print(re.findall('href="(.*?)"','<a href="http://www.baidu.com">点击</a>'))
print(re.findall('compan(y|ies)','Too many companies have gone bankrupt,and the next one is my company'))
print(re.findall('compan(?:y|ies)','Too many companies have gone bankrupt,and the next one is my company')) #分组中加入?:表示将整体匹配出来而只是()里的内容。
输出:
['cs', ' zhao', ' 日天']
['http://www.baidu.com']
['ies', 'y']
['companies', 'company']
贪婪和非贪婪值的是:
贪婪是尽可能多的匹配,非贪婪是指只匹配到满足的就停。
16.爬虫
输入urlopen模块后,并没有read方法将网页源码读出来。
面向对象
面向对象的基本概念
类的概念
类是对一群具有相同特征或者行为的事物的一个统称,是抽象的,不能直接使用
- 特征在类中被称为属性
- 行为在类中被称为方法
对象的概念
对象是由类创建出来的一个具体存在,可以直接使用
由哪一个类创建出来的对象,就拥有在哪一个类中定义的
- 属性
- 方法
对象就相当于制造出来的飞机,能执行某种特定的功能。
在程序开发中,应该先有类,后有对象
类和对象的关系
- 类是模板,对象是根据类这个模板创建出来的,应该先有类,后创建对象
- 类只有一个,而对象可以有很多个
- 不同的对象之间属性可能会各不相同
- 类中定义了什么属性和方法,对象中就有什么属性和方法,不可能多也不可能少
类的设计
在程序开发之前,应该首先分析需求,确定一下,程序中需要包含那些类
在设计一个类时,通常需要满足以下三个要素
- 类名,这类事物的名字,应该采用大驼峰命名法(单词的首字母大写,不能用_下划线连接),并且类名应该设得通俗易懂。
- 属性,这类事物具有什么样的特征
- 方法,这类事物具有什么样的行为
案例:
class human:
"""定义人类"""
flag = 'this is human'
def __init__(self,name,age,height):
self.name = name
self.age = age
self.height = height
def run(self):
print('我会跑')
def eat(self):
print('我能吃')
xiaoming = human('小明',18,1.75)
xiaomei = human('小美',17,1.65)
print(xiaoming.name)
print(xiaoming.age)
print(xiaomei.height)
xiaomei.eat()
从类名的角度研究类
类名操作静态属性
print(human.__dict__)
print(Human.__dict__['mind'])
Human.__dict__['mind'] = '高智慧'
print(Human.__dict__)
# 通过这种方式只能查询,不能增删改
class Human:
mind = '有思想'
dic = {}
l1 = []
def work(self):
print('会工作')
print(Human.mind)
Human.mind = '高智慧'
print(Human.mind)
del Human.mind
Human.walk = '用脚走'
print(Human.walk)
# 通过万能的点 可以增删改查类中的单个属性
类名操作动态方法
class Human:
mind = '有思想'
dic = {}
l1 = []
def work(self):
print(self,'会工作')
Human.work('chensong')
Human.__dict__['work']('chensong')
从对象的角度研究类
对象是从类中创建的,只要是类名加上(),这就是一个实例化的过程,这就会实例化一个对象。
实例化一个对象时总共发生了三件事
- 在内存中开辟了一个对象空间
- 自动执行类中的init方法,并将这个对象空间(内存地址)传给init方法的第一个位置参数self
- 在init方法中通过self给对象空间添加属性
class human: """定义人类""" flag = 'this is human' def __init__(self,name,age,height): self.name = name self.age = age self.height = height xiaoming = human('xiaoming','18','181') //实例化了小明这个对象
对象操作对象空间属性
xiaoming = human('xiaoming','18','181') print(xiaoming.__dict__)
对象操作对象中的单个属性
class Human: mind = '有思想' language = '实用语言' def __init__(self,name,sex,age,hobby): # self 和 obj 指向的是同一个内存地址同一个空间,下面就是通过self给这个对象空间 封装四个属性。 self.n = name self.s = sex self.a = age self.h = hobby obj = Human('chensong','男',18,'男') # 添加属性 obj.job = 'IT' # 删除属性 del obj.n # 修改属性 obj.s = '女' print(obj.s) # 查看属性 print(obj.__dict__)
对象查看类中的属性
```shell class Human: mind = ‘有思想’ language = ‘实用语言’ def init(self,name,sex,age,hobby):self 和 obj 指向的是同一个内存地址同一个空间,下面就是通过self给这个对象空间
封装四个属性。 self.n = name self.s = sex self.a = age self.h = hobby obj = Human(‘chensong’,’男’,18,’男’) print(obj.mind) print(obj.language) obj.a = 666 print(obj.a)
<a name="bASzw"></a>
##### 对象操作类中的方法
```shell
class Human:
mind = '有思想'
language = '实用语言'
def __init__(self,name,sex,age,hobby):
# self 和 obj 指向的是同一个内存地址同一个空间,下面就是通过self给这个对象空间
封装四个属性。
self.n = name
self.s = sex
self.a = age
self.h = hobby
def work(self):
print(self)
print(self.n,'会工作')
def tools(self):
print(self.n,'会使用工具')
obj = Human('chensong','男',18,'男')
obj.work()
obj.tools()
类的内置函数
__new__ //创建对象时自动调用
__init__ //对象被初始化时,会被自动调用
__del__ //对象被从内存中销毁前,会被自动调用
__str__ //返回对象的描述信息,print函数输出使用
__dir__ //查看对象内的所有属性以及方法
类的空间问题
添加对象属性
class A:
def __init__(self,name):
self.name = name
def func(self,sex):
self.sex = sexa
obj = A('chensong')
obj.age = 18
print(obj.__dict__)
class A:
def __init__(self,name):
self.name = name
def func(self,sex):
self.sex = sex
obj = A('chensong')
obj.func('男')
print(obj.__dict__)
总结:对象的属性不仅可以通过init函数添加,也可以在类的其他方法和类的外面通过点的形式添加
添加类的属性
class A:
def __init__(self,name):
self.name = name
def func(self,sex):
self.sex = sex
def func1(self):
A.bbb = self
A.aaa = 'test' # 类的外部添加
print(A.__dict__)
A.func1('123') # 类的内部添加
print(A.__dict__)
总结:类的属性不仅可以在类的内部添加,也可以在类的外部添加。
对象如何找到类的属性
- 产生这个类的对象空间,并有一个类对象指针。关联类与对象的关系
- 执行init方法,给对象封装属性
对象查找属性的顺序,对象空间,类空间,父类空间
类名查找属性的顺序,本类空间,父类空间
类名不可能找到对象的属性
类与类之间的关系
- 依赖关系
- 关联关系
- 组合关系
- 聚合关系
- 实现关系
- 继承关系
class Elphant:
def __init__(self,name):
self.name = name
def open(self,obj1):
print(self.name + '打开冰箱')
obj1.open_door()
def close(self,obj1):
print(self.name + '关闭冰箱')
obj1.close_door()
def into(self):
print(self.name + '进入冰箱')
class BingXiang:
def __init__(self,name):
self.name = name
def open_door(self):
print(f'成功打开了{self.name}的门')
def close_door(self):
print(f'成功关闭了{self.name}的门')
xiaoxiang = Elphant('小象')
bingxiang = BingXiang('美的冰箱')
xiaoxiang.open(bingxiang)
xiaoxiang.close(bingxiang)
依赖关系是指,一个类中封装了另一个类的类方法。