一、函数的特殊用法
1.变量可以指向函数
代码演示:
#abs------>absolute #abs()是一个系统的内置函数【built-in function】 print(abs(-10)) #10 print(abs) #<built-in function abs> #结论一:abs(-10)是函数的调用,而abs是函数本身 x = abs(-20) print(x) #20 f = abs print(f) #<built-in function abs> #结论二;函数本身也可以直接赋值给一个变量,也就是说:变量可以指向一个函数 num = 10 #如果一个变量指向了一个函数,则可以通过这个变量去调用这个函数 print(f(-30)) #结论三:f = abs, 则表示f已经指向了abs所表示的函数,调用abs和调用f实现的效果是一样的 def test(): return "fjskghs" print(test()) fun = test print(fun())
2.函数名是一个变量
代码演示:
#函数的特殊用法之函数名是一个变量 #结论一:函数名其实就是指向函数的变量 #abs():可以将abs看做一个变量,指向了一个可以计算绝对值的函数 #abs更改指向【变量的重新赋值】 num = 10 num = "hello" #让abs指向一个整型 print(abs) abs = 10 print(abs) #print(abs(-100))
3.函数作为参数
代码演示:
#函数的特殊用法之函数作为参数 #变量可以指向函数,函数名是一个变量,而函数的形参本身就是一个变量,可以接收实参,那么一个函数就可以接收另一个函数作为参数 #高阶函数【一个函数就可以接收另一个函数作为参数】 #一个简单的高阶函数【需求:求两个数的绝对值的和】 #参数:x和y就是需要参与运算的数据,fun是一个函数 """ def add(x,y): return abs(x) + abs(y) """ def add(x,y,fun): return fun(x) + fun(y) #abs(x) + abs(y) #将函数名abs作为参数使用 result = add(-5,6,abs) #x = -5 y = 6 fun = abs fun() print(result) #自定义函数 def show(): print("abc") def func(f): print("hello") f() func(show)
二、偏函数【了解】
默认参数:可以降低函数调用的难度
偏函数:对函数参数做一些控制的函数
注意:偏函数不需要自定义,直接使用【系统函数】
代码演示:
import functools # 偏函数的使用 #int(x) 可以将字符串或者浮点型转换为整型 #默认:将其中的字符串按照十进制输出 print(int("123")) #int()还提供了一个额外的参数:base #print(int("abc123")) #base;指明前面数据的进制,int()执行完成之后,最终还是以十进制输出 print(int("123",base = 10)) #123 print(int("123",base = 8)) #83 print(int("110",base = 2)) print(int("11010",base = 2)) print(int("110001",base = 2)) #转换大量的二进制,每次传入base = 2麻烦,可以将这个功能提取出来 def customInt(x,base=2): return int(x,base) print(customInt("110")) #6 print(customInt("11010")) #26 #上面通过默认参数模仿了偏函数的使用,但是系统提供了功能:functools.partial可以创建一个偏函数【前提;需要导入import functools】 #参数:需要创建偏函数的原函数名 需要设定的参数 int2 = functools.partial(int,base=2) print(int2("110")) #6 print(int2("11010")) #26 print(int2("110",base=10)) #110 #总结:偏函数 #主要针对的是系统函数,如果系统默认的操作满足不了需求,则可以在这个系统函数的基础上生成一个新的函数【偏函数】,两个函数可以实现不同的需求
三、闭包【掌握】
如果在一个函数的内部定义另外一个函数,外部的函数叫做外函数,内部的函数叫做内函数
如果在一个外部函数中定义一个内部函数,并且外部函数的返回值是内部函数,就构成了一个闭包,则这个内部函数就被称为闭包【closure】
代码演示:
""" 如果在一个函数的内部定义另外一个函数,外部的函数叫做外函数,内部的函数叫做内函数 如果在一个外部函数中定义一个内部函数,并且外部函数的返回值是内部函数,就构成了一个闭包, 则这个内部函数就被称为闭包【closure】 """ #1.最简单的闭包 #外函数 def func(str): #内函数【闭包】 def innerFunc(): print("hello") return innerFunc #f中存储了外函数func的返回值,而func的返回值是innerFunc,j就相当于f = innerFunc f = func("abc") #f = innerFunc #f()就相当于innerFunc() f() #2. #a和b被称为外函数中的临时变量【自由变量】 def outer(a): b = 10 def inner(): #在内函数中可以直接使用外函数中临时变量 print(a + b) return inner f1 = outer(5) f1() #3. def outer1(num1): def inner1(num2): #在内函数中可以直接使用外函数中临时变量 print(num1,num2) return inner1 f2 = outer1(10) f2(20) #应用场景:装饰器
四、变量的作用域
1.出现的原因
变量的作用域:变量可以被使用【被访问】的范围
程序中的变量并不是在任意的语句中都可以被访问,访问权限取决于这个变量被定义在哪个位置
2.作用范围划分
局部作用域:L【Local】
函数作用域:E【Enclosing】 将变量定义在闭包外的函数中
全局作用域:G【Global】
內建作用域:B【Built-in】
代码演示:
#1.不同作用域变量的定义 num4 = int(2.9) #B;內建作用域 num3 = 3 #G;全局作用域 def outer(): num1 = 1 #E:函数作用域 def inner(): num2 = 2 #L:局部作用域 #注意:当所有的变量不同名的时候,在闭包中,可以任意访问四种不同作用域对应的变量 print(num4,num3,num2,num1) return inner f = outer() f()
3.变量的查找规则
查找的顺序:L------>E------>G------>B【极端情况:当所有的变量同名的情况下】【面试题】
代码演示:
#变量的查找规则 #注意:全局作用域和内置作用域,当重名的时候,谁出现在后面,则先匹配到谁 x = 0 x1 = int(3.3) def outer1(): j = 1 def inner1(): i = 2 #【就近原则】 print(x) return inner1 f1 = outer1() f1()
#函数 def show(a): num1 = 10 print(num1,a) show(20) #print(num1,a) #结论一:在函数中定义的变量【形参,在函数体中定义的变量】,作用域仅限于函数内部 # 【变量的生命周随着函数的出现而出现,函数执行完毕则变量随着被销毁】 #if语句 if True: msg = "hello" print(msg) print(msg) #for循环 for i in range(0,5): print(i) print(i) #结论二;Python中只有模块【module】、类【class】和函数【def,lambda】才会引入新的作用域 #其他的代码块:if语句,while语句,for语句,try-except语句都不会引入新的作用域
4.全局变量和局部变量【掌握】
全局变量:将变量定义在函数的外面
局部变量:将变量定义在函数的内部
注意:局部变量只能在其被声明的当前函数中使用,而全局变量可以在整个程序中使用
代码演示:
#全局变量 total = 0 def show(): print(total) show() if True: total = 20 print(total) total = 10 print(total) def add(arg1,arg2): #arg1,arg2,total1都属于局部变量 total1 = arg1 + arg2 print(total1) add(10,20) #print(total1)
作业讲解:
#1.计算1~某个数范围内奇数的和并返回 def fun1(num): sum = 0 for i in range(1,num + 1): if i % 2 == 1: sum += 1 return sum print(fun1(100)) #2.判断某个数是否是质数,返回结果 def fun2(num): is_prime = True for x in range(2,num): if num % x == 0: is_prime = False break if is_prime and num != 1: return "质数" else: return "不是质数" fun2(10)
5.global和nonlocal关键字的使用【掌握】
使用场景:当内部作用域【局部作用域,函数作用域】想要修改全局变量的作用域的时候
1.global
代码演示:
#global #全局变量 num = 1 def fun1(): #此时要使用全局变量中的num,需要给编译器做一个声明,声明此处使用num就是使用的全局变量中的num global num print(num) print(id(num)) #1355785312 #局部变量 num = 123 print(num) print(id(num)) #1355789216 fun1() #练习 a = 10 def test(): global a a = a + 1 print(a) test()
2.nonlocal
代码演示:
#前提:nonlocal关键字是定义在闭包中 x = 0 def outer(): x = 1 def inner(): #使用nonlocal关键字进行声明,相当于将局部作用域范围扩大了 nonlocal x x = 2 print("inner:",x) #return inner inner() print("outer:",x) #2 outer() print("global:",x) """ 原本: inner: 2 outer: 1 global: 0 """ """ 修改: inner: 2 outer: 2 global: 0 """
五、列表生成式和生成器【掌握】
1.列表生成式
list comprehension
系统内置的用于创建list的方式
range(start,end,step)缺点:生成的列表一般情况下都是等差数列
代码演示:
#列表生成式 list1 = list(range(1,11)) print(list1) #[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] #需求:[1, 4, 9, 16, 25] list2 = [] for x in range(1,11): list2.append(x ** 2) print(list2) #[1, 4, 9, 16, 25, 36, 49, 64, 81, 100] #1 #使用列表生成式完成上面的需求 #列表生成式的格式:[生成的元素 for-in循环] list3 = [x ** 2 for x in range(1,11)] print(list3) #[1, 4, 9, 16, 25, 36, 49, 64, 81, 100] #2. #[4,16,36,64,100] list4 = [x ** 2 for x in range(1,11) if x % 2 == 0] print(list4) #3.嵌套for循环,第一个循环就相当于外层循环,第二个循环就相当于内层循环 list5 = [m + n for m in "ABC" for n in "XYZ"] print(list5) #["AX","AY","AZ"....] """ for m in "ABC": for n in "XYZ": print(m + n) """ #4 #for k,v in dict.items)(): d = {"x":"1","y":"2","z":"3"} for k,v in d.items(): print(k,v) list6 = [k + "=" + v for k,v in d.items()] print(list6) #['x=1', 'y=2', 'z=3'] #练习:使用列表生成式生成一个新的列表,将一个已知列表中的所有的字符变为小写 l1 = ["Hello","GOOD","ABC","kkH"] #l2 = ["hello","good","abc","kkh"] newList1 = [] for element in l1: str = element.lower() newList1.append(str) print(newList1) newList2 = [s.lower() for s in l1] print(newList2)
2.生成器
generator
next()
代码演示:
#生成器 #方式一:(),将列表生成式中的[]改成() #列表生成式的类型是list,生成器的类型是generator【当做一种新的数据类型】 r1 = (x ** 2 for x in range(1,6)) print(r1) #(1,4,9,16,25) print(type(r1)) """ for i in r1: print(i) """ #生成器区别于列表生成式:可以使用next遍历,每调用一次则获取一个元素 #next() print(next(r1)) print(next(r1)) print(next(r1)) print(next(r1)) print(next(r1)) #注意:当生成器中的元素全部获取完成之后,接着调用next函数的,则会出现StopIteration #print(next(r1)) #StopIteration异常 #方式二:yield---->让步 #(x for x in range(1,6))----->1,2,3,4,5 def test(n): for i in range(1, n + 1): #执行到yield的时候,则函数会停止,将yiled后面的变量返回 yield i ** 2 #yield后面的代码的执行时机:当调用next函数的时候 print(i) t = test(5) print(t) #<generator object test at 0x0000019CC432A1A8> print(next(t)) print(next(t)) print(next(t)) print(next(t)) print(next(t))
六、迭代器【掌握】
1.可迭代对象
可迭代对象【实体】:可以直接作用于for循环的实体【Iterable】
可以直接作用于for循环的数据类型:
a.list,tuple,dict,set,string
b.generator【() 和yield】
isinstance:判断一个实体是否是可迭代的对象
代码演示:
#一、可迭代对象 #1.导入 from collections import Iterable #2.使用isinstance(数据,Iterable) print(isinstance([],Iterable)) print(isinstance((),Iterable)) print(isinstance({},Iterable)) print(isinstance((x for x in range(10)),Iterable)) print(isinstance("hello",Iterable)) print(isinstance(10,Iterable)) #False print(isinstance(True,Iterable)) #False print("****88")
2.迭代器
不但可以作用于for循环,还可以被next函数遍历【不断调用并返回一个元素,直到最后一个元素被遍历完成,则出现StopIteration】
目前为止,只有生成器才是迭代器【Iterator】
结论:迭代器肯定是可迭代对象,但是,可迭代对象不一定是迭代器
isinstance:判断一个实体是否是迭代器
代码演示:
#二、迭代器 from collections import Iterator print(isinstance([],Iterator)) print(isinstance((),Iterator)) print(isinstance({},Iterator)) print(isinstance("hello",Iterator)) print(isinstance((x for x in range(10)),Iterator)) #True print("****88")
3.可迭代对象和迭代器之间的转换
可以将可迭代对象转换为迭代器:iter()
代码演示:
#三、虽然list、tuple、dict、set、string都不是迭代器 #iter():将list、tuple、dict、set、string的 Iterable转换为Iterator print(isinstance(iter([]),Iterator)) print(isinstance(iter(()),Iterator)) print(isinstance(iter({}),Iterator)) print(isinstance(iter("hello"),Iterator))
总结:
a.凡是可以作用于for循环的对象都是Iterable类型
b.凡是可以作用于next函数的对象都是Iterator类型
c.list/tuple/dict/set/string都不是Iterator,可以通过iter()获得一个Iterator对象
【面试题】
区分可迭代对象和迭代器
七、装饰器【掌握】
1.案例
代码演示:
def test(): print("拼搏到无能为力,坚持到感动自己") f = test() #变量可以指向指向函数,函数名也是一个变量,所以变量可以当做函数调用 f() #思考问题:test增加功能,但是不能修改test函数内部----->装饰器
在代码运行期间,可以动态增加函数功能的方式,被称为装饰器【Decorator】
也就是说,在不修改原函数的基础上,给原函数增加功能
好处:在团队开发中,如果两个或者两个以上的程序员会用到相同的功能,但是功能又有细微的差别,采用装饰器:相互不影响,代码简化
2.使用
2.1简单装饰器
代码演示:
#1.简单的装饰器 def test(): print("拼搏到无能为力,坚持到感动自己") #a.书写闭包 #b.给外部函数设置参数,fun表示的是原函数 def outer(fun): def inner(): # d.给原函数增加功能 print("hello") #c.调用原函数 fun() return inner #e.使用闭包 f = outer(test) #f = inner f() #inner() #注意:增加的功能可以写在原函数调用的前面或者后面 #注意:outer函数就被称为装饰器 #练习:给下面的函数添加功能,打印九九乘法表 def show(): for i in range(10): print(i) def outer1(fun): def inner1(): fun() for i in range(1,10): for j in range(1,i + 1): print("%dx%d=%d"%(j,i,i * j),end=" ") print("") return inner1 f1 = outer1(show) f1()
2.2有参数的装饰器
代码演示:
#2.原函数有参数的装饰器 def getAge(age): print(age) getAge(10) getAge(-5) print("************") #需求:在不修改原函数的基础上,进行数据的过滤:当用户输入age为负数的时候,则置为0 def wrapper(fun): #注意:当原函数有参数,装饰器的作用是为了操作原函数中的参数,给inner设置参数 def inner(num): #增加新功能:过滤负数 if num < 0: num = 0 #调用原函数 fun(num) #age = num return inner f = wrapper(getAge) f(10) #num = 10 f(-5)
2.3系统的简写
代码演示:
#3.简化demo2中的操作:@装饰器的名称 应用到原函数中 #需求:在不修改原函数的基础上,进行数据的过滤:当用户输入age为负数的时候,则置为0 def wrapper(fun): #注意:当原函数有参数,装饰器的作用是为了操作原函数中的参数,给inner设置参数 def inner(num): #增加新功能:过滤负数 if num < 0: num = 0 #调用原函数 fun(num) #age = num return inner #将wrapper装饰器应用在了getAge函数上, @wrapper def getAge(age): print(age) getAge(10) getAge(-5) """ @wrapper 等价于 f = wrapper(getAge) f(10) #num = 10 #注意;当使用@的时候,在同一个文件中,装饰器必须出现的原函数的前面 """
2.4不定长参数的装饰器
代码演示:
#4.不定长参数的装饰器 #应用场景:当同一个装饰器作用于不同函数的时候,这些函数的参数的个数是不相同的 def wrapper(fun): def inner(*args): print("hello") fun(*args) #a = args[0] b = args[1] return inner @wrapper def fun1(a,b): print(a + b) @wrapper def fun2(a,b,c,d): print(a,b,c,d) fun1(10,20) #args = (10,20) fun2(1,2,3,4)
2.5多个装饰器作用于同一个函数
代码演示:
#5.将多个装饰器应用到同一个函数上 def wrapper1(fun): def inner1(): print("1~~~~") fun() return inner1 def wrapper2(fun): def inner2(): print("2~~~~") fun() return inner2 @wrapper1 @wrapper2 def show(): print("hello") show() """ 1~~~~ 2~~~~ hello """ #结论:多个装饰器作用于同一个函数的时候,从第一个装饰器开始,从上往下依次执行,但是,原函数只会被执行一次
八、函数递归【掌握】
1.概念
递归函数:一个会调用自身的函数【在一个函数的内部,自己调用自己】
递归调用
递归中包含了一种隐式的循环,他会重复指定某段代码【函数体】,但这种循环不需要条件控制
使用递归解决问题思路:
a.找到一个临界条件【临界值】
b.找到相邻两次循环之间的关系
c.一般情况下,会找到一个规律【公式】
2.使用
代码演示:
#案例一 """ 1 2 3 4 5 6 7 8 9 10 11.。。。 斐波那契数列:1,1,2,3,5,8,13,21,34,55,89..... 解决问题:报一个数,输出数列中对应的数 规律: a.第一个位置和第二个位置上数是固定的,都是1 b.第n个位置上的数:第 n - 1 的数 + 第 n - 2 的数 r1 = func1(1) ------>1 r2 = func1(2) ------>1 r3 = fun1(3) ------>func1(1) + func1(2)----->1 + 1 = 2 r4 = fun1(4)------->fun1(3) + fun1(2) ----->func1(1) + func1(2) + fun1(2) ---->1 + 1 + 1 = 3 r5 = fun1(5) ----->fun1(4) + fun1(3) ----->fun1(3) + fun1(2) + func1(1) + func1(2)--->func1(1) + func1(2) ++ fun1(2) + func1(1) + func1(2)--->5 ..... rn = fun1(n) ----->fun1(n- 1) + fun1(n - 2) """ def func1(num): #临界值 if num == 1 or num == 2: return 1 else: #print("~~~~",num) result = func1(num- 1) + func1(num - 2) #result = func1(1) + func1(2) --->1 + 1 =2 return result print(func1(10)) #练习;使用递归计算1~某个数之间的和 """ add(1) = 1 :临界值 add(2) = add(1) + 2 add(3) = add(2) + 3 ---->add(1) + 2 + 3 = 1 + 2 + 3 add(4) = add(3) + 4---->add(2) + 3 + 4 ---->add(1) + 2 + 3 + 4---->1 + 2 + 3 + 4 .... add(n) = add(n - 1) + n """ def add(num): """ n = 1 sum = 0 while n <= 100: sum += n n += 1 return sum sum1 = 0 for i in range(1,num + 1): sum1 += i return sum1 """ #使用递归实现 if num == 1: return 1 else: return add(num - 1) + num print(add(100))
注意:以后在实际项目中尽量少用递归,如果隐式循环的次数太多,会导致内存泄漏【栈溢出】
优点:简化代码,逻辑清晰
九、栈和队列【了解】
用于存储数据的线性表
栈:在表的一端进行插入和删除
队列:在表的一端进行插入,在表的另一端进行数据的删除
1.栈
Stack
开口向上的容器:先进后出,后进先出
代码演示:
#list的底层维护了一个栈的线性表 myStack = [] #插入数据 #数据入栈【压栈】 myStack.append(1) print(myStack) myStack.append(2) print(myStack) myStack.append(3) print(myStack) myStack.append(4) print(myStack) #[1,2,3,4] #出栈 myStack.pop() print(myStack) myStack.pop() print(myStack) myStack.pop() print(myStack) myStack.pop() print(myStack)
2.队列
queue
水平放置的水管:先进先出,后进后出
代码演示:
import collections #数据结构的集合 queue = collections.deque([1,2,3,4]) print(queue) #入队【存储数据】 queue.append(5) print(queue) queue.append(6) print(queue) #出队【获取数据】 queue.popleft() print(queue) queue.popleft() print(queue) queue.popleft() print(queue)
十、目录遍历
os 用于获取系统的功能,主要用于操作文件或者文件夹
代码演示:
import os path = r"C:\Users\Administrator\Desktop\SZ-Python" #获取指定目录下所有的文件以及文件夹,返回值为一个列表 filesList = os.listdir(path) print(filesList) #C:\Users\Administrator\Desktop\SZ-Python #通过初始路径拼接子文件或者子文件夹形成新的路径 filePath = os.path.join(path,"作业") print(filePath) #判断指定的路径是否是文件夹【目录】 result = os.path.isdir(filePath) print(result)
1.使用递归遍历目录
代码演示:
#递归 import os def getAll(path): #1.获取当前目录下所有的文件以及文件夹 fileList = os.listdir(path) print(fileList) #2.遍历列表 for i in fileList: #3.拼接路径 filePath = os.path.join(path,i) #4.判断filePath是否是文件夹 if os.path.isdir(filePath): #文件夹:递归 getAll(filePath) else: #文件 print("文件:",i) getAll(r"C:\Users\Administrator\Desktop\SZ-Python")
十一、包
包:初期理解为文件夹 作用:一种管理Python模块命名空间的形式,采用"点语法" os.path 包和文件夹之间的区别:Python的包中有一个特殊的文件__init__.py文件,前期里面不写任何内容,但是,就是为了告诉编译器,当前这个目录不是普通目录,是一个包 创建方式:选中工程,创建Python package
代码演示:
""" 1.在Python中,一个py文件其实就是一个模块 2.如果要跨模块调用函数,需要在运行的模块中导入需要使用的模块,调用函数的时候需要指明函数的路径 """ #第一步:导入模块 #导入格式:包名.模块名 import aaa.textDemo01 import ccc.module #os.path.isdir() aaa.textDemo01.test() ccc.module.test() #包存在的意义:在团队开发的过程中,为了解决文件命名冲突的问题,只要保证最上层的包命名不相同,就不会与别人的发生冲突
十二、模块
1.概述
为了解决维护问题,一般情况下,在一个完整的项目中,会将特定的功能分组,分别放到不同的文件中,在使用的过程中,可以单独维护,各个不同的文件之间互不影响,每个.py文件就被称为一个模块,通过结合包的使用来组织文件
封装思路: 函数 => 类 => 模块 => 包 => 项目
优点:
a.提高了代码的可维护性
b.提高了代码的复用性【当一个模块被完成之后,可以在多个文件中使用】
c.引用其他的模块【第三方模块】
d.避免函数名和变量的命名冲突
2 os模块
提供有关于操作系统的函数,处理文件或者文件夹
代码演示:
import os # operation system 操作系统 #1.获取当前操作系统的名称 # nt----->Windows # posix------>Linux, Mac os print(os.name) #2.获取当前系统的环境变量 #以字典的形式返回 print(os.environ) #通过key获取对应的value print(os.environ.get("APPDATA")) #3,获取指定目录下所有的文件或者文件夹的列表 l = os.listdir(r"C:\Users\Administrator\Desktop\SZ-Python") print(l) #4.在指定的路径下创建文件夹 #os.mkdir(r"C:\Users\Administrator\Desktop\aaa") #5.删除文件夹 #os.rmdir(r"C:\Users\Administrator\Desktop\aaa") #删除文件 #os.remove("") #6.获取文件属性 #print(os.stat(r"C:\Users\Administrator\Desktop\aaa")) #7.给文件或者文件夹重命名 #注意:当前的文件在关闭状态 #rename(old,new) #os.rename(r"C:\Users\Administrator\Desktop\aaa",r"C:\Users\Administrator\Desktop\abc") #os.path模块下 #1.路径的拼接 path = os.path.join(r"C:\Users\Administrator\Desktop\SZ-Python","Day1Code") print(path) #2.绝对路径和相对路径【掌握】 """ 绝对路径:带有盘符的路径,缺点:只能在指定的计算机上使用 相对路径:不带盘符的路径,一般情况下是以当前的工程为参照物 例如: aaa/textDemo01.py ccc/module.py """ #os.rename("bbb/check.py","bbb/show.py") #3.拆分路径 #注意:返回的结果为元组,默认情况下只会拆分最后的文件或者文件夹 tuple1 = os.path.split(r"C:\Users\Administrator\Desktop\SZ-Python\Day1Code") print(tuple) #4.拆分路径,获取指定路径对应的文件的扩展名 print(os.path.splitext(r"C:\Users\Administrator\Desktop\SZ-Python\Day2Code\assignDemo.py")) #5.判断指定路径是否是文件夹 print(os.path.isdir("aaa/textDemo01.py")) #6.判断指定路径是否是文件 print(os.path.isfile("aaa/textDemo01.py")) #7.判断一个指定路径是否存在 print(os.path.exists("aaa/textDemo01.py")) #8.获取文件的大小【字节】 print(os.path.getsize("aaa/textDemo01.py")) #9. #获取指定文件夹的父路径 print(os.path.dirname(r"C:\Users\Administrator\Desktop\SZ-Python\Day1Code")) #获取当前文件夹的名称 print(os.path.basename(r"C:\Users\Administrator\Desktop\SZ-Python\Day1Code"))
练习:
import os #练习:获取指定目录下所有的py文件或者txt文件 """ 思路: 1.判断指定的目录是否存在 2.获取指定目录下所有的文件以及文件夹: listdir() 3.拼接路径: 4.判断拼接之后的路径是否是文件 5.判断文件名称的后缀 """ def getFile(path): #1. if os.path.exists(path): #2 fileList = os.listdir(path) #3. for fileName in fileList: filePath = os.path.join(path,fileName) #4 if os.path.isfile(filePath): #5 if fileName.endswith("py") or fileName.endswith("txt"): print(fileName) else: print(fileName,"不是文件") else: print("指定的路径不存在") getFile(r"C:\Users\Administrator\Desktop\SZ-Python\Day5Code")
3.自定义模块【掌握】
3.1自定义import模块
代码演示:
#1.格式:import 包1.包2.模块的名称 #注意1:通过点语法区分包的层级关系 #引入模块 #注意2:如果要同时导入多个模块,有两种方式 #方式一 """ import os import datetime import math """ #方式二 import os,math,datetime #注意3:当导入自定义模块的时候,需要注意包的存在 #注意5:当通过import将模块导入的时候,将模块对应的文件整个加载了一遍 import ccc.module import moduleTextDemo01 print("***************") #注意4:当模块有包的层级关系时,需要调用其中函数的时候,需要指明函数的路径 ccc.module.test() #os.path.isdir() moduleTextDemo01.fun1() moduleTextDemo01.fun2() moduleTextDemo01.fun3() print(moduleTextDemo01.num)
3.2自定义from-import模块
代码演示:
#form 模块名 import 函数名1/类名,函数名2.。。。 #import moduleTextDemo01 from moduleTextDemo01 import fun1,fun2,fun3 #注意:采用了form。。。import的方式导入指定的函数之后,可以直接调用函数 fun1() fun2() fun3() #好处:进行局部的导入,避免内存空间的浪费 #注意:当前文件中如果存在和模块中同名的函数的时候,当前文件中的函数仍然会将模块中的函数给覆盖掉 def fun1(): print("hello") fun1()
3.3自定义from-import*模块
代码演示:
#from 。。。。 import * *代表全部 """ 下面三种导入方式完全等价:将moduleTextDemo01模块中的所有的内容全部导入 from moduleTextDemo01 import * import moduleTextDemo01 from moduleTextDemo01 import fun1,fun2,fun3 """ from moduleTextDemo01 import * fun1()
总结:在python中,每个py文件其实都是一个模块,如果跨模块调用函数,则采用导入的方式
将不同的功能进行划分,调用函数的时候相对比较方便的
4.__name__属性和dir函数
4.1name属性
#__name__的作用:如果不想让模块中的某些代码执行,可以通过属性仅仅调用程序中的一部分功能 #【写在if判断中的代码只有当前模块被执行的时候才会被执行,检测到是其他的文件在使用当前的模块,则if语句中的代码不会被执行】 def fun1(): print("aaa") def fun2(): print("bbb") def fun3(): print("ccc") #作用:写在下面判断中的代码,只有当前模块运行的时候才会被执行【起到屏蔽的作用】 if __name__ == "__main__": fun1() fun2() fun3()
4.2dir函数
代码演示:
#dir: import math,moduleTextDemo01 #获取指定模块里面的所有的内容 #dir(模块名称) 返回的是一个列表 print(dir(math)) print(dir(moduleTextDemo01))
十三. 系统模块
UTC:国际标准时间, 格林尼治天文时间,UTC+8
时间戳:指定时间距离1970.1.1 00:00:00的秒数
time:时间
datetime:日期
calendar:万年历
os:系统,文件和文件夹
2.1time时间模块
时间的表示形式:
a. 时间戳: 如:1523158416.681
b. 元组格式
c. 字符串
tm_year: 年 tm_mon: 月(1~12) tm_mday:天(1~31) tm_hour:时(0~23) tm_min:分(0~59) tm_sec:秒(0~59) tm_wday: 一周中的第几天(0~6,0表示星期一) tm_yday:一年中的第几天(1~366) tm_isdst:是否是夏令时
c.时间字符串:如:2019-09-08 11:11:11
%y 两位数的年份表示(00-99) %Y 四位数的年份表示(000-9999) %m 月份(01-12) %d 月内中的一天(0-31) %H 24小时制小时数(0-23) %I 12小时制小时数(01-12) %M 分钟数(00-59) %S 秒(00-59) %a 本地简化星期名称 %A 本地完整星期名称 %b 本地简化的月份名称 %B 本地完整的月份名称 %c 本地相应的日期表示和时间表示 %j 年内的一天(001-366) %p 本地A.M.或P.M.的等价符 %U 一年中的星期数(00-53)星期天为星期的开始 %w 星期(0-6),星期天为星期的开始 %W 一年中的星期数(00-53)星期一为星期的开始 %x 本地相应的日期表示 %X 本地相应的时间表示 %% %号本身
代码演示:
#导入 import time #1。获取当前时间对应的时间戳,使用浮点型表示【掌握】 t1 = time.time() print(t1) #2。将时间戳转换为UTC g = time.gmtime(t1) print(g) #time.struct_time(tm_year=2018, tm_mon=5, tm_mday=29, tm_hour=2, tm_min=29, tm_sec=1, tm_wday=1, tm_yday=149, tm_isdst=0) #3.根据时间戳生成当地时间【掌握】 l = time.localtime(t1) print(l) #4.将具体时间转换为时间戳【掌握】 m = time.mktime(l) print(m) #5.将时间转换为字符串形式【掌握】 a = time.asctime(l) print(a) #Tue May 29 10:36:57 2018 #6.将时间戳转换为字符串形式 c = time.ctime(t1) print(c) #7.将时间进行格式化【指定字符串的格式】【掌握】 """ %Y:年 %m:月 %d:日 %H:时【24小时制】 %h:时【12小时制】 %M:分 %S:秒 """ s1 = time.strftime("%Y.%m.%d %H:%M:%S",l) #string format print(s1) #8.休眠,参数的单位为秒【掌握】 print("4674747") time.sleep(2) print("hello") #9.用浮点数【一般用科学计数法】计算的秒数返回当前cpu的时间,用于衡量不同程序的耗时 print(time.clock()) # 新版本Python已经不再支持
练习:
#需求;已知一个时间的字符串,然后输出三天之后的时间 """ 思路: 1.将已知的字符串转换为对应的时间戳 2.利用时间戳计算三天后的时间【加法运算 + 3 * 24 * 3600】 3.将时间戳转换为时间的字符串,并且将时间格式化 """ import time str = "2017-5-20" #1.将已知的字符串转换为对应的时间戳 newStr = time.strptime(str,"%Y-%m-%d") print(newStr) time1 = time.mktime(newStr) print(time1) #2.利用时间戳计算三天后的时间【加法运算 + 3 * 24 * 3600】 time2 = time1 + 3 * 24 * 3600 #3.将时间戳转换为时间的字符串,并且将时间格式化 time3 = time.strftime("%Y-%m-%d",time.localtime(time2)) print(time3) #2017-05-23
2.2datetime日期模块【掌握】
是对time模块的封装,比time模块更加全面
dt_now = datetime.datetime.now() 获取当前的日期对象,包含时间的 dt_ziding = datetime.datetime() 根据指定的日期、时间生成一个日期对象 dt.strftime() 将日期对象转化为指定的格式 dt.date() 获取日期对象中的日期 dt.time() 获取日期对象中的时间 dt.timestamp() 获取日期对象的时间戳 dt.hour\minute\second 获取小时、分钟、秒 datetime.datetime.fromtimestamp() 根据一个时间戳,转化为指定的日期对象 datetime.timedelta() 生成一个差值对象,可以和日期对象直接进行相加减 参数有,days,hours,minutes,seconds
代码演示:
import datetime #1.获取当前时间 d1 = datetime.datetime.now() print(d1) #2018-05-29 11:20:51.432757 #2.获取指定的时间,通过元组形式 d2 = datetime.datetime(2015,10,1,10,23,23,1234) print(d2) #3.将时间格式化 d3 = d1.strftime("%Y.%m.%d") print(d3) #4.将时间字符串转换为datetime实体 d4 = datetime.datetime.strptime(d3,"%Y.%m.%d") print(d4) #5.直接进行加减运算 date1 = datetime.datetime(2015,10,1,10,23,23,0) print(date1) date2 = datetime.datetime(2015,10,4,10,23,23,0) date3 = date2 - date1 print(date3) #3 days, 0:00:00 print(date3.days) print(date3.seconds)
2.3calendar日历模块(了解)
calendar(year,w=2,l=1,c=6) 打印某一年的日历【c间隔距离; w每日宽度间隔; l是每星期行数 】 isleap(year) 判断是否是闰年 leapdays(y1, y2) [y1, y2) 中间闰年的个数 month(year,month,w=2,l=1) 打印指定月份的日历 monthcalendar(year,month) 返回一个整数的单层嵌套列表。每个子列表装载代表一个星期的整数。 Year年month月外的日期都设为0;范围内的日子都由该月第几日表示,从1开始。 monthrange(year,month) 返回两个整数。第一个是该月的星期几的日期码,第二个是该月的日期码。 日从0(星期一)到6(星期日);月从1到12。
代码演示:
import calendar #返回指定年份中指定月份的万年历表示 print(calendar.month(2018,5)) #返回指定年份的万年历表示 print(calendar.calendar(2018)) #返回一个列表【二维列表】 print(calendar.monthcalendar(2018,5)) #当前周起始的日期 print(calendar.firstweekday()) #判断某年是否为闰年 print(calendar.isleap(2010)) #统计两个年份之间闰年的总数 print(calendar.leapdays(2000,2020)) #获取的是星期,0【星期一】~6【星期天】 1~12 print(calendar.weekday(2018,5,29))
十四、面向对象思想
1.面向对象思想设计
基于哲学观点:万物皆对象, 一切皆对象
举例说明:
案例一:我想吃大盘鸡
面向过程 面向对象
1.自己去买菜 1.委托一个人帮忙买菜
2.自己择菜 2.委托一个人帮忙择菜
3.自己做菜 3.委托一个人厨师做菜
4.自己吃 4.自己吃
案例二:小明是一个电脑小白,想要配置一台电脑
面向过程 面向对象
1.小明补充电脑知识 1.委托一个懂电脑的人买零件
2.小明去买零件 2.委托一个人组装
3.小明把零件运回来 3.小明打开玩游戏
4.小明组装
5.小明打开玩游戏
案例三:一辆红色的法拉利在京藏高速上奔驰
法拉利 京藏高速
2.面向过程和面向对象的区别
2.1面向过程
process:处理
在生活案例中:
一种看待问题的思维方式,在解决问题的时候,侧重于问题是怎样一步一步解决的,然后亲力亲为的去解决
在程序中:
代码从上往下依次执行
各个模块之间的关系尽可能的独立的,当import的时候,加载的顺序也是从上往下依次加载
每个模块中的语句结构:顺序,分支,循环
2.2面向对象
在生活案例中:
一种看待问题的思维方式,侧重于找到一个具有特殊功能的个体,然后委托这个个体帮忙完成某件事情,这个个体就被称为对象
好处:可以将复杂的问题简单化,将程序员从执行者变成了指挥者
在程序中:
根据不同的需求执行代码【代码执行顺序不一定】
程序的流程完全由需求决定【对象】
思想:如果对象存在,则直接使用;如果对象不存在,则创建对象
注意:面向对象只是一种思想,并不是一门编程语言
Python是一门面向对象的编程语言,类和对象是 面向对象的核心
示例: 小狗吃食(闻一闻smell、舔一舔lick、咬一咬bite) 分别采用面向过程和面向对象来分析 面向过程 : 先闻一闻, 然后再舔一舔, 最后再咬一咬 (注重过程) 面向对象 : 小狗是一个对象, 它可以闻一闻食物, 可以舔一舔食物, 可以咬一咬食物. (不注重过程, 注重对象)
十五、类和对象【掌握】
1.类和对象的概念
类:多个具有特殊功能的个体的集合
对象:在一个类中,一个具有特殊功能的个体,能够帮忙解决某件特定的事情,也被称为实例【instance】
两者之间的关系:类用于描述某一类对象的共同特征,而对象是类的具体的存在【包含关系】
思考问题:先有类还是先有对象?
【不好说,但是,在程序中使用的时候,一般是先定义类,然后创建对象】
举例:
类 对象
人 王麻子,李四. 赵四。。
快递 韵达,中通,圆通。。
SupreHero 蝙蝠侠,蜘蛛侠,美国队。。
帮忙理解:类其实也是一种数据类型,只不过一般情况下是自定义的,所以可以将类认为是自定义的数据类型,用法和整型,string,list等基本是相同的【定义变量,传参】
2.类的定义
语法:
class 类名( ):
类体
说明:
a.Python中使用class关键字定义类
b.类名只要是一个合法的标识符即可,但是要求:遵循大驼峰命名法则【首单词的首字母大写,不同单词之间首字母大写】
c.通过缩进区分类体
d.类体一般包含两部分内容:对类的特征的描述、对类的行为的描述
代码演示:
#类的定义 #类的声明 class MyClass(): #类的实现 #类体 #print("hello") #一般不会这么书写 pass #注意:在同一个py文件中可以同时定义多个类,但是,为了提高代码的可读性,结合模块的使用,最好是一个文件一个类 class MyClass1(): pass
3.类的设计【类体的实现】
三要素:
事物名称【类名】:举例:人
事物的特征【变量】:名词,举例:姓名,年龄。。。。
事物的行为【函数/方法】:动词,举例:吃,跑。。。。
十六、类中的方法和变量【掌握】
1.类中的方法和变量的定义
类中的方法和变量是为了描述事物的行为和特征
类中定义的方法被称为成员方法
类中定义的变量被称为成员变量,也被称为属性 [os.name]
成员变量:类具有的特征
成员方法:类具有的行为
类存在的意义:拥有相同特征和行为的对象可以抽取出来一个类,类的存在是为了创建一个具体的对象
代码演示:
#定义类 #1.事物的名称:类名 class Person(): #2.事物的特征:成员变量、属性 name = "" age = 0 height = 0.0 #3.事物的行为:成员方法【函数】 #注意:类中的成员方法区别于普通方法:参数部分一定包含self,而且最好self出现在参数列表的第一个 #调用函数的时候,self不需要被传参 #初次之外,成员方法的用法和普通方法的使用完全相同,也可以设置默认参数或者关键字参数,不定长参数 #注意:self:自己,代表类的实例【对象】 #此处的self可以是任意的标识符,只不过为了结合其他编程的使用,习惯上使用self def eat(self,food): print("eating",food) def run(self): print("running")
2.类中方法和属性的使用
2.1创建对象【实例化对象】
已知类,通过类创建对象
对象的创建过程被对象的实例化过程
语法:变量名 = 值
对象名 = 类名()
代码演示:
#定义类 #1.事物的名称:类名 class Person(): #2.事物的特征:成员变量、属性 name = "" age = 0 height = 0.0 #3.事物的行为:成员方法【函数】 #注意:类中的成员方法区别于普通方法:参数部分一定包含self,而且最好self出现在参数列表的第一个 #调用函数的时候,self不需要被传参 #初次之外,成员方法的用法和普通方法的使用完全相同,也可以设置默认参数或者关键字参数,不定长参数 #注意:self:自己,代表类的实例【对象】 #此处的self可以是任意的标识符,只不过为了结合其他编程的使用,习惯上使用self def eat(self,food): print("eating",food) def run(self): print("running") print("self的地址:", id(self)) #对象的创建 p1 = Person() print(p1) p2 = Person() print(p2) #p1和p2被称为对象,变量名,引用,指向了真正的对象 #p1和p2在栈空间中开辟了空间,真正的对象的被存储在堆空间中 #通过对象调用类中的成员方法和访问类中的成员变量 #1.访问属性 #语法:对象.属性名 #赋值:对象.属性 = 值 per = Person() print(per.name) per.name = "小姐姐" print(per.name) per.age = 18 print(per.age) per.height = 1.70 print(per.height) #2.调用方法 #语法:对象.函数名(参数列表) #注意:self不需要被传参,传参的时候注意区分参数的类型【默认参数,不定长参数,关键字参数】 per.run() print("per的地址:",id(per)) """ self的地址: 2687721120880 per的地址: 2687721120880 """ per.eat("apple") person = Person() person.name = "张三" person.age = 20 print(person.name,person.age) person.run() person.eat("") #结论:类中的成员变量和成员方法随着对象的出现而出现
总结:
访问变量采用:对象名.属性名
访问方法采用:对象名.方法名(参数列表)
3.内存中的对象
per = Person()
说明:
a.程序中定义的Person类型的变量per实际上是一个变量名,它被存放在栈内存中,他指向实际的Person对象,而真正的Person对象则存放于堆内存中
b.类中的成员变量随着对象的出现而出现,随着对象的消失而消失
c.每个对象的成员变量会在堆空间中开辟一份自己的空间,相互之间互不影响
4.动态绑定属性和限制绑定
__slots__变量的作用:限制一个类中的成员变量【程序在运行的过程中,就不能随意的动态绑定属性】 语法:__slots__ = (属性的名称)
代码演示:
#1.类的定义 class MyClass(): #2.成员变量 """ num1 = 0 num2 = 10 """ #限制属性 #注意:被限制的属性的名称通过字符串的方式出现在元组的元素中 __slots__ = ("num1","num2") #3.成员方法 def fun1(self): print("fun1") def fun2(self,num): print(num) #4.创建对象 my = MyClass() #5.访问类中的成员变量 my.num1 = 11 my.num2 = 22 print(my.num1,my.num2) #6.调用类中的成员方法 my.fun1() my.fun2(30) #成员变量随着对象的出现而出现的 #属性的动态绑定【Python是一门动态语言】 my.n = 100 print(my.n) my1 = MyClass() #print(my1.n)
5.综合案例一
代码演示:
practiceDemo01.py文件【测试模块】
""" 需求:使用面向对象的思想描述下面这个情景 开学了,王老师让小明,小花,小丽分别做自我介绍 需要介绍姓名,年龄,爱好,来一段才艺展示 """ """ 分析: 老师类 特性:姓名 行为:让学生做自我介绍 学生类 特征:姓名,年龄,爱好 行为:一段才艺展示 """ #导入 """ import practice01.teacher import practice01.student """ from practice01.teacher import Teacher from practice01.student import Student #1.创建一个老师的对象 wang = Teacher() wang.name = "王老师" #2.创建一个学生的对象 xiaohua = Student() xiaohua.name = "小花" xiaohua.age = 18 xiaohua.hobby = "唱歌" #3.让老师执行自己的行为 wang.letStudentIntroduce(wang.name,xiaohua) #stu = xiaohua xiaoli = Student() xiaoli.name = "小丽" xiaoli.age = 20 xiaoli.hobby = "跳舞" wang.letStudentIntroduce(wang.name,xiaoli) xiaoming = Student() xiaoming.name = "小明" xiaoming.age = 25 xiaoming.hobby = "吹牛逼" wang.letStudentIntroduce(wang.name,xiaoming)
teacher.py文件【实体类】
#老师类 class Teacher(): #特征:成员变量 name = "" #行为:成员方法 def letStudentIntroduce(self,name,stu): #老师发出指令 print(name + "让" + stu.name + "做自我介绍") #执行指令 stu.introduce(stu.name,stu.age,stu.hobby) #不同的学生展示不同的才艺 if stu.name == "小花": stu.singSong() elif stu.name == "小丽": stu.dance() else: stu.lie()
student.py文件【实体类】
#学生类 class Student(): #特征:成员变量 name = "" age = 0 hobby = "" #行为:成员方法 def introduce(self,name,age,hobby): print("大家好,我是%s,今年%d,爱好%s"%(name,age,hobby)) #唱歌 def singSong(self): print("娘子~啊哈") #跳舞 def dance(self): print("广场舞") #吹牛逼 def lie(self): print("我家可穷了,就养了几百头牛")
十七、构造函数和析构函数
1.构造函数【掌握】
采用上面的方式创建对象【直接给成员变量赋值】,很多的类一般倾向于创建成有初始状态的 __init__:构造函数【作用:创建对象,给对象的成员变量赋初始值】 构造函数:构造器 调用的时机:当一个对象被创建的时候,第一个被自动调用的函数 per = Person() 语法: def __init__(self,args1,args2....) 函数体 说明: a.之前的写法中并没有显式的定义__init__函数,说明系统默认提供了一个无参的构造函数 b.args1,args2...一般设置的形参列表和成员变量有关
代码演示:
#1.构造函数被调用的时机 class Check(): num1 = 0 str1 = "" #构造函数 def __init__(self): print("jfahj") def show(self): print("show") #注意:当创建对象的时候,默认调用了系统提供的无参的构造函数 c = Check() c.show() #2.给构造函数添加参数 class Check1(): name = "" age = 0 """ def __init__(self,n,a): print("fajkgak") """ #注意2:当使用构造函数的时候,可以使用无参的,也可以使用有参的,在Python中的解决办法:设置不定长参数 #注意3:Python中,一个类中只能有一个构造函数 def __init__(self, *n): print("fajkgak") #注意1:当手动头添加了有参的构造函数之后,系统将不再提供无参的构造函数 c1 = Check1() c11 = Check1("fsiugh") #3.有参构造函数的使用 class Check2(): name = "" age = 0 #构造函数的形参列表:和成员变量有关 def __init__(self,n,a): print(n,a) name = n age = a #注意1:当手动头添加了有参的构造函数之后,系统将不再提供无参的构造函数 c2 = Check2("zhangsan",10) print(c2.name,c2.age) #0 #4.self的使用 class Check3(): name = "" age = 0 #构造函数的形参列表:和成员变量有关 def __init__(self,n,a): print(n,a) #self的使用:通过self来区分成员变量和局部变量,所以self.name代表name是一个全局变量【成员变量】 self.name = n self.age = a c3 = Check3("zhangsan",10) print(c3.name,c3.age) #10 #5.使用self之后,可以省略成员变量的定义【掌握】 #self只是一个标识符,可以替换成任意的标识符 class Check4(): #构造函数的形参列表:和成员变量有关 def __init__(self,name,age): print(name,age) #self的使用:通过self来区分成员变量和局部变量,所以self.name代表name是一个全局变量【成员变量】 self.name = name self.age = age def show(self): print("showing") c4 = Check4("lisi",20) print(c4.name,c4.age) c4.show()
2.析构函数
与构造函数正好相反,当对象被销毁的时候自动调用的函数,被称为析构函数 __del__: 删除变量: del 变量名,此时可以触发析构函数的调用 使用情景:清理工作,比如关闭数据库,关闭文件等
代码演示:
import time class Pig(): def __init__(self,name,age): self.name = name self.age = age print("构造函数被执行") def show(self): print("show") #析构函数 def __del__(self): print("~析构函数被调用") #析构函数被调用的时机:1:当程序运行完成的时候 2:使用del删除变量 p = Pig("abc",10) del p #注意:对象释放以后就不能再访问了【相当于根本未创建过这个对象】 #print(p.age) time.sleep(5) #在函数里定义的对象,会在函数结束时自动释放,这样可以用来减少内存空间的浪费 #其实就是作用域的问题 def func(): per2 = Person("aa", 1, 1, 1) func()
3.综合案例二
practiceDemo02.py文件【测试模块】
""" 需求:富二代王思聪开着豪车,很自豪的向他的新女友炫耀 富二代类 特征:姓名 行为:开车,炫耀 汽车类 特征:品牌,颜色 行为:奔驰 """ #测试模块 from practice02.car import Car from practice02.richMan import RichMan #1.创建一个富二代的对象 wang = RichMan("王思聪") #2.创建一个汽车的对象 c = Car("玛莎拉蒂","闷骚红") c.run() #3.让富二代执行自己的行为 wang.driveCar(c) wang.showCar(c)
richMan.py文件【实体类】
class RichMan(): #构造函数 def __init__(self,name): self.name = name #成员函数 def driveCar(self,car): print("富二代%s开着他的豪车%s"%(self.name,car.brand)) def showCar(self,car): print(car.brand,car.color)
car.py文件【实体类】
class Car(): #构造函数 def __init__(self,brand,color): self.brand = brand self.color = color #成员函数 def run(self): print("%s在马路上奔驰"%(self.brand))
十八、封装【private】
1.概念
广义的封装:函数和类的定义本身,就是封装的体现
狭义的封装:一个类的某些属性,在使用的过程 中,不希望被外界直接访问,而是把这个属性给作为私有的【只有当前类持有】,然后暴露给外界一个访问的方法即可【间接访问属性】
封装的本质:就是属性私有化的过程
封装的好处:提高了数据的安全性,提高了数据的复用性
说明:举例:插排,不需要关心属性在类的内部做了什么样的操作,只需要关心将值传进去,或者将结果获取出来
封装: 函数 => 类 => 模块 => 包
2.属性私有化
如果想让成员变量不被外界直接访问,则可以在属性名称的前面添加两个下划线__,成员变量则被称为私有成员变量
私有属性的特点:只能在类的内部直接被访问,在外界不能直接访问
代码演示:
#1.属性不私有化的时候 class Person(): def __init__(self,name,age): self.name = name self.age = age def myPrint(self): print(self.name,self.age) #通过构造函数给属性赋值 per = Person("张三",10) per.myPrint() #张三 10 #通过对象直接访问属性,并且给属性赋值 per.name = "李四" per.age = 22 per.myPrint() #李四 22 #2.属性私有化 #写法:在属性的前面添加两个下划线 #用法:只能在类的内部被访问,外界不能直接访问 class Person1(): def __init__(self,name,age): self.name = name self.__age = age def myPrint(self): print(self.name,self.__age) p1 = Person1("abc",10) p1.myPrint() #abc 10 p1.name = "hello" #其实动态绑定属性,age和__age其实是两个不同的变量 p1.age = 222 p1.myPrint() print(p1.age) #AttributeError: 'Person1' object has no attribute '__age',私有化了,在外界不能直接访问 #print(p1.__age)
3.get函数和set函数
get函数和set函数并不是系统的函数,而是自定义的,为了和封装的概念相吻合,起名为getXxx和setXxx
get函数:获取值
set函数:赋值【传值】
代码演示:
#3.get函数和set函数 class Person2(): def __init__(self,name,age): self.name = name self.__age = age #特殊情况一 self.__weight__ = 20.0 #特殊情况二 self._height = 155.0 def myPrint(self): print(self.name,self.__age) # 书写私有属性age的get函数和set函数【通过自定义的函数进行私有属性的赋值和获取值,暴露给外界】 """ get函数和set函数并不是系统的函数,而是自定义的,为了和封装的概念相吻合,起名为getXxx和setXxx get函数:获取值 set函数:赋值【传值】 """ #set函数:给成员变量赋值 #命名方式:setXxx #特点:需要设置参数,参数和私有成员变量有关 def setAge(self,age): #数据的过滤 if age < 0: age = 0 self.__age = age #get函数:获取成员变量的值 #命名方式:getXxx #特点:需要设置返回值,将成员变量的值返回 def getAge(self): return self.__age #注意:有几个私有属性,则书写几对get函数和set函数 p2 = Person2("abc",10) p2.myPrint() #abc 10 #print(p2.__age) #间接的访问了私有的成员变量 print(p2.getAge()) p2.setAge(22) print(p2.getAge()) p2.setAge(-20) print(p2.getAge()) #总结:通过将属性私有化之后,然后提供get函数和set函数,外部代码就不能随意更改成员变量的值,这样在一定程度上保证了数据的安全性 #4.工作原理【了解】 #当编译器加载了程序之后,不能直接访问p2.__age,Python解释器把__age解释成_Person2__age #p2.__age = 100 p2._Person2__age = 100 print(p2.getAge()) #5.特殊情况:尽量不要直接访问 #a.在一个变量的前后各加两个下划线,在Python中被认为特殊成员变量,将不再属于私有变量 #print(p2.__weight__) #b.特殊变量 #print(p2._height) #面试题:下面变量的含义 """ xxx:普通的变量 _xxx:受保护的变量,不建议使用这种形式 __xxx:表示私有的,外界无法直接访问,只能通过暴露给外界的函数访问 __xxxx__:一般是系统的内置变量,比如:__name__,__solts__,自定义标识符的时候尽量不要使用这种形式 """
4.@property装饰器
装饰器的作用:可以给函数动态添加功能,对于类的成员方法,装饰器一样起作用
Python内置的@property装饰器的作用:将一个函数变成属性使用
@property装饰器:简化get函数和set函数
使用:@property装饰器作用相当于get函数,同时,会生成一个新的装饰器@属性名.settter,相当于set函数的作用
作用:使用在类中的成员函数中,可以简化代码,同时可以保证对参数做校验
代码演示:
class Person1(): def __init__(self,name,age): self.__name = name self.__age = age def myPrint(self): print(self.__name,self.__age) """ def setAge(self,age): #数据的过滤 if age < 0: age = 0 self.__age = age def getAge(self): return self.__age """ #注意:函数的命名方式:变量的名称 #作用:相当于get函数,设置返回值,将成员变量的值返回 @property def age(self): return self.__age #注意:函数的命名方式:需要和@property中函数的命名保持一致 #作用:相当于set函数,设置参数,给成员变量赋值 @age.setter def age(self,age): if age < 0: age = 0 self.__age = age @property def name(self): return self.__name @name.setter def name(self,name): self.__name = name p1 = Person1("abc",10) p1.myPrint() #abc 10 #p1.setAge(20) #print(p1.getAge()) print(p1.age) #10 p1.age = 18 #相当于调用了set函数,将18传值,实质调用的是@age.setter修饰的函数 print(p1.age) #相当于调用了get函数,将成员变量的值获取出来,实质调用的是@peoperty修饰的函数 p1.name = "zhangsan" print(p1.name)
5.私有方法
如果类中的一个函数名前面添加__,则认为这个成员函数时私有化的
特点:也不能在外界直接调用,只能在类的内类调用
代码演示:
class Site(): def __init__(self,name): self.name = name def who(self): print(self.name) self.__foo() #私有成员方法,只能在当前类的内部内调用 def __foo(self): #私有函数 print("foo") def foo(self): #公开函数 print("foo~~~~") #注意:以上两个函数是两个不同的函数,不存在覆盖的问题 s = Site("千锋") s.who() #s.__foo() #AttributeError: 'Site' object has no attribute 'foo' s.foo()
十九、继承【extends】
1.概念
如果两个或者两个以上的类具有相同的属性或者成员方法,我们可以抽取一个类出来,在抽取的类中声明公共的部分
被抽取出来的类:父类,基类,超类,根类
两个或者两个以上的类:子类,派生类
他们之间的关系:子类 继承自 父类
注意:
a.object是所有类的父类,如果一个类没有显式指明它的父类,则默认为object
b.简化代码,提高代码的复用性
2.单继承
2.1使用
简单来说,一个子类只能有一个父类,被称为单继承
语法:
父类:
class 父类类名(object):
类体【所有子类公共的部分】
子类:
class 子类类名(父类类名):
类体【子类特有的属性和成员方法】
说明:一般情况下,如果一个类没有显式的指明父类,则统统书写为object
代码演示:
person.py文件【父类】
#1.定义父类 class Person(object): #构造函数【成员变量】 def __init__(self,name,age): self.name = name self.age = age #成员方法 def show(self): print("show") def __fun(self): print("fun")
worker.py文件【子类1】
from extends01.person import Person #2.定义子类 class Worker(Person): #构造函数【成员变量】 def __init__(self,name,age,job): """ self.name = name self.age = age """ self.job = job #6.在子类的构造函数中调用父类的构造函数【从父类中继承父类中的成员变量】 #方式一:super(当前子类,self).__init__(属性列表) #super(Worker, self).__init__(name,age) #方式二:父类名.__init__(self,属性列表) Person.__init__(self,name,age) #方式三:super().__init__(属性列表) #super().__init__(name,age) #成员方法 def work(self): print("work")
student.py文件【子类2】
from extends01.person import Person class Student(Person): # 构造函数【成员变量】 def __init__(self, name, age, score): Person.__init__(self,name,age) self.score = score # 成员方法 def study(self): print("study")
extendsDemo01.py文件【测试模块】
#测试模块 from extends01.person import Person from extends01.worker import Worker from extends01.student import Student #3.创建父类的对象 p = Person("zhangsan",10) p.show() #p.__fun() #4.创建子类的对象 w = Worker("aaa",20,"工人") w.work() #5.子类对象访问父类中的内容 #结论一:子类对象可以调用父类中的公开的成员方法【因为继承,私有方法除外】 w.show() #w.__fun() #结论二:通过在子类的构造函数中调用父类的构造函数,子类对象可以直接访问父类中的成员变量【私有变量除外】 print(w.name,w.age,w.job) s = Student("小明",9,90) s.study() s.show()
2.2特殊用法
代码演示:
#6.子类中出现一个和父类同名的成员函数,则优先调用子类中的成员函数 #子类的成员函数覆盖了父类中的同名的成员函数 s = Student("小明",9,90) s.study() s.show() #7.父类对象能不能访问子类中特有的成员函数和成员变量?----->不能 per = Person("gs",10) #per.work()
#8.slots属性能否应用在子类中 #结论三:在父类中定义slots属性限制属性的定义,子类中是无法使用,除非在子类中添加自己的限制 #父类 class Student(object): __slots__ = ("name","age") #子类 class SeniorStudent(Student): pass s = Student() s.name = "zhangsan" s.age = 10 #s.score = 90 ss = SeniorStudent() ss.name = "lisi" ss.age = 20 ss.score = 60
总结:
继承的特点:
a.子类对象可以直接访问父类中非私有化的属性
b.子类对象可以调用父类中非私有化的成员方法
c.父类对象不能访问或者调用子类 中任意的内容
继承的优缺点:
优点:
a.简化代码,减少代码的冗余
b.提高代码的复用性
c.提高了代码的可维护性
d.继承是多态的前提
缺点:
通常使用耦合性来描述类与类之间的关系,耦合性越低,则说明代码的质量越高
但是,在继承关系中,耦合性相对较高【如果修改父类,则子类也会随着发生改变】
3.多继承
一个子类可以有多个父类
语法:
class 子类类名(父类1,父类2,父类3.。。。):
类体
代码演示:
father.py文件【父类1】
class Father(object): def __init__(self,money): self.money = money def play(self): print("playing") def fun(self): print("father中的fun")
mother.py文件【父类2】
class Mother(object): def __init__(self,faceValue): self.faceValue = faceValue def eat(self): print("eating") def fun(self): print("mother中的fun")
child.py文件【子类】
from extends02.father import Father from extends02.mother import Mother #定义子类,有多个父类 class Child(Mother,Father): def __init__(self,money,faceValue,hobby): #调用父类中的构造函数 Father.__init__(self,money) Mother.__init__(self,faceValue) self.hobby = hobby def study(self): print("study")
extendsDemo03.py文件【测试模块】
from extends02.father import Father from extends02.mother import Mother from extends02.child import Child f = Father(100000) m = Mother(3.0) #创建子类对象 c = Child(1000,3.0,"打游戏") #子类对象调用父类中的成员方法 c.play() c.eat() #结论;如果多个父类中有相同的函数,通过子类的对象调用,调用的是哪个父类中的函数取决于在父类列表中出现的先后顺序 c.fun()
4.函数重写【override】
在子类中出现和父类同名的函数,则认为该函数是对父类中函数的重写
4.1系统函数重写
__str__ __repr__
代码演示:
class Animal(object): def __init__(self,name,age): self.name = name self.age = age #重写__str__函数,重写之后一般return一个字符串,有关于成员变量 def __str__(self): return "name=%s age=%d"%(self.name,self.age) #重写__repr__,作用和str是相同的,优先使用str def __repr__(self): return "name=%s age=%d"%(self.name,self.age) a = Animal("大黄",10) print(a) #<__main__.Animal object at 0x00000226A87AC240> print(a.__str__()) #当一个类继承自object的时候,打印对象获取的是对象的地址,等同于通过子类对象调用父类中__str__ #当打印对象的时候,默认调用了__str__函数 #重写__str__的作用:为了调试程序 """ 总结:【面试题】 a.__str__和__repr__都未被重写的时候,使用对象调用的是__str__,此时__str__返回的是对象的地址 b.__str__和__repr__都被重写之后,使用对象调用的是__str__,此时__str__返回的是自定义的字符串 c.重写了__str__,但是没有重写__repr__,则使用对象调用的是__str__,此时__str__返回的是自定义的字符串 d.未重写__str__,但是重写了__repr__,则使用对象调用的是__repr__,此时,__repr__返回的是自定义的字符串 """ #使用时机:当一个对象的属性有很多的时候,并且都需要打印,则可以重写__str__,可以简化代码,调试程序
4.2自定义函数重写
代码演示:
#函数重写的时机:在继承关系中,如果父类中函数的功能满足不了子类的需求,则在子类中需要重写 #父类 class People(object): def __init__(self,name): self.name = name def fun(self): print("fun") #子类 class Student(People): def __init__(self,name,score): self.score = score super(Student,self).__init__(name) #重写;将函数的声明和实现重新写一遍 def fun(self): #在子类函数中调用父类中的函数【1.想使用父类中的功能,2.需要添加新的功能】 #根据具体的需求决定需不需要调用父类中的函数 super(Student,self).fun() print("fajfhak") s = Student("fhafh",10) s.fun()