一、Python概述
这是我参与更文挑战的第4天,活动详情查看: 更文挑战
1.1 Python的特点
- Python是一们面向对象的语言,在Python中一切皆对象
- Python是一们解释性语言
- Python是一们交互式语言,即其在终端中进行命令编程
- Python是一们跨平台的语言【没有操作系统 的限制,在任何操作系统 上都可以运行Python代码】
- Python拥有强大和丰富的库,又被称为胶水语言,能把其他语言(主要C/c++)写的模块很轻松的结合在一起
1.2 Python的优缺点
- 优点:
- 易于维护和学习
- 广泛的标准库【提供了大量的工具】
- 可扩展性强
- Python对于大多数据库都有相应的接口【Mysql、sqliters3、MongoDB、Redis等】
- 支持GUI编程【图形化界面】
- 缺点:
- 和C语言相比较Python的代码运行速度慢
- 代码无法加密
1.3 Python代码的执行过程
流程:先把源码编译为字节码(.pyc) --> Python虚拟机 --> 执行编译好的字节码 --> Python虚拟机将字节码翻译成相对应的机器指令(机器码)。
Python程序运行时,先编译字节码并保存到内存中,当程序运行结束后,Python解释器将内存中的字节码对象写到.pyc文件中。
第二次再执行时,先从硬盘中寻找.pyc文件,如果找到,则直接载入,否则重复上面过程。
优点:不重复编译,提高执行效率
二、Python中的基础语法
2.1 Python中变量在内存中的存储
2.1.1 引用和对象
-
对象:当创建数据对象时,在内存中会保存对象的值,这个值就是对象自己
-
引用:对象保存在内存空间,外部想要使用对象的值,就需要使用引用来操作对象。内存中会保存对象引用的数量,当某个对象的引用为0时,对象就会被回收。
2.1.2 可变数据类型和不可变数据类型
-
可变数据类型
可变数据类型:允许变量的值进行改变,对于变量给值的过程中,只是改变了变量的值,而不会新建一个对象,所以可变数据类型的意思就是说对一个变量进行操作时,其值是可变的,值的变化并不会引起新建对象,即地址是不会变的,只是地址中的内容变化了或者地址得到了扩充。 -
不可变数据类型
对象本身的值不可变,如果改变了变量的值,相当于新建一个对象,而对于相同的值的对象,在内存中只有一个对象
可变数据对象:列表(list)、字典(dict)
不可变数据对象:整形(int)、浮点型(float)、字符串(str)、元组(tuple)
注:此处的可变和不可变,是指内存中的对象(value)是否可以改变,对于不可变类型的对象,在对对象进行操作的时候,必须要在内存中重新开辟一块新的区域。对于可变类型的对象,在对对象进行操作的时候,不会重新申请新的地址,而是在该 对象的地址后面继续申请即可。
2.1.3 引用传递和值传递(函数传值)
说明:可变对象为引用传递,不可变对象为值传递。
-
引用传递:传递列表或者字典时,如果改变引用的值,就修改了原始的对象
def check(a): # 因为列表是可变对象 print(a) print(id(a)) a.append([1, 2, 3]) return a a = [3, 4, 5] print(check(a)) print(id(a)) """ [3, 4, 5] 44909288 [3, 4, 5, [1, 2, 3]] 44909288 """ -
值传递:当传递不可变对象时,如果改变引用变量的值,只是创建了不同的对象,原始对象并没有改变
def check1(a): # 因为字符串是不可变对象,所有重新创建了地址 print(a) print(id(a)) a = "i am test" print(id(a)) return a a = "This is aa" print(check1(a)) print(id(a)) """ This is aa 58547040 58547120 i am test 58547040 """
2.1.4 深拷贝和浅拷贝
-
浅拷贝:语法:copy.copy(),浅拷贝是创建了一个和原对象一样的类型,但是其内容是对原对象元素的引用。
1.对于可变数据,不可变数据类型来说浅拷贝复制是内存引用
浅拷贝只拷贝第一层,深层次的数据改变都会影响其他.
a = [9, 8, [1, 2], 6] # 浅拷贝是拷贝父对象的引用,无论a,b那方都会改变地址 b = a.copy() b = a[:] # 也表示浅拷贝 print(a, b) a.append(2) b.append(3) print("第一层:", a, b) # 没有发生改变 a[2].append(3) b[2].append(4) print("第二层:", a, b) # 发生改变 """ [9, 8, [1, 2], 6] [9, 8, [1, 2], 6] [9, 8, [1, 2], 6, 2] [9, 8, [1, 2], 6, 3] [9, 8, [1, 2, 3, 4], 6, 2] [9, 8, [1, 2, 3, 4], 6, 3] """ -
深拷贝:语法:copy.deepcopy(),在内存中将所有的数据重新建立一份。
1.对于可变的数据类型来说,深拷贝也会将嵌套内容进行重新开辟
2.对于不可变数据类型来说,深拷贝同样复制的内存的引用
""" 深拷贝就是在内存中重新开辟一块空间,不管数据结构有多复杂,只要数据发生改变,就重新开辟一块内存空间把内容复制下来,直到最后一层,通俗点讲就是,深拷贝就是重新定义一个变量,于之前的没有半毛钱关系,所以更改里面的内容,原来的并不会发生改变。 """ import copy a = {1: [1, 2, 3]} # 深拷贝会重新开辟一个内存空间 c = copy.deepcopy(a) print(a, c) a[1].append(4) c[1].append(5) print(a, c) """ {1: [1, 2, 3]} {1: [1, 2, 3]} {1: [1, 2, 3, 4]} {1: [1, 2, 3, 5]} """
变量的本质:在内存中开辟一块空间,其中存储了指定类型的数据【实体(对象)存储在堆中,变量(引用)存储在栈空间中】
2.2 基本数据类型
Python中常用的数据类型有:整型(int)、浮点型(float)、字符串(str)、空值(None)、布尔值(True/False)、复数(complex)、类、函数 、列表(list)、元组(Tuple)、集合(set)、字典(Dictionary)
- **不可变数据(3 个):**Number(数字)、String(字符串)、Tuple(元组);
- **可变数据(3 个):**List(列表)、Dictionary(字典)、Set(集合)。
Python中常量的命名规则使用变量名全大写。(STATIC="A")
Python中的变量不需要声明,每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。(a = b = c = 1 ,a, b, c = 1, 2, "a")
2.2.1 Python数据类型转换
| 函数 | 描述 |
|---|---|
| chr(x) | 将一个整数转换为一个字符 |
| int(x) | 将x转换为一个整数 |
| ord(x) | 将一个字符转换成对应的acsii码 |
| ... | ... |
2.3 关键字、标识符和内置函数
2.3.1 关键字
关键字:在Python中被赋予了特殊含义的英文单词
Python中的关键字的查看使用keyword模块
import keyword
print(keyword.kwlist)
"""
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
"""
2.3.2 标识符的命名规则
标识符的命名规则:标识符的命名构成:数字、字母、下划线,但不能以数字开头,在标识符的命名中严格区分大小写
命名规范:
Python官方:所有单词全部小写,不同单词之间使用下划线分割,普遍是用驼峰命名法(小驼峰与大驼峰)
2.3.3 内置函数
import sysprint(dir(sys.modules))
内置函数表;
2.4 Python运算符
2.4.1 算术、赋值运算符
算术运算符:
** 求幂运算
// 整除运算或者成为地板除
% 取余运算
常用算数运算符的优先级:** -> * -> / // % -> + -
赋值运算符:
赋值运算符:=
复合运算符:+= -= *= /=
关系比较:== != >= <=
2.4.2 逻辑运算
运算顺序:() => not => and => or
| 运算符 | 逻辑表达式 | 描述 |
|---|---|---|
| and | x and y | 布尔"与" x,y都为True,返回True |
| or | x or y | 布尔"或" x,y一个为True,返回True |
| not | not x | 布尔"非" - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。 |
| 算符 | 描述 |
|---|---|
| & | 按位与运算符:参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0 |
| | | 按位或运算符:只要对应的二个二进位有一个为1时,结果位就为1。 |
| 按位异或运算符:当两对应的二进位相异时,结果为1 | |
| ~ | 按位取反运算符:对数据的每个二进制位取反,即把1变为0,把0变为1。~x 类似于 -x-1。(运算原理:计算补码,按位取反,转为原码,末尾加1) |
| << | 左移动运算符:运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。 |
| >> | 右移动运算符:把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数 |
0表示False 1表示True
2.4.3 成员及身份运算
成员运算符:in not in (在其中,不在其中)
身份运算符:is is not(是,或者不是)
2.5 Python中的语句
2.5.1 if语句
if语句在Python中的两种结构为:
# 1
if 条件表达式:
代码块
# 2
if 条件表达式:
代码块
elif 条件表达式:
代码块
# 3
if 条件表达式:
代码块
else:
代码块
2.5.2 for语句
while 条件表达式: 代码块
# 9*9乘法表
line = 0
while line < 10:
temp = 1
while temp <= line:
print(temp, "*", line, "=", temp * line, end=" ")
temp += 1
print(" ")
line += 1
"""
1 * 1 = 1
1 * 2 = 2 2 * 2 = 4
1 * 3 = 3 2 * 3 = 6 3 * 3 = 9
1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16
1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25
1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36
1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49
1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64
1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81
"""
break 直接跳出当前循环
continue 结束当前正在执行的循环,继续下一次循环
三、Python内置数据结构
Python中最基本的数据结构是序列(sequence),python包含6种内建的序列,包括列表、元组、字符串、Unicode字符串、buffer对象和xrange对象
3.1 字符串(str)
3.1.1 字符串的创建
将文本放在单引号、双引号和三引号之间
str1 = ' hello, fanison 'str1 = “ hello, fanison ”str1 = """hello word"""
3.1.2 字符串的转义
原生字符串:使用r
str1 = r"hello, fanison"
3.1.3 字符串的切片操作
顾头不顾尾
-
索引运算符s[i] 返回一个序列的元素i
-
切片运算符s[i:j] 返回一个在字符串中从第i个元素到j-1的元素
-
扩展切片运算符 s[i:j:step] 在字符串中以step的步长从第i个到j-1个元素之间的元素,step为正数表示正着取,i<i或者说s[i,-1]倒着取
len(s) s中的元素个数
min(s) s的最小值
max(s) s的最大值
a = [1, 2, 3, 4, 5, 6, 7, 8]
print(a[2:]) # 下标从0开始
print(a[2:5])
print(a[2:7:2])
print(a[2:-1])
print(a[::-1]) # 反转列表
print(a[-1::-1]) # 反转列表
"""
[3, 4, 5, 6, 7, 8]
[3, 4, 5]
[3, 5, 7]
[3, 4, 5, 6, 7]
[8, 7, 6, 5, 4, 3, 2, 1]
"""
3.1.4 字符串的相关方法
s.index(sub [,start [,end]]) 找到指定字符串sub首次出现的位置,找不到就报错
s.find(str,beg=0,end=len(string)) 找到字符串sub首次出现位置,与index不同是不报错而返回-1
s.upper() 将一个字符串转换为大写形式
s.lower() 将一个字符串转化为小写形式
s.join(t) 使用s作为分隔符连接序列t中的字符串 s.strip() 将s两边不显示的符号去掉之后返回(lstrip、rstrip)
s.split(t) 获取以字符t将s切割的列表
s.encode() 获取s的指定编码的bytes值
bytes.decode() 获取bytes的对应编码的字符串,在python2中使用decode函数
s.endswith(suffix,beg=0, end=len(string)) 检查中是否以suffix字符串结尾
x = "do you fuck me?"
print(x.index("y")) # 找不到就报错
print(x.find("z")) # 找不到就返回-1
print(x.upper())
print(x.lower())
print(x.join("-x"))
print(x.split(" "))
print(x.encode())
print(x.endswith("f")) # 判断结尾是不是以f
"""
3
-1
DO YOU FUCK ME?
do you fuck me?
-do you fuck me?x
['do', 'you', 'fuck', 'me?']
b'do you fuck me?'
False
"""
3.2 列表(list)
变量:相当于是一个窗口,每次只能存储一个数据
作用:相当于是一个窗口,可以同时存储多个数据
本质:一种有序的集体【有序:数据的存放顺序和底层存储的顺序是相同】
列表:允许存放重复元素,允许存放不同类型的数据
list1 = [45,23,2,54,54,6,"hello",4637,False]list2 = [1, 2, 3]
查
列表元素的访问:从0开始
- 下标,索引
- 切片
- 判断
- len:长度 max():最大值 min():最小值 index():下标
# 通过索引访问
print(list1[0])
print(list1[-1])
# print(list1[1000]) 列表下标越界
# 切片 格式:列表名[start:end:step],包头不包尾
print(list3[2:]) # 获取从开头到指定下标的元素。。。。
print(list3[2:6]) # 截取指定区间
print(list3[4:100]) # 特殊情况1:如果end超出了下标的范围,则默认获取从指定下标开始到结尾的元素 等价于print(list3[4:])
print(list3[1:6:2]) #1,3,5#特殊情况2:step在默认情况下为1
print(list3[-1:-6]) #当start和end都为负数,step为正数的时候,获取的结果为[]
#将列表倒序输出 当start,end和step都为负数的时候,表示倒序
print(list3[-1::-1])
print(list3[::-1])
print(list3[-1:-6:-2])
print(2 in list2) # 判断某个元素是否在列表中,true,false
print(2 not in list2)
# 3.1len(),获取列表长度
print(len(list1))
# 3.2max(),获取列表中的最大值
print(max(list1))
# 3.3min(),获取列表中的最小值
print(min(list1))
# 3.4index(),获取指定元素在原列表中第一次匹配到的索引
print(list1.index(11)) # 11在list1列表的索引
改
列表元素的替换/修改
- 通过下标
list1[0] = 99
print(list1)
print(list1 + list2) # 生成一个新的列表
print(list2 * 3) # 列表元素的重复
增
添加元素
- append:追加,在列表的末尾添加元素
- extend:扩展,在列表的末尾添加元素
- insert:在指定位置插入
l1 = [22, 33, 44, 55]
# 1.1append,追加,在列表的末尾添加元素 常用
l1.append(66) # 追加单个元素
# l1.append(77,88) #报错:TypeError: append() takes exactly one argument (2 given)
l1.append([77, 88]) # 追加多个元素,不能直接追加,通过列表的形式追加,形成了一个二维列表
# 1.2extend,扩展,在列表的末尾添加元素
l2 = [22, 33, 44, 55]
# l2.extend(66) #T报错:ypeError: 'int' object is not iterable
l2.extend([66]) # 追加单个元素,不能直接添加,参数一定是可迭代的
l2.extend([77, 88])
# 1.3insert,插入,在指定索引处插入一个元素,后面的元素向后顺延
# 列表名.insert(索引,被插入的元素)
l3 = [22, 33, 44, 55]
l3.insert(2, 66) # 插入单个元素
l3.insert(1, [77, 88]) # 插入多个元素:和append类似,将整个列表直接插入
"""
总结:
列表名.功能名(xx)
append可以直接添加单个元素,而extend不能
append在添加多个元素的时候,是以列表的形式添加,而extend只添加元素【打碎加入】
insert():在指定位置插入元素
""""""
删除
删除元素
- pop()
- remove()
- clear
# pop,弹出,移除并获取列表中指定索引处的元素 在栈中【列表的底层工作原理是栈】
list1 = [11, 22, 33, 44, 55]
# 1.pop() 注意:pop在默认情况下删除的是最后一个元素
result1 = list1.pop()
print(list1)
print(result1) # 删除的元素
result2 = list1.pop(2) # 删除下标为2的
print(list1)
# 2.remove() 移除,直接操作的是元素
list2 = [11, 22, 33, 44, 55, 33, 33, 55]
list2.remove(22)
print(list2)
# 注意:移除指定元素在列表中第一次匹配到的元素【从左往右】
list2.remove(33)
print(list2)
# 3.clea()r 清除,将指定列表变为空列表 使用场景:循环中,每次需要清空【重置】列表
list2.clear()
其它用法
- reverse 反转列表
- sort() 排序(内部进行排序,不生成新列表)
- sored() 排序(生成了一个新的列表)
- copy() 在列表中
- list2 = list1 # 不新开辟内存空间
- list2 = list1.copy() #开辟一个新的内存空间
- 转换list() tuple()
# 4.其他用法
# 4.1.reverse,反转
list1 = [35, 4, 5, 4, 654]
print(list1[-1::-1])
list1.reverse()
print(list1)
# 4.2.1sort,排序,默认为升序,在列表内部进行排序
# 列表名.sort()
list1.sort() # 升序
list1.sort(reverse=True) # 降序
# 4.2.2sorted,排序,默认为升序,生成了一个新的列表
# sorted(列表名)
newList = sorted(list1) # 升序
newList2 = sorted(list1, reverse=True) # 降序
# 4.3copy,拷贝
list1 = [23, 54, 56]
list2 = list1 # 没有重新开辟内存空间,
list2[1] = 100
print(list1)
print(list2)
print(id(list1) == id(list2))
# copy,深拷贝,堆空间层面上的拷贝【实体】
list1 = [23, 54, 56]
list2 = list1.copy() # 重新开辟内存空间,但是只有第一层
list2[1] = 100
print(list1)
print(list2)
print(id(list1) == id(list2))
# 4.4转换
# list(),tuple()
# 二维列表:遍历
l1 = [[11, 22, 33, 44], [44, 55, 66]]
print(l1[0]) # l1[0] = [11, 22, 33, 44]
print(l1[0][2])
for i in l1:
for j in i:
print(j)
面试题
list1 = [54, 6, 57, 5, 57, 7, 6, 7,22, 57, 57, 57, 436]
# 模拟index
for i in range(len(list1)):
if list1[i] == 22:
print(i)
# count(),统计个数,统计指定元素在列表中出现的次数
print(list1.count(22)) # 统计22在列表中出现的次数
# 删除所有的57
for i in list1[:]: # 这里一定要注意
if i == 57:
list1.remove(57)
print(list1)
# 模块copy ,copy(),deeepcopy()【面试题】
# 内容角度上的拷贝,copy只拷贝最外层,deepcopy可以拷贝内层的内容【二维列表中】
a = [1, 2, 3]
b = [4, 5, 6]
c = [a, b]
d = copy.copy(c) # 浅拷贝,但用的还是父的引用
print(id(d) == id(c)) # False
e = copy.deepcopy(c) # 深拷贝
print(id(e) == id(c)) # False
a.append(4)
print(c) # [[1, 2, 3, 4], [4, 5, 6]]
print(d) # [[1, 2, 3, 4], [4, 5, 6]] # 浅拷贝只第一层,这里是第二层
print(e) # [[1, 2, 3], [4, 5, 6]]
"""
d:[[1, 2, 3], [4, 5, 6]]
[list1,list2]---->list1:[1,2,3] list2:[4,5,6]
""""
3.3 元组(tuple)
和列表类似,本质是一种有序的集合(但是元组没有增删改)
元组和列表的不同之处:
- 定义不同:列表[] 元组()
- 是否能够修改:列表可以进行增加或者删除元素的操作,但是,元组一旦被定义之后,其中的元素将不能进行任何的更改
- 允许存储重复元素,允许存储不同类型的数据
t1 = (1, 1, 2, 3, 4, 5, "Hello", False)
- 当元组中只有一个元素的时候,会识别为一个普通变量,我们要在后面加一个,元组名=(元素,)
t = (10,)
- 特殊情况:如果在元组中的元素是列表,则列表中的元素依旧可以修改
依据:元组和列表中存储都是变量的地址,元素不允许修改,只需要保证未发生改变即可其中的地址
t5 = (23, 5, 3, 5, [235, 345, 5, 45, 4])
print(t5)
print(t5[4]) # [235,345,5,45,4]
t5[4][1] = 100
print(t5)
- 其它用法
# 列表和元组的遍历
# 直接获取元素
for element in t5:
print(element)
# 获取索引
for i in range(len(t5)):
print(i, t5[i])
# 同时遍历索引和元素
# 需要将元组或者列表转换为枚举类型
# 注意:下面的i并不是元组或者列表的索引,而是枚举中的编号
for i, element in enumerate(t5):
print(i, element)
查
元组元素的访问
元组名[索引]
print(t1[5])
3.4 字典(dict)
字典:也是一种存储数据的方式,但是字典是无序的(其它方法和列表类似)。
类似于list或者tuple,但是,字典采用键-值对的方式存储数据
作用:具有极快的查找速度
键【key】相当于list或者tuple中的索引
key的特点:
- 字典的key是唯一的【key不允许重复】
- key必须为不可变的数据
list是可变的,不能用来当做key
tuple,数据型,字符串,布尔值都是不可变的,可以被充当key
dict1 = {"zhangsan": 10, "lisi": 13, "zhaoliu": 50}
查
- 字典名["key"] 如果key不存在,则报错
- 字典名.get("key") 如果key不存在,返回None,不会报错
# 1.访问键值对
print(dict1["lisi"])
# 访问一个不存在的key,则报错
# print(dict1["abc"]) #KeyError: 'abc'
# 2.get()
result1 = dict1.get("zhaoliu")
print(result1)
# 如果key不存在,则不会报错,返回None,一般用于判断
result2 = dict1.get("def")
print(result2)
改
- 字典名[key] = value
dict1["zhaoliu"] = 100result0 = dict1["zhaoliu"]print(result0)
增
- 字典名[key] = value
# 注意:如果key存在,则表示修改value的值;如果key不存在,则表示在字典中添加一对新的键值对dict1["abc"] = 20print(dict1)
删除
- dict1.pop("key") 通过key删除
- del dict1["key"] 通过key删除
- del dict1 删除整个字典
- dict1.clear() 清空字典里面的元素
注意:删除指定的key,对应的value也会随着删除
dict1.pop("lisi")dict1.clear() # 清空字典del dict1['zhaoliu'] #删除key为zhaoliu的del dict1 # 删除字典print(dict1)
字典的遍历
- 直接遍历就是遍历的key
- dict.values() 值
- enumerate(dict) 编号和key
- dict.items() 同时遍历key和value
# 3.字典的遍历
# 3.1直接遍历key 掌握
for key in dict1:
print(key, dict1[key])
for key in dict1.keys():
print(key, dict1[key])
# 3.2直接遍历value
for value in dict1.values():
print(value)
# 3.3,遍历的是键值对的编号和key
for i, element in enumerate(dict1):
print(i, element)
# 3.4同时遍历key和value 掌握
for key, value in dict1.items():
print(key, value)
面试题
"""
【面试题:dict和list之间的区别】
1.dict查找和插入的速度不会因为key-value的增多而变慢,
而list在每次查找的时候都是从头到尾进行遍历 ,当数据量大的时候,list速度肯定会变慢
2.dict需要占用大量的内存空间,内存浪费多,
而list只相当于存储了字典中的key或者value,并且list数据是紧密排列的
# 1.逐一显示列表l1 = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]中索引为奇数的元素
l1 = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
for i, index in enumerate(l1):
if i % 2 != 0:
print(i, index)
# 2.将属于列表l1 = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],但不属于列表l2 = ["Sun","Mon","Thu","Fri","Sat"]的所有元素定义为一个新列表l3
l1 = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
l2 = ["Sun", "Mon", "Thu", "Fri", "Sat"]
l3 = []
for i in l1:
if i not in l2: # 判断i不在l2中就添加到l3中
l3.append(i)
print(l3)
s = set(l1)
s1 = set(l2)
print(s ^ s1)
# 3.已知列表namelist=['stu1','stu2','stu3','stu4','stu5','stu6','stu7'],删除列表removelist=['stu3', 'stu7', 'stu9'];请将属于removelist列表中的每个元素从namelist中移除(属于removelist,但不属于namelist的忽略即可);
namelist = ['stu1', 'stu2', 'stu3', 'stu4', 'stu5', 'stu6', 'stu7']
removelist = ['stu3', 'stu7', 'stu9']
for i in removelist:
if i in namelist: # 在里面的话,就用remove()删除
namelist.remove(i)
print(namelist)
# 4.有一个字符串是一句英文,统计每个单词出现的次数,生成一个字典,单词作为key,次数作为value生成一个字典dict1
str1 = "today is a good day today is a bad day today is a nice day"
str2 = str1.split(" ") # 以空格分割,成列表
dict1 = {}
for i in str2:
c = dict1.get(i) # key:单词 value:次数 用.get()获取不到的话就为None(不会报错)
if c == None:
dict1[i] = 1 # 添加到字典
else:
dict1[i] += 1
print(dict1)
# 5.已知列表list1 = [0,1,2,3,4,5,6],list2 = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],以list1中的元素作为key,list2中的元素作为value生成一个字典dict2
list1 = [0, 1, 2, 3, 4, 5, 6]
list2 = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
# 定义一个变量,作为list1和list2的索引
index = 0
dict2 = {}
if len(list1) == len(list2): # 如果两个列表长度相等
while index < len(list1):
dict2[list1[index]] = list2[index]
index += 1
elif len(list1) > len(list2): # 如果两个列表长度不相等
while index < len(list2):
dict2[list1[index]] = list2[index]
index += 1
else:
while index < len(list1):
dict2[list1[index]] = list2[index]
index += 1
print(dict2)
x = dict(zip(list2,list1))
3.5 集合(set)
集合:不允许重复元素,而且进行交集以及并集的运算
表示:{} 和dict之间的关系:set中只是存储了key
本质:无序且无重复元素的集合
增
- add() 添加
- 单个元素,如果添加的元素存在,则添加失败,不报错
- 多个元素,在set中,使用add添加,则只能添加元组,不能添加list和dict
- update() 更新
- 不能直接添加,参数可以是列表,元组,字典等(添加字典的话,只会把key添加进去)
# add(),添加,
set1 = {11, 22, 33, 44, 55}
# 单个元素
set1.add(66)
set1.add(55) # 如果添加的元素存在,则添加失败,不报错
# 多个元素
# s1.add([77,88]) #TypeError: unhashable type: 'list'
s1.add((77, 88))
# s1.add({1:"a"})
# 结论:在set中,使用add添加,则只能添加元组,不能添加list和dict
# update(),更新,update的参数只能是可迭代对象【打碎加入】
set2 = {11, 22, 33, 44, 55}
# set2.update(66) #报错:TypeError: 'int' object is not iterable
set2.update([66]) # []
set2.update((77, 88)) # ()
set2.update({"12": 14, "13": 13}) # 只会把key添加进去
set2.update("hgjhgaa") # 把字符个个的添加进去(不会把重复的添加进去)
删除
- remove() 如果删除的元素不存在,则会发生错误.
- discard() 如果删除的元素不存在,不会发生错误.
- pop() 随机删除集合中的一个元素
- clear() 清空集合
set2.remove(77)set2.discard(33)set2.pop()set2.clear()
交集和并集
# 交集和并集
s1 = {3, 54, 4, 5, 7}
s2 = {3, 54, 4, 8, 90}
# 交集:&【按位与】
print(s1 & s2)
# 并集:|【按位或】
print(s1 | s2)
rint(s1 ^ s2)
面试题
# 掌握:去除列表中的重复元素
s2 = set([3, 46, 5, 65, 7, 65, 7]) # []
s3 = set((2, 43, 54, 5, 4, 5)) # ()
s4 = set({10: "a", 20: "b"}) # 字典的话,只有ke
3.6 list,tuple,dict,set的区别和联系
- 定义方式: list[] tuple() dict{} set{}
- 有序: list tuple
- 无序: dict set
- 是否允许出现重复元素: list,tuple:允许 dict:key不允许,value可以, set不允许
- 都属于可迭代对象
- set相当于存储了字典中的key
- 可以相互转化
四、Python函数总结
4.1、函数的基本用法
4.1.1 概念
函数是对程序逻辑进行结构化或是过程化的一种编程方法,其是组织好的,可重复使用,用来实现单一,或者相同功能的代码段.
函数提高了应用点的模块性和代码的重复利用率
本质:函数是对功能的封闭
形式参数:简称形参,本质是一个没有值的变量.
实际参数:简称实参,本质是常量,变量或者表达式
传参:实参给形参赋值的过程,而实参的类型取决于形参的需要
4.1.2 函数定义
def 函数名(参数1,参数2,参数3.。。。):
函数体
return 返回值
注:
a、函数命名遵循标识符规则,做到见名知意,小驼峰命名法、
b、参数1,参数2,参数3.....形式参数,不同的参数之间使用逗号隔开,参数的数量没有限制,依据具体的需求决定参数的数量
c、函数体:被封装的功能
d、return:结束函数,将返回值返回给调用者,也可单独使用
e、返回值可为常量、变量、表达式
4.1.3 返回值
返回值:表示一个函数执行完毕之后得到的结果
注:对于return语句不带参数,则返回一个None
4.1.4 调用
函数的调用:实质就是函数入栈出栈的过程
即:函数的入栈:函数被调用;函数的出栈:函数被调用完毕
注:在函数调用的过程要注意避免出现死循环
4.1.5 变量的作用域
变量的作用域:指变量可以被访问的范围
作用域的划分:
L:(local)局部作用域,最内层,包含局部变量,比如一个函数/方法内部。
E:(Enclosing)函数作用域(闭包),nonlocal(必须使用在闭包中)
G:(Global)全局作用域,当前脚本的最外层,比如当前模块的全局变量。
B:(Built-in)内置作用域,包含了内建的变量/关键字等。,最后被搜索
变量的查找规则(变量重名):
python中变量的作用域由大到小,依次为内建(built_in B) >全局(glbal G)> 函数的闭包外(enclosing E)> 局部(local L)
注:在变量重名情况下在函数内部访问变量时使用就近原则。
如果将全局变量的名字声明在一个函数体内的时候,全局变量的名字能被局部变量给覆盖掉,此时我们就需要使用global或者nonlocal来声明变量了。
全局变量和局部变量
total = 0 # 这是一个全局变量
# 可写函数说明
def sum(arg1, arg2):
# 返回2个参数的和."
total = arg1 + arg2 # total在这里是局部变量.
print("函数内是局部变量 : ", total)
return total
# 调用sum函数
sum(10, 20)
print("函数外是全局变量 : ", total)
global和nonlocal关键字
当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了
global
num = 1
def fun1():
global num # 需要使用 global 关键字声明
print(num) # 1
num = 123
print(num) # 123
fun1()
print(num) # 123,因为函数内部使用了global修改了变量
nonlocal (必需使用在闭包中)
如果要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量则需要nonlocal关键字了.(闭包)
def outer():
num = 10
def inner():
nonlocal num # nonlocal关键字声明
num = 100
print(num)
inner()
print(num) # 100 因为用nonlocal修改了局部变量
outer()
4.1.6 参数
- 参数的传递
参数的传递有值传递和引用传递
- 值传递:传递不可变类型的数据,例:num,str,tuple等,在值传递时,形参的改变并不会影响实参
def check(s):
print(s)
print(id(s))
s = "i am test"
print(id(s))
return s
s = "This is a test"
print(check(s))
print(s)
-
引用传递:传递可变类型的数据,例:list,dict,set等;形参的改变会影响实参的使用
# 引用传递:传递列表或者字典时,如果改变引用的值,就修改了原始的对象 def check(l): print(l) print(id(l)) l.append([1, 23, 2, 3, 4]) return ll = [1, 2, 3, 4] print(check(l)) print(id(l)) '''[1, 2, 3, 4]53235528[1, 2, 3, 4, [1, 23, 2, 3, 4]]53235528'''
在函数中参数传递的是对象的引用
-
参数的类型
-
必须参数:必备参数须以正确的顺序传入函数,调用时的数量必须和声明时的一样.
def printme(str): "打印任何传入的字符串" print(str) return# 调用printme函数printme("aa") # 必须参数 -
关键字参数:允许实参的顺序和形参的顺序不一致,因为Python解释器会根据关键字参数的名称自动的匹配
-
def show2(name, age):
print("name:%s age:%d" % (name, age))
show2(name="jack", age=47)
# 注:关键字参数使用在实参列表中,不要求顺序保持一致
show2(age=36, name="fadj")
# 注:在实参列表中,可以不全部设置为关键字参数
show2("zhangsan", age=15)
# 注:关键字参数只能出现在实参列表的后面
def show3(a, b, c):
print(a, b, c)
show3(1, 4, 4)
show3(a=3, c=5, b=5)
show3(45, b=9, c=18)
# show3(a=45,9,18) #SyntaxError: positional argument follows keyword argumen
- 默认参数:调用函数的时候,如果没有传递参数,则使用默认值[default]
# 注意1:默认参数体现在形参列表中
def func3(name="abc", age=18):
print("name:%s age:%d" % (name, age))
# 注意2:使用了默认参数,则可以选择不传参,使用的默认值,如果传参,则相当于给形参重新赋值
func3()
func3("jack", 19)
-
不定长参数:可以处理比声明时更多的参数 *args **kwargs
- *args,是以元组的形式
- **wkargs是以字典的形式
def text(*args, **kwargs): print(args) # 都是接收一个或多个参数 print(kwargs) text(10, 11, {"a": 1, "b": 2}, x=12, y=13) """(10, 11, {'a': 1, 'b': 2}){'x': 12, 'y': 13}"""
4.1.7 匿名函数
lambda表达式:python中的匿名函数主要用来处理一些简单逻辑表达式的封装,使用lambda关键字
优点:不占用内存,提高代码的运行速度
一般格式为:
var = lambda args:表达式
例如:
# 可写函数说明
sum = lambda arg1, arg2: arg1 + arg2
# 调用sum函数
print "相加后的值为 : ", sum( 10, 20 )
print "相加后的值为 : ", sum( 20, 20 ))
-
普通匿名函数
abc = lambda a,b:a+bx = abc(5,5)print(x) -
匿名函数作为参数
def func(x, y, func): # 这里的func其实就是匿名函数的表达式 print(x, y) print(func) s = func(x, y) print(s)
func(1, 2, lambda a, b: a + b))
3. 匿名函数与内置函数的结合使用
```python
list2 = [{'a': 10, 'b': 2}, {'a': 50, 'b': 3}, {'a': 1, 'b': 1}, {'a': 89, 'b': 1}]
# max()
m = max(list2, key=lambda x: x['a']) # 找列表嵌套字典,a的最大值
print(m)
# sorted() 升序
print(sorted(list2, key=lambda x: x["a"], reverse=True))
# filter() 条件 查找a大于10的
result = filter(lambda x: x["a"] > 10, list2)
print(list(result))
# reduce (有时必需导入包) reduce(function, sequence[, initial]) -> value
from functools import reduce
tuple1 = (3, 4, 6, 1, 2, 3)
result = reduce(lambda x, y: x * y, tuple1, 50) # 3*4*6*1*2*3*50对元组中的每一数依次相乘
result2 = reduce(lambda x, y: x + y, tuple1, 50) # 3+4+6+1+2+3+50依次相加在加一个50
result3 = reduce(lambda x, y: x - y, tuple1, 10)
print(result, result2, result3)
# map 源码 map(func, *iterables) --> map object func:函数 *iterables迭代器
# 对列表中的奇数进行加100操作
result = map(lambda x: x if x % 2 == 0 else x + 100, list1)
# 遍历list1的元素,如果元素除以2等于0的话就返回本身,否则就加100,
print(list(result))
4.2、函数进阶
4.2.1 概念[特殊用法]
-
变量可以指向函数
x = abs(-35) print(x) # 35 # 一个普通的变量可以指向一个函数,该变量就可以被当做函数调用 f = abs # abc返回参数的绝对值 print(f) print(f(-100)) def check(): print("check") check() f1 = check # 这里的f1就被当成函数调用 !!!赋值的时候不要加括号 f1() -
函数也可以作为变量名
函数就没有了原来的功能
# 本质:函数名就是一个指向函数的变量 print(abs(-28)) # abs = "hello" # print(abs(-7)) #TypeError: 'str' object is not callable -
函数作为参数使用
把函数作为参数传入函数中不需要加括号
# 调用形参中的函数,必须和原函数保持一致【注意是否需要传递参数】 def test(a, b, fun): return fun(a) + fun(b) # abs(43) + abs(-27) print(test(43, -27, abs)) # fun = abs def test1(s1, s2, func): return func(s1) + func(s2) print(test1("hello", "word", len)) # func=len 函数做为参数都不要打括号
4.2.2 闭包
在函数内部定义了另一个函数,即存在外部函数和内部函数
条件:
1.外部函数中定义了内部函数2.外部函数一定要有返回值,返回的值是:内部函数名3.内部函数引用了外部函数的变量
def func(a, b): # 101 99
c = 100
def inner_func(): # 1.外部函数中定义了内部函数
D = 20
print("闭包", a + b + c + D) # 3.内部函数引用了外部函数的变量
return inner_func # 2.外部函数一定要有返回值,返回的值是:内部函数名(不要加括号)
x = func(101, 99) # 接收的是内部函数的地址
x() # 调用内部函数
闭包的优点:在外部函数中定义的变量,在内部函数可以直接访问
闭包有什么缺点呢?
闭包的缺点1,作用域没有那么直观
闭包的缺点2,因为变量不会被垃圾回收所以有一定的内存占用问
闭包的作用:
-
可以使用同级的作用域
-
读取其他元素的内部变量
-
延长作用域
闭包总结:
-
闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成。
-
由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有用时释放,消耗内存
-
闭包的好处,使代码变得简洁,便于阅读代码。
-
闭包是理解装饰器的基础
4.2.3 装饰器
装饰器【Decorator】:使其他函数在不需要做任何代码的变动下,为函数添加功能,装饰器的返回值也是一个函数。
本质:装饰器的本质就是一个闭包,其作用是将一个函数作为参数,返回另一个函数
-
装饰器函数运行在函数定义的时候
-
装饰器需要返回一个可执行的对象
-
装饰器返回的可执行对象要兼容函数f的参数
装饰器条件:
1.外部函数中定义了内部函数
2.外部函数一定要有返回值,返回的值是:内部函数名
3.内部函数引用了外部函数的变量
4.函数作为外层函数参数
使用装饰器:
@装饰器名字
def 函数名():
pass
-
简单的装饰器
在闭包的前提下引用函数作为外层函数的参数
import time # 装饰器带参数 def zhuang1(func): # 接收f1函数作为参数 def wrapper(*args, **kwargs): # 这里就要加*args,**kwargs 1.外部函数中定义了内部函数 print("正在校验中.....") time.sleep(2) print("校验完毕.....") func(*args, **kwargs) # 调用原函数f1 4.函数作为外层函数参数 return wrapper # 把装饰器在返回给f1 2.外部函数一定要有返回值,返回的值是:内部函数名 @zhuang1 def f1(): print("我是户主one------one") f1() -
带有参数的函数
import time
# 装饰器带参数
def zhuang1(func): # 接收f1
def wrapper(*args, **kwargs): # 这里就要加*args,**kwargs
print("正在校验中.....")
time.sleep(2)
print("校验完毕.....")
# 调用原函数
func(*args, **kwargs)
return wrapper # 把装饰器在返回给f1
sum = 10000
@zhuang1
def f1(sum):
print("我是户主one------one", sum)
f1(sum)
@zhuang1
def f2(sum, name="abc"):
print("我是户主{}-----two".format(name), sum)
f2(sum)
@zhuang1
def f2(sum, name="aaaaaa"):
print("我是户主{}-----two".format(name), sum)
f2(sum, name="adfafasdasd") # 覆盖了name的值
-
装饰的函数有返回值
一定要返回原函数
# 如果装饰的函数有返回值需要在装饰器的内部函数中用return返回 装饰函数的运行结果
def say(func):
def inner():
print("-----2-----")
return func() # 这里一定要返回原函数
return inner
@say
def show():
return "-----1-----"
print(show())
- 使用多个装饰器装饰同一个函数
'''
如果装饰器是多层的,谁距离函数最近就优先使用哪个装饰器
'''
def zhuangfeng1(func): # 定义装饰器1
print("-----one---start")
def warper():
print("刷漆..")
return func() # 返回原函数
print("end")
return warper
def zhuangfeng2(func): # 定义装饰器2
print("-----two---start")
def warper():
print("打麻椒..")
return func() # 返回原函数
print("end")
return warper
# 使用装饰器
@zhuangfeng2
@zhuangfeng1
def fz1():
return "户主一"
print(fz1())
"""
-----one---start
end
-----two---start
end
打麻椒..
刷漆..
户主一
"""
#总结:多个装饰器作用于同一个函数的时候,从上往下依次执行,但是,原函数只被调用一次
-
装饰器带有参数
有三层
'''
装饰器带参数
带参数的装饰器是三层的
第一层: 负责接收装饰器的参数
第二层: 负责接收函数
第三层: 去接收函数里面的参数
'''
def outer(a): # 第一层: 负责接收装饰器的参数
def decorate(func): # 第二层: 负责接收函数
def wraper(*args, **kwargs): # 第三层: 去买个接收函数的参数
func(*args, **kwargs)
print("--->铺地砖{}块".format(a))
return wraper
return decorate
@outer(100) # 装饰器带有参数
def house(time):
print("我{}日期拿到了毛坯房".format(time))
house("2020-9-9")
4.2.4 偏函数
通过设定默认参数,可以降低调用的难度,偏函数也可以起到这样的作用
概念:对函数的参数做一些控制的函数
注意:偏函数一般不需要自己定义,直接使用[functools模块其中提供了偏函数的使用]
import functools
def int2(x, base=2):
return int(x, base)
print(int2("1011"))
print(int2("1010", 8))
# 系统的functools模块中提供了偏函数的实现
# 参数:已经存在的函数名 默认参数
int3 = functools.partial(int, base=2)
print(int3("1110"))
print(int3("1110", base=10))
# 思想:根据一个已经存在的函数,通过修改该函数参数的默认值,生成一个新的函数,被称为偏函数
4.3、高阶函数
4.3.1 filter() 过滤
filter(function,iterable) :function:判断的条件 ,iterable可迭代对象
- 必须要返回True和False,因为filter只判断True和False看保留与否
"""
filter(function,iterable) 通过一定的条件过滤可迭代对象中的元素
工作原理:把传入的函数依次作用于可迭代对象的每一个元素,根据返回的布尔值【True或者False】决定是否保留元素
如果返回True,则表示需要保留该元素;如果返回False,则表示需要过滤掉该元素
"""
# 将列表中的偶数筛选出来
list1 = [1, 2, 3, 4, 5, 6, 7, 8]
def func(num):
# 保留偶数元素
if num % 2 == 0:
return True
# 过滤奇数元素
return False
# 依次遍历list1列表中的值,并放到func函数中判断,如果返回的为True就保留下来,并放入列表。为False就舍弃
newList4 = list(filter(func, list1))
print(newList4)
# 将爱好为"无"的数据剔除掉
data = [['姓名', '爱好', '年龄'], ['tom', '无', 10], ['jack', '唱歌', 28]]
def func2(s):
if s == "无":
return False
return True
for line in data:
result = list(filter(func2, line))
print(result)
4.3.2 map() 映射
map(function,Iterable); function对象每个迭代对象进行处理,iterable:可迭代对象
- function对象中一定要有返回值
"""
map(function,Iterable) 会根据的函数对指定的序列做出映射
function:函数
iterable:可迭代对象,序列
工作原理:函数会以序列中的每一个元素作为参数,返回包含函数的功能的新列表
功能:将传入的函数依次作用于序列中的每一个元素,并把结果作为新的iterable返回
"""
"""
传给map的函数的要求:
a.参数只能有一个【默认将可迭代对象的一个元素传递给该函数】
b.该函"
# 计算列表中各个元素的平方
list1 = [1, 2, 3, 4, 5]
# 方式一:
# map[def定义的函数]
def func(x):
return x ** 2 # 必须要有返回值
result = map(func, list1) # 将列表list1中的元素每个元素迭代,经过函数func的处理返回值
print(result) # map对象 <map object at 0x02C770E8
newList4 = list(result)
print(newList4)
# 方式二:map[匿名函数]
newList5 = list(map(lambda x: x ** 2, list1))
print(newList5)
4.3.3 reduce() 迭代[累积]
reduce(function,iterable) function:需要进行的处理 iterable可迭代对象
- function对象中一定要有返回值
- 函数里面必须要有两个参数
"""
导入模块:functools
reduce(function,iterable):函数会对序列中的元素进行累积
功能:用传给reduce的函数先序列中的第1,2个元素进行操作,
用得到的结果和第3个元素进行操作,用得到的结果和第4个元素进行操作。。。。
举例:
f,[a,b,c,d]
reduce(f,[a,b,c,d])
工作原理:f(f(f(a,b),c),d),类似于递归 其实就是[1,2,3,4] (((1+2)+3)+4)
"""
"""
函数的注意事项:
a.必须有两个参数
b.必须设置返回值【报错:TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'】
"""
from functools import reduce
# 计算一个整数列表中元素的和
# 一:reduce【def定义函数】
def mySum(x, y):
return x + y # 函数必须设置返回值
result0 = reduce(mySum, list1)
print(result0)
"""
mySum(1,2)---->3
mySum(3,3)---->6
mySum(6,4)---->10
mySum(10,5)---->15
"""
# 二:reduce[匿名函数]
result1 = reduce(lambda x, y: x + y, list1)
print(result1)
面试题
from functools import reduce
# 将[1,3,5,7,9]转换为整数13579
list1 = [1, 3, 5, 7, 9]
def func(num, y): # 函数里面必须要有两个参数
return (num * 10) + y # 必须要有返回值
new = reduce(func, list1)
print(new)
from functools import reduce
# 自定义一个函数,实现str转换为int的函数 int(xx)
def charToNum(s):
# 自定义字符串和整型之间的映射关系
digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
return digits[s] # 返回字典对应的key
def func(num, y): # 函数里面必须要有两个参数
return (num * 10) + y
# 需求:“24356”------》24356
# a.产生映射关系
r0 = list(map(charToNum, "243567890")) # 把返回的值添加到列表中
print(r0)
# b.使用reduce累积
r1 = reduce(func, r0)
print(r1)
print(type(r1))
五、Python的面向对象
5.1 面向对象的基本用法
5.1.1 概念
面向过程:具体化,流程化,解决一个问题,需要一步一步的分析,一步一步的实现.
面向对象:模型化,将现实中的事物抽象为程序中的属性或者功能,并且自己无需管理模型中方法实现的过程,只需要会调用.
面向对象的底层其实还是面向过程
面向对象的三大特征:1.封装性 2.继承性 3.多态性
优缺点:
面向过程:
- 优点:性能比面向对象好,因为类调用时需要实例化,内存开销较大
- 缺点:不易维护,不易复用,扩展
面向对象:
- 优点:易维护,易复用,易扩展
- 缺点:内存开销大,性能比面向过程差
类:类是对象的抽象,将现实生活中的具体的事物中的属性或者行为抽象出来,作为程序中的方法或者属性,类是抽象的
对象:对象是类的实例,通过类创建现实生活中的一个具体的实物
5.1.2 定义一个类
一个类是由属性和方法组成的
1. 属性: 普通属性,私有属性__
#定义类和属性
class Student:
name = "李小小" #属性
age = 20
__phone = 123 # 私有属性
You = Student()
print(You.name)
print(You.age)
You.age = 18
print(You.age)
You.set = "女" #自己后面创建的
print(You.set)
2. 方法: 普通方法 类方法 静态方法 魔术方法
-
普通方法
定义:第一个参数必须是实例对象,该参数名一般约定为“self”,通过它来传递实例的属性和方法(也可以传类的属性和方法);
调用:只能由实例对象调用。
格式:
def 函数名(): pass
class Phone:
brand = "小米" # 属性
price = 1999
type = "mate 90"
# Phone类里面普通方法:call
def call(self):
print("self---->", self) # 指的当前类对象
print("我的手机是:{},{},{}".format(self.brand, self.price, self.type)) # 通过self来访问类中的属性
print("正在访问通讯录:")
for i in self.addbook: # 访问外部的属性
print(i)
print("正在打电话")
print("留言:", self.note) # 访问外部的属性
p1 = Phone()
p1.note = "我是phone1的note" # 添加的属性
p1.addbook = [{1001: "张三"}, {1002: "李四"}]
p1.call()
-
类方法
定义:使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法);
调用:实例对象和类对象都可以调用。
"""
类方法
特点:
1.定义需要依赖装饰器@classmethod
2.类方法中参数不是一个对象,而是类 cls
3.类方法中只可以使用类属性
4.类方法中可以使用类中的普通方法,不过要先创建实例调用 cls().show()
类方法的作用:
因为只能访问类属性和类方法,所以可以在对象创建之前,如果需要完成一些动作(功能)
修改传参
def show1(cls, b="abc"): # 方法一,在类方法中定义
cls("类方法-->").eat() # 方法二,修改普通方法的传参
可用类方法修改私有属性:
cls.__age = 200 # 用类方法修改私有属性
"""
class Dog:
a = "abc" # 类属性
def __init__(self, name): # 初始化方法
self.name = name
def run(self):
print("{}在跑".format(self.name))
def eat(self): # 普通方法的参数为self
print("{}在吃东西".format(self.name))
self.run() # 调用类中的普通方法
@classmethod
def show(cls, a="dfg"): # 类方法,需要定义@classmethod,类方法的参数为cls
print("我是类方法{}".format(a))
print(cls) # <class '__main__.Dog'>
@classmethod # 只能访问类属性和类方法 调用类方法是用cls.类方法名()
def show1(cls, b="abc"):
print("我是类方法2{}".format(b))
cls.show() # 在类方法中调用类方法
cls("类方法-->").eat() # 修改普通方法的传参
c = Dog("小花")
c.run() # 普通方法
c.eat()
print(c.a) # 获取类中的属性
c.show1()
"""
小花在跑
小花在吃东西
小花在跑
abc
我是类方法2abc
我是类方法dfg
<class '__main__.Dog'>
类方法--》在吃东西
类方法--》在跑
"""```
```python
class Person:
__age = 18
def show(self):
print("--->", Person.__age)
@classmethod
def update_age(cls):
cls.__age = 200 # 用类方法修改私有属性
print("我是修改的类方法")
@classmethod
def show_age(cls):
print("我是修改后的类方法", cls.__age)
cls().show()
a = Person()
a.show()
a.update_age()
a.show_age()
"""
---> 18
我是修改的类方法
我是修改后的类方法 200
---> 200
"""
- 静态方法
静态方法:很类似于类方法
1.需要装饰器@staticmethod
2.静态方法是需要传递参数(cls,self)
3.也只能访问类的属性和方法,对象的是无法访问的
4.加载时机同类方法
静态方法可以调用没有self的普通方法,可以调用类方法
class Person:
__age = 18
def show(self):
print("--->", Person.__age)
def show1():
print("123456676767867")
@classmethod
def update_age(cls):
cls.__age = 200 # 用类方法修改私有属性
print("我是修改的类方法")
@classmethod
def show_age(cls):
cls.update_age()
print("我是修改后的类方法", cls.__age)
@staticmethod
def test():
print("我是静态方法")
Person.show_age() # 静态方法可以调用类方法
# Person.show() #静态方法不能调用里面有self的普通方法
Person.show1() # 里面没有self的就可以调用
s = Person.__age
print("我是调用的类属性", s)
a = Person()
a.test()
"""
我是静态方法
我是修改的类方法
我是修改后的类方法 200
123456676767867
我是调用的类属性 200
"""
-
魔术方法
""" 魔术方法 魔术方法就是一个类/对象中的方法,和普通方法唯一的不同是,普通方法需要调用!而魔术方法是在特定时刻自动触发 1.__init__: 初始化魔术方法 触发时机:初始化对象时触发(不是实例化,但是和实例化在一个操作中) 2.__new__: 实例化的魔术方法 触发时机:在实例化对时触发,不用调用先执行,在init之前执行 3.__call__:对象调用方法 触发时机:将对象当成函数使用的时候,会默认调用此函数中内容 4.__del__ : del p1 删除地址的引用,当一块空间没有了任何引用,默认执行del(其实就是当什么语句函数执行完后就执行) 5.__str__: 触发时机:打印对象名, 自动触发去调用 __str__里面的内容 注意:一定要在__str__方法中添加return,return后面的内容就是打印对象看到的内容 """
class Person:
def __init__(self, name): # 初始化对象
print("---->init", self)
self.name = name
def __new__(cls, *args, **kwargs): # 执行函数的时候,先执行new,在init()之前
# new不用调用,先执行
print("--->new")
nc_new = object.__new__(cls)
print("我是new魔法方法的内存地址", nc_new)
return nc_new
def __call__(self, name): # 将对象当成函数使用的时候,会默认调用此函数中的内容
print("把对象当前函数用 --->call")
print("执行对象得到参数是:", name)
p = Person("aa") #
p("x") # 将对象当成函数使用的时候,会默认调用此函数中的内容
'''
del
1.对象赋值
p = Person1()
p1 = p
说明:p和p1共同指向同一个地址
2.删除地址的引用
del p1 删除p1对Person1的地址引用
3.查看对地址的引用次数
import sys
sys.getrefcount(p)
4.当一块空间没有了任何引用,默认执行del
'''
import sys
class Person1:
def __init__(self, name):
self.name = name
def __del__(self):
print("我是del方法")
p = Person1("CCTV")
p1 = p
p2 = p
print(sys.getrefcount(p))
del p1 # 删除地址的引用,当一块空间没有了任何引用,默认执行del
print(sys.getrefcount(p))
# __str__
class Person2:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self): # 和java中的重写toString方法一样
return "姓名:" + self.name + "年龄:" + str(self.age)
p = Person2("tom", 20)
print(p)
# 单纯打印对象名称,出来的是一个地址
# 如果想在打印对象名的时候能够给开发者更多一些信息量,就重写str方法
总结
静态方法只能访问没有slef的普通方法,静态方法通过类名访问
类方法通过cls访问
类方法 静态方法
不同:
1.装饰器不同
2.类方法是有参数的(cls),静态方法没有参数
相同:
1.只能访问类的属性和方法,对象的是无法访问的
2.都可以通过类名调用访问
3.都可以在创建对象之前使用,因为是不依赖于对象
普通方法 与 两者区别:
不同:
1.没有装饰器
2.普通方法永远是要依赖对象,因为每一个普通方法都有一个self
3.只有创建了对象才可以调用普通方法,否则无法调用。
5.2 面向对象的三大特性
面向对象的三大特性:封装,继承,多态
5.2.1 封装
封装:
- 私有化属性
- 定义公有set和get方法
格式: __属性(私有属性):就是将属性私有化,访问范围仅仅限于类中
好处:
- 隐藏属性不被外界随意修改
- 如果要修改的话就要修改set,get方法
封装实例 就是将属性私有化
"""
__xxx = 1 # 私有属性
def setXXX(self,name): #set是加判断
if name是否符合条件
赋值
else:
不赋值
def getXXX(self): #通过get获取值
return self.__xxx
"""
class Student:
def __init__(self, name, age):
self.__name = name # 私有属性
self.__age = age
self.__scort = 99
# set 和 get方法
def setName(self, name): # 用set来设置,name的长度,小于等于6位的话就可以赋值
if len(name) <= 6:
self.__name = name
else:
print("名字输入有误")
def getName(self):
return self.__name
def __str__(self): #
return "姓名:{},年龄:{},成绩:{}".format(self.__name, self.__age, self.__scort)
p = Student("李小", 20)
p.setName("ffff") # set是用来修改
print(p.getName()) # get是用来获取
print(p)
1. 访问私有属性的方法
方法一: set 和 get 方法
class Student:
def __init__(self, name):
self.__name = name
def setName(self, name): # 用set来设置,name的长度,小于等于6位的话就可以赋值
if len(name) <= 6:
self.__name = name
else:
print("名字输入有误")
def getName(self):
return self.__name
p = Student("李小")
p.setName("ffff") # set是用来修改
print(p.getName()) # 获取私有属性
方法二:使用装饰器的方式
@property #其实就是get
def a():
pass
@a.setter #就是set
def b():
pass
class Student:
def __init__(self, name, age):
self.name = name
self.__age = age
# 先有get的,在用set的
@property
def age(self): # 其实就是get
return self.__age
@age.setter
def age(self, age): # 其实就是set
if age > 0 and age < 100:
self.__age = age
else:
print("输入有误")
def __str__(self):
return "姓名:{},年龄:{}".format(self.name, self.__age)
s = Student("李小小", 20)
print(s.age) # 获取私有属性
print(s)
方法三:通过_类__私有属性名
class Stu(object):
def __init__(self):
self.__age = 99
s = Stu()
print(s._Stu__age) # 通过_类名__私有属性名 访问
# 查看创建对象里面的属性
print(dir(s)) # 查看里面的属性
print(s.__dir__()) # 查看里面的属性 和dir(p)一样
总结
总结:封装就是将属性私有化,
方法一:定义set,get方法。
方法二:使用装饰器@property(get),@方法名.setter(set)
方法三:可以用_类名__私有属性名 ,访问类中的私有属
用dir(),或者对象名.dir() ,查看里面的属性
5.2.2 继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制.
通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类。python没有重载, 只有重写...........
单继承语法
class 派生类名(基类名)
...
在python中子类继承的一些特点:
-
特点: 1.如果类中不定义__init__,就默认调用父类super class的__init__ 2.如果类继承父类,也需要定义自己的__init__,就需要在当前类的__init__调用一下父类的_init__:可以用自己的init 3.如何调用父类__init__: super().init(参数) super(类名,对象).init(参数) 4.override:重写,如果子类重写了父类的方式,就调用 子类的方法 5.子类中可以调用父类方法 super().方法名(参数)
6.私有属性,只能在自己的类中访问。但是可以调用父类中使用私有属性的方法。方法中的属性是父类自己的私有属性
class Person: # 父类,基类
def __init__(self, name, age):
self.name = name # 王一
self.age = age # 30
self.__phone = "123"
def run(self):
print("{}跑步中....{}".format(self.name, self.__phone))
def eat(self):
print("{}吃饭中....".format(self.name))
class Student(Person): # 继承了父类,就表示获取了它所有的方法和属性(私有不算)
def __init__(self, name, age, sex): # 差不多是重写了构造方法
super().__init__(name, age) # 使用了父类定义的属性,继承了父类的属性
self.age = 99
self.sex = sex
# 这样出现的是警告,波浪线才是错误
def eat(self, food): # 重写了父类的eat方法,python只有重载,没有重写
super().run() # 调用父类的eat方法
print("{}正在吃{},{},{}".format(self.name, food, self.sex, self.age))
Person("王一", 30)
stu = Student("李小小", 20, "男")
stu.eat("屎")
print("-------------------" * 2)
# 验证子类能不能访问父类的私有属性
# 1.自己的类中只能用自己类中的私有属性
class Person1:
def __init__(self, sex):
self.sex = sex
self.__money = 200
self.name = "abc"
def show1(self):
print("我是父类的show{},{}".format(self.__money, self.name))
class Student2(Person1):
def __init__(self, sex):
super(Student2, self).__init__(sex) # 使用super(),访问父类中的普通属性
self.__money2 = 500 # 私有属性,只可以在本类中访问
self.name = "deg"
super().show1() # 使用super().show1() 访问父类中使用私有属性的方法
def show(self):
print(self.__money2, "------", self.sex) # 访问自己类中的私有属性,不能访问父类的私有属性
class Stu3(Person1):
def __init__(self, sex):
super(Stu3, self).__init__(sex)
def show(self):
print(self.name, "--=-", self.sex) # 访问父类的普通属性
s = Student2("男")
s.show()
s3 = Stu3("女人")
s3.show()
"""
我是父类的show200,deg
500 ------ 男
abc --=- 女人
"""
如果在继承元组中列了一个以上的类,那么它就被称作"多重继承" 。
多继承语法:
派生类的声明,与他们的父类类似,继承的基类列表跟在类名之后,如下所示:
class SubClassName (ParentClass1[, ParentClass2, ...]): ...
'''
python多继承
A(B,C) 逗号隔开
经典类class A, 新式类:class A(oobject)
但python3中取消了经典类,默认继承object,即python3中class A也是新式类。
'''
# 深度优先:当调用A类test函数时,如下图所示,先查找A类中有无test函数,再查找B类,再查找D类,最后查找C类。 python2
# A-->B-->D-->C
# 广度优先:当调用A类test函数时,如下图所示,先查找A类有无test函数,再查找B类,再查找C类,最后查找D类。 python3
# A-->B-->C-->D
class D: # 默认继承了object
def test(self):
print("D")
class C(D):
def test(self):
print("C")
class B(D):
pass
# def test(self):
# print("B")
class A(B, C):
pass
# def test(self):
# print("A")
a = A()
a.test()
# 查看执行顺序
print(A.__mro__)
import inspect
print(inspect.getmro(A))
总结
总结:继承就是子类继承父类
-
has a :使用自定义的类型,系统类型
-
Is a :子类(父类) ,子类不能继承父类的私有属性,私有属性,可能在自己的类中使用
-
多继承 :广度优先(python3) ,深度优先(python2)
查看执行顺序:对象名.mro 或者import inspect print(inspect.getmro(对象名))
5.2.3 多态
python中多态的作用:让具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容(功能)的函数
继承是多态实现的前提
多态的好处:提高代码的复用性
Python中多态的特点
1、只关心对象的实例方法是否同名,不关心对象所属的类型;
2、对象所属的类之间,继承关系可有可无;
3、多态的好处可以增加代码的外部调用灵活度,让代码更加通用,兼容性比较强;
4、多态是调用方法的技巧,不会影响到类的内部设计。
多态性:
Java中多态性的表现: 多态性,可以理解为一个事物的多种形态。同样python中也支持多态,但是是有限的的支持多态性, 主要是因为python中变量的使用不用声明,所以不存在父类引用指向子类对象的多态体现,同时python不支持重载。 在python中 多态的使用不如Java中那么明显,所以python中刻意谈到多态的意义不是特别大
import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
@abc.abstractmethod
def talk(self):
pass
class Cat(Animal): #动物的形态之一:猫
def talk(self):
print('say miaomiao')
class Dog(Animal): #动物的形态之二:狗
def talk(self):
print('say wangwang')
class Pig(Animal): #动物的形态之三:猪
def talk(self):
print('say aoao')
c = Cat()
d = Dog()
p = Pig()
def func(obj):
obj.talk()
func(c)
func(d)
func(p)
"""
say miaomiao
say wangwang
say aoao
"""
其实就是调用相同的方法得到不同的结果
总结
总结:python没有严格意义上的多态,因为它没有java里面的访问修改符,所以都可以访问,但我们可以用isintance()判断 该对象是不是类中的,是类中的就让它通过。
5.2.4 单例模式
单例模式:是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.
当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。
其实就是一个类里,只想有一个实例化对象。不然多个会导致严重浪费内存资源
class Person: # 其实就是先创建一个对象空间,并保存,下次在创建的时候就使用上一次创建的对象空间
__instance = None
# 重写__new__方法
def __new__(cls): # 在init前执行
if cls.__instance is None:
cls.__instance = object.__new__(cls) # 把第一次创建的地址给__instance
return cls.__instance
else:
return cls.__instance
# 执行过程:就是先创建一个对象空间p,然后进入new方法中,把开辟的地址p,给cls.__instance,并返回了
# 第二次创建的p2,进入new后__instance它不是为空的,所以就直接else 返回第一次创建的地址。这样就形成了 永远就用同一个地址
p = Person()
p1 = Person()
print(p, p1)