Python

Python能做什么?

爬虫

大数据与数据分析 Spark

自动化运维与自动化测试

Web开发:Flask、Django

机器学习:Tensor Flow

胶水语言:混合其他如C++、Java等来编程。能够把用其他语言制作的各种模块(尤其是C/C++)很轻松地联结在一起

基本数据类型

查看数据类型

特殊:

‘/‘ 除法会自动转换为浮点数

‘//‘ 是整除

1
2
type(2/2) # <class 'float'>
type(2//2) # <class 'int'>

查看ascii码

1
ord()

查看变量在内存里的地址

1
id()

转字符串

python不能字符串 + 数字,所以数字要先转成字符串

1
str()

数字

整数 int:

bin( ) 转换为二进制

int( ) 转换为十进制

hex( ) 转换为十六进制

oct( ) 转换为八进制

浮点数 float:

默认都是双精度

布尔类型 bool:

只要非0即为真

1
2
3
4
bool(1.1)
bool(-1.1)
bool('abc')
bool([1,2,3])

空值会被认为是假

1
2
3
4
bool('')
bool([])
bool({})
bool(None)

复数 complex:

用小写字母j表示,例如 a = 1 + 36j

序列

序列是有序的

字符串 str

字符串定义:

单引号,双引号:单行字符串

1
2
a = '字符串1'
b = "字符串2"

三引号:多行字符串

1
2
3
4
a = '''
多行字符串
哈哈
'''

原始字符串:

字符串前面加一个 r ,那么它就不是一个普通字符串了,而是一个原始字符串(忽略转义)

1
2
3
4
5
6
7
8
a = '12\n456'
print(a)
# 输出:
# 12
# 456
b = r'12\n456'
print(b)
# 12\n456

拼接字符串:

1
2
3
4
a = "123"
b = "456"
c = a + b
print(c) # 123456

字符串乘法:

1
2
3
a = "123"
a *= 3
print(a) # 123123123

字符串元素的访问:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
a = "0123456789"
print(a[5]) # 5

# [-n]代表字符串从后往前数n次,得到的字符
print(a[-1]) # 9

# 获取一组字符 前闭后开
print(a[0:5]) # 01234
print(a[0:-1]) # 012345678

# 获取从一个字符开始到字符串末尾的所有字符
print(a[3:]) # 3456789
print(a[-3:]) # 789

# 获取从头开始到指定字符的所有字符
print(a[:5]) # 01234
print(a[:-3]) # 0123456

列表 list

列表中的元素不一定要是同类型的:

1
2
a = [1, 2, "哈哈"]
print(type(a)) # <class 'list'>

列表中还可以嵌套列表:

1
2
a = [[1, 2, 3], ["123", 0]]
print(type(a)) # <class 'list'>

列表元素的访问:

下标访问和字符串的访问一样,可以用下标单个访问,也可以用[0 : 2]范围访问

用单个下标访问,得到的是元素本身的类型

用 : 范围访问,得到的仍然是一个列表

1
2
3
4
5
a = [1, 2, 3, "数字"]
print(type(a[0])) # <class 'int'>
print(type(a[-1])) # <class 'str'>
print(type(a[0:])) # <class 'list'>
print((a[0:])) # [1, 2, 3, '数字']

列表运算:

支持列表相加和列表乘以常数

1
2
3
4
5
6
a = [1, 2, 3];
b = [4, 5, 6];
c = a + b;
print(c) # [1, 2, 3, 4, 5, 6]
b *= 3;
print(b) # [4, 5, 6, 4, 5, 6, 4, 5, 6]

元组 tuple

元组中的元素不一定要是同类型的:

1
2
a = (1, 2, "345")
print(type(a)) # <class 'tuple'>

下标访问和字符串的访问一样,和列表也一样,支持单个元素下标访问,也支持范围访问

支持元组相加

支持元组乘以常数

定义只有一个元素的元组:

编译器规定,如果只有一个元素,不会吧( )识别成元组,而是识别成数学运算的( )去计算

1
2
3
4
a = ("hello")
b = (1)
print(type(a)) # <class 'str'>
print(type(b)) # <class 'int'>

定义只有一个元素的元组就在元素后面再加一个逗号即可:

1
2
3
4
a = ("hello",)
b = (1,)
print(type(a)) # <class 'tuple'>
print(type(b)) # <class 'tuple'>

定义空的元组:直接空括号即可

1
2
a = ()
print(type(a)) # <class 'tuple'>

序列的共同特点

str,list,tuple

  • 序列里面的元素都会被分配一个序号

    1
    2
    a = [1, 2, 3]
    a[1]
  • 序列支持切片

    1
    2
    a = [1, 2, 3]
    a[0:2]
  • 序列可以相加,可以乘以常数

  • 判断一个序列中是否包含某个元素:in运算符

    1
    2
    a = [1, 2, 3]
    print(3 in a) # True
  • 判断一个序列中是否不包含某个元素:not in运算符

    1
    2
    a = [1, 2, 3]
    print(3 not in a) # False
  • 序列内部元素个数:len()

    1
    2
    3
    4
    5
    6
    a = [1, 2, 3]
    print(len(a)) # 3
    b = "asd"
    print(len(b)) # 3
    c = (1,)
    print(len(c)) # 1
  • 序列中最大的元素:max()

    1
    2
    c = (1, 2, 9)
    print(max(c)) # 9
  • 序列中最小的元素:min()

    1
    2
    c = "helloworld"
    print(min(c)) # d

集合

集合 set

set 是无序的,不支持下标索引,不支持切片:

1
2
3
a = {1, 2, 3, 4, 5, 6}
# a[0]
print(type(a)) # <class 'set'>

集合元素不重复:

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

查看集合长度:len()

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

支持in 和 not in:

1
2
3
a = {1, 1, 2, 2, 3, 4, 5, 6}
print(1 in a)
print(1 not in a)

支持max 和 min:

1
2
3
a = {1, 1, 2, 2, 3, 4, 5, 6}
print(max(a))
print(min(a))

支持求集合差集:

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

支持求交集:

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

支持求并集:

1
2
3
4
a = {1, 2, 3, 4, 5, 6}
b = {3, 4, 7}
c = a | b
print(c) # {1, 2, 3, 4, 5, 6, 7}

定义一个空的集合:

1
2
3
a = set()
print(type(a)) # <class 'set'>
print(len(a)) # 0

字典 dict

空的字典直接 a = {}即可

字典dict也是无序的

字典有key和value,通过key访问value

定义方式:

1
a = {key1:value1, key2:value2....}
1
2
a = {1:1, 2:2, 3:3}
print(type(a)) # <class 'dict'>

例如:一个按键对应一个技能

1
2
a = {'Q':'新月打击', 'W':'苍白之瀑', 'E':'月之降临', 'R':'月神冲刺'}
print(a['Q']) # 新月打击

字典不能有重复的key

后插入的会覆盖前面插入的

1
2
a = {'Q':'新月打击', 'Q':'苍白之瀑', 'E':'月之降临', 'R':'月神冲刺'}
print(a) # {'Q': '苍白之瀑', 'E': '月之降临', 'R': '月神冲刺'}

value取值:

可以取python中任意的类型

key取值:

必须是不可变的类型,int,str,tuple

变量与运算符

变量

变量类型不固定:

1
2
3
4
a = 1
a = '1'
a = (1, 2, 3)
a = {1, 2, 3}

最好不要用系统的函数名当作变量名,可以是可以,但是之后再用这个函数的时候会报错:

1
2
3
4
5
6
7
type = 1
print(type(1))
// 报错
Traceback (most recent call last):
File "D:/computer/coding/python/demo.py", line 2, in <module>
print(type(1))
TypeError: 'int' object is not callable

值类型

int,str,tuple 不可变的

1
2
3
4
a = 1
b = a
a = 3
print(b) # 1

实验1:a + ‘python’ 以后其实变成了一个新的字符串,内存地址变了

1
2
3
4
5
a = 'hello'
print(id(a)) # 1779851895952
a = a + 'python'
print(id(a)) # 1779852958384
print(a) # hellopython

实验2:改内部就会报错

1
2
3
4
5
6
7
a = 'hello'
a[0] = 'o'
// 报错
Traceback (most recent call last):
File "D:/computer/coding/python/demo.py", line 2, in <module>
a[0] = 'o'
TypeError: 'str' object does not support item assignment

实验3:tuple内部也是不能变的

1
2
3
4
5
6
7
8
9
a = (1, 2, 3)
a[0] = '1'
// 报错
Traceback (most recent call last):
File "D:/computer/coding/python/demo.py", line 2, in <module>
a[0] = '1'
TypeError: 'tuple' object does not support item assignment

Process finished with exit code 1

引用类型

list,set,dict 可变的

1
2
3
4
5
a = [1, 2, 3, 4, 5]
b = a
a[0] = '1'
print(a) # ['1', 2, 3, 4, 5]
print(b) # ['1', 2, 3, 4, 5]

实验1:list改变以后,地址不变,仍然是同一个list

1
2
3
4
a = [1, 2, 3]
print(id(a)) # 1580993122696
a += [4, 5, 6]
print(id(a)) # 1580993122696

实验2:对比list和tuple

1
2
3
4
5
6
7
a = [1, 2, 3]
a.append(4)
print(a)

c = (1, 2, 3)
# c.append(4) 元组不支持append增加元素
print(c)

实验3:tuple不可变,但是内部的列表可以变

1
2
3
a = (1, 2, 4, [1, 2, 3])
a[3][2] = 'q'
print(a[3][2])

运算符

运算符优先级

image-20220831114057961

算数运算符

整除:5 // 2 = 2

除法:5 / 2 = 2.5

次方:5 ** 2 = 25,5 ** 3 = 125

逻辑运算符

与:and

或:or

非:not

int,float:值为0时,被认为是False,非0被认为是True

字符串:空串被认为是False,非空字符串是True

list,set,tuple,dict:空被认为是False,非空是True

成员运算符

in:元素是否在后面的组中

1
2
print(1 in [1, 2, 3]) # True
print('q' in 'qwer') # True

not in:元素是否不在后面的组中

1
print('q' in [1, 2, 3]) # False

注意:字典的成员运算,看的是key在不在dict中

身份运算符

is:比较两个变量的内存地址是否相等,==是比较两个变量的值是否相等

实验1:值比较

1
2
3
print(1 is 1.0) # False
print(1 is 1) # True
print(1 == 1.0) # True

实验2:list比较,因为集合是无序的,1,2,3还是2,1,3不影响值相等

1
2
3
4
5
6
a = {1, 2, 3}
b = {2, 1, 3}
print(id(a)) # 2438124459624
print(a == b) # True
print(id(b)) # 2438125466344
print(a is b) # False

实验3:tuple比较,因为元组是有序的,1,2,3还是2,1,3值不一样

1
2
3
4
5
6
a = (1, 2, 3)
b = (2, 1, 3)
print(id(a)) # 2843689161280
print(a == b) # False
print(id(b)) # 2843689163368
print(a is b) # False

is not:比较两个变量的内存地址是否不相等

值,身份,类型

对象的三个特征:值,身份,类型

Python中一切都是对象,int,”str”,(),{},[]

值比较:a == b

身份比较:a is b

类型比较:isinstance

1
2
3
4
5
6
7
a = 1
print(type(a) == int) # True

b = "hello"
print(isinstance(b, str)) # True
print(isinstance(b, int)) # False
print(isinstance(b, (int, str, float))) # True

位运算符

&:按位与

|:按位或

^:按位异或

~:按位取反

<<:左移

>>:右移

分支、循环、条件与枚举

接受控制台输入:input()

pass:占位符

if , else , elif

1
2
3
4
5
6
7
8
9
10
11
12
13
account = 'guolin'
password = '123456'

print('please input account')
user_account = input()

print('please input password')
user_password = input()

if account == user_account and password == user_password:
print('success')
else:
print('fail')

while,else

1
2
3
4
5
6
a = 0
while a <= 10:
a += 1
print(a)
else:
print('EOF')

for,else

主要是用来遍历/循环 序列,集合或者字典

print(y, end = '')控制每个输出的末尾是什么,如果不指定end,默认是换行

1
2
3
4
5
6
7
# 主要是用来遍历/循环  序列,集合或者字典
a = [['a', 'b', 'c'], (1, 2, 3)]
for x in a:
for y in x:
print(y, end = ' ')
else:
print('for-else')

range

1
2
3
# range(a, b, c)构建一个序列 前开后闭,c为步长(间隔多少)
for x in range(0, 10, 2):
print(x)
1
2
3
# range(a, b, c)构建一个序列 前开后闭,c为步长(间隔多少)
for x in range(10, 0, -2):
print(x)
1
2
3
4
5
6
7
8
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in range(0, len(a), 2):
print(a[i], end = ' | ') # 1 | 3 | 5 | 7 | 9 |

print()

b = a[0:len(a):2]
print(b) # [1, 3, 5, 7, 9]

break和continue

1
2
3
4
5
6
7
8
9
10
a = (1, 2 ,3)
for x in a:
if x == 2:
break
print(x)

for x in a:
if x == 2:
continue
print(x)

包、模块、类

定义

一个文件夹就是一个包,并且这个文件夹下有__init__.py文件,如果没有__init__.py,python会认为这是一个普通的文件夹,__init__.py文件也是一个模块,并且这个模块的模块名就是包名

模块就是 .py 文件

模块里面的class就是类

import导包

同级别的模块导入

image-20220831160423351

从子包里面的模块导入

image-20220831161038220

as别名

1
2
3
import t.c1 as m

print(m.a)

编译器自动生成的字节码文件

image-20220831161135548

直接导入具体的变量

from module import a

1
2
3
4
5
# import t.c1 as m
# print(m.a)

from t.c1 import a
print(a)

这样也可以

1
2
from t import c1
print(c1.a)

image-20220831161814594

使用*一次性导入所有变量

c1:

image-20220831162217729

c2:

1
2
3
4
from t.c1 import *
print(a) # 2
print(b) # 3
print(c) # 4

__all__列表,指定*导入的变量

c1:

1
2
3
4
5
__all__ = [a, c]

a = 2
b = 3
c = 4

c2:

1
2
3
4
5
from t.c1 import *
print(a) # 2
print(c) # 4

print(b) # 报错,找不到b,因为c1定义了__all__ = [a, c]

在init文件中使用all:

可以指定 * 导入的时候,导入哪些包

例如:

all只指定了c1,没有指定c4:

image-20220831164244737

那么用 * 一次性导入的时候,只会导入c1,也就是init中的all列表中的模块

image-20220831164150250

导入换行

不换行:

1
2
3
4
from t.c1 import a, b, c
print(a) # 2
print(b) # 3
print(c) # 4

用\换行:

1
2
from t.c1 import a, b, \
c

用()换行:

1
2
from t.c1 import (a, b, 
c)

init文件

导入包的时候,python会自动运行init文件

init文件:

1
2
a = 'This is __init__.py file'
print(a)

其他文件导包的时候:

1
import t

输出结果:

1
This is __init__.py file

应用:批量导入

可以在init文件里面统一导包,然后再在其他包里面导入init所在的包即可

image-20220831165903855

image-20220831170132343

注意事项

包和模块是不会重复导入的

避免循环导入

p1文件:

1
2
3
from p2 import p2
p1 = 1
print(p2)

p2文件:

1
2
from p1 import p1
p2 = 2

python导入模块的时候,会自动执行模块里面的所有代码

函数

python默认递归深度是995(不同计算机和系统默认深度不一样)

下列语句可以自定义最大递归深度

1
2
import sys
sys.setrecursionlimit(1000000)

函数返回值

函数如果没有返回值就会返回None

1
2
3
4
5
6
7
8
9
10
def add(x, y):
return x + y

def print_code(code):
print(code)

a = add(1, 2)
b = print_code(a)

print(a, b) # 3 None

序列解包

自动解包,指定有意义的变量名称,利于维护

1
2
3
4
5
6
7
8
9
10
11
12
13
def damage(skill1, skill2):
damage1 = skill1 * 3
damage2 = skill2 * 2 + 10
return damage1, damage2

# 接受可以直接用一个变量接收,返回的是一个元组
damages = damage(3, 6)
print(type(damages)) # <class 'tuple'>
print(damages) # (9, 22)

# 也可以用多个变量,进行序列解包
skill1_damage, skill2_damage = damage(3, 6)
print(skill1_damage, skill2_damage) # 9 22

例子:

1
2
3
4
5
6
7
# 序列解包
a, b, c = 1, 2, 3

d = 1, 2, 3
print(type(d))
# 序列解包
x, y, z = d

参数

必须参数

1
2
3
4
5
def add(x, y):
return x + y

# 必须参数
c = add(3, 2)

关键字参数

1
2
3
4
5
def add(x, y):
return x + y

# 关键字参数,可以调换参数的顺序
d = add(y = 2, x = 3)

可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict

1
2
3
4
5
6
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)

person('Michael', 30) # name: Michael age: 30 other: {}
person('Bob', 35, city='Beijing') # name: Bob age: 35 other: {'city': 'Beijing'}
person('Adam', 45, gender='M', job='Engineer') # name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}

关键字参数有什么用?它可以扩展函数的功能。比如,在person函数里,我们保证能接收到nameage这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。

和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去:

1
2
3
4
5
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)

extra = {'city': 'Beijing', 'job': 'Engineer'}
person('Jack', 24, **extra) # name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

默认参数

1
2
3
4
5
def add(x, y=5):
return x + y

c = add(3)
print(c) # 8

可变参数

在Python函数中,还可以定义可变参数。顾名思义,可变参数就是传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个。

我们以数学题为例子,给定一组数字a,b,c……,请计算a2 + b2 + c2

要定义出这个函数,我们必须确定输入的参数。由于参数个数不确定,我们首先想到可以把a,b,c……作为一个list或tuple传进来,这样,函数可以定义如下:

1
2
3
4
5
def calc(numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum

但是调用的时候,需要先组装出一个list或tuple:

1
2
3
4
5
6
7
8
9
def calc(numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum

print(calc([1, 2, 3])) # 14

print(calc((1, 3, 5, 7))) # 84

如果利用可变参数,调用函数的方式可以简化成这样:

1
2
3
4
5
6
7
8
9
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum

print(calc(1, 2, 3)) # 14

print(calc(1, 3, 5, 7)) # 84

定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*号。在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数:

1
2
3
4
5
6
7
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum

print(calc()) # 0

如果已经有一个list或者tuple,要调用一个可变参数怎么办?可以这样做:

1
2
3
4
5
6
7
8
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum

nums = [1, 2, 3]
print(calc(*nums)) # 14

global关键字

global 标志实际上是为了提示 python 解释器,表明被其修饰的变量是全局变量

1
2
3
4
5
6
7
8
9
10
11
12
origin = 0

def go(step):
# 申明origin是全局变量
global origin
new_pos = origin + step
origin = new_pos
return new_pos

print(go(2)) # 2
print(go(3)) # 5
print(go(6)) # 11

报错的例子:

1
2
3
4
5
6
7
8
9
10
11
12
origin = 0

def go(step):
# 如果不申明origin是全局变量,就会报错,origin还未定义,因为内部有origin,函数就不会引用外部的origin了
# global origin
new_pos = origin + step
origin = new_pos
return new_pos

print(go(2))
print(go(3))
print(go(6))

nonlocal关键字

强制申明一个变量不是局部变量,要从外部引用

闭包的方式解决:

pos和go函数一起形成了闭包,闭包可以使pos记忆上一次调用的状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
origin = 0

def factory(pos):
def go(step):
nonlocal pos
new_pos = pos + step
pos = new_pos
return new_pos
return go

tourist = factory(origin)
print(tourist(2)) # 2
print(tourist(3)) # 5
print(tourist(6)) # 11

面向对象

变量直接定义在模块里面,就是全局变量,函数中可以直接使用

变量定义在类下面,被称为数据成员

函数里面的是局部变量

类的定义和方法调用

1
2
3
4
5
6
7
8
9
10
11
12
class Student():
name = ''
age = 0

def print_file(self):
print('name: ' + self.name)
print('age: ' + str(self.age))

# 类的实例化
student = Student()
# 调用类下面的方法
student.print_file()

调用其他模块的类

image-20220901104834214

image-20220901103238409

构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Student():
name = ''
age = 0

# 构造函数
def __init__(self):
print('student')

def do_homework(self):
print()

student1 = Student()

# 构造函数也可以显式调用
a = student1.__init__()
# 构造函数返回的是None
print(a) # None
print(type(a)) # <class 'NoneType'>

构造函数默认返回值是None,也可以显式的返回None

1
2
3
4
5
6
7
8
class Student():
name = ''
age = 0

# 构造函数
def __init__(self):
print('student')
return None

但是和其他函数不同的是,构造函数不能返回除了None以外的其他值

1
2
3
4
5
6
7
8
class Student():
name = ''
age = 0

# 构造函数
def __init__(self):
print('student')
return 'student'

报错:

1
2
3
4
Traceback (most recent call last):
File "c1.py", line 14, in <module>
student1 = Student()
TypeError: __init__() should return None, not 'str'

类变量和实例变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Student():
# 这里是定义的类变量,与其他语言不同!!!
name = ''
age = 0

# 构造函数
def __init__(self, name, age):
# 这里是定义了两个实例变量,每个实例都分别持有各自的实例变量
# 实例变量的定义方法就是self.xxx
self.name = name
self.age = age

def do_homework(self):
print('do_homework')

student1 = Student('小明', 18)
student2 = Student('小红', 19)
print(student1.name)
print(student1.age)
print(student2.name)
print(student2.age)

实例变量并不能改变类变量的值,类变量只和类相关,不受对象的影响:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Student():
# 这里是定义的类变量,与其他语言不同!!!
name = 'guolin'
age = 0

# 构造函数
def __init__(self, name, age):
# 这里是定义了两个实例变量,每个实例都分别持有各自的实例变量
# 实例变量的定义方法就是self.xxx
self.name = name
self.age = age

def do_homework(self):
print('do_homework')

student1 = Student('小明', 18)
student2 = Student('小红', 19)
print(student1.name) # 小明
print(student2.name) # 小红
print(Student.name) # guolin

没有定义类变量,一样可以有实例变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Student():
# 构造函数
def __init__(self, name, age):
# 这里是定义了两个实例变量,每个实例都分别持有各自的实例变量
# 实例变量的定义方法就是self.xxx
self.name = name
self.age = age

def do_homework(self):
print('do_homework')

student1 = Student('小明', 18)
student2 = Student('小红', 19)
print(student1.name) # 小明
print(student2.name) # 小红

dict变量

python中每个实例中自带了一个__dict__字典,可以通过实例名.__dict__的方式查看这个实例的实例变量有哪些

1
2
3
4
5
6
7
8
9
10
11
class Student():

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

def do_homework(self):
print('do_homework')

student1 = Student('小明', 18)
print(student1.__dict__) # {'name': '小明', 'age': 18}

python机制:在class外部调用一个变量,如果对象里面,没有找到指定的实例变量,就会自动去类里面找,如果还没找到,会去父类里面找,所以下面的student1没有实例变量,python自动去类变量里面找,所以student1.nameStudent.name输出的值一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Student():
name = 'guolin'
age = 0

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

def do_homework(self):
print('do_homework')

student1 = Student('小明', 18)
print(student1.__dict__) # {}
print(student1.name) # guolin
print(Student.name) # guolin

类也可以用__dict__查看类的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Student():
name = 'guolin'
age = 0

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

def do_homework(self):
print('do_homework')

student1 = Student('小明', 18)
print(student1.__dict__) # {}
print(Student.__dict__) # {'__module__': '__main__', 'name': 'guolin', 'age': 0, '__init__': <function Student.__init__ at 0x0000022B2F23B8C8>, 'do_homework': <function Student.do_homework at 0x0000022B2F23B950>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}

实例方法内部访问类变量

通过类名.类变量访问

通过self.__class__.类变量访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Student():
# 一个学校的学生总数
sum = 0

# 实例方法,实例可以调用的方法
def __init__(self, name, age):
self.name = name
self.age = age
print(Student.sum)
print(self.__class__.sum)

def do_homework(self):
print('do_homework')

student1 = Student('小明', 18)

类方法

装饰器:@classmethod

类可以调用类方法,对象也可以直接调用类方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Student():
# 一个学校的学生总数
sum = 0

# 实例方法,实例可以调用的方法
def __init__(self, name, age):
self.name = name
self.age = age

def do_homework(self):
print('do_homework')

@classmethod
def plus_sum(cls): # cls就是这个类本身
cls.sum += 1
print(cls.sum)

student1 = Student('小明', 18)
Student.plus_sum()
student1 = Student('小红', 18)
Student.plus_sum()
student1 = Student('小军', 18)
Student.plus_sum()

静态方法

@staticmethod

对象和类都可以直接调用静态方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Student():
# 一个学校的学生总数
sum = 0

# 实例方法,实例可以调用的方法
def __init__(self, name, age):
self.name = name
self.age = age

def do_homework(self):
print('do_homework')

@classmethod
def plus_sum(cls):
cls.sum += 1
print(cls.sum)

@staticmethod
def add(x, y):
print('This is a staticmethod')

student1 = Student('小明', 18)
student1.add(1, 2)
Student.add(1, 2)

可见性

实例方法内部可以调用其他方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Student():
# 一个学校的学生总数
sum = 0

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

def do_homework(self):
self.do_english_homework()
print('do_homework')

def do_english_homework(self):
print('do_english_homework')

@classmethod
def plus_sum(cls):
cls.sum += 1
print(cls.sum)

@staticmethod
def add(x, y):
print('This is a staticmethod')

student1 = Student('小明', 18)
student1.do_homework()

public,private:

在方法或者变量面前加双下划线,python就会认为这个变量是私有的,否则就是公开的

python的私有变量保护机制其实就是把变量名换了,例如本来是__score,python会改成_Student__score

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Student():
# 一个学校的学生总数
sum = 0

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

def marking(self, score):
if score < 0:
return '不能打负分'
self.__score = score
print(self.name + '分数为: ' + str(self.__score))

def do_homework(self):
self.do_english_homework()
print('do_homework')

def do_english_homework(self):
print('do_english_homework')

@classmethod
def plus_sum(cls):
cls.sum += 1
print(cls.sum)

@staticmethod
def add(x, y):
print('This is a staticmethod')

student1 = Student('小明', 18)
result = student1.marking(-59)
print(result)

print(student1.__dict__) # {'name': '小明', 'age': 18, '_Student__score': 0}
student1._Student__score = -1 # 换一个名字,就可以访问私有变量了
print(student1._Student__score) # 也可以访问

继承

Python

子类可以继承父类的类变量和实例变量,方法

调用父类的方法:super(Student, self).do_homework()

父类:

1
2
3
4
5
6
7
8
9
10
11
12
class Human():
sum = 0
def __init__(self, name, age):
self.name = name
self.age = age
self.__class__.sum += 1

def get_name(self):
print(self.name)

def do_homework(self):
print('This is a parent method!')

子类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from c1 import Human

class Student(Human):
def __init__(self, school, name, age):
self.school = school
# 调用父类的方法:两种方法
# Human.__init__(self, name, age)
super(Student, self).__init__(name, age)

def do_homework(self):
super(Student, self).do_homework()
print('do_homework')

student1 = Student('人民路小学', '石敢当', 18)
student1.do_homework()
print(student1.name)
print(student1.age)

对比Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class test1 {
public static void main(String[] args) {
Student student = new Student("小明", 18, "人民路小学");
System.out.println(student.getName());
System.out.println(student.getAge());
System.out.println(student.getSchool());
}
}

class Human {
private String name;
private int age;

public Human() {
}

public Human(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}

class Student extends Human {
private String school;

public Student() {
}

public Student(String school) {
this.school = school;
}

public Student(String name, int age, String school) {
super(name, age);
this.school = school;
}

public String getSchool() {
return school;
}

public void setSchool(String school) {
this.school = school;
}
}

正则表达式

定义:正则表达式是一个特殊的字符序列,一个字符串是否与我们所设定这样的字符序列,相匹配

作用:快速检索文本、实现一些替换文本的操作

应用:

1.检查一串数字是否是电话号码

2.检测一个字符串是否符合email

3.把一个文本里指定的单词替换为另外一个单词

python内置函数

1
2
3
4
5
6
a = 'c|c++|c#|java|python'

# a.index() > -1则包含,否则不包含
print(a.index('python')) # 14
print(a.index('python') > -1) # True
print('python' in a) # True

findall方法

1
2
3
4
5
6
7
8
9
import re

a = 'c|c++|c#|java|python'

# 找寻全部的python正则表达式,以列表形式返回
r = re.findall('python', a)
if len(r) > 0:
print('字符串中包含python')
print(r) # ['python']

概括字符集

  • 数字:\d == [0-9]

  • 非数字:\D ==[^0-9]

  • 数字+字母+下划线(单词字符):\w == [A-Za-z0-9_]

  • 非数字+字母+下划线(非单词字符):\W == [^A-Za-z0-9_][' ', '\n', '&', '\r', '\t']都是非单词字符

  • 匹配任何不可见字符:\s == [ \f\n\r\t\v],包括空格、制表符、换页符等等

  • 匹配任何可见字符(非空白字符):\S

  • 匹配除换行符之外其他所有字符:.

匹配一个数字字符:

普通字符:’python’,元字符:’\d’

1
2
3
4
5
6
import re

a = 'c0c++1c#3java5python'

r = re.findall('\d', a)
print(r) # ['0', '1', '3', '5']

匹配一个非数字字符:

1
2
3
4
5
6
import re

a = 'c0c++1c#3java5python'

r = re.findall('\D', a)
print(r) # ['c', 'c', '+', '+', 'c', '#', 'j', 'a', 'v', 'a', 'p', 'y', 't', 'h', 'o', 'n']

字符集:普通字符+元字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import re

s = 'abc, acc, adc, aec, afc, ahc'

# 找到中间字符是c或者f的单词
r = re.findall('a[cf]c', s)
print(r) # ['acc', 'afc']

# 找到中间字符不是c或者f或者d的单词
r = re.findall('a[^cfd]c', s)
print(r) # ['abc', 'aec', 'ahc']

# 找到中间字符是c-f的单词
r = re.findall('a[c-f]c', s)
print(r) # ['acc', 'adc', 'aec', 'afc']

数量词

1
2
3
4
5
6
7
import re

s = 'python 1111java678php'

# 字符a-z,长度为3-6的区间范围内
r = re.findall('[a-z]{3,6}', s)
print(r) # ['python', 'java', 'php']

贪婪与非贪婪

python的正则表达式数量词,默认是贪婪的,自动的匹配更多:

1
2
3
4
5
6
7
import re

s = 'python 1111java678php'

# 字符a-z,长度为3-6的区间范围内
r = re.findall('[a-z]{3,6}', s)
print(r) # ['python', 'java', 'php']

非贪婪:

1
2
3
4
5
6
import re

s = 'python 1111java678php'

r = re.findall('[a-z]{3,6}?', s)
print(r) # ['pyt', 'hon', 'jav', 'php']

*号,+号,?号

*号前面的字符可以是任意次,包括0次

1
2
3
4
5
6
import re

s = 'pytho0python1pythonn2'

r = re.findall('python*', s)
print(r) # ['pytho', 'python', 'pythonn']

+号前面的字符为1次到无限多次

1
2
3
4
5
6
import re

s = 'pytho0python1pythonn2'

r = re.findall('python+', s)
print(r) # ['python', 'pythonn']

?号前面的字符为0次或1次,可以用来去重

1
2
3
4
5
6
import re

s = 'pytho0python1pythonn2'

r = re.findall('python?', s)
print(r) # ['pytho', 'python', 'python']

边界匹配

判断qq号是否为4-8位:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 判断qq号是否为4-8位
import re

# 小于4位可以正常判断出来
qq = '101'
r = re.findall('\d{4,8}', qq)
print(r) # []

# 4-8位可以正常判断出来
qq = '100001'
r = re.findall('\d{4,8}', qq)
print(r) # ['100001']

# 大于8位无法正常判断
qq = '100000001'
r = re.findall('\d{4,8}', qq)
print(r) # ['10000000']

# 边界匹配,匹配正则表达式完整的字段,大于8位也可以正常判断出来,并排除
qq = '100000001'
r = re.findall('^\d{4,8}$', qq)
print(r) # []

^与$的解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
import re

qq = '100000001'
r = re.findall('000', qq)
print(r) # ['000', '000']

# ^表示必须以0开头,从头开始匹配是不是000
r = re.findall('^000', qq)
print(r) # []

# $表示必须以0结尾,从尾开始往前匹配是不是000
r = re.findall('000$', qq)
print(r) # []

分组数量词

1
2
3
4
5
6
import re

a = 'pythonpythonpythonpython'

r = re.findall('(python){2}', a)
print(r) # ['python', 'python']

匹配模式参数

第三个参数是匹配模式参数

re.I为忽略大小写

原本.是匹配除换行符之外其他所有字符,re.S可以让.匹配所有字符,包括换行符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import re

a = 'PythonC#\nJavaPHP'

r = re.findall('c#', a)
print(r) # []

# 第三个参数是匹配模式参数
# re.I为忽略大小写
r = re.findall('c#', a, re.I)
print(r) # ['C#']

# 原本.是匹配除换行符之外其他所有字符
r = re.findall('c#.{1}', a, re.I)
print(r) # []

# re.S可以让.匹配所有字符,包括换行符。
r = re.findall('c#.{1}', a, re.I | re.S)
print(r) # ['C#\n']

sub方法,替换

替换,默认把所有参数替换掉,第四个参数默认是0,替换所有

第四个参数的意思是,匹配到后,所能够替换的最大的次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import re

a = 'PythonC#JavaC#C#PHP'

# 替换,默认把所有参数替换掉,第四个参数默认是0,替换所有
r = re.sub('C#', 'GO', a)
print(r) # PythonGOJavaGOGOPHP

# 第四个参数的意思是,匹配到后,所能够替换的最大的次数
r = re.sub('C#', 'GO', a, 1)
print(r) # PythonGOJavaC#C#PHP

r = re.sub('C#', 'GO', a, 2)
print(r) # PythonGOJavaGOC#PHP

Python内置的的替换函数replace:

1
2
3
4
5
6
7
8
9
10
11
a = 'PythonC#JavaC#C#PHP'

# Python内置的替换函数replace

# 发现没变,因为字符串是不可变的
a.replace('C#', 'GO')
print(a) # PythonC#JavaC#C#PHP

# 必须将结果赋值给一个新的变量
a = a.replace('C#', 'GO')
print(a) # PythonGOJavaGOGOPHP

把函数作为参数传入sub:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import re

a = 'PythonC#JavaC#C#PHP'

def convert(value):
# value传入以后并不是字符串,不能直接返回
print(type(value)) # <class '_sre.SRE_Match'>

# value.group()方法,可以得到字符串
return '!!' + value.group() + '!!'

# 如果C#能够匹配到,C#就会以value传入convert(value)这个函数,函数返回值就是替换后的结果
r = re.sub('C#', convert, a)
print(r) # Python!!C#!!Java!!C#!!!!C#!!PHP

将小于6的数字变成0,大于等于6的改成9:

1
2
3
4
5
6
7
8
9
10
11
12
13
import re

s = 'A8Casd867328918'

def convert(value):
matched = value.group()
if int(matched) >= 6:
return '9'
else:
return '0'

r = re.sub('\d', convert, s)
print(r) # A9Casd999009909

match与search方法

1
2
3
4
5
6
7
8
9
10
11
import re

s = 'qwe1231DW123Q4GE2RW'

# match尝试从字符串的首字符开始匹配,如果没有找到相应结果,就会返回None
r = re.match('\d', s)
print(r)

# search将搜索整个字符串,直到找到第一个满足条件的结果,并返回
r1 = re.search('\d', s)
print(r1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import re

s = '1231DW123Q4GE2RW'

r = re.match('\d', s)
print(r) # <_sre.SRE_Match object; span=(0, 1), match='1'>
print(r.group()) # 1
# span返回满足条件的第一个字符的位置
print(r.span()) # (0, 1)

r1 = re.search('\d', s)
print(r1) # <_sre.SRE_Match object; span=(0, 1), match='1'>
print(r.group()) # 1
print(r.span()) # (0, 1)

r2 = re.findall('\d', s)
print(r2) # ['1', '2', '3', '1', '1', '2', '3', '4', '2']

group分组

1
2
3
4
5
6
7
8
9
10
11
12
import re

s = 'life is short, i use python'

# 提取life到python间所有字符
r = re.search('life (.*) python', s)
# group(0)是完整匹配结果
print(r.group(0)) # life is short, i use python
print(r.group(1)) # is short, i use

r = re.findall('life (.*) python', s)
print(r) # ['is short, i use']

groups:

1
2
3
4
5
6
7
8
9
10
11
import re

s = 'life is short, i use python, i love python'

r = re.search('life(.*)python(.*)python', s)
print(r.group(0)) # life is short, i use python, i love python
print(r.group(1)) # is short, i use
print(r.group(2)) # , i love

print(r.group(0, 1, 2)) # ('life is short, i use python, i love python', ' is short, i use ', ', i love ')
print(r.groups()) # (' is short, i use ', ', i love ')

JSON

JSON介绍

定义:JavaScript Object Notation(JavaScript对像标记)是一种轻量级的数据交换格式

载体:字符串是JSON的表现形式,符合JSON格式的字符串叫做JSON字符串

优点:易于阅读,易于解析,网络传输效率高,跨语言交换数据

XML长什么样:

image-20220903135529528

JSON跨语言,数据交换:

image-20220903135732840

Python中JSON的格式

JSON中字符串必须以双引号表示,JSON的每个key都是字符串,要加双引号

value如果是字符串也要加双引号,数字不用加,bool类型也不用加

1
2
json_str = '{"name":"qiyue", "age":18}'
json_str = '[{"name":"qiyue", "age":18}, {"name":"qiyue", "age":18}]'

反序列化

将JSON转换为Python中的数据结构

image-20220903142329836

loads方法

将json字符串转换为dict:

1
2
3
4
5
6
7
8
import json

json_str = '{"name":"qiyue", "age":18}'

# 将json字符串转换为dict
student = json.loads(json_str)
print(type(student)) # <class 'dict'>
print(student) # {'name': 'qiyue', 'age': 18}

将json数组转换为list,list里面每个元素是一个dict:

1
2
3
4
5
6
7
import json

json_str = '[{"name":"qiyue", "age":18}, {"name":"qiyue", "age":18}]'

student = json.loads(json_str)
print(type(student)) # <class 'list'>
print(student) # [{'name': 'qiyue', 'age': 18}, {'name': 'qiyue', 'age': 18}]

序列化

将Python数据类型转换为JSON

dumps方法

1
2
3
4
5
6
7
8
9
10
import json

student = [
{"name":"qiyue", "age":18, "flag":False},
{"name":"qiyue", "age":18}
]

json_str = json.dumps(student)
print(type(json_str)) # <class 'str'>
print(json_str) # [{"name": "qiyue", "age": 18, "flag": false}, {"name": "qiyue", "age": 18}]

JSON,JSON对象,JSON字符串

JSON是一种中间的数据类型,实现不同语言之间的快速转换

image-20220903144628207

JSON有自己的数据类型,虽然它和JavaScript的数据类型有些相似

JSON对象和JSON字符串其实脱离语言来看,是一样的

枚举

枚举是一个类,所有的枚举都是Enum的子类

为什么要用枚举?

其他三种方式的缺陷:值可变,没有防止相同标签的功能

1
2
3
4
5
6
7
8
9
10
11
yellow = 1
green = 2

{'yellow':1, 'green':2}

class TypeDiamond():
yellow = 1
green = 2

# 值可变
# 没有防止相同标签的功能

枚举的值不可变

1
2
3
4
5
6
7
8
9
10
11
from enum import Enum

class VIP(Enum):
YELLOW = 1
GREEN = 2
BLACK = 3
RED = 4

print(VIP.YELLOW)

# VIP.YELLOW = 6 报错,枚举类型不允许更改

枚举不允许相同标签

1
2
3
4
5
6
7
8
9
from enum import Enum

class VIP(Enum):
YELLOW = 1
YELLOW = 2 # 报错
BLACK = 3
RED = 4

print(VIP.YELLOW)

相关操作

枚举类型,枚举的名字,枚举的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from enum import Enum

class VIP(Enum):
YELLOW = 1
GREEN = 2
BLACK = 3
RED = 4

print(type(VIP.GREEN)) # <enum 'VIP'>
print(VIP.GREEN) # VIP.YELLOW

# 获取标签的数值
print(type(VIP.GREEN.value)) # <class 'int'>
print(VIP.GREEN.value) # 2

# 获取标签的名字
print(type(VIP.GREEN.name)) # <class 'str'>
print(VIP.GREEN.name) # GREEN

遍历枚举

1
2
3
4
5
6
7
8
9
10
from enum import Enum

class VIP(Enum):
YELLOW = 1
GREEN = 2
BLACK = 3
RED = 4

for v in VIP:
print(v)

枚举间的比较

枚举类型之间不能做大小比较,但是可以做等值比较

枚举类型可以做身份比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from enum import Enum

class VIP(Enum):
YELLOW = 1
GREEN = 1
BLACK = 3
RED = 4

result = VIP.YELLOW == VIP.GREEN
print(result) # True

# 枚举类型之间不能做大小比较,但是可以做等值比较
# result = VIP.YELLOW > VIP.GREEN
# print(result)

result = VIP.BLACK is VIP.BLACK
print(result) # True

# 可以做身份比较
result = VIP.YELLOW is VIP.GREEN
print(result) # True

标签别名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from enum import Enum

class VIP(Enum):
YELLOW = 1
YELLOW_ALIAD = 1
BLACK = 3
RED = 4

# YELLOW_ALIAD可以看作YELLOW的别名,一般并不会打印出来
print(VIP.YELLOW_ALIAD) # VIP.YELLOW

# 普通遍历也不会展示别名
for v in VIP:
print(v) # VIP.YELLOW VIP.BLACK VIP.RED

# 这样才能打印出别名
for v in VIP.__members__:
print(v)
# YELLOW
# YELLOW_ALIAD
# BLACK
# RED

类型转换

将数字转换为枚举类型

1
2
3
4
5
6
7
8
9
10
11
12
from enum import Enum

class VIP(Enum):
YELLOW = 1
GREEN = 1
BLACK = 3
RED = 4

a = 1
print(VIP(a)) # VIP.YELLOW
print(type(VIP.BLACK)) # <enum 'VIP'>
print(isinstance(VIP.BLACK, VIP)) # True

标签的值的类型

标签的值可以是其他类型,不一定要是数字

1
2
3
4
5
6
7
from enum import Enum

class VIP(Enum):
YELLOW = 1
GREEN = (1, 2, 3)
BLACK = 'str'
RED = {213:1}

强制指定标签的值为int

1
2
3
4
5
6
7
from enum import IntEnum

class VIP(IntEnum):
YELLOW = 1
GREEN = 2
BLACK = 3
RED = 4

限制标签的值不相同

枚举类的上面加装饰器@unique

1
2
3
4
5
6
7
8
from enum import IntEnum, unique

@unique
class VIP(IntEnum):
YELLOW = 1
GREEN = 1 # 报错
BLACK = 3
RED = 4

枚举底层

枚举类型的实现是单例模式,不能对枚举类型实例化

一切皆对象

函数也是对象,函数可以赋值给一个变量

可以把函数作为另一个函数的参数,传递到另外的函数里

把一个函数当作另一个函数的返回值

1
2
3
4
def a():
pass

print(type(a)) # <class 'function'>
1
2
3
4
5
6
7
def curve_pre():
def curve():
print('this is a function')
return curve

f = curve_pre()
f() # this is a function

闭包

保存了一个现场

由函数以及它在定义时候的外部的环境变量(不能是全局变量)所构成的整体,就是闭包

当闭包形成之后,这个函数在任何地方调用的时候,都不会受到重新赋值的影响,还是会用闭包时的环境变量

外部的环境变量的例子:形成闭包

1
2
3
4
5
6
7
8
9
def curve_pre():
a = 25
def curve(x):
return a*x*x
return curve

a = 10
f = curve_pre()
print(f(2)) # 100

全局变量的例子:没有形成闭包

1
2
3
4
5
6
7
8
9
a = 25
def curve_pre():
def curve(x):
return a*x*x
return curve

a = 10
f = curve_pre()
print(f(2)) # 40

局部变量的改变无法影响全局变量:

1
2
3
4
5
6
7
8
9
10
11
def f1():
a = 10
def f2():
# a 会被认为是一个局部变量,不会引用环境变量了,没有形成闭包
a = 20
print(a)
print(a) # 10
f2() # 20
print(a) # 10

f1()

没有形成闭包的例子:

1
2
3
4
5
6
7
8
9
10
11
12
def f1():
a = 10
def f2():
# a 会被认为是一个局部变量,没有引用环境变量,所以没有形成闭包
a = 20
return a
return f2

f = f1()
print(f) # <function f1.<locals>.f2 at 0x0000024EE10870D0>
# 不是一个闭包
print(f.__closure__) # None

改成闭包:

1
2
3
4
5
6
7
8
9
10
11
def f1():
a = 10
def f2():
# 此时删除了a局部变量,这里就必须引用环境变量中的a,形成闭包
return a
return f2

f = f1()
print(f) # <function f1.<locals>.f2 at 0x0000024EE10870D0>
# 是一个闭包
print(f.__closure__) # (<cell at 0x0000027E79E0B528: int object at 0x000000006DBA6F00>,)

global全局变量

1
2
3
4
5
6
7
8
9
10
11
12
origin = 0

def go(step):
# 申明origin是全局变量
global origin
new_pos = origin + step
origin = new_pos
return new_pos

print(go(2)) # 2
print(go(3)) # 5
print(go(6)) # 11

报错的例子:

1
2
3
4
5
6
7
8
9
10
11
12
origin = 0

def go(step):
# 如果不申明origin是全局变量,就会报错,origin还未定义,因为内部有origin,函数就不会引用外部的origin了
# global origin
new_pos = origin + step
origin = new_pos
return new_pos

print(go(2))
print(go(3))
print(go(6))

nonlocal非局部变量

强制申明一个变量不是局部变量,要从外部引用

闭包的方式解决:

pos和go函数一起形成了闭包,闭包可以使pos记忆上一次调用的状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
origin = 0

def factory(pos):
def go(step):
nonlocal pos
new_pos = pos + step
pos = new_pos
return new_pos
return go

tourist = factory(origin)
print(tourist(2)) # 2
print(tourist(3)) # 5
print(tourist(6)) # 11

lambda匿名函数

lambda表达式的定义

定义匿名函数:lambda 参数列表: 函数返回值

1
2
3
4
5
6
def add(x, y):
return x + y
print(add(1, 2)) # 3

f = lambda x, y: x + y
print(f(1, 2)) # 3

三元表达式

其他语言中x > y ? x : y

Python中x if x > y else y也就是

条件为真时返回的结果 if 条件判断 else 条件为假时的返回结果

1
2
f = lambda x, y : x if x > y else y
print(f(1, 2)) # 2

map类

map(函数, 集合),map会把集合里面的所有元素都传入函数,然后返回一个map对象,里面保存了结果

map配合lambda

单参数:

1
2
3
4
5
6
7
8
9
10
list_x = [1,2,3,4,5,6,7,8]

def square(x):
return x * x

r = map(square, list_x)
print(list(r)) # [1, 4, 9, 16, 25, 36, 49, 64]

r = map(lambda x : x * x, list_x)
print(list(r)) # [1, 4, 9, 16, 25, 36, 49, 64]

多参数:

1
2
3
4
5
list_x = [1,2,3,4,5,6,7,8]
list_y = [1,2,3,4,5,6,7,8]

r = map(lambda x, y : x * x + y, list_x, list_y)
print(list(r)) # [2, 6, 12, 20, 30, 42, 56, 72]

结果列表的元素个数取决于传入的比较小的集合的长度:

1
2
3
4
5
list_x = [1,2,3,4,5,6,7,8]
list_y = [1,2,3,4,5,6]

r = map(lambda x, y : x * x + y, list_x, list_y)
print(list(r)) # [2, 6, 12, 20, 30, 42]

reduce函数

reduce函数:每一次lambda表达式的结果,将作为下一次调用lambda的参数去计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from functools import reduce

list_x = [1,2,3,4,5,6,7,8]

# 连续计算,连续调用lambda
r = reduce(lambda x, y : x + y, list_x)
print(r) # 36

'''
第一次调用:x = 1, y = 2, x + y = 3
第二次调用:x = 3, y = 3, x + y = 6
第三次调用:x = 6, y = 4, x + y = 10
第四次调用:x = 10, y = 5, x + y = 15
第五次调用:x = 15, y = 6, x + y = 21
第六次调用:x = 21, y = 7, x + y = 28
第七次调用:x = 28, y = 8, x + y = 36
'''

第三个参数:初始值

1
2
3
4
5
6
7
from functools import reduce

list_x = [1,2,3,4,5,6,7,8]

# 第三个参数是初始值
r = reduce(lambda x, y : x + y, list_x, 10)
print(r) # 46
1
2
3
4
5
6
7
from functools import reduce

list_x = ['1','2','3']

# 第三个参数是初始值
r = reduce(lambda x, y : x + y, list_x, 'aaa')
print(r) # aaa123

filter类

filter传入的函数必须返回真或者假,为真,元素保留,为假,剔除此元素

1
2
3
4
5
list_x = [0, 1, 0, 1, 0, 0, 1]

# 去除0
r = filter(lambda x: True if x == 1 else False, list_x)
print(list(r)) # [1, 1, 1]

装饰器

优点

稳定性:想对被封装的单元做出代码上的修改,可以不改变具体的单元实现,而是通过装饰器,改变函数的行为

复用性:可以加在多个函数上,增加这个功能装饰器

体现了AOP的编程思想

开闭原则:对修改是封闭的,对拓展是开放的

打印时间戳:

1
2
3
4
5
6
7
8
import time

def f1():
# unix时间戳
print(time.time())
print('This is a function')

f1()

用单独的函数打印时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import time

def f1():
print('This is a function')

def f2():
print('This is a function')

def print_current_time(func):
print(time.time())
func()

print_current_time(f1)
print_current_time(f2)

'''
1662212278.4627192
This is a function
1662212278.4637156
This is a function
'''

用装饰器打印时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import time

# 装饰的
def decorator(func):
# 被封装的
def wrapper():
print(time.time())
func()
return wrapper

def f1():
print('This is a function')

f = decorator(f1)
f()
'''
1662212710.1250434
This is a function
'''

@语法糖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import time

# 装饰的
def decorator(func):
# 被封装的
def wrapper():
print(time.time())
func()
return wrapper

@decorator
def f1():
print('This is a function')

f1()

接收可变参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import time

def decorator(func):
def wrapper(*args):
print(time.time())
func(*args)
return wrapper

@decorator
def f1(func_name):
print('This is a function ' + func_name)

@decorator
def f2(func_name1, func_name2):
print('This is a function ' + func_name1)
print('This is a function ' + func_name2)

f1('test func')
f2('test func1', 'test func2')

接收关键字参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import time

def decorator(func):
def wrapper(*args, **kw):
print(time.time())
func(*args, **kw)
return wrapper

@decorator
def f1(func_name):
print('This is a function ' + func_name)

@decorator
def f2(func_name1, func_name2):
print('This is a function ' + func_name1)
print('This is a function ' + func_name2)

@decorator
def f3(func_name1, func_name2, **kw):
print('This is a function ' + func_name1)
print('This is a function ' + func_name2)
print(kw)

f1('test func')
f2('test func1', 'test func2')
f3('test func1', 'test func2', a=1, b=2, c='123')

Python技巧

用字典映射代替switch case语句

不用下标访问,而是用get方法访问,第一个参数是key,第二个参数是如果key不存在的时候,将返回的结果

1
2
3
4
5
6
7
8
9
10
11
12
day = 6

switcher = {
0 : 'Sunday',
1 : 'Monday',
2 : 'Tuesday'
}

# 不用下标访问,而是用get方法访问,第一个参数是key,第二个参数是如果key不存在的时候,将返回的结果
# day_name = switcher[day]
day_name = switcher.get(day, 'Unkown')
print(day_name)

函数名+()调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
day = 6

def get_sunday():
return 'Sunday'

def get_monday():
return 'Monday'

def get_Tuesday():
return 'Tuesday'

def get_unkown():
return 'Unkown'

switcher = {
0 : get_sunday,
1 : get_monday,
2 : get_Tuesday
}

# 不用下标访问,而是用get方法访问,第一个参数是key,第二个参数是如果key不存在的时候,将返回的结果
# day_name = switcher[day]
day_name = switcher.get(day, get_unkown)()
print(day_name)

列表推导式

1
2
3
4
a = [1,2,3,4,5,6,7,8]

b = [i**2 for i in a]
print(b) # [1, 4, 9, 16, 25, 36, 49, 64]

有条件的列表推导式

1
2
3
4
a = [1,2,3,4,5,6,7,8]

b = [i**2 for i in a if i >= 5]
print(b) # [25, 36, 49, 64]

集合推导式

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

b = {i**2 for i in a if i >= 5}
print(b) # {64, 25, 36, 49}

元组推导式

为什么会得到一个generator对象呢?因为元组是不可变的

1
2
3
4
5
a = (1,2,3,4,5,6,7,8)

b = (i**2 for i in a if i >= 5)
for item in b:
print(item, end=' ') # 25 36 49 64

字典推导式

得到key的列表:

1
2
3
4
5
6
7
8
students = {
'喜小乐':18,
'石敢当':20,
'横小五':15
}

b = [key for key,value in students.items()]
print(b) # ['喜小乐', '石敢当', '横小五']

颠倒key,value:

1
2
3
4
5
6
7
8
students = {
'喜小乐':18,
'石敢当':20,
'横小五':15
}

b = {value:key for key,value in students.items()}
print(b) # {18: '喜小乐', 20: '石敢当', 15: '横小五'}

item方法:返回可遍历的(键, 值) 元组数组

1
2
3
4
5
6
7
8
9
students = {
'喜小乐':18,
'石敢当':20,
'横小五':15
}
# 返回可遍历的(键, 值) 元组数组
s = students.items()
print(type(s)) # <class 'dict_items'>
print(s) # dict_items([('喜小乐', 18), ('石敢当', 20), ('横小五', 15)])

key变元组:为什么会得到一个generator对象呢?因为元组是不可变的

1
2
3
4
5
6
7
8
9
10
students = {
'喜小乐':18,
'石敢当':20,
'横小五':15
}

b = (key for key,value in students.items())
print(b) # <generator object <genexpr> at 0x000001FF02B75990>
for x in b:
print(x, end=' ') # 喜小乐 石敢当 横小五

None

从类型和值这两个方面来讲,None不等于不等于空字符串,不等于空列表,不等于0,不等于False

空本身也是一个对象,也是一个类型

1
2
3
4
5
6
7
8
9
10
11
12
13
a = ''
b = False
c = []

print(a == None) # False
print(b == None) # False
print(c == None) # False

print(a is None) # False
print(b is None) # False
print(c is None) # False

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

对象存在并不一定是True

None对应的是False:

1
2
3
4
if None:
print(1)
else:
print(0) # 0

一般除了None以外的对象是True:

1
2
3
4
5
6
7
class Test():
pass

test = Test()

if test:
print('S')

对象存在,但是也被转化为False的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Test():
def __len__(self):
return 0

test = Test()

print(bool(None)) # False
print(bool([])) # False
print(bool(test)) # False

if test:
print('S')
else:
print('F') # F

__len__与__bool__内置方法

其实len()和bool()函数,实际上都是调用了类内部的__len__方法

如果没有定义bool和len方法,则这个对象会被认为是True

1
2
3
4
5
6
class Test():
pass

test = Test()

print(bool(test)) # True

如果len方法返回的是0,则对象被认为是False

len方法只能返回整型和布尔值,返回其他会报错

1
2
3
4
5
6
7
class Test():
def __len__(self):
return 0

test = Test()

print(bool(test)) # False

如果__bool__方法存在,那么__len__方法将不再影响bool的取值

1
2
3
4
5
6
7
8
9
class Test():
def __len__(self):
return 0
def __bool__(self):
return True

test = Test()

print(bool(test)) # True

为什么__bool__方法存在,那么__len__方法将不再影响bool的取值呢?

因为如果__bool__方法存在,那么bool(test)的时候,__len__方法将不再被调用

1
2
3
4
5
6
7
8
9
10
class Test():
def __len__(self):
print('called')
return 0
def __bool__(self):
return True

test = Test()

print(bool(test)) # True

__bool__方法只能返回bool,也就是True或者False,如果返回整型,也会报错

-------------本文结束感谢您的阅读-------------