python基础
文章目录
- python基础
- python入门
- 基础概念
- 序列
- 列表
- 元组 -- 不可变序列
- 字典
- 字典的本质
- 集合
- 控制语句
- 选择结构 - 条件判断结构
- 循环结构
- zip()
- 推导式
- 函数及原理
- 参数
- LEGB规则
- 面向对象
- 私有属性和私有方法
- 面向对象的特征
- 重写__str__()方法
- super获得父类的定义
- 特殊方法和运算符重载
- 组合 将父类对象作为子类的属性
python入门
- turtle库的使用
import turtleturtle.width(50) # 字迹的宽度
turtle.showturtle() # 显示箭头
turtle.write("你好") # 写文字
turtle.forward(300) # 前进300像素
turtle.left(90) # 箭头左转
turtle.color("red") # 改变画笔的颜色
turtle.goto(-10,30) # 去坐标x,y处
turtle.penup() # 抬笔,不显示字迹
turtle.pendown() # 落笔,显示字迹
turtle.circle(100) # 画圆,参数是半径,单位是像素
turtle.done() # 保持窗口
基础概念
-
“\”行连接符,把一行语句,拆成多行,便与观看
-
强制缩进表示层级结构,不用{}
-
del 变量名 清楚变量,回收变量,类似释放空间
-
python不支持定义常量,只能你自己不去修改
-
解包赋值: a,b,c=1,2,3 --> a=1,b=2,c=3 等号左边写变量用逗号隔开,右边写上值,就会自动按顺序赋值
-
交互变量值:a=1,b=2;a,b=b,a;交换了ab的值
-
python里面的变量都是object对象的引用,int a = 1实际上是object a,a.type=int,a.value=1
-
object有三个基本成员变量,id存地址的,type存类型,value存值。我们使用a的时候,或者访问a都是使用了a的id。也就是指针
-
科学计数法:314e-3 , 就是314乘以10的-3次方
-
/一个,就是浮点数除法,有小数部分,两个//就是整数除法,只保留整数部分,不会四舍五入
-
divmod(13,3) —>(4,1)使用这个函数可以同时得到商和余数,返回的是元组
-
0b二进制0o八进制0x十六进制
-
int() 类型转换:浮点数直接舍弃小数,布尔值True=1,字符串类型的数字也可以直接转换—不能有非数字的字符
-
整数和浮点数运算,会自动转化到浮点型
-
python3无最大数的上限,int可以定义超大数
-
round(value) 返回一个四舍五入后的结果,不会修改value,需要重新接收
-
计算机的时间从1970年1月1号算起,称unix时间点
-
python3里面的True和False本质上就是0和1,可以把布尔值进行加减运算
-
bool(变量) 返回的是布尔值
-
可以使用连续比较运算:if(3<a<10): print(a) 其他语言几乎不支持这种方式
-
bin(数值) 返回数值对应的二进制格式
-
列表进行乘法运算的时候,就是几个列表拼接
-
字符串乘以某个数,就是几个字符串拼接
-
不支持自增++自减–
-
==的本质是调用了eq方法
-
同一运算符is,用来比较两个对象的id是否相等,print(a is b)
-
python会进行整数缓冲,提前把一个整数数组创建起来,你创建整型变量的时候,是直接把数组元素的地址给变量
-
在命令行模式下,缓存的范围是[-5,256] 在文件模式下,就是无穷小到无穷大。文件模式下也有底层数组在,但是超出范围的数以链表缓存到数组里面
-
在命令行模式下,超出范围的数,都是新建对象来储存,哪怕数值一样,也是不同的对象,不同的id。文件模式下数值都被缓存,是同一id
-
成员运算符,in和not in。比如,“py” in “python”,很明显就是判断在不在里面,有没有被包含。也可以判断一个数字是否在一个数组里面。返回值是布尔类型
-
运算符的优先级,使用小括号自己分级,就不会出错了
-
Python 对一些不可变的对象(如整数、浮点数、字符串和元组)进行了缓存,以提高内存使用效率和性能。
-
字符串的内置函数:ord() 将字符转换成对应的unicode码,chr() 把十进制数字转换成对应的字符
-
使用三个单双引号定义的文本,会按照你的格式原封不动的打印出来,不会压缩
-
python默认使用的字符集是Unicode,单个汉字也是单字符了
-
转义字符就是反斜杠,在行末就是续行符,\t是制表符\n换行符
-
直接把两个字符串放在一起默认进行拼接操作,”a“”b“ ,就是"a"+“b”
-
在print打印的时候不想实现换行,可以在print的参数里面,使用end=‘ ’ ,在打印完内容之后会打印这个空格,然后接着打印下一条语句,默认end=‘\n’。print(‘hello’,end=’ ')
-
input获取用户输入的信息
-
字符串不支持直接修改,就是已经存在字符串不能像数组那样修改,只能使用replace方法来修改,参数1是被替换的字符,参数2是替换后的字符,replace也不能改变原来的字符串,也是在原来的基础上进行替换重新生成字符串
-
print语句严格来说只能打印字符串内容,平时可以输出各种类型的变量,都是print语句默默进行了字符串转换
-
打印字符串的单个字符,也被成为字符串索引,就是下标,从左到右从0开始,从右到左从-1开始
-
字符串切片slice就是提取子字符串,参数[起始偏移:终止偏移量:步长],左闭右开,右边的字符取不到。如果范围超了就是把后面所有字符都拿到,超的不管
- [:] 提取整个字符串,也就是原封不动
- [a:]提取字符串下标a开始到结尾的所有内容
- [:b]提取字符串从头开始,一直到下标为b的位置,如果b超了,就是全部的字符串了,超了就当len-1
- [a:b]提取字符串下标为a到b的所有内容
- [a🅱️c]提取字符串下标从a到b的所有内容,每隔c个字符取一个字符
- [-a:] 提取字符串的最后a个字符
- [-a:-b]提取字符串倒数a到b的字符
- [::-1]字符串逆序
- 没有这个slice方法,是靠”字符串+[]“实现的
- 这个切片操作适用于列表,元组,字符串等等,一模一样的
-
split()分隔join()合并
- split按照参数里面的字符串进行切割,默认是按照空格字符切割,并把切割后的字串放在列表里面
- ‘’.join(列表) 参数里面写列表,在前面的字符串里面写链接字符串的标志,加星号就是,”子串1*子串2*子串“
- join函数的原理和+一样,但是使用+,每使用一次就会新建一个对象,join只会新建一次,更灵活
-
字符串是一个常量,跟数字一样当成常量储存,也叫字符串的驻留机制。这个概念没啥用。略过即可
-
字符串函数
-
a.len() 求字符串长度
-
a.startswith() 判断字符串的开头是不是以参数开头的
-
a.endswith() 判断字符串是不是以参数结尾,返回值是布尔值
-
a.find() 返回字符串中参数第一次出现的位置
-
a.rfind() 返回字符串中参数的最后一次出现的位置,也就是倒着找
-
a.count() 返回字符串中参数出现的次数
-
a.isalnum() 判断字符串里面都是字母或数字,返回布尔值
-
a.strip() 去除字符串首尾的指定信息,如果不写参数,就是去除字符串首尾两端的空格,如果是一大堆空格就会一直删,直到不是空格,除了首尾,之外的字符不管
-
a.lstrip() 去除左端指定的字符 如果是相连的就会连续去除
-
a.rstrip() 去除尾端指定的字符
-
下面的函数是新建一个字符串
- a.capitalize() 首字母大写
- a.title() 每个单词的首字母大写
- a.upper() 所有字母大写
- a.lower() 所有字母小写
- a.swapcase() 大小写互换,大写变小写,小写变大写
-
字符串排版函数
- a.center(排版后字符串长度,”填充的字符“) 居中对齐*abc**,如果填充的字符为空,没写这个参数,就是用空格代替
- a.ljust(字符串长度,”填充的字符“) 靠左对齐 ***abc
- a.rjust(字符串长度,“填充的字符”) 靠右对齐 abc***
-
字符串特征—返回布尔值
- isalnum() 就是is all number 都是数字或字母吗?
- isalpha() 检测字符串都是字母或者汉字
- isdigit() 只有数字
- isspace() 都是空白字符
- isupper() 都是大写字母
- islower() 都是小写字母
-
-
字符串格式化format
- 可以设计一个字符串的模板,然后使用format调用这个模板,并填入参数即可
- 模板:a = “{0}是{1}的” 调用:a.format(“小红”,‘好样的’) 输出:小红是好样的的
- 模板里面的{0}{1}就是要替换的地方,这个位置可以颠倒,参数按照顺序填入他们
- 在花括号里面可以写参数名字,比如{name},后面在替换的时候可以指定参数填充format(name=“小红”)
- {1:*^<>10} 在模板里面也可以对参数进行对齐和填充两个操作 这里的冒号左边是索引,右边第一个是对齐的时候填充的字符,后面的分别是居中对齐^,左对齐<,右对齐>。最后就是填充之后的字符串长度
- {1:*^6} 参数二填充的地方,使用*进行填充对齐,对齐方式是居中对齐,对齐后是长度为6的字符串
-
数字格式化 浮点数用f,整数用d
- {:.2f} 3.14 就是小数点后保留两位效数
- {:+.2f} 3.14 带符号保留小数点后两位
- {:.0f} 3 不带小数
- {:0>2d} 05 整数左侧补零,宽度为2
- {:x<4d} 5xxx 整数右侧补x,宽度为4
- {:,} 1,000,000 用逗号分隔的科学计数法
- {:.2%} 25.00% 百分比格式
- {:.2e} 1.00E+09 指数格式
- {:10d} 13 默认右对齐,宽度为10
- {:<10d} 13 左对齐,宽度为10
- {:^10d} 13 居中对齐,宽度为10
-
可变字符串io.StringIO()或者数组
- 需要导入io模块
- 使用a = io.StringIO(字符串),直接打印a是一个object对象,想要查看这个字符串使用函数a.getvalue() 就可以打印字符串的内容了
- 修改字符串内容,使用.seek(num) num是一个指针,使用seek把指针移动到了字符串下标为num的地方,再使用.write(“***”)就可以修改字符串的内容了,前面指定了位置,write的参数是代替的字符内容
-
类型转换总结
- int() 整型
- long() 长整数
- float() 浮点数
- complex() 创建复数
- str() 字符串
- repr() 表达式字符串
- eval() 计算表达式,返回对象
- Complex() 参数转化为复数
- tupls() 序列变元组
- list() 序列变列表
- set() 可变集合
- dict() 字典,参数必须是元组格式
- frozenset() 不可变集合
- chr() 整数变字符
- unichr() 整数变Unicode字符
- ord() 字符变整数
- hex() 整数变十六进制
- oct() 整数变八进制
序列
包含:列表,元组,字典,集合,字符串
在python当中的序列,本质上是一个指针数组,指针数组的每一个元素都指向一个对象
列表
- 列表 存储任意数目,任意类型的集合
- 内置可变序列,包含多元素的有序连续内存空间
- 元素是任意类型的内容
- 可大可小,自动变换
- 列表的操作方法
- append(元素) 增加元素至尾部
- extend(新增列表名) 增加列表到尾部,在原列表上操作
- insert(元素,下标) 增加元素在指定位置 ,从0开始, 尽量不使用,直接在尾部操作最好
- remove(元素) 删除指定元素 ,不存在就报错
- pop(元素下标) 默认删除并返回最后一个元素,可以指定元素下标
- clear() 删除所有元素,但不删除列表对象
- index(元素) 返回第一个指定元素的下标
- index(value,start,end) 写一个参数是找参数出现的位置
- 写两个参数,第二个参数是找元素的时候的起始位置
- 第三个参数是找元素最后的截止位置,截止位置不算
- count(元素) 计数,返回指定元素出现的次数,可以判断变量在不在列表里面,和”变量 in 列表“的效果一样
- len(列表名) 求长度、个数,这个不是列表的方法,是一个函数,参数写列表
- reverse() 翻转列表
- sort() 排序列表
- copy() 返回列表的浅拷贝
- +列表元素 对一个列表使用+,也可以添加元素到列表尾部,但是是对原列表进行复制,在拷贝列表中新增元素,有一个复制的操作,效率低
- *列表,对一个列表做乘法,就跟对字符串做乘法一样,产生新对象,复制列表整体
- 列表的删除使用 del + 列表名[元素下标]
- 列表的创建
- 使用[]
- 使用list()
- 如果参数为空,就是空列表
- 参数写range(10) 就是一个从0到9的列表,没有10
- range(start,end,step) 第一个参数是起始的数字,默认是0可以不写,第二个参数是结尾数字必写,第三个参数是步长默认是1,如果步长是负数就是从大到小输出内容,-1就是逆序给列表
- 如果填两个参数要保证起始小于结束的数值
- 如果第三个参数是负数,要保证起始大于结束
- 如果参数写一个字符串,列表就是把字符串按照单个字符储存
- 推导式生成列表
- a = [x*2 for x in range(5)] = [0,2,4,6,8] 这种就是推导式生成的列表
- a = [x*2 for x in range(5) if x%4 != 0] 在上面的基础上还加了一个if判断,条件是对4取余不等于0,符合条件的进入列表
- print(range(5)) 输出的结果是range(0,5) range实际上是一个可迭代的对象,只能给循环或者列表使用,不可以直接进行乘除操作
- range(5)*5 是错的
- 复制新列表
- 如果只是把原有的列表名赋值给新变量,这个新变量只是一个指针,没有新列表
- 新列表 = [] + 列表名 用空列表对原列表进行加法运算就可以产生新列表了
- 还有copy模块
- 列表排序有两种
- 修改原列表,使用sort方法,也可以在sort里面传参reverse=True就是降序,使用random.shuffle(列表) 打乱原列表顺序
- 新建列表排序,使用内置函数sorted(列表) 返回一个排好序的新列表,实现降序也是传参reverse=True,原列表不变
- reversed() 返回的是一个迭代器,需要使用list方法加工一下才是列表。这是原列表的逆序列表迭代器
- max和min函数
- max取出列表的最大元素
- min取出最小元素
- sum 对列表进行求和运算
元组 – 不可变序列
-
元组和列表一样,列表是可变序列,可以增删改操作,元组是不可变序列,不可增删改。只能查看,计数
-
只需要学会元组的创建删除,访问计数即可
- 元组的创建:a = (1,2,3) / a = 1,2,3这两种方式的效果是一样的
- 如果创建元组的时候,只有一个元素,这个元素后面必须跟一个逗号 比如:(100,) 如果没有这个逗号,就是整型100
- tuple(可迭代对象) 和list() 的操作一样,参数为空,字符串,range,列表 都可以
- list也可以接收元组作为参数
- 因为元组不可修改,想要对元组进行排序,只能新建,使用方法sorted(元组),会把原来的元组进行排序放在新的列表里面
-
zip函数
-
zip(列表a,列表b,列表c) zip的参数是多个列表,他的返回值是zip对象,可以使用list方法配合print打印出来
-
zip的返回值类似一个大列表,他里面的每一个元素都是元组,元组的元素是不同列表同一位置的元素
-
a = [1,4,7] b = [2,5,8] c = [3,6,9,10] print(list(zip(a,b,c))) [(1, 2, 3), (4, 5, 6), (7, 8, 9)] 因为是list函数,使用tuple就是元组了
-
不难发现,zip返回值的第一个元素是一个元组,元素是每个列表的第一个元素。同理第二个元组的内容是每个列表第二个元素的集合
-
-
生成式推导元组,只能用一次,本质上不!=元组,是元组生成器
-
a = (x*2 for x in range(10)) b = tuple(a) print(b) c = tuple(a) print(c) # 空
-
和列表的生成式推导一样,根据你写的逻辑生成一个序列。但是这个生成器不能多次使用,只能用一次,明明a一直是一个生成式元组,没有修改,但是b使用过一次a,c就不能使用a了
-
把a的外壳改成列表,就可以多次使用了,bc都能正常访问
-
生成式元组,可以使用生成器自带的函数进行遍历,同样的只能遍历一次,使用a.——next——()就可以依次得到元组的元素了
-
-
元组的访问速度比列表快
-
与整数和字符串一样,可以作为字典的键,列表不可以成为字典的键
字典
是 键值对 的无序可变序列,每个元素都是键值对。
通过键,可以对值进行查,改操作
键不可以重复,如果重复了,最后相同的键只会出现一次,他的值是最后一次的值,先前的都覆盖了
-
键 必须是不可变对象,也不能重复出现,一般是整数,元组,字符串,浮点数
-
值 可以是任意类型数据,可以重复出现
-
创建字典的方式
-
使用花括号直接创建
-
使用dict()函数
a = {'name':'小黑','age':20} b = dict(name='小红',age=21) c = dict([('name','小红'),('age',22)])print(a) print(b) print(c)
- 上面是创建字典的三种方式,c几乎不用,b还凑合,a比较常用。
- a是最经典的方式,b可以省去键的引号
- 参数不写,就是空字典
-
使用zip函数,利用zip函数的特性,我们把 键和值分别写在两个列表里面,然后再使用zip操作,最后使用dict对zip的返回值操作
-
a = ['name','age'] b = ['小白',20] c = dict(zip(a,b))print(c) {'name': '小白', 'age': 20}
- 上面是zip配合dict的效果
-
使用dict.fromkeys([各种键]) 方法
- 需要在参数里面填写各种键构成的列表
- 直接打印,就是一个所有值为None的字典{‘name’:None,‘age’:None}
-
-
字典元素的访问
-
a[键] 就可以获得这个键的值,如果键不存在,就报错
-
a.get(键,默认值)
- 这个参数一般写一个,也就是键,返回 值
- 第二个参数是键不存在的时候,返回的内容,默认是None,不会报错。也可以自己设定键不存在时返回的提示词
-
字典的函数
- a.items() 输出整个字典的键值对,dict_items([(键值对),(键值对)]) 外面这一堆是自动补充的格式,键值对在元组里面以逗号隔开
- a.keys() 输出字典中所有的键 dict_keys([键]) 用逗号隔开的键
- a.values() 输出字典中所有的值 dict_values([值]) 用逗号隔开的值
- 字典也可以用len函数,结果是键值对的个数
- 也可以使用in来判断,键是否在字典里面,只能判断键,不能判断值在不在字典里面
-
字典元素的增删查改
- 新增键值对 若键存在,会覆盖旧的值,不存在就新增键值对
- a[‘age’] = 18 直接用字典名加中括号和键,赋值 值,就可以在字典里面新增键值对了
- 新增字典加到旧字典里面,使用函数update()
- a.update(b) 这里ab都是字典,现在是把b的键值对添加到a里面去
- 删除键值对
- del(a[‘name’]) 直接删除
- b = a.pop(‘age’) 删除键值对,并返回删除的键值对的值
- a.clear() 使用字典自带的clear方法,就可以清空字典了
- a.popitem() 这个方法是在字典里面随机去除一个键值对,因为字典无序,所以是随机删除键值对
- 新增键值对 若键存在,会覆盖旧的值,不存在就新增键值对
-
序列解包
用于元组、列表、字典。
a = {'name':'哈哈哈','age':15,'id':10}
x,y,z = a.items()键值对 返回元组 x[0] 就是键
x,y,z = a.keys()键
x,y,z = a.values()值
print(x,y,z)
使用字典的这三种方法,分别可以得到字典里面的键值对、键、值
上面这种得到字典数据的方式叫序列解包
字典的本质
本质是散列表。就是一个两列的二维数组。对应字典的键值对。散列表:数组里面的元素不是紧密相连的,每一个单元都是有间隔的。可以自动扩容,mallco。
每个键值对也叫一个单元bucker
集合
本质上是一个只有键的字典。因此集合内部不能有重复的元素
集合跟字典一样,无序可变不可重复
- a.add(元素) 使用add函数可以在集合里面添加新元素
- 使用set(可迭代的对象)方法,可以把列表、元组等可以迭代的对象转换成集合。set可以删除重复数据保留一个
- remove(元素) 可以删除集合中的指定元素
- clear() 清空集合
集合的操作
- 并集
- a|b
- a.union(b)
- 交集
- a&b
- a.intersection(b)
- 差集 去掉共同的部分,应该是按照后面的这个公式理解:a-b = a - (a&b)
- a-b
- a.difference(b)
控制语句
分三类:顺序、选择、循环
选择结构 - 条件判断结构
单分支if ,双分支if else,多分枝if elif else
- 条件语句判断为False的情况:
- False ,0,0.0,None,空序列(空列表,空元组,空集合,空字典,空字符串),空的range,空迭代对象
- 除了上面的情况,均为True
- 条件语句中不允许出现赋值运算符,就是一个等号=,进行赋值操作不允许
- 三元条件表达式 (条件为真的结果 if (条件语句) else 条件为假的结果)
- print(num if num<20 else num-10)
- 在多分枝里面,else不是必要存在的,可以只有if和elif
循环结构
-
python里面的可迭代对象:
- 序列:字典,列表,元组,字符串
- 迭代器
- 生成器
- 文件
-
遍历字典的时候,如果直接写字典名,就是遍历字典的键key,想要遍历字典的值就需要对字典调用对应的方法keys()啥的
-
break 跳出最近的循环,循环结束 continue 结束本次循环,循环不结束,直接进入下一次循环
-
else 语句,如果while和for循环不是break语句结束的,就会执行else语句
-
比如下面的两段代码,第一个是正常结束for循环,就会执行else语句,第二个就不执行else语句
-
for i in range(5):print(i) else:print('done')for i in range(5):print(i)if i == 3:break else:print('done')
-
-
循环的优化:
-
尽量减少循环内的计算
-
嵌套循环,最内层尽量少的计算
-
尽量使用局部变量
-
使用join()连接字符串
-
zip()
这是一个迭代器类似range
同时遍历多个序列,提取元素赋值给变量,按最短的序列结束循环
aa = [1,2,3,4]
bb = ['q','w','e','r']
cc = [9,8,7]
for a,b,c in zip(aa,bb,cc):print(a,b,c)for i in range(min(len(aa),len(bb),len(cc))):print(aa[i],bb[i],cc[i])
zip函数遍历了三次,因为最短的序列是cc,只有三个元素。
不难发现zip就是range和min函数的结合
推导式
- 列表推导式
- [表达式 for 变量 in 可迭代对象]
- {表达式 for 变量 in 可迭代对象 if 条件判断}
- 字典推导式
- {key表达式:value表达式 for key,value in 可迭代对象 }
- {key:value for key,value in zip(range(1,5),[‘a’,‘b’,‘c’,‘d’])}
- 利用字符串的count方法,可以得到参数里面字符出现的次数
- sign_num = {sign:a.count(sign) for sign in a} 利用键的不重复性,就可以得到字符的字典集合
- 集合推导式 把列表推导式的中括号变成花括号就是集合推导式了,里面的元素是乱序的,因为集合无序
- 生成器推导式 把列表生成器的中括号变成圆括号就是元组,但是不能直接使用,是一个迭代对象,需要再次遍历一遍,也只能使用一次,这个推导式的返回值是一个类似range的对象,还要遍历。只能使用一次,是一次性的消耗品
函数及原理
def 函数名([参数列表]): # 用中括号括起来的意思是可有可无'''在pycharm里面,在函数体内打三个单引号+回车可以自动解释说明,就是方便写注释了:param a:这是第一个参数。。。:param c::return:'''函数体
-
在python里面,某某函数也叫某某方法
-
使用函数help(函数名) 可以打印函数声明格式+解释说明,也就是这个注释可以打印出来
-
使用函数的__doc__这个内置函数,也可以打印注释信息,这个不打印函数声明。这里的注释仅包括长文本注释即三引号的
-
python里面一切变量都是对象,函数也是一个对象。
-
python里面没有指针,变量就是指针,把函数名交给一个变量,这个变量也就函数指针。
-
def add(n):''''''n = n+1 c = add c(2) 可以直接当成函数指针使用
-
函数名后面加圆括号就是调用该函数
-
-
函数内部使用全局变量需要使用global声明一下,函数内部直接访问全局变量不需要global声明,但是想要修改全局变量就要global声明
-
locals()获取函数内部所有的局部变量 globals()获取所有的全局变量/对象。以字典的格式返回
-
局部变量调用速度比全局变量更快
-
因为python都是对象,所有的数据的创建都是引用,没用赋值操作。
-
如果操作的对象是可变对象,我们修改他的值,就是直接修改。如果不是可变对象,就是新建对象,比如元组
-
不可变对象:数字、字符串、元组、函数等
-
id(变量) 可以得到对象的地址
-
因为都是对象,都是引用关系,所以传参的时候,也是传引用,也就是指针,指向同一个地址/对象
-
浅拷贝copy只拷贝对象的引用,深拷贝deepcopy是对对象所有内容进行拷贝
-
浅拷贝只复制了对象的第一层结构(例如,第一层的列表或字典),而对包含的对象的引用仍然指向原始对象中的元素
-
如果不可变对象内含有可变对象,比如元组里面嵌一个列表,修改这个列表的值,不会创建新的元组对象来储存,还可以继续使用这个元组对象(地址不变)
参数
参数有5类
- 位置参数 就是函数需要几个参数,就传几个参数
- 默认值参数 写函数参数的时候,直接给参数先赋值,如果传参不够,就使用你设定的数,也就是默认值 add(a,b,c=1,d=2) add(1,2)
- 默认值参数只能在位置参数后面
- 命名参数 如果函数的参数是add(a,b,c) 传参的时候可以手动命名,add(b=1,c=2,a=3) 对应的函数里面的参数能得到相应的值
- 可变参数 也要把可变参数放在位置参数后面
- *a 一个星号,将传过去的多余参数放在元组对象里面,函数add(a,b,*c) 调用的时候是add(4,5,6,7) 最后是a=4,b=5,c= (6,7)
- **a 俩星号 将多余的参数放在字典对象里面,这个多余的参数要以‘参数名=参数’的形式传递
- 强制命名参数 如果把可变参数放在命名参数之前,就可以构成强制命名参数。add(*c,a,b) add(14,7,2,a=5,b=8) c得到了(14,7,2)
lambda表达式 匿名函数
- 基本语法:lambda 参数1,参数2,参数3。。。: 表达式/函数体
- f = lambda a,b,c:a+b+c print(f(10,20,30))-----> 60
- 把匿名函数写在列表里面,使用列表加中括号的形式,再跟一个圆括号,写参数,就可以调用匿名函数了。比如g[1](10),g是一个匿名函数列表,[1]是他的第2个匿名函数,后面是参数。就可以得到返回值了
- lambda是关键词
- 会生成函数对象,和正常的函数一样
eval函数,把字符串参数当成代码语句使用
-
eval(‘print(123)’) 就是正常输出123
-
eval(‘a+b’,num1) 这里面还可以执行表达式,如果不写第二个参数指定参数的来源,就会从代码里面找参数然后执行,如果指定了参数的来源,比如这里是num1,说明参数在这里面,就会从num1里面拿参数的值并计算
-
因为eval会把字符串转换成语句执行,而字符串里面可能含有不安全的语句。
-
在函数内部再定义一个函数,就是内部函数,也叫嵌套函数,一般是加密的地方。内部函数只能在这个函数里面使用。出了函数就无法访问了
-
global声明全局变量 nonlocal声明函数里面,内部函数之外的变量。对于内部函数而言,不在内部函数里面但在整个函数里面的变量就是内部函数的外部变量,需要使用nonlocal声明。当然也是修改变量的时候才要声明,仅访问是不需要声明的。
-
a = 1def add():b = 2def num():nonlocal b 声明内部函数的外部变量global a 声明全局变量
LEGB规则
python在查找变量的时候的规则
- Local-》Enclosed-》Global-》Built in
- 分别是,函数内部,内部函数外部变量,全局变量,python的特殊名称。
- 一般最后一个用不上,全局变量都没找到就直接报错了
面向对象
数据的集合就是数组,数组的集合就是结构体,结构体和方法发集合就是对象。
-
类的对象object和类的实例instance是一个意思
-
类里面的变量叫类的属性,类里面的函数叫类的方法
init构造方法,是对象的初始化工作。构造函数不是建房子,而是粉刷房子。new方法是建房子
-
init的第一个参数只能是self,后面跟其他参数。初始化的意思是给实例的属性赋值
-
如果不写这个函数,系统也会自我生成一个无参版的构造函数
-
new方法一般不用写,也是自己生成的方法。
-
class Student:def __init__(self,age):self.age = agedef print_info(self):self.num = 100 这个函数不被调用,这个属性不会被添加,但这是类里面的属性,只要调用就有s1 = Student('Tom') s2 = Student('Jerry') s2.address = 'beijing' 以这种方式也可以给s2对象增加属性,但是只有s2才有这个新增的属性,s1没有,因为不是添加到类里面的属性
-
调用对象方法的表达式:对象.方法([参数列表]) 传参的时候不用写self,系统会自动添加
-
dir(对象) 获得对象的全部属性和方法
-
对象.dict 获得对象全部属性,以字典的格式
-
isinstance(对象,类型) 判断参数一,是不是参数二的类型
-
类对象的类型是type类,意思是图纸类,模板类
-
类只要被定义了,就会生成类对象,后面使用不使用没关系
-
类对象是类对象,类的实例化对象,是实例化对象,两个不一样
-
class Student:count = 0def __init__(self,age):self.age = age 这里是给类的实例化对象赋值,也就是实例属性Student.count = Student.count + 1 这里是给Student类对象赋值,就是类属性def print_info(self):self.num = 100
-
-
类方法 通过装饰器定义 必须要有cls
-
@classmethod 这个就是装饰器 def 类方法名(cls [,形参列表]): 第一个是cls,必须有,是关键词方法体
-
类方法不能访问实例属性和实例方法
class Student():def __init__(self, name, age):self.name = nameself.age = agedef getName(self):print(self.name)@classmethoddef getAge(cls):print('15') 这个就是类方法的类函数,不能访问实例化对象的值。比如self.age不可以的。Student.getAge() 这是调用类的类方法
- 静态类方法 staticmethod 不需要传参,跟普通函数一样,只是写在了类里面,需要用类来调用。也不能访问实例属性
class Student():def __init__(self, name, age):self.name = nameself.age = agedef getName(self):print(self.name)@staticmethoddef getAge():print(15)@staticmethoddef add(a,b):print(a + b)Student.getAge()
Student.add(11, 22) 只是需要使用这个类来调用这个函数,这个函数寄存在类里面
- 析构函数del 垃圾回收机制,释放空间的。python自动回收,一般不用管。del 实例化对象,就可以销毁这个对象,释放空间 。
- del+对象语句的本质,就是调用了类里面的del析构函数
call方法
我们在python里面调用函数的时候,都是函数名加括号。本质上是调用函数这个对象,然后再调用函数对象里面的call方法
所以类也可以设计call方法,把类像函数一样,直接调用
class Student():def __call__(self, age, number):print('call')print(f'{age}...{number}')s = Student() 类的实例化对象
s(15,20) 对象名+()
python不支持重载,就是类里面的方法名称不能一样,如果方法的名称有一样的,就以最后一个为准。也只能调用最后一个同名的方法。
可以使用赋值的方式给类增加方法,由于类方法在调用的时候默认传参self,也就是至少一个参数,所以我们需要把要添加到类里面的函数,设置一个参数,哪怕不使用,也要留一个。如果有参数就不用管了。
- python还支持给类增加函数,这个叫方法的动态性
class Student():def add(self):print('add')def work(s):print('work')def add(a):print('add2')
Student.work = work 给Student类增加了一个方法
Student.add = add 这里替换了类原本的add方法q = Student()
q.work()
q.add()
私有属性和私有方法
python里面没有严格的私有方法。在类外面也可以访问类的私有属性或者方法。
一般是约定,在类外面不要访问类内部的属性或方法。
关于约定:
- 双下划线开头的属性,是私有的。
- 类里面可以访问私有属性
- 类外面不能直接访问私有属性方法。(只是不能直接,还是有方法的)
- 在类的外面使用,下划线开头+类名+双下划线+私有属性/方法 访问私有属性/方法
class Student():num = 0__age = 10 私有属性,在预处理的时候变成了_Student__agedef __init__(self, name):self.__name = name 实例对象的方法里面也可以设置私有属性def print_num(self):print(Student.__age)类里面访问类属性print(self.__name) 在类里面访问实例对象的私有属性
print(Student.num)
print(Student._Student__age) 类外面访问私有属性
s = Student("John")
print(s._Student__name) 在类外面访问实例化对象的私有属性
- 函数的本质也是一个属性,访问私有方法的步骤和访问私有属性,是一样的
@property装饰器 作用:在调用类方法的时候不需要使用括号执行了,就是省了括号
class Student():num = 0def __init__(self, name, age):self.name = nameself.age = age@propertydef getName(self):print(self.name)@getName.setter 这个装饰器的工作是验证我们想要修改类属性的值是否合法def getName(self,name):if name: 这里可以验证一下name是不是空值self.name = nameelse:print('ERROR') 如果格式不规范,就不可以更改a = Student("John", 18)
a.getName = 'zhao' 这里是这个装饰器 @getName.setter
a.getName 这里是调用了这个装饰器 @property,因此不用括号就可以执行方法
-
@name.setter:
-
这个装饰器定义了用于设置
name
属性的方法。当你使用student.name = "NewName"
的形式来给name
属性赋值时,实际调用的是name
方法下的 setter。 -
setter 方法允许你在设置属性值的时候执行一些额外的操作,比如验证输入值的类型。
-
-
一个下划线开头的是保护成员,就是不能使用from import导入
-
双下划线开头的是私有成员。
-
双下划线开头结尾的是特殊成员
-
一般把相邻的方法,使用一个空行隔开,相邻的类使用两个空行隔开
None也是一个对象,类型是NoneType,也是一个常量。只能赋值,不能人为创建这个类型。表示不指向任何对象。
None不是false、0、‘空字符串’。None和任意类型比较都是False
使用==和is判断,空列表()[],空字符串不会自动转成False
面向对象的特征
-
封装/隐藏
- 隐藏对象的属性和细节,对外提供方法。对外暴露相关调用方法
-
继承
-
让子类拥有父类的属性或者方法,提高代码的利用效率。保证父类不变的情况下,增加新功能改进已有的算法
-
改善已有的类
-
格式:class 子类类名(父类,如果有多个就用逗号隔开):
-
如果我们没用指定父类,就会默认指向object类。
-
如果子类不重写构造函数,默认是自动调用父类的构造函数
-
class Person:def __init__(self, name, age):self.name = nameself.age = ageprint('Preson')class Student(Person):def __init__(self, name, age, num):# super(Student,self).__init__(name, age) 方法一Person.__init__(self, name, age) 方法二 这是两种继承父类构造函数的方法self.num = num 子类独有的属性,继承父类属性的同时也有自己的属性print('Student')a = Student('John', 22, 80)
-
因为在父类的构造函数里面,有我们子类可以使用的初始化属性,然后我们使用super或者方法二,就可以使用父类的初始化了,还能再添加新的属性num
-
父类的私有属性也可以继承
-
子类可以覆盖父类的方法,这个操作叫方法重写
-
使用类方法:类对象.mro() 或者类属性__mro__可以得到类的继承层次,就是打印继承的父类的类名
-
子类可以继承父类中除构造方法外的所有成员
-
-
多态
-
由于对象的不同,产生不同的行动,输出不同的内容。
-
方法才有多态,属性没有多态
-
多态存在的必要条件:继承、方法重写
-
class Person:def say(self):print("Person says")class Student(Person):def say(self):print("Student says")class Teacher(Person): 就是两个子类,继承同一个父类,然后在子类里面分别定义相同名称的方法。def say(self):print("Teacher says")def say_name(m): 再用别的方法去调用上面两个子类的方法m.say()say_name(Student()) 因为传入的类不同,所以调用的方法也不同,这就是多态 say_name(Teacher())
-
object类
-
使用快捷键alt+7在pycharm里面可以看到类的层级,还可以看到类的所有方法
- 灰色的方法是继承父类的,白色的方法是这个类自己的
-
按住ctrl键鼠标移到想要看的类上面,点击一次,就可以自动跳转到这个类的构造函数所在的位置
-
类方法,也是类属性,我们在使用类方法的时候,把括号去掉,也可以得到值,得到的是一个method类型,类方法的属性是method
重写__str__()方法
我们把一个对象转换成字符串的时候,本质上是调用了str这个方法,我们使用print函数去直接打印这个对象的时候,也是调用了str这个方法
class Student(Person):def __init__(self, name, age, num):# super(Student,self).__init__(name, age)Person.__init__(self, name, age)self.num = numprint('Student')def __str__(self):print('重写str') 只要调用str方法,我们就可以看到这句话return '重写的str返回内容'a = Student('John', 22,80)
print(a) 这是打印的str返回值
str(a) 这里没有打印str的返回值,只是调用了str方法
- 多重继承:一个子类可以有多个父类。比较复杂,尽量少用。
- 在类名后面的括号里写什么类,什么类就是这个类的父类。
- 存在多个父类的时候,我们调用父类的方法,是从左到右依次去查找。class st(bb,aa): 会先从bb里面找调用的方法,找不到就去aa里面找。
super获得父类的定义
在子类中获得父类的方法:
- 获得父类的构造方法:super(子类的名称,self).–init–(参数列表)
- 第一个括号里面的参数可以不写,一般是默认继承父类,当然只适用于python3的简单继承。python2里面就是上面这种写法。
- 这个子类的名称,就是super所在的这个类的名称
特殊方法和运算符重载
因为python里面都是对象,我们进行运算的时候,都是使用对象里面的方法。
a+b
a.__add__(b) 本质
比如上面两个语句,是同一个意思。第一行是我们直观的用法,第二行是完整的写法
常见特殊方法:
__init__ 构造方法
__del__ 析构方法
__repr__,__str__ 打印,转化
__call__ 调用,执行
__getattr__ 点号运算 本质是:a.say --->>> a.__getattr__(say)
__setattr__ 属性赋值
__getitem__ 索引运算 a[3] a.__getitem__(3)
__setitem__ 索引赋值
__len__ 长度
运算符的方法
+ add
- sub
< <= == lt le eq
> >= != gt ge ne
| ^ & or xor and
<< >> lshift rshift
* / % // mul truediv mod floordiv
** pow
特殊属性
obj.__dict__ 对象属性的字典
obj.__class__ 对象所属的类
class.__bases__ 类的直接父类,多继承
class.__base__ 类的父类,单继承
class.__mro__ 类的层次结构
class.__subclasses__ 类的子类列表
组合 将父类对象作为子类的属性
class Person:def say(self):print("Person")class Student(Person):def stu(self):print("Student")class Teacher(Person):def tea(self):print("Teacher")class st:def __init__(self,s,t): 就是把实例对象,初始化为自己的属性,然后调用实例对象,间接调用实例对象的方法self.s = sself.t = ta = Student()
b = Teacher() 这是两个实例化对象
s = st(a,b) 把实例化对象传给st自己的属性
s.s.stu()
s.t.tea()
和继承差不多。
class Person:def say(self):print("Person")class Student(Person):def stu(self):print("Student")class Teacher(Person):def tea(self):print("Teacher")class st(Student, Teacher):passs = st()
s.stu()
s.tea()
-
关于继承和组合的使用场景:
- 如果是从属关系,就用继承。如果是包含的关系就用组合。比如,手机有屏幕,就是组合。
-
工厂模式和单例模式
- 工厂模式,就是用一个函数,根据参数的不同返回不同的类对象,使用ifelse语句判断。
- 单例模式,就是利用类对象属性,比如把类属性弄成布尔值,然后自己在初始化实例对象的时候修改一下类属性,然后每次初始化的时候都要验证一下类属性。如果是第一次,就正常创建,否则就继续返回第一次创建的对象。