Python基础知识

0 阅读35分钟

前言

最近ai势头正猛,公司也是要求学习ai了!自己先从Python入手,整理了份笔记,记录一下python语法。其语法和其他语言是相通的,只是写法不同。

一、字面量

  1. 所谓字面量,就是直接写在代码中的具体值
  2. Python的字符串中可以包含任意字符,且必须使用引号包起来。
  3. 字符串的引号,可以是:单引号、双引号、三个单引号、三个双引号。
  4. 写在Python文件头部的字符串,会被自动识别成docstring(文档字符串)
  5. 文档字符串的主要作用是:对当前Python文件进行一些说明,且文档字符串必须用三个双引号
"""python"""

"python"
'python'
'''python'''

二、变量

语法:变量名 = 值

name = '李四'
age = 18
height = '188'
print('你好!我叫' + name, '今年', age, '岁,身高' + height + 'cm。')

三、标识符命名规则

  1. 只能包含:数字、字母、下划线,且不能以数字开头,不能包含空格
  2. 标识符区分大小写,例如 Name和name是两个不同的标识符。
  3. 标识符不能使用关键字,
  4. 标识符尽量不要与内置函数同名。
  5. 标识符虽然没有长度限制,但应追求:简洁清晰,具有描述性。
Python中的关键字
Falseassertcontinueexceptif
nonlocalreturnNoneasyncdef
finallyimportnottryTrue
awaitdelforinor
whileandbreakeliffrom
ispasswithasclass
elsegloballambdaraiseyield

四、常量

一般约定全大写变量名来表示常量,涉及多个单词则以下划线分割。

python中没有强制的常量机制,其本质还是变量,只是约定好不要去修改!

# ctrl+shift+u 转换大小写
AGE = 13
NAME = 'abc'

五、数据类型

类型名称英文名举例说明
字符串string'李四'用引号包裹
整型int18没有小数的数字
浮点型float20.26有小数的数字
...

type() 用来判断类型

print(type('李四')) # <class 'str'>
print(type(18)) # <class 'int'>
print(type(23.12)) # <class 'float'>

整型 int

# 整型:没有小数点的整数,可以是正数,负数,0
age = 18
age2 = 23.12
age3 = 0

# 当数很大时,可以使用下划线_,使数字易读
num = 10000
price =  100_000_999
# print(num, price)

# Python中整数值的上限,取决于执行代码的计算机的内存和处理能力
max_limit = 999 ** 9999
print(max_limit)

浮点型 float

# 浮点型就是带有小数的数字
height = 182.5
wight = 80.0
unit = -9.0

# 浮点型的科学计数法表示
speed = 3.4e+2   # 3.4乘以10的2次方 340.00
pi = 3.14e3   # 3.14乘以10的3次方 3140.
num_a = 5E8   # 5乘以10的8次方 500000000.0
num_b = 6E+6   # 6乘以10的6次方 6000000.0

num_c = 1E-2   # 1乘以10-2次方 0.01
num_d = 1e-3   # 1乘以10-3次方 0.001

字符串 string

字符串拼接

year = 2020
day = 6.20
school = '德州学院'

# 写法1:字符串之间以加号相连,但代码较乱且不能拼接数字
decr = '我毕业于' + school + ',毕业时间'

# 写法2:占位符 %s字符串 %i整数 %f浮点数 %d占位十进制整数
#decr2 = '我毕业于%s,毕业时间%i年的%f号'%(school,year,day) # 我毕业于德州学院,毕业时间2020年的6.200000号
decr2 = '我毕业于%s,毕业时间%i年的%d号'%(school,year,day) # 我毕业于德州学院,毕业时间2020年的6号

# 写法3:f-string
decr3 = f'我毕业于{school},毕业时间{year}年的{day}号'  # 我毕业于德州学院,毕业时间2020年的6.2号

占位符精度控制 %m.nf

m

  1. 最小宽度,位数不够会使用空格补全,位数小于整数位数,则自动失效
  2. 正数是右对齐,负数是左对齐

n

  1. 精度控制,含义:最少用n位显示数字。
  2. 位数不够用0来补,位数小于整数位,则自动失效。
name = '李四'
age  = 18
height = 188.45

info = '我是%4s,今年%-3i,身高%2.2f'%(name,age,height)  # 我是  李四,今年18 ,身高188.45

# 写法
info2 = f'{height:.2f}'

转义字符

# 使用 \
print('输出一个'字符')
print('输出一个"字符')

# 使用\n换行
print('你好\n你也好')

# 使用\ 输出 \
print('D:\file')

# 使用\b删除前一个字符
print('helloo\b')

# \r 使光标回到本行开头,覆盖输出
print('10%\r20%')

# \t 表示水平制表符,让光标跳转到下一个制表位
print('1234123412341234')
print('ab\tcd')
print('abc\td')
print('abcd\ta')
print('显示\t中文')
"""
1234123412341234
ab  cd
abc d
abcd    a
显示  中文
"""

布尔类型 boolean

只用两个值 True 和 False

a = True
b = False
c = 1 > 2
print(type(a), a) # <class 'bool'>
print(type(b), b) # <class 'bool'> False
print(type(c), c) # <class 'bool'> False

boolean值本质是int的子类型,底层是用1表示True,用0表示False

print(int(True), int(False))  # 1 0

数据类型转换

str() 把数据转为字符串


result = str(10)
result2 = str(10.11)
result3 = str(1.1e3)
result4 = str(10_101)

print(type(result), result)    # <class 'str'> 10
print(type(result2), result2)  # <class 'str'> 10.11
print(type(result3), result3)  # <class 'str'> 1100.0
print(type(result4), result4)  # <class 'str'> 10101

int() 把数据转为整型

result = int(10)
result2 = int('10')
result3 = int(10.99)
result4 = int('  10   ')
print(type(result), result)    # <class 'int'> 10
print(type(result2), result2)  # <class 'int'> 10
print(type(result3), result3)  # <class 'int'> 10
print(type(result4), result4)  # <class 'int'> 10

无效:

  • int(' 1 0 ') 中间有空格
  • int('中文') 中文
  • int('10天')
  • int('1.2')

float() 把数据转为浮点型

result = float(10)
result2 = float('20.2')
result3 = float('  30.3   ')
result4 = float(40.4)
result5 = float('40')
print(type(result), result)    # <class 'float'>  10.0
print(type(result2), result2)  # <class 'float'>  20.2
print(type(result3), result3)  # <class 'float'>  30.3
print(type(result4), result4)  # <class 'float'>  40.4
print(type(result5), result5)  # <class 'float'>  40.0

无效:

  • int(' 1. 0 ') 中间有空格
  • int('中文') 中文
  • int('10天')
  • int('1.2.3')

bool()将数据转为布尔类型

# 使用
print(bool(1), bool(0), bool('1'), bool(''))
# True False True False

六、运算符

1、算术运算符

运算符说明示例结果
+1 + 23
-2 - 11
*1 * 22
/3 / 21.5
//取整3 / 21
%取余8 / 53
**指数2 ** 38

2、赋值与复合赋值运算符

运算符说明示例
=赋值运算name = '李四'
+=加法赋值运算num += 2num = num + 2
-=减法赋值运算num -= 2num = num - 2
*=乘法赋值运算num *= 2num = num * 2
/=除法赋值运算num /= 2num = num / 2
//=取整赋值运算num //= 2num = num // 2
%=取余赋值运算num %= 2num = num % 2
**=指数赋值运算num **= 2num = num ** 2

3、比较运算符

运算符 作用
== 比较左右两侧是否相等
!= 比较左右两侧是否不相等
> 比较左侧是否大于右侧 同类型比较
< 比较左侧是否小于右侧
>= 比较左侧是否大于等于右侧
<= 比较左侧是否小于等于右侧

注:

  1. 比较结果True or False,首字母是大写的
  2. 字符串进行比较时,是依次比较每个字符的Unicode编码
  3. 使用 ord() 查看字符Unicode编码,使用 chr() 将Unicode码转为字符

4、逻辑运算符

运算符名称说明
and逻辑与判断两侧的值,是否都为True
or逻辑或判断两侧的值,是否至少有一个为True
not逻辑非对值取反

and

  • and 返回的不一定是布尔值,而是某个参与计算的值本身
  • and 会先看左边,若左边是“假”,则直接返回左边,否则返回右边
  • 若参与and运算的值不是布尔值,会自动转为布尔值,再进行逻辑与判断
print(True and True)   # True
print(True and False)  # False
print(False and True)  # False
print(False and False) # False

print(2 > 1 and 2 > 0)
# # print(3/2 and 3 / 0)

print(1 - 1 and True)  #  0
print('' and True)     #  
print(True and 2 - 1)  #  1
print(1 + 2 and 2 * 1) #  2

or

  • or 返回的不一定是布尔值,而是参与计算的值本身
  • or 会先看左边,若左边是“真”,则直接返回左边,否则返回右边
  • 若参与or运算的值不是布尔值,会自动转为布尔值,再进行逻辑判断
print(True or True)  # True
print(True or False) # True
print(False or True) # True
print(False or False)# False

print(2 - 1 or False)   # 1
print('你好' or '您好')  # 你好
print(False or 2 / 1)   #  2.0
print(1 + 2 or 2 * 1)   #  3

not

  • 若参与not运算的值不是布尔值,会转为布尔值,再进行逻辑判断
  • not返回的值一定是布尔值
print(not True)  # False
print(not False) # True
print(not 2 > 1) # False
print(not 2 < 1) # True
print(not 0)     # True

七、进制

  • 0b开头表示二进制
  • 0o开头表示八进制
  • 0x开头表示十六进制
# 0b开头表示二进制
num = 0b11001

# 0o开头表示八进制
num2 = 0o1034

# 0x开头表示十六进制
num3 = 0x1cf

Python中所有的非十进制数字,只是代码层面的编写方式,计算时会自动将其转为十进制

# Python中所有的非十进制数字,只是代码层面的编写方式,计算时会自动将其转为十进制
print(num, num2, num3)  # 25 540 463
print(num + 1)  # 26
print(num2 > 1)  # True
print(str(num3))  # 463

Python中的进制转换

方法 说明 示例
bin() 十进制转二进制字符串 bin(25) => '0b11001'
oct() 十进制转八进制字符串 bin(540) => '0o1034'
hex() 十进制转十六进制字符串 hex(463) => '0x1cf'
int() 其他进制转十进制 int('0b11001', 2) => 25
int('0o1034', 8) => 540
int('0x1cf', 16) => 463

八、输入语句

input() 用于获取用户的输入

input()获取到的内容全是字符串类型

name = input('请输入你的名字:')
age = input('请输入你的年龄:')
# 将age转为整型
age = int(age)
print(f'你的名字是{name},今年{age + 1}岁。')

九、流程控制语句

单分支 if

num = int(input('请输入数字:'))
if num >= 18:
    print('输入的数字大于等于18')

双分支 if else

if num > 18:
    print('输入的数字大')
else:
    print('输入的数字小')

多分支 if elif if

if num > 18:
    print('输入的数字大')
elif num < 18:
    print('输入的数字小')
else:
    print('相等')

嵌套分支

if xxx:
    xxx
    if xxx:
        xxx
    elif xxx:
        xxx
    else:
        xxx
else:
    xxx

while 循环

n = 1
while n <= 10:
    print(f'第{n}次')
    n += 1

练习

print('请输入正确密码,才能登录!')
text = '请输入密码!'
password = '123'
inputPassword = ''

while inputPassword != password:
    print(f'{text}')
    inputPassword = input('密码:')
    if inputPassword == password:
        print('密码正确')
    else:
        print('密码错误')

for 循环

遍历range()范围内的数字

# range(10)的范围即[0, 10),为左闭右开
for n in range(10):
    print(n) # 0 1 2 3 4 5 6 7 8 9

for n in range(1, 10):
    print(n) # 1 2 3 4 5 6 7 8 9

遍历字符串

for n in 'abcdef':
    print(n) # a b c d e f

练习:加解密

# 练习1:
text = input('请输入要加密的文字:')
secret = ''
for char in text:
    secret += chr(ord(char) + 1)
print(f'最终加密后:{secret}')

# 练习2:
secret = input('请输入要解密的文字:')
text = ''
for char in secret:
    text += chr(ord(char) - 1)
print(f'最终解密后:{text}')

练习:九九乘法表

while n <= 9:
    m = 1
    while m <= n:
        print(f'{m} * {n} = {n * m}', end='\t')
        if m == n:
            print('')
        m += 1
    n += 1

print('', end ='') 可使输出不换行

continue与break

  • continue 跳出本次循环剩余语句,进入下一次循环
  • break 终止循环,不再执行剩余循环
  • 两者在while和for循环中均可使用,作用一样
  • 两者在嵌套循环中,只能作用在其所在的循环
for m in range(1, 11):
    print(f'第{m}次')
    continue
    print('你好')

for m in range(1, 11):
    print(f'第{m}次')
    if m == 2:
        continue
    print('你好')
for m in range(1, 11):
    print(f'第{m}次')
    break
    print('你好')

for m in range(1, 11):
    print(f'第{m}次')
    if m == 2:
        break
    print('你好')

十、函数

内置函数:docs.python.org/zh-cn/3.13/…

内置模块:docs.python.org/zh-cn/3.13/…

语法:

# 定义
def 函数名():
    函数体
    
# 调用
函数名()
def say():
    print('你好')

say()

参数:

位置参数:

def 函数名(参数1, 参数2...)

def say(name, content):
    print(f'{name}{content}')

say('李四', 'nice to meet you')
say('张三','你好')

关键字参数:

调用时 函数名(形参名=值):

def say(name, age, gender, height):
    print(f'我是{name},年龄{age},性别{gender},身高{height}')
    
say(name='王五', age=18, gender='男', height=188)

# 位置参数需在关键字参数之前使用
say('赵六', 20, gender='女', height=190)

  • 位置参数需在关键字参数之前使用

限制传参方式:

  • / 前面只能用位置参数,* 后面只能用关键字参数
  • / 和 * 同时使用时, / 只能在 * 前面
def say(name, /, age, *, gender, height):
    print(f'我是{name},年龄{age},性别{gender},身高{height}')

say('tom', 18, gender='男', height=190)

参数默认值

  • 语法 def func(参数名=值)
  • 形参使用了默认值,该形参后面的参数也要有默认值
def say(name, age, gender='男', height=180):
    print(f'我是{name},年龄{age},性别{gender},身高{height}')

say('tom', 18)

可变参数

定义函数时,在形参前加 * ,可接受任意数量的位置参数,并打包成一个元组

def func(*args):
    print(args)

func(1, 2, 3)    # (1, 2, 3)

定义函数时,在形参前加 ** ,可接受任意数量的关键字参数,并打包成一个字典


def func(**kwargs):
    print(kwargs)

func(name='李四', age=19, height=76, weight=90)   
# {'name': '李四', 'age': 19, 'height': 76, 'weight': 90}

NoneType 类型:None

  1. None是一个特殊的字面量,它表示:空值/无值/无意义
  2. None的类型是NoneType。
  3. None转为布尔值是False。
  4. None不能参与数学运算,也不能与字符串拼接。
  5. 不给函数设置返回值,函数会默认返回None。
name = None

print(type(name))  # <class 'NoneType'>

nameC = bool(name)

print(nameC)  # False

返回值

使用关键字 return,得到函数返回值

def add(n, m):
    return n+m

res = add(1,2)
print(res)

作用域

全局和局部

在局部作用域中可使用global声明变量是全局变量

n = 1

def add():
    global n
    n = 100
    m = n + 1
    print(m)

add()
add()
print(n)

十一、列表

数据容器:一种能存放多个数据的数据类型

定义列表

定义一个列表[元素1, 元素2, 元素3, 元素4...]

list1 = [1, 2, 3, 4, 5]
list2 = ['a', 'b', 'c', 'd', 'e']
list3 = [1, True, 'a']
list4 = [1,False,'b', [2,4,6]]

# 定义空列表
list5 = []
list6 = list()

print(list1, type(list1)) # [1, 2, 3, 4, 5] <class 'list'>
print(list2, type(list2))
print(list3, type(list3))
print(list4, type(list4))
print(list5, type(list5)) # [] <class 'list'>
print(list6, type(list6))

列表添加元素的方法

方法说明
append()向列表尾部追加一个元素
insert()在指定下标处,插入一个元素
extend()将可迭代对象中的内容依次取出,追加至列表尾部
list1 = [1,2,3]

# append() 尾部追加一个元素
list1.append(6)
print(list1)

# insert() 在指定下标处,插入一个元素
list2 = [1, 2, 3]
list2.insert(2,4)
print(list2)

# extend() 将可迭代对象中的内容依次取出,追加至列表尾部
list1.extend('星期五')
list1.extend(range(1,4))
print(list1)

列表删除元素的方法

方法说明
pop()删除指定位置元素,并返回该元素
remove()移除首次出现的元素
clear()清空所有元素
del删除指定元素
# pop() 删除指定位置元素,并返回该元素
list1 = [1,2,3]
res = list1.pop(1)
print(res, list1)

# remove() 移除首次出现的元素
list2 = [1,2,3,1]
list2.remove(1)
print(list2)

# clear() 清空所有元素
list2.clear()
print(list2)

# del 删除指定元素
list3 = [1,2,3,4]
del list3[1]
print(list3)

列表修改元素的方法 列表名[下标] = 值

列表查询元素的方法 列表名[下标]

常用方法

方法说明
index()返回元素首次出现的下标
count()返回元素出现的次数
reverse()反转列表无返回值
sort(reverse=True/False)排序列表无返回值
list1 = ['a', 'b', 'c', 'a']

# index() 返回元素首次出现的下标
res1 = list1.index('a')
print(res1)

# count() 返回元素出现的次数
res2 = list1.count('a')
print(res2)

# reverse() 反转列表元素,无返回值
list1.reverse()
print(list1)

# sort() 排序列表,无返回值
list1.sort(reverse=False) # reverse=True/False 可控制是否排序
print(list1)

常用内置函数

方法说明
sorted(容器, reverse=True/False)排序返回排序后的容器
max()找出最大值返回最大值
min()找出最小值返回最小值
len()获取容器中元素总数返回元素总数
sum()对容器元素求和<字符串不能使用sum>返回元素总和
list1 = [4, 5, 7, 1, 2, 3, 8, 6]

# sorted() 对容器进行排序
res1 = sorted(list1)
print(res1)
print(list1)

# max() 找出最大值
res2 = max(list1)
print(res2)

# min() 找出最小值
res3 = min(list1)
print(res3)

# len() 获取元素总数
res4 = len(list1)
print(res4)

# sum() 对容器元素求和<字符串不能使用sum>
res5 = sum(list1)
print(res5)

循环遍历

  1. 通过 for item in list1: 获取每项元素item
  2. for index in range(len(list1)): 获取到元素下标index
  3. for index, item in enumerate(list1, start=5):获取到元素下标index和元素item,参数start可让计数从指定值开始

十二、元组

  • 元组是和列表类似的数据容器,区别是元组中的元素不可修改
  • 元组中存在可变类型(列表),那可变类型中的元素可修改

定义一个元组(元素1, 元素2, 元素3, 元素4...)

定义空元组

t1 = ()
t2 = tuple()

定义只有一个元素的元组:元素后须跟逗号

t1 = ('a',)
t2 = (1,)
# 定义元组
t1 = (1, 2, 3, 4, 5)
t2= ('a', 'b', 'c', 'd', 'e')
t3 = (1,True,'哈哈', (30,230,239))

# 元组下标
print(t1[1])
print(t2[-1])
print(t3[2])
print(t3[3][0])

# 元组中存在可变类型(列表),那可变类型中的元素可修改
t4 = (1,2,3,4, [5,6,7,8, ('你好', 'hh')])
# t4[0] = 10 X
# t4[4] = 100 X
t4[4][1] = 55
# t4[4][1][0] = 22 X
print(t4)

常用方法

方法说明
index()返回元素首次出现的下标
count()返回元素出现的次数
t1 = (1,2,2,4,5,2)

print(t1.index(2))  # 1

print(t1.count(2))  # 3

常用内置函数

方法说明
sorted(容器, reverse=True/False)排序返回排序后的容器
max()找出最大值返回最大值
min()找出最小值返回最小值
len()获取元组中元素总数返回元组长度
sum()对元组元素求和<字符串不能使用sum>返回元素总和
t1 = (1, 2, 2, 4, 5, 2)

print(max(t1))  # 5
print(min(t1))  # 1
print(len(t1))  # 6
print(sorted(t1))  # [1, 2, 2, 2, 4, 5]
print(sum(t1))  # 16

使用*对解包列表或元组

def test(*args):
    print(args)

tt1 = (1,2,3,4,5,6)
tt2 = ('a','b','c','d','e','f')
test(*tt1) # 相当于test(1,2,3,4,5,6)
test(*tt2)

十三、字符串 str

  • 字符串同样有下标
  • 字符串中的字符不可修改
  • 字符串不能嵌套

常用方法

方法说明
index()返回字符首次出现的下标
split()按照指定字符分割,组成新列表
replace()将某个字符替换成目标字符
count()返回字符出现的次数
strip()删除指定字符串中的任意字符
  • strip() 从字符两端开始删除,直到遇到第一个不在字符串中的字符就停止
str1 = 'welcome to python'

print(str1[1])
# str1[1] = '2' # X

print(str1.index('e')) # 1
print(str1.split(' ')) # ['welcome', 'to', 'python']
print(str1.replace('e','*')) # w*lcom* to python
print(str1.count('e')) # 2


# strip() 从字符两端开始删除,直到遇到第一个不在字符串中的字符就停止

str3 = '111we1l1come111'
res = str3.strip('1')
print(res) # we1l1come

str4 = '1234py32thon1342'
res2 = str4.strip('1234')
print(res2) # py32thon

str5 = '12534py32thon16342'
res5 = str5.strip('321')
print(res5) # 534py32thon1634

# strip()不传参数,用于去两端空格
str6 = '   python    '
res6 = str6.strip()
print(res6)  # python

常用内置函数

方法说明
len()统计字符串中字符的个数
max()找出字符串中unicode编码值最大的字符
min()找出字符串中unicode编码值最小的字符
sorted()按照unicode值排序

序列的切片操作

  • 序列:能连续存放元素的数据容器,元素有先后顺序,可通过下标访问,列表、元组、字符串都是序列
  • 切片:从序列中按照指定范围,取出一部分元素,形成一个新序列的操作

语法:[起始索引:结束索引:步长] 默认起始索引为0,结束索引截取到末尾,步长为1

起始索引大于结束索引时,步长需为负数

list1 = [10, 20, 30, 40, 50, 60, 70, 80, 90]

res1 = list1[1:5:1]  # [20, 30, 40, 50]
res2 = list1[2:8:2] # [30, 50, 70]
res3 = list1[::]  # [10, 20, 30, 40, 50, 60, 70, 80, 90]
res4 = list1[5:2:-1]  # [60, 50, 40]
res = list1[::-1]  # [90, 80, 70, 60, 50, 40, 30, 20, 10]

相加:新序列 = 序列1 + 序列2 同类型的才能相加

list2 = [1,2,3]
list3 = [4,5,6]
res23 = list2 + list3   # [1, 2, 3, 4, 5, 6]

t1 = ('a','b','c')
t2 = ('c','b','a')

t12 = t1 + t2 # ('a', 'b', 'c', 'c', 'b', 'a')

str1 = 'hello'
str2 = ' world'
str3 = str1 + str2  # hello world

相乘(重复)新序列 = 序列 * n n必须为整数

list2 = [1,2,3]
t1 = ('a','b','c')
str1 = 'hello'

print(list2*2) # [1, 2, 3, 1, 2, 3]
print(t1*2) # ('a', 'b', 'c', 'a', 'b', 'c')
print(str1*2) # hellohello

十四、集合

集合内部元素无序,不能通过下标访问元素,会自动去重

set()

可变集合:创建后可以增删元素 {元素0, 元素1, 元素3...}

s1 = {10, True, 1, '你好'}
s2 = {1, 2, 3, 1, 3, 4, 5}
s3 = {'a', 'b', 'c', 'a', 'd'}
print(s1, type(s1))  # {True, 10, '你好'} <class 'set'>
print(s2, type(s2))  # {1, 2, 3, 4, 5} <class 'set'>
print(s3, type(s3))  # {'c', 'b', 'a', 'd'} <class 'set'>

定义空集合 s1 = set()

不可定义s1 = {},因为直接写{}定义的是空字典

frozenset()

不可变集合:创建后不可增删元素 frozenset({元素0, 元素1, 元素3...})

s1 = frozenset({10, True, 1, '你好'})
s2 = frozenset({1, 2, 3, 1, 3, 4, 5})
s3 = frozenset({'a', 'b', 'c', 'a', 'd'})
print(s1, type(s1))  # frozenset({True, 10, '你好'}) <class 'frozenset'>
print(s2, type(s2))  # frozenset({1, 2, 3, 4, 5}) <class 'frozenset'>
print(s3, type(s3))  # frozenset({'d', 'c', 'a', 'b'}) <class 'frozenset'>

frozenset接收的参数,可以是任意可迭代参数,但最终返回的一定是【不可变集合】

s1 = frozenset(['a', 'b', 'c', 'd'])
s2 = frozenset(['a', 'b', 'c', 'd'])
s3 = frozenset('hello')
print(s1, type(s1)) # frozenset({'b', 'c', 'd', 'a'}) <class 'frozenset'>
print(s2, type(s2)) # frozenset({'b', 'c', 'd', 'a'}) <class 'frozenset'>
print(s3, type(s3)) # frozenset({'o', 'e', 'h', 'l'}) <class 'frozenset'>

定义空集合 s1 = frozenset()

集合中不可嵌套【可变集合】,但可以嵌套【不可变集合】

s1 = {1, 2, 3, 4, 5}
s2 = frozenset({10, 20, 30, 40, 50})
l1 = [100, 200, 300, 400, 500]
t1 = (11, 22, 33, 44, 55)
# s3 = {66, 77, 88, s1} # 报错
s3 = {66, 77, 88, s2}  # 运行正常
# s4 = {6, 7, 8, l1}  # 报错
s4 = {6, 7, 8, t1}  # 运行正常

集合的增删改

增:add()、update()

# add() 方法向集合中添加元素
s1.add(4)
s1.add(5)
print(s1)

# update() 方法向集合中添加元素(必须传递可迭代对象,列表、元组、集合等)
s1.update([10, 20, 30])
s1.update((40, 50))
s1.update({60, 70, 80})
s1.update(range(100, 105))
print(s1)

删:remove()、discard()、pop()、clear()

s1 = {1, 2, 3, 4, 5}
# remove() 从集合中移除指定元素,若不存在,会报错,无返回值
# s1.remove(1)

# discard() 从集合中移除指定元素,若不存在,不会报错,无返回值
# s1.discard(5)

# pop() 从集合中移除任意一个元素(随机),返回移除的元素
res = s1.pop()

# clear() 清空集合
s1.clear()
print(res, s1)

集合没有下标,也不支持切片操作

改:

可通过remove和add达到修改操作

s1 = {1, 2, 3, 4, 5}
s1.remove(4)
s1.add(6)

查:

可通过【成员运算符】innot in去查看集合中是否有某个元素

s1 = {1, 2, 3, 4, 5}

res = 1 in s1
res2 = 1 not in s1
print(res, res2)

常用方法

difference()

a = {1, 2, 3, 5}
b = {4, 5, 6, 1}

# difference找出集合a中不同于集合b的元素,集合a和集合b都不变,返回一个新集合
res = a.difference(b)

print(a) # {1, 2, 3, 5}
print(b) # {1, 4, 5, 6}
print(res) # {2, 3}

difference_update()

a = {1, 2, 3, 5}
b = {4, 5, 6, 1}

# difference_update 从集合a中,删除b中存在的元素,集合a会被修改,b不会,无返回值
a.difference_update(b)

print(a) # {2, 3}
print(b) # {1, 4, 5, 6}

union()

a = {1, 2, 3, 5}
b = {4, 5, 6, 1}

# 合并两个集合,集合a、b都不变,返回一个新集合
res = a.union(b)

print(a) # {1, 2, 3, 5}
print(b) # {1, 4, 5, 6}
print(res) # {1, 2, 3, 4, 5, 6}

issubset()、issuperset()、isdisjoint()


a = {1, 2, 3}
b = {1, 2, 3, 4, 5, 6}
c = {4, 5, 6, 7, 8, 9}

# 判断集合a是否是集合b的子集,返回布尔值
print(a.issubset(b)) # True
print(c.issubset(b)) # False

# 判断集合a是否是集合b的超集,返回布尔值
print(b.issuperset(a)) # True
print(c.issuperset(b)) # False

# 判断集合a和集合b是否没有交集,返回布尔值
print(a.isdisjoint(b)) # False
print(a.isdisjoint(c)) # True

集合中的数学运算

并集 集合a | 集合b

a = {1, 2, 3, 4, 5, 6}
b = {4, 5, 6, 7, 8, 9}

# 并集:合并集合的元素
res = a | b # {1, 2, 3, 4, 5, 6, 7, 8, 9}

交集 集合a & 集合b

# 交集:找出集合中共有的元素
# res = a & b # {4, 5, 6}

差集 集合a - 集合b

# 差集 去除集合a中,集合b中也有的元素
# res = a - b # {1, 2, 3}

对称差集 集合a ^ 集合b

# 对称差集 在并集中去除掉交集
# res = a ^ b # {1, 2, 3, 7, 8, 9}

集合的循环遍历

集合没有下标,所以不能使用while,但可以使用for


b = {4, 5, 6, 7, 8, 9}

# 循环遍历
for item in b:
    print(item)
    
# 集合没有下标 不能使用while
# index = 0
# while index < len(b):
#     print(b[index])

十五、字典

定义字典 {key: value}

d1 = {'张三': 11, '李四': 2, '王五': 3, 'tom': 4}
print(d1, type(d1)) # {'张三': 11, '李四': 2, '王五': 3, 'tom': 4} <class 'dict'>

# 定义空字典
d2 = {}
d3 = dict()

字典key不能重复

# 字典的key不能重复,若出现重复,后者会覆盖前者
d1 = {'张三': 11, '李四': 2, '王五': 3, 'tom': 4, '张三': 22}
print(d1)

key必须是不可变类型,但value可为任意类型

# key必须是不可变类型,但value可为任意类型
d1 = {12: 12, '李四': 22}
d2 = {('你好', 'python'): 12, '李四': 22}
d3 = {['你好', 'python']: 23} # 报错

字典可以嵌套

# 字典嵌套
d1 = {
    2001: {'张三': 11, '李四': 2, '王五': 3, 'tom': 4},
    2002: {'张三': 12, '李四': 22, '王五': 3, 'tom': 4},
    2003: {'张三': 13, '李四': 23, '王五': 3, 'tom': 4},
}

字典增删改查

</tbody>
操作 写法 说明 返回值
新增 字典[key] = 值 新增一组键值对(若键已存在则变为修改)
删除
del 字典[key] 删除指定键对应的键值对(键不存在会报错)
字典.pop(key, 默认值) 删除指定键对应的键值对 返回被删除键的值或默认值
字典.clear() 删除所有键值对
修改
字典[key] = 值 修改指定键对应的键值对(键不存在则为新增)
字典.update(其他字典) 批量修改或合并多个键值对
查询
字典[key] 根据键取值(键不存在会报错) 键对应的值
字典.get(key, 默认值) 安全取值,键不存在则返回默认值,未设置默认值返回None 键对应的值或默认值

常用方法

方法说明返回值类型
字典.keys()获取所有的键'dict_keys'
字典.values()获取所有的值'dict_values'
字典.items()获取所有的键值对'dict_items'
dict1 = {'a': 1, 'b': 2, 'c': 3, }

# keys:获取字典中所有的键
res = dict1.keys() # dict_keys(['a', 'b', 'c']) <class 'dict_keys'>

# dict_keys和列表相似,可以遍历,但不能用下标访问
for item in res:
    print(item, end='\t')
# print(res[0])

# 借助内置函数list函数,可将dict_keys转为list
res_list = list(res) # ['a', 'b', 'c'] <class 'list'>

# values: 获取字典中所有的值
res = dict1.values() # dict_values([1, 2, 3]) <class 'dict_values'>

# items: 用于获取字典中所有键值对
res = dict1.items() # dict_items([('a', 1), ('b', 2), ('c', 3)]) <class 'dict_items'>

十六、类

定义一个类

类名通常使用大驼峰写法

  • 当一个函数被定义在了类中时,那这个函数就被称为:方法
  • init 方法:初始化方法,是给当前正在创建的实例对象添加属性
  • init 方法接收到的参数:当前正在创建的实例对象(self)、其他的自定义参数
  • 当创建Person类实例的时候,Python会自动调用__init__
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 创建实例对象
p1 = Person('张三', 19)

实例属性

  • 给实例添加属性(语法为:self.属性名 = 值 )
  • 通过实例.属性名 = 值 给实例添加的属性,就是实例属性
  • 实例属性只能通过实例访问,不能通过类访问,每个实例间的实例属性都是互不干扰的

通过 实例.dict 可以查看实例上的所有属性

# print(p1.__dict__) 
# {'name': '张三', 'age': 18, 'gender': 'male'}

类属性

  • 类属性可以通过类访问,也可以通过实例访问
  • 类属性通常用于保存 公共数据
class Person:
    # 类属性
    max_age = 120
    min_age = 90
    
    def __init__(self, name, age):
        self.name = name
        if age > Person.max_age:
            self.age = Person.max_age
        else:
            self.age = age

# 类属性
p1 = Person('张三', 910)
# p1.speak('哈哈哈')

# 类属性是存在类身上的,实例上是没有的
print(p1.__dict__)
# {'name': '张三', 'age': 120}
print(Person.__dict__)
# {'__module__': '__main__', '__firstlineno__': 2, 'max_age': 120, 'min_age': 90, '__init__': <function Person.__init__ at 0x0000021FF0966610>, 'speak': <function Person.speak at 0x0000021FF0966820>, '__static_attributes__': ('age', 'name'), '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

# 类属性可以通过类访问,也可通过实例访问
print(Person.min_age)
print(p1.min_age) # 查找min_age的过程:1、实例自身(p1) => 2、实例的缔造者(Person)

自定义方法

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def speak(self, words):
        print(f'我是{self.name},年龄{self.age},说了{words}')


p2 = Person('李四', 22)
# speak 方法是存在Person类身上的,Person的实例对象上是没有speak方法的
# p2.speak('侬好!')
print(p2.__dict__)
# {'name': '李四', 'age': 22}
print(Person.__dict__)
# {'__module__': '__main__', '__firstlineno__': 2, '__init__': <function Person.__init__ at 0x0000024F783A6610>, 'speak': <function Person.speak at 0x0000024F783A6820>, '__static_attributes__': ('age', 'name'), '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

# 当执行p2.speak()时,查找speak的过程:1.实例对象自身(p2) => 2.实例的缔造者身上(Person)
def speak():
    print('你好~')
p2.speak = speak
p2.speak()
# 你好~

实例方法

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    # speak、run方法都存在类身上,主要是供实例使用,即实例方法
    def speak(self, msg):
        print(f'我是{self.name},年龄{self.age},说了{msg}')

    def run(self, km):
        print(f'{self.name}距离还有{km}')

# 创建实例
p1 = Person('张三', 88)
# 通过实例调用实例方法
p1.run(100)
# 通过类调用实例方法
Person.run(p1,200)

类方法

  • 通过@classmethod装饰过的方法就是类方法,类方法是保存在类身上的
  • 类方法收到的参数:当前类本身cls、自定义的参数
  • cls:就可访问类属性
  • 类方法通常用于实现与类相关的逻辑:操作类级别的信息,一些工厂函数
from datetime import datetime

class Person:
    max_age = 120
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # 实例方法
    def speak(self, msg):
        print(f'我是{self.name},年龄{self.age},说了{msg}')

    def run(self, km):
        print(f'{self.name}距离还有{km}')

    # 类方法
    @classmethod
    def test(cls, data):
        print(f'这里是类方法{data}')

    @classmethod
    def get_age(cls, value):
        print(cls, cls.max_age) # <class '__main__.Person'> 120
        current_year = datetime.now().year
        age = current_year - value
        return age



p1 = Person('张三', 88)

# 类方法是存在类身上的
# print(Person.__dict__)

# 类方法要通过类调用
# Person.test(100)

res = Person.get_age(1997)
print(res)

静态方法

  • 使用 @staticmethod 装饰过的方法,就叫:静态方法,静态方法也是保存在类身上的
  • 静态方法只是单纯的定义在类中,它不会收到:self、cls它收到的参数都是自定义参数
  • 由于静态方法没有收到:self、cls参数,所以其内部不会访问任何:类和实例相关的内容
  • 静态方法常用于定义:与类相关的工具方法
from datetime import datetime

class Person:
    age_limit = 18

    def __init__(self, name, age):
        self.name = name
        self.age = age

    @staticmethod
    def is_adult(year):
        current_year = datetime.now().year
        age = current_year - year
        return age >= 18

# 静态方法是存在类身上的
print(Person.__dict__)
# {'__module__': '__main__', '__firstlineno__': 3, 'age_limit': 18, '__init__': <function Person.__init__ at 0x000001E1C4D06610>, 'is_adult': <staticmethod(<function Person.is_adult at 0x000001E1C4D06820>)>, '__static_attributes__': ('age', 'name'), '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

# 静态方法要通过类去调用
res = Person.is_adult(2015)
print(res)

继承

定义一个Student类(子类、派生类),继承自Person类(父类、超类、基类) class Student(Person):

class Person:

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def speak(self, words):
        print(f'{self.name} is {self.age} years old, said {words}')

    # 定义一个Student类(子类、派生类),继承自Person类(父类、超类、基类)
class Student(Person):
    pass

# 创建Student类的实例
s1 = Student('李四', 18)
print(s1.name)
print(s1.age)

# 查找speak的过程:1、实例自身(s1) => 2、Student类 => 3、Person类
s1.speak('hello')

在子类中,有两种方式去调用父类的初始化方法,来实现对继承属性name,age的初始化

class Person:

    def __init__(self, name, age):
        self.name = name
        self.age = age

# 定义一个Student类
class Student(Person):
    def __init__(self, name, age, stu_id, stu_grade):
        
        # 方式一
        super().__init__(name, age)

        # 方式二
        Person.__init__(self, name, age)

        # 子类独有的属性 需自己初始化
        self.stu_id = stu_id
        self.stu_grade = stu_grade

    def study(self ):
        print(f'我叫{self.name},现在{self.stu_grade}年级')


# 创建Student类的实例
s1 = Student('李四', 18, 1001, 2)

# 查找study的过程:1、实例自身(s1) => 2、Student类 => 3、Person类
s1.study()

方法重写

当子类中定义一个与父类中相同的方法,那子类中的方法就会“重写”父类中的方法

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def speak(self, words):
        print(f'{self.name} is {self.age} years old, said {words}')

# 定义一个子类继承父类
class Student(Person):
    def __init__(self, name, age, stu_id, stu_grade):
        super().__init__(name, age)
        self.stu_id = stu_id
        self.stu_grade = stu_grade

    # 方法重写
    def speak(self, words):
        # 调用父类
        super().speak(words)
        print(f'学号是{self.stu_id},今年{self.stu_grade}年级,说了{words}')

# 创建实例
s1 = Student('李四',19, '10021', 3)
s1.speak('你好')

常用方法

  1. issubclass(Class, Class) 判断某个类是否为另一个类的子类
  2. isinstance(instance, Class) 判断某个对象是否为指定类或子类的实例
...
p1 = Person('张三', 28)
s1 = Student('李四',19, '10021', 3)

# isinstance
print(isinstance(p1, Person))
print(isinstance(s1, Student))
print(isinstance(s1, Person))
print(isinstance(p1, Student))
# True True True False

# issubclass
print(issubclass(Student, Person))
print(issubclass(Person, Student))
# True False

多重继承

定义一个子类,继承多个父类class Student(Person, Worker):

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def speak(self, words):
        print(f'{self.name} is {self.age} years old, said {words}')

class Worker:
    def __init__(self, work_name):
        self.work_name = work_name

    def do_work(self):
        print(f'{self.work_name} is doing work')

# 定义一个子类,继承多个父类
class Student(Person, Worker):
    def __init__(self, name, age, work_name, grade):
        # 用类名去初始
        Person.__init__(self, name, age)
        Worker.__init__(self, work_name)
        self.grade = grade

    def study(self):
        print(f'study hard,{self.grade} grade!')

s1 = Student('张三', 10, 'waiter',5)
print(s1.__dict__)
s1.speak('hello')
s1.do_work()
s1.study()

# 类的__mro__属性,用于记录属性和方法的查找顺序
# 通过实例查询属性或方法时,会现在实例自身找,若没有,再按照__mro__记录的顺序去查找
print(Student.__mro__)
# (<class '__main__.Student'>, <class '__main__.Person'>, <class '__main__.Worker'>, <class 'object'>)

三种访问限制

  1. 共有属性 属性名 当前类、子类、类外部,都能访问
  2. 受保护的属性 _属性名 当前类、子类,都能访问
  3. 私有属性 __属性名 仅能在当前类中访问
class Person:
    def __init__(self, name, age, id_number):
        self.name = name  # 共有属性
        self._age = age  # 受保护的属性
        self.__id_number = id_number  # 私有属性
        
    def speak(self):
        print(f'名称:{self.name}, 年龄:{self._age}, 身份证:{self.__id_number}')

class Student(Person):
    def study(self):
        print(f'子类中-名称:{self.name}, 年龄:{self._age}, 身份证:{self.__id_number}')

# 当前类中访问
p1 = Person('李四', 19, '10002')
p1.speak() # 名称:李四, 年龄:19, 身份证:10002

# 子类中访问
s1 = Student('张三', 22, '10003')
# s1.study() # AttributeError: 'Student' object has no attribute '_Student__id_number'

# 在外部访问
print(p1.name) # 李四 共有属性可以正常访问
# print(p1._age) # 19 受保护的属性强制访问,可以访问,但不推荐
# print(p1.__id_number) # 私有属性访问报错 AttributeError: 'Person' object has no attribute '__id_number'

# Python底层是通过重命名实现私有属性的
print(p1.__dict__) # {'name': '李四', '_age': 19, '_Person__id_number': '10002'}
print(p1._Person__id_number) # 10002 不推荐

getter&setter

注册age属性的getter方法

@property
def age(self):
...

注册age属性的setter方法

@age.setter
def age(self,value):
...
class Person:
    max_age = 120

    def __init__(self, name, age, id_number):
        self.name = name
        self._age = age
        self.__id_number = id_number

    # 注册age属性的getter方法,当访问Person实例的age属性时,下面的age方法就会被自动调用
    @property
    def age(self):
        return self._age
        
    # 注册age属性的setter方法,当修改Person实例的age属性时,下面的age方法就会被自动调用
    @age.setter
    def age(self,value):
        if value > self.max_age:
            print('年龄超出限制')
        else:
            self._age = value
            
    # 注册id_number属性的getter方法,当访问Person实例的id_number属性时,下面的id_number方法就会被自动调用
    @property
    def id_number(self):
        return self.__id_number[:6] + '*******'+ self.__id_number[-4:]
    
    # 注册id_number属性的setter方法,当修改Person实例的id_number属性时,下面的id_number方法就会被自动调用
    @id_number.setter
    def id_number(self,value):
        print('身份证号不允许修改!')

p1 = Person('张三', 22, '370831199712345678')
print(p1.age)
p1.age = 130
print(p1.age)
print(p1.id_number)
p1.id_number = '10002'
print(p1.id_number)

魔法方法

__xxx__双下划线开头和结尾命名的特殊方法

方法调用时机
__str__当调用print(对象)或str(对象)时
__len__当调用len(对象)时
__gt__当执行对象1>对象2时
__lt__当执行对象1<对象2时
__eq__当执行对象1==对象2时
__getattr__当访问不存在属性时

class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    # 当执行 print(Person的实例对象) 或 str(Person的实例对象) 时调用
    def __str__(self):
        # 默认返回 <__main__.Person object at 0x0000021B2B7270E0>
        # 修改返回 我是张三,今年22
        return f'我是{self.name},今年{self.age},性别{self.gender}'

    # 当执行 len(Person的实例对象) 时调用
    def __len__(self):
        return len(self.__dict__)

    # 当执行 Person实例对象1 > Person实例对象2 时调用
    def __gt__(self, other):
        return self.age > other.age

    # 当执行 Person实例对象1 < Person对象实例2 时调用
    def __lt__(self, other):
        return self.age < other.age

    # 当执行 Person实例对象1 == Person对象实例2 时调用
    def __eq__(self, other):
        return self.__dict__ == other.__dict__

    # 当访问不存在属性时
    def __getattr__(self, item):
        return f'属性{item}不存在'

p1 = Person('张三', 22, '男')
p2 = Person('李四', 23, '女')
# print(p1)
# print(str(p1))

# print(len(p1))

# res = p1 > p2
# print(res)

# res = p1 < p2
# print(res)

res = p1 == p2
print(res)

print(p1.no_name)

object类

# Python中,所有的类都继承了 object 类,即object类是所有类的顶层父类
class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age


# Person类继承了object类
# print(issubclass(Person, object))

# 所有的类继承了object类
# print(issubclass(int, object))
# print(issubclass(str, object))
# print(issubclass(bool, object))
# print(issubclass(list, object))
# print(issubclass(tuple, object))

# object是所有类的父类,所以所有对象,都间接是object的实例
p1 = Person('张三', 12)
# print(isinstance(p1, Person))
# print(isinstance(p1, object))
# print(isinstance(200, object))
# print(isinstance('python', object))
# print(isinstance(True, object))
# print(isinstance(None, object))
# print(isinstance([1, 2, 3], object))
# print(isinstance({'a', 'b'}, object))

# 调用对象都继承了object类所提供的 各种属性和方法,从而保证了每个对象都具备统一的基本能力
for item in object.__dict__:
    print(item)

多态

  • 多态:同一个方法,在不同的对象上调用时,能呈现出不同的行为
  • 多态又分 标准多态、鸭子多态

标准多态

标准多态的三个条件:继承关系、方法重写、类型限制

class Animal:
    def speak(self):
        print('动物们的叫声')

class Dog(Animal):
    def speak(self):
        print('汪汪汪')

class Cat(Animal):
    def speak(self):
        print('喵喵喵')

class Pig:
    def speak(self):
        print('哼哼哼')

def make_sound(animal: Animal): # 类型注解
    # 多态的体现
    animal.speak()

# 创建实例
a1 = Animal()
d1 = Dog()
c1 = Cat()
p1 = Pig()

make_sound(a1)
make_sound(d1)
make_sound(c1)
make_sound(p1) # 不报错,但不推荐

鸭子多态

鸭子类型是一种编程风格,不检查对象的类型,只关注对象能否“做某件事”(是否有对应的方法)

# 鸭子多态

class Animal:
    def speak(self):
        print('动物们的叫声')

class Dog:
    def speak(self):
        print('汪汪汪')

class Cat:
    def speak(self):
        print('喵喵喵')

class Pig:
    def speak(self):
        print('哼哼哼')

def make_sound(animal):
    animal.speak()


# 创建实例
a1 = Animal()
d1 = Dog()
c1 = Cat()
p1 = Pig()

make_sound(a1)
make_sound(d1)
make_sound(c1)
make_sound(p1)

抽象类

抽象类是一种不能直接实例化的类,它通常作为“规范”,让子类去继承,并实现其中定义的的抽象方法

from abc import ABC, abstractmethod

# MustRun类一旦继承就ABC类,那么MustRun类就是抽象类了
class MustRun(ABC):
    @abstractmethod
    def run(self):
        pass

# m1 = MustRun()     #无法实例化抽象类 'MustRun'

# 类 Person 必须实现所有 abstract 方法
class Person(MustRun):
    def __init__(self, name):
        self.name = name
    def run(self):
        print(f'{self.name} is running...')

p1 = Person('P1')
p1.run()