python学习(mysql最后的综合案例没做)

1.掌握字面量的含义

代码中,被写在代码中固定的值

2.常见的字面量类型

整数、浮点数、字符串

3.如何基于print语句完成各类字面量的输出

注释的作用:代码中的解释性语句,用来对代码内容进行注释,注释不是代码,不会被程序执行

注释的分类

单行注释:以#开头,#右边 的所有文字当作说明,而不是真正要执行的程序,起辅助说明作用

建议在#和注释内容之间,间隔一个空格

单行注释一般用于对一行或一小部分代码进行解释

多行注释:以一对三个双引号引起来("""注释内容""")来解释说明一段代码的作用使用方法 

变量:在程序运行时,能储存计算结果或能表示值的抽象概念,简单的说,变量就是在程序运行时,记录数据用的

变量的定义格式:

变量名称=变量的值 

变量的特征:变量的值可以发生改变

print语句如何输出多份内容

print(内容1,内容2,......内容n)

python中如何做减法

使用符号-即可完成减法运算

类型描述说明
string字符串类型用引号引起来的数据都是字符串
int整型(有符号)数字类型,存放整数 如 -1,10,0等
float浮点数(有符号)数字类型,存放小数

type()语句

变量有类型吗

我们通过type(变量)可以输出类型,这是查看变量的类型还是数据的类型

查看的是:变量存储的数据的类型。因为,变量无类型,但是它存储的数据有

数据类型的转换,将会是我们以后经常使用的功能,如:

从文件中读取的数字,默认是字符串,我们需要转换成数字类型

后续学习的input语句,默认结果是字符串,若需要数字也需要转换

将数字转换成字符串用以写出到外部系统

语句(函数)说明
int(x)将x转换成一个整数
float(x)将x转换成一个浮点数
str(x)将对象x转换成字符串

 任何类型都可以转换成字符串,对不对?

正确

字符串可以随意转换成数字,对不对?

错误,字符串内必须只有数字才可以

浮点数转换成整数会丢失精度

什么是标识符

在python中,我们可以给很多东西起名字

变量的名字

方法的名字

类的名字,等等

这些名字,我们统一称为标识符,用来做内容的标识

标识符:用来给变量、类、方法等命名

标识符命名规则-内容限定

标识符命名中,只允许出现

  • 英文
  • 中文
  • 数字
  • 下划线

这四类元素

1.不推荐使用中文

2.数字不可以开头

标识符命名规则-大小写敏感

标识符命名规则-不可使用关键字

python中有一系列单词,称之为关键字,有特定用途,不可以使用它们作为标识符

变量命名规范

  • 变量名
  • 类名
  • 方法名

不同的标识符,有不同的规范

变量的命名规范-见名知意

变量命名规范-下划线命名法

多个单词组合变量名,要使用下划线做分隔

first_number

student_nickname

变量命名规范-英文字母全小写

命名变量中的英文字母,应全部小写

运算符

运算符描述实例
+
-
*
/
//取整数
%取余
**取指

复合赋值运算符

运算符描述实例
+=
-=
*=
/=
%=
**=
//=

 字符串的三种定义方式

1.单引号定义法

2.双引号定义法

3.三引号定义法(支持换行操作,使用变量接收它,它就是字符串,不使用变量接收它,就可以作为多行注释使用

字符串的引号嵌套

单引号定义法,可以内含双引号

双引号定义法,可以内含单引号

可以使用转移字符(\)来将引号解除效用,变成普通字符串

字符串拼接

print("学IT来黑马"+”月薪过万")

字符串格式化

1.变量过多,拼接起来实在是太麻烦了

2.字符串无法和数字或者其他类型完成拼接

其中的%s:

%表示:我要占位

s表示:将变量变成字符串放入占位的地方

综合起来的意思是:我先占个位置,等一会有个变量过来,我把它变成字符串放到占位的位置

多个变量占位

变量要用括号括起来

并按照占位的顺序填入

python中,支持非常多的数据类型占位

最常用的是如下三类

格式符号转化
%s将内容转换成字符串,放入占位位置
%d将内容转换成整数,放入占位位置
%f将内容转换成浮点数,放入占位位置

1.掌握格式化字符串的过程中做数字的精度控制

字符串格式化-数字精度控制

我们可以使用辅助符号“m.n"来控制数据的宽度和精度

m,控制宽度,要求是数字(很少使用),设置的宽度小于数字自身,不生效

.n,控制小数点精度,要求是数字,会进行小数的四舍五入

示例:

%5d:表示将整数的宽度控制在5位,如数字11,被设置为5d,就会变成:   11,用3个空格补足宽度

%5.2f:表示将宽度控制为5,将小数点精度设置为2

小数点和小数部分也算入宽度计算。如,对11.345设置了%7.2f后,结果是:  11.35。两个空格补足宽度,小数部分限制2位精度后,四舍五入为.35

%.2f:表示不限制宽度,只设置小数点精度为2,如11.345设置%.2f后,结果是11.35

字符串格式化-快速写法

目前通过%符号占位已经很方便了,还能进行精度控制

通过语法:f"内容{变量}"的格式来快速格式化

这种方式:

  • 不理会类型
  • 不做精度控制

适合对精度没有要求的适合快速使用

对表达式进行格式化

表达式:一条具有明确执行结果的代码语句

在无需使用变量进行数据存储的时候,可以直接格式化表达式,简化代码哦

获取键盘输入

试想一下,我们经常遇到过程序需要我们输入信息的场景

比如:银行取钱

数据输出:print

数据输入:input

input()语句的功能是,获取键盘输入的数据

可以使用input(提示信息)

要注意,无论键盘输入什么类型的数据,获取到的数据永远是字符串类型

布尔类型的定义

布尔类型的字面量:

True表示真(是、肯定)

False表示假(否、否定)

定义变量存储布尔类型数据:

变量名称 = 布尔类型字面量

布尔类型不仅可以自行定义 同时也可以通过计算的来

也就是使用比较运算符进行比较运算得到布尔类型的结果

if语句的基本格式

if 要判断的条件:

    条件成立时,要做的事

if else语句

if 条件:

    事情1

    事情2

    事情3

else:

    不满足条件时要做的事情1

if else语句:

if 条件1:

    条件1满足应该做的事

elif 条件2:

    条件2满足应该做的事

else:

    所有条件都不满足应该做的事

判断语句的嵌套

许多逻辑的判断,是嵌套的,多层次的

对于这种需求,我们可以:自由组合 if elif else,完成特定需求的要求

循环语句的嵌套,要注意空格缩进

基于空格的缩进来决定层次关系

注意条件的设置,避免出现无限循环

print输出不换行

print("Hello",end=' ')

同while循环不同,for循环是无法定义循环条件的

只能从被处理的数据集中,依次取出内容进行处理

所以,理论上讲,python的for循环无法构建无限循环(被处理的数据集不可能无限大)

range语句

for 临时变量 in 待处理数据集

    循环满足条件时执行的代码

待处理数据集,严格来说,称之为:序列类型

序列类型指:其内容可以一个个依次取出的一种类型,包括:

  • 字符串
  • 列表
  • 元组

语法1:

range(num)

获取一个从0开始,到num结束的数字序列(不含num本身)

如range(5)取得的数据是:[0,1,2,3,4]

语法2:

range(num1,num2)

获得一个从num1开始,到num2结束的数字序列(不含num2本身)

如,range(5,10)取得的数据是:[5,6,7,8,9]

语法3:

range(num1,num2,step)

获得一个从num1开始,到num2结束的数字序列(不含num2本身)

数字之间的步长,以step为准(step默认为1)

如,range(5,10,2)取得的数据是:[5,7,9]

for循环的变量作用域

临时变量,在编程规范上,作用范围是作用域,只限定在for循环内部

如果在for循环外部访问临时变量

  • 实际上是可以访问到的
  • 在编程规范上是不允许、不建议这么做的

for循环中的临时变量,其作用域限定为:

循环内

这种限定:

是编程规范的限定,而非强制限定

不遵守也能正常运行,但是不建议这么做

如需访问临时变量,可以预先在循环外定义它

continue和break

用以对循环进行临时跳过和直接结束

continue

continue关键字用于:中断本次循环,直接进入下一次循环

continue可以用于:for循环和while循环,效果一致

continue在嵌套循环中的应用

break关键字同样只可以控制:它所在的循环结束

函数

函数:是组织好的,可重复使用的,用来实现特定功能的代码段

函数的定义

def 函数名(传入参数):

        函数体

        return 返回值

函数使用步骤:

先定义函数

后调用函数

注意事项:参数不需要,可以省略 返回值不需要,可以省略

函数的传入参数

传入参数的功能是:在函数进行计算的时候,接受外部调用时提供的数据

传入参数的作用:

在函数运行的时候,接受外部传入的数据

返回值的语法

def 函数(参数...):

        函数体

        return 返回值

变量 = 函数(参数)

函数体在遇到return后就结束了,所以写在return后的代码不会执行

None类型

None表示:空的、无实际意义的意思

None作为一个特殊的字面量,用于表示:空、无意义,其有非常多的应用场景

用在if判断上

在if判断中,none等同于false

一般用于在函数中主动返回none,配合if判断做相关处理

函数的说明文档

局部变量

变量作用域指的是变量的作用范围(变量在哪里可用,在哪里不可用)

主要分为两类:局部变量和全局变量

所谓局部变量:定义在函数体内部的变量,只在函数内部生效

global关键字

使用global关键字 可以在函数内部声明

字典的常用操作

字典的特点:可以容纳多个数据,可以容纳不同类型的数据,每一份数据是keyvalue键值对,可以通过key获取到value,key不可重复,不支持下标索引,可以修改,支持for循环,不支持while循环

数据容器可以从以下视角进行简单分类:

是否支持下标索引

支持:列表、元组、字符串-序列类型

不支持:集合、字典-非序列类型

是否支持重复元素:

支持:列表、元组、字符串-序列类型

不支持:集合、字典-非序列类型

是否可以修改

支持-列表、集合、字典

不支持:元组、字符串

列表:一批数据,可修改、可重复的场景

元组:一批数据,不可修改、可重复的储存场景

字符串:一串字符串的存储场景

集合:一批数据,去重存储场景

字典:一批数据,可用key检索value1的存储场景

在遍历上

5类数据容器都支持for循环

列表、元组、字符串支持while循环、集合、字典不支持(无法下标索引)

尽管遍历的形式各有不同,但是,它们都支持遍历操作

数据容器的通用统计功能

len(容器)

统计容器的元素个数

max(容器)

统计容器的最大元素

min(容器)

统计容器的最小元素

容器的通用转换功能

list(容器)

将给定容器转换成列表

str(容器)

将给定容器转换成字符串

tuple(容器)

将给定容器转换成元组

set(容器)

将给定容器转换成集合

多个返回值

如果一个函数要有多个返回值,该如何书写代码

def test_return():

   return 1,2

x,y = test_return()

print(x)

print(y)

函数参数种类

使用方式上的不同,函数有4种常见参数使用方式:

位置参数

关键字参数:函数调用时通过“键=值”的形式传递参数

函数调用时,如果有位置参数时,位置参数必须在关键字参数前面,但关键字参数之间不存在先后顺序

缺省参数:默认参数,用于定义函数,为参数提供默认值,调用函数时可不传该默认参数的值(注意:所有位置参数必须出现在默认参数前,包括函数定义和调用)

作用:当调用函数时没有传递参数,就会使用默认是用缺省参数对应的值

不定长参数 

不定长参数:不定长参数也叫可变参数,用于不确定调用的时候会传递多少个参数(不传参也可以)的场景

作用:当调用参数时不确定参数个数时,可以使用不定长参数

不定长参数的类型:

位置传递

关键字传递

位置传递

def user_info(*args):

    print(args)

#('TOM',)

user_info('TOM')

#('TOM',18)

user_info('TOM',18)

注意:传进的所有参数都会被args变量收集,它会根据传进参数的位置合并为一个元组(tuple),args是元组类型,这就是位置传递

关键字传递

def user_info(**kwargs):

      print(kwargs)

# {'name':'TOM','age':18,'id':110}

user_info(name='TOM',age=18,id=110)

注意:参数是“键=值”形式的情况下,所有的“键=值”都会被kwargs接收,同时会根据“键=值”组成字典

函数作为参数传递

函数都是接受数据作为参数传入:

数字

字符串

字典、列表、元组等

函数作为参数传递

如下代码:

def test_func(compute):

    result = compute(1,2)

    print(result)

def compute(x,y):

    return x+y

test_func(compute)

计算逻辑的传递而非数据的传递、

就像上述代码那样,不仅仅是相加、相减、相除、等任何逻辑都可以自行定义并作为函数传入

函数本身是可以作为参数,传入另一个函数中进行使用的

将函数传入的作用在于:传入计算逻辑,而非传入数据

函数的定义中

def关键字,可以定义带有名称的函数

lambda关键字,可以定义匿名函数(无名称)

有名称的函数,可以基于名称重复使用

无名称的匿名函数,只可临时使用一次

匿名函数定义语法:

lambda 传入参数:函数体(一行代码)

lambda是关键字,表示定义匿名函数

传入参数表示匿名函数的形式参数,如:x,y表示接收2个形式参数

函数体,就是函数的执行逻辑,要注意:只能写一行,无法写多行

文件编码

计算机只能识别:0和1,那么我们丰富的文本文件是如何被计算机识别,并存储在硬盘中的呢

答案:使用编码技术(密码本)将内容翻译成0和1存入

编码技术:翻译的规则,记录了如何将内容翻译成二进制,以及如何将二进制翻译回可识别内容

计算机中有许多可用编码:

UTF-8

GBK

Big5

编码有许多,要使用正确的编码,才能对文件进行正确的读写操作

UTF-8是目前全球通用的编码格式

除非有特殊需求,否则,一律以UTF-8格式进行文件编码即可

什么是文件

一篇文章、一段视频、一个可执行程序,都可以被保存为一个文件,并赋予一个文件名。操作系统以文件为单位管理磁盘中的数据。一般来说,文件可分为文本文件、视频文件、音频文件、图像文件、可执行文件等多种类别

文件操作包含哪些内容

文件操作主要包括打开、关闭、读、写等操作

打开文件

读写文件

关闭文件

open()打开函数

在python,使用open函数,可以打开一个已经存在的文件,或者创建一个新文件,语法如下:

open(name,mode,encoding)

name:是要打开的目标文件名的字符串(可以包含文件所在的具体路径)

mode:设置打开文件的模式(访问模式):只读、写入、追加等

encoding:编码格式(推荐使用utf-8)

示例代码:

f = open('python.txt','r'encoding=UTF-8)

#encoding的顺序不是第三位,所以不能用位置参数,用关键字参数直接指定

注意事项:此时的f是open函数的文件对象,对象是python中一种特殊的数据类型,拥有属性和方法,可以使用对象.属性或对象.方法对其进行访问

mode常用的三种基础访问模式

r:以只读方式打开文件,文件的指针将会放在文件的开头,这是默认模式

w:打开一个文件只用于写入,如果该文件已存在则打开文件,并从开头开始编辑,原有内容会被删除,如果该文件不存在,创建新文件

a:打开一个文件用于追加。如果该文件已经存在,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入

读操作相关方法

read()方法:

文件对象.read(num)

num表示要从文件中读取的数据的长度(单位是字节),如果没有传入num,那么就表示读取文件中所有的数据

readlines()方法:

readlines可以按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个元素

readline()方法:一次读取一行内容

f=open('python.txt')

content = f.readline()

print(f'第一行:{content}')

content = f.readline()

print(f'第二行:{content}')

# 关闭文件

f.close()

# 读取文件 - readline()

line1 = f.readline()

line2 = f.readline()

line3 = f.readline()

读操作相关方法

for循环读取文件行

for line in open("python.txt","r"):

    print(line)

# 每一个line临时变量,就记录了文件的一行数据

写操作快速入门

案例演示:

# 1.打开文件

f = open('python.txt','w')

#2.文件写入

f.write('hello world')

#3.内容刷新

f.flush()

注意:

直接调用write,内容并未真正写入文件,而是会积攒在程序的内存中,称之为缓冲区

当调用flush的时候,内容会真正写入文件

1.写入文件使用open函数的"w"模式进行写入

2.写入的方法有:

write(),写入内容

flush(),刷新内容到硬盘

3.注意事项

w模式,文件不存在,会创建新文件

w模式,文件存在,会清空原有内容

close()方法,带有flush()方法的功能

文件的追加

追加写入操作快速入门

案例演示:

# 1.打开文件,通过a模式打开即可

f = open('python.txt','a')

# 2.文件写入

f.write('hello world')

# 3.内容刷新

f.flush()

注意:

a模式,文件不存在会创建文件

a模式,文件存在会在最后,追加写入文件

什么是异常

当检测到一个错误时,python解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的异常,也就是我们常说的bug

什么是异常:异常就是程序运行的过程中出现了错误

bug是什么意思:bug就是指异常的意思

异常的捕获

为什么需要捕获异常

整个程序因为一个bug停止运行

对bug进行提醒,整个程序继续运行

捕获异常的作用在于:提前假设某处会出现异常,做好提前准备,当真的出现异常的时候,可以有后续手段

基本语法:

try:

    可能发生错误的代码

except:

   如果出现异常执行的代码

快速入门

需求:尝试以“r“模式打开文件,如果文件不存在,则以”w“方式打开

try:

    f = open('linux.txt','r')

except:

    f = open('linux.txt','w')

捕获指定异常

基本语法:

try:

    print(name)

except NameError as e:

    print('name变量名称未定义错误')

注意事项

1.如果尝试执行的代码的异常类型和要捕获的异常类型不一致,则无法捕获异常

2.一般try下方只放一行尝试执行的代码

捕获多个异常

当捕获多个异常时,可以把要捕获的异常类型的名字,放到except后,并使用元组的方式进行书写

 异常else

else表示的是如果没有异常要执行的代码

try:

    print(1)

except Exception as e:

    print(e)

else:

    print('我是else,是没有异常的时候执行的代码')

异常的finally

finally表示的是无论是否异常都要执行的代码,例如关闭文件

异常具有传递性 

python模块,是一个python文件,以.py结尾。模块能定义函数,类和变量,模块里也能包含可执行的代码

模块的作用:python中有很多各种不同的模块,每一个模块都可以帮助我们快速的实现一些功能

大白话:模块就是一个python文件,里面有类、函数、变量等,我们可以拿过来用(导入模块去使用)

模块的导入方式

模块在使用前需要先导入 导入的语法如下

[from 模块名] import [模块|类|变量|函数|*] [as 别名]

常见的组合形式如:

import 模块名

from 模块名 import 类、变量、方法等

from 模块名 import *

import 模块名 as 别名

from 模块名 import 功能名 as 别名 

import 模块名

基本语法:

import 模块名

import 模块名1,模块名2

模块名.功能名()

案例:导入time模块

# 导入时间模块

import time

print("开始")

# 让程序睡眠1秒(阻塞)

print("结束")

制作自定义模块

python中已经帮我们实现了很多模块,不过有时候我们需要一些个性化的模块,这里就可以通过自定义模块实现

每个python文件都可以作为一个模块,模块的名字就是文件的名字

__all__

如果一个模块文件中有”__all__“变量,当使用‘from xxx import *'导入时,只能导入这个列表中的元素

if __name__ == '__main__':
    test_a(1,2)

只有当程序是直接执行的才会进入if内部,如果是被导入的,则if无法进入

注意事项:

不同模块,同名的功能,如果都被导入,那么后导入的会覆盖先导入的

什么是python包

从物理上来看,包就是一个文件夹,在该文件夹下包含了一个__init__.py文件,该文件夹可用于包含多个模块文件,从逻辑上看,包的本质依然是模块

包的作用:当我们的模块文件越来越多时,包可以帮助我们管理这些模块,包的作用就是包含多个模块,包的本质依然是模块

快速入门

步骤如下:

新建包”my_package"

新建包内模块:“my_module1"和”my_module2"

模块内代码如下

导入包

方式一:

import 包名.模块名

包名.模块名.目标

导入包

方式二:

注意:必须在__init__.py文件中添加__all__=[],控制允许导入的模块列表

from 包名 import *

模块名.目标

1.什么是python的包

包就是一个文件夹,里面可以存放许多python的模块(代码文件),通过包,在逻辑上将一批模块归为一类,方便使用

2.__init__.py文件的作用

创建包会默认自动创建的文件,通过这个文件来表示一个文件夹是python的包,而非普通的文件夹

3.__all__变量的作用

同模块中学习到的是一个作用,控制 import *能够导入的内容

什么是第三方包

包可以包含一堆的python模块,而每个模块又包含许多的功能

所以,我们可以认为:一个包,就是一堆同类型功能的集合体

在python程序的生态中,有许多非常多的第三方包

科学计算中常用的:numpy包

数据分析中常用的:pandas包

大数据计算中常用到的:pyspark、apache-flink包

图形可视化常用的:matplotlib、pyecharts包

人工智能常用的:tensorflow等

安装第三方包-pip

pip install 包名称

即可通过网络快速安装第三方包

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名称

什么是json

json是一种轻量级的数据交互格式。可以按照json指定的格式去组织和封装数据

json本质上是一个带有特定格式的字符串

主要功能:json就是一种在各个编程语言中流通的数据格式,负责不同编程语言中的数据传递和交互,类似于:

国际通用语言-英语

中国56个民族不同地区的通用语言-普通话

json有什么用

各种编程语言存储数据的容器不尽相同,在python中有字典dict这样的数据类型,而其他语言可能没有对应的字典

为了让不同的语言都能够相互通用的互相传递数据,json就是一种非常良好的中转数据格式

json格式数据转化

json数据的格式可以是

{"name":"admin","age":18}

也可以是:

[{"name":"admin","age":18},{"name":"root","age":16},{"name":"张三","age":20}]

python数据和json数据的相互转化

#导入json模块

#准备符号格式json格式要求的是python数据

data=[{"name":"老王","age":16},{"name":"张三","age":20}]

# 通过json.dumps(data)方法把python数据转化为了json数据

data = json.dumps(data)

# 通过json.loads(data)方法把json数据转化为了python数据

data = json.loads(data)

pyecharts模块

如果想要做出数据可视化效果图,可以jiezhupyecharts模块来完成

概况:

pyecharts的入门使用

基础折线图

# 导包,导入line功能固件折线图对象

from pyecharts.charts import Line

# 得到折线图对象

line = Line()

# 添加x轴数据

line.add_xaxis(["中国","美国","英国"])

# 添加y轴数据

line.add_yaxis("GDP",["中国","美国","英国"])

# 生成图表

line.render()

pyecharts有哪些配置选项

pyecharts模块中有很多的配置选项,常用到2个类别的选项

全局配置选项

系列配置选项

set_global_opts方法

这里全局配置选项可以通过set_global_opts方法来进行配置,相应的选项和选项的功能如下

通过bar构建基础柱状图

列表的sort方法

使用方式:

列表.sort(key=选择排序依据的函数,reverse=True|False)

参数key,是要求传入一个函数,表示将列表的每一个元素都传入函数中,返回排序的依据

参数reverse,是否反转排序结果,True表示降序,False表示升序

使用对象组织数据

1.在程序中设计表格,我们称之为:设计类(class)

class Student:

    name = None # 记录学生姓名

2.在程序中打印生产表格,我们称之为:创建对象

#基于类创建对象

stu_1 = Student()

stu_2 = Student()

3.在程序中填写表格,我们称之为:对象属性赋值

stu_1.name="周杰伦"

stu_2.name="林俊杰"

类的定义和使用

class 类名称:

    类的属性

    类的行为

类的属性:定义在类中的变量(成员变量)

类的行为:定义在类中的函数(成员方法)

成员方法的定义语法:

def 方法名(self,形参1,......形参n):

    方法体

self关键字是成员方法定义的时候,必须填写的

它用来表示类对象自身的意思

当我们使用类对象调用方法的是,self会自动被python传入

在方法内部,想要访问类的成员变量,必须使用self

类只是一种程序内的“设计图纸”,需要基于图纸产生实体(对象),才能正常工作,这种套路,称之为:面向对象编程

构造方法

python类可以使用:__init__()方法,称之为构造方法

可以实现:

在创建类对象(构造类)的时候,会自动执行

在创建类对象(构造类)的时候,将传入参数自动传递给__init__方法使用

python类可以使用:__init__()方法,称之为构造方法

可以实现:

在创建类对象(构造类)的时候会自动执行

在创建类对象(构造类)的时候,将传入参数自动传递给__init__方法使用

魔术方法

内置的类方法,各自有各自特殊的功能,这些内置方法我们称之为魔术方法

__str__:字符串方法

__lt__:小于、大于符号比较

__le__:小于等于、大于等于符号比较

__eq__:==符号比较

内存地址没有多大作用,我们可以通过__str__方法,控制类转换为字符串的行为

方法名:__str__

返回值:字符串

内容:自行定义

__lt__小于符号比较方法

直接对2个对象进行比较是不可以的

方法名:__lt__

传入参数:other,另一个类对象

返回值:True或False

内容:自行定义

方法名:__eq__ 比较运算符实现方法

传入参数:other,另一个类对象

返回值:True或False

内容:自行定义

如果没有实现__eq__方法的话,比较的是内存地址

私有成员

既然现实事物有不公开的属性和行为,那么作为现实事物在程序中映射的类,也应该支持

类中提供了私有成员的形式来支持

私有成员变量

私有成员方法

私有成员变量:变量名以__开头

私有成员方法:方法名以__开头

使用私有成员

私有成员无法被类对象使用,但是可以被其他的成员使用

封装的概念:将现实世界事物在类中描述为属性和方法,即为封装

私有成员的访问限制

类对象无法访问私有成员

类中的其他成员可以访问私有成员

私有成员的意义:在类中提供仅供内部使用的属性和方法,而不对外开放(类对象无法使用)

单继承

继承的写法

class 类名(父类名):

    类内容体

继承分为:单继承和多继承

继承表示:将从父类那里继承(复制)来成员变量和成员方法(不含私有)

class 类名(父类1,父类2,...父类n):

    类内容体

多继承注意事项

多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级

即:先继承的保留,后继承的被覆盖

pass的作用

复写

子类继承父类成员属性和成员方法后,如果对其不满意,可以进行复写

即:在子类中重新定义同名的属性或方法即可

调用父类同名成员

一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员

如果需要使用被复写的父类的成员,需要特殊的调用方式:

方式1:调用父类成员

使用成员变量:父类名.成员变量

使用成员方法:父类名.成员方法(self)

方式2:使用super()调用父类成员

使用成员变量:super()成员变量

使用成员方法:super()成员方法()

类型注解

ctrl+p可以弹出相关的提示

为什么需要类型注解

pycharm无法通过代码确定应传入什么类型,我们需要使用类型注解

类型注解:在代码中设计数据交互的地方,提供数据类型的注解(显式的说明)

主要功能:

帮助第三方IDE工具(如pycharm)对代码进行类型推断,协助做代码提示

帮助开发者自身对变量进行类型注释

支持:

变量的类型注解

函数(方法)形参列表和返回值的类型注解

类型注解的语法

为变量设置类型注解

基础语法:变量:类型

var_1:int = 10

var_2:float = 3.1415926

var_3:bool = True

var_4:str="itheima"

类对象类型注解

class Student:

    pass

stu:Student = Student()

基础容器类型注解

my_list :list = [1,2,3]

my_tuple:tuple = (1,2,3)

my_set:set={1,2,3}

my_dict:dict={"itheima":666}

my_str:str = "itheima"

容器类型详细注解

my_list:list[int] =[1,2,3]

my_tuple:tuple[str,int,bool]=("itheima",666,True)

my_set:set[int]={1,2,3}

my_dict:dict[str,int]={"itheima":666}

注意:

元组类型设置类型详细注解,需要将每一个元素都标记出来

字典类型设置类型详细注解,需要2个类型,第一个是key,第二个是value

类型注解的语法

除了使用 变量:类型,这种语法做注解外,也可以在注释中进行类型注解

语法:

# type:类型

class Student:

    pass

var_1 = random.randint(1,10)  # type:int

var_2 = json.loads(data)          # type:dict[str,int]

var_3 = func()   # type:Student

为变量设置注解,显示的变量定义,一般无需注解:

my_list :list = [1,2,3]

my_tuple:tuple = (1,2,3)

my_set:set={1,2,3}

my_dict:dict={"itheima":666}

my_str:str = "itheima"

如图,就算不写注解,也明确的知晓变量的类型

一般,无法直接看出变量类型时会添加变量的类型注解

var_1 = random.randint(1,10)  # type:int

var_2 = json.loads(data)          # type:dict[str,int]

var_3 = func()   # type:Student

并不会真正的对类型做验证和判断

函数(方法)的类型注解-形参注解

函数和方法的形参类型注解语法:

def 函数方法名(形参名:类型,形参名:类型,....):

    pass

def add(x:int,y:int):

    return x+y

def func(data:list):

    pass

函数(方法)的类型注解-返回值注解

def 函数方法名(形参:类型,....,形参:类型)->返回值类型:

    pass

Union类型

my_list:list[int] =[1,2,3]

my_dict:dict[str,int] = {"age":11,"num":3}

使用Union[类型,.....,类型]

可以定义联合类型注解

多态

多态,指的是:多种状态,即完成某个行为时,使用不同的对象会得到不同的状态

同样的行为(函数),传入不同的对象,得到不同的状态

多态常常作用在继承关系上

比如

函数(方法)形参声明接受父类对象

实际传入父类的子类对象进行工作

即:

以父类做定义声明

以子类做实际工作

用以获得同一个行为,不同的状态

抽象类(接口)

父类用来确定有哪些方法

具体的方法实现,由子类自行决定

这种写法,就叫做抽象类(也可以称之为接口)

抽象类:含有抽象方法的类称之为抽象类

抽象方法:方法体是空实现的(pass)称之为抽象方法

抽象类就好比定义了一个标准,包含了一些抽象的方法,要求子类必须实现

class AC:

    def cool_wind(self):

        pass

什么是多态

多态指的是,同一个行为,使用不同的对象获得不同的状态

如:定义函数(方法),通过类型注解声明需要父类对象,实际传入子类对象进行工作,从而获得不同的工作状态

什么是抽象类(接口)

包含抽象方法的类,称之为抽象类。抽象方法是指:没有具体实现的方法(pass)称之为抽象方法

抽象类的作用

多用于顶层设计(设计标准),以便子类做具体实现

也是对子类的一种软性约束,要求子类必须复写(实现)父类的一些方法

无处不在的数据库

使用数据库软件去获取库-》表-》数据,这种数据组织、存储的能力

并借助sql语言,完成对数据的增删改查等操作

show databases:查看有哪些数据库

use 数据库名 使用某个数据库

show tables 查看数据库内有哪些表

exit 退出mysql的命令行环境

数据定义:DDL(data definition language)

库的创建删除、表的创建删除等

数据操纵:DML(data manipulation language)

新增数据、删除数据、修改数据等

数据控制:DCL(data control language)

新增用户、删除用户、密码修改、权限管理

数据查询:DQL(data query language)

基于需求查询和计算数据

SQL是不区分大小写的

SQL可以单行或多行书写,最后以;结束

SQL支持注释:

单行注释:--注释内容(--后面一定要有一个空格)

单行注释:# 注释内容(# 后面可以不加空格,推荐加上)

多行注释:/*注释内容*/

DDL-库管理

查看数据库

show databases;

使用数据库

use 数据库名称;

创建数据库

create database 数据库名称 [CHARSET UTF8];

删除数据库

DROP DATABASE 数据库名称;

查看当前使用的数据库

SELECT DATDABASE();

DDL-表管理

查看有哪些表

show table; 注意:需要先选择数据库哦

创建表

create table 表名称(

     列名称 列类型,

     列名称 列类型,

     .......

);

列类型有:

int  --整数

flaot --浮点数

varchar(长度)  --文本,长度为数字,做最大长度限制

date --日期类型

timestamp --时间戳类型

掌握DML:INSERT数据插入

掌握DML:DELETE数据删除

掌握DML:UPDATE数据更新

DML是指数据操作语言

基础语法

insert into 表[(列1,列2,.....列n)] values (值1,值2,......,值n)[,(值1,值2,....值n)]

数据删除 delete

基础语法:

delete from 表名称 [where 条件判断]

条件判断:列 操作符 值

操作符:= < > <= >= != 等等,

如 id = 5 id < 3 id >=6 id != 5

数据更新 update

基础语法

update 表名 set 列=值 [where 条件判断];

注意事项

字符串的值,出现在sql语句中,必须要用单引号包围起来

分组聚合

分组聚合的应用常见非常多,如:统计班级中,男生和女生的人数

这种需求就需要:

按照性别分组

统计每个组的人数

这就称之为:分组聚合

基础语法:

select 字段|聚合函数 from 表 [where 条件] group by 列

聚合函数有:

-sum(列)求和

-AVG(列)求平均值

-MIN(列)求最小值

-MAX(列)求最大值

-COUNT(列|*)求数量

select database();

create table student(

id int,

name varchar(10),

age int

);

insert into student(id) values(1),(2),(3);

insert into student(id,name,age) values(4,'周杰伦',31),(5,'林俊杰',33);

delete from student where id = 1;

update student set name = '张学友' where id = 4;

select  gender,avg(age) from student group by gender 

当group by里面写了谁,就在select语句里出现谁

分组聚合的注意事项:

group by中出现了哪个列,哪个列才能出现在select中的非聚合中

掌握DQL:对查询结果进行排序分页

结果分页限制

可以使用limit关键字,对查询结果进行数量限制或分页显示

select 列|聚合函数|* from 表

where ...

order by ...[ASC|DESC]

limit n[,m]

执行顺序:

from -> where -> group by和聚合函数 ->select ->order by ->limit

pymysql

除了使用图形化工具以外,我们也可以使用编程语言来执行sql从而操作数据库

在python中,使用第三方库:pymysql来完成对mysql数据库的操作

安装:

pip install pymysql

from pymysql import Connection

# 构建到mysql数据库的连接
conn = Connection(
    host="localhost", #主机名(IP)
    port=3306,       # 端口
    user='root',   # 账户
    password="byj021119"  # 密码
)
print(conn.get_server_info())
# 执行非查询性质sql
cursor = conn.cursor()    # 获取到游标对象
# 选择数据库
conn.select_db("zuel")
# 执行查询性质sql
# cursor.execute("create table test_pymysql(id int);")
cursor.execute("select * from student")
results = cursor.fetchall()
for r in results:
    print(r)

# 关闭连接
conn.close()

commit提交

pymysql在执行数据插入或其他产生数据更改的sql语句时,默认是需要提交更改的,即,需要通过代码确认这种更改行为

通过连接对象.commit()方法即可确认此行为

python操作(138综合案例没有做)

pyspark的执行环境入口对象是:类SparkContext的类对象

sparkcontext类对象,是pyspark编程中一切功能的入口

pyspark的编程,主要分为如下三大步骤

RDD:弹性分布式数据集(Reslilient Distributed Datasets)

python数据容器转RDD对象

pyspark支持通过sparkcontext对象的parallelize成员方法,将

List

tuple

set

dict

str

转换为pyspark的rdd对象

注意:

字符串会被拆分出一个个的字符,存入rdd对象

字典仅有的key会被存入rdd对象

读取文件转rdd对象

pyspark也支持通过sparkcontext入口对象,来读取文件,来构建出rdd对象

rdd对象称之为分布式弹性数据集,是pyspark中数据计算的载体,它可以:

提供数据存储

提供数据计算的各种方法

数据计算的方法,返回值依旧是rdd(rdd迭代计算)

后续对数据进行各类计算,都是基于rdd对象进行

如何输入数据到spark(即得到rdd对象)

通过sparkcontext的parallelize成员方法,将python数据容器转换为rdd对象

通过sparkcontext的textfile成员方法,读取文本文件得到rdd对象

map方法

功能:map算子,是将rdd的数据一条条处理(处理的逻辑基于map算子中接收的处理函数)返回新的rdd

接受一个处理函数,可用lambda表达式快速编写

对rdd内的元素逐个处理,并返回一个新的rdd

链式调用

对于返回值是新rdd的算子,可以通过链式调用的方法多次调用算子

flatMap算子

功能:对rdd执行map操作,然后进行解除嵌套操作

reduceByKey算子

功能:针对KV型rdd,自动按照key分组,然后根据提供的聚合逻辑,完成组内数据(value)的聚合操作

用法:

rdd.reduceByKey(func)

# func:(V,V) -> V

接受2个传入参数(类型要一致),返回一个返回值,类型和传入要求一致

Filter

功能:过滤想要的数据进行保留

语法:

rdd.filter(func)

func:(T) -> bool 传入1个参数进来随意类型,返回值必须是true or false

filter算子

接受一个处理函数,可用lambda快速编写

函数对rdd数据逐个处理,得到true的保留至返回值的rdd中

distinct算子

功能:对rdd数据进行去重,返回新rdd

语法:

rdd.distinct() 无需传参

sortBy算子

功能:对rdd数据进行排序,基于你指定的排序依据

语法:

rdd.sortBy(func,ascending=False,numPartitions=1)

# func:(T)->U:告知按照rdd中的哪个数据进行排序,比如lambda x:x[1]表示按照rdd中的第二列元素进行排序

# ascending True升序 False 降序

# numPartitions:用多少分区排序

sortBy算子

接收一个处理函数,可用lambda快速编写

函数表示用来决定排序的依据

可以控制升序或降序

全局排序需要设置分区数为1

collect算子

功能:将rdd各个分区内的数据,统一收集到driver中,形成一个list对象

用法:

rdd.collect()

返回值是一个list 

reduce算子

功能:对rdd数据集按照你传入的逻辑进行聚合

语法:

rdd.reduce(func)

# func:(T,T)->T

# 2参数传入1个返回值,返回值和参数要求类型一致

take算子

功能:取rdd的前n个元素,组合成list返回

用法:sc.parallelize([3,2,1,4,5,6]).take(5)

count算子

功能:计算rdd有多少条数据,返回值是一个数字

用法:

sc.parallelize([1,2,3,4,5]).count()

saveAsTextFile算子

功能:将rdd的数据写入文本文件中

支持本地写出,hdfs等文件系统

注意事项

调用保存文件的算子,需要配置hadoop依赖

下载hadoop安装包

修改rdd分区为1个

方式1,sparkConf对象设置属性全局并行度为1:

conf = SparkConf().setMaster("local[*]").setAppName("test_spark")

conf.set('spark.default.parallelism","1")

sc = SparkContext(conf=conf)

方式2,创建rdd的时候设置(parallelize方法传入numSlices参数为1)

rdd1 = c.parallelize([1,2,3,4,5],numSlices=1)

rdd1 = sc.parallelize([1,2,3,4,5],1]

rdd输出到文件的方法

rdd.saveAsTextFile(路径)

输出的结果是一个文件夹

有几个分区就输出多少个结果文件夹

闭包

在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包

修改外部函数变量的值

需要使用nonlocal关键字修饰外部函数的变量才可在内部函数中修改它

闭包的注意事项

优点,使用闭包可以让我们得到

无需定义全局变量即可实现通过函数,持续访问、修改某个值

闭包使用的变量所用于在函数内,难以被错误的调用修改

缺点:

由于内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一直占用内存

什么是闭包:

定义双层嵌套函数,内层函数可以访问外层函数的变量

将内存函数作为外层函数的返回,此内层函数就是闭包函数

nonlocal关键字的使用

在闭包函数(内部函数中)想要修改外部函数的变量值

需要用nonlocal声明这个外部变量

装饰器

装饰器其实也是一种闭包,其功能就是在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能

装饰器的一般写法(闭包写法)

def outer(func):

    def inner():

        print("我要睡觉了")

        func()

        print("我要起床了")

    return inner

装饰器的语法糖写法

@outer

def sleep():

    import random

    import time

    print("睡眠中...")

    time.sleep(random.randint(1,5)) 

sleep()

什么是装饰器

装饰器就是使用创建一个闭包函数,在闭包函数内调用目标函数,可以达到不改动目标函数的同时,增加额外的功能

设计模式

设计模式是一种编程套路,可以极大的方便程序的开发

单例、工厂模式

建造者、责任链、状态、备忘录、解释器、访问者、观察者、中介、模板、代理模式

单例模式

创建类的实例后,就可以得到一个完整的、独立的类对象

单例模式

定义:保证一个类只有一个实例,并提供一个访问它的全局访问点

适用场景:当一个类只能有一个实例,而客户可以从一个众所周知的访问点访问它时

class StrTools:

    pass

设计模式就是一种编程套路

使用特定的套路得到特定的效果

什么是单例设计模式

单例模式就是对一个类,只获取其唯一的类实例对象,持续复用它

节省内存

节省创建对象的开销

工厂模式

当需要大量创建一个类的实例的时候,可以使用工厂模式

即,从原生的使用类的构造去创建对象的形式

迁移到,基于工厂提供的方法去创建对象的形式

class Person:

    pass

class Student(Person):

    pass 

class Teacher(Person):

    pass

class Factory:

    def get_person(self,p_type):

        if p_type == 'w':

            return Worker()

        elif p_type == 's':

            return Student()

        else:

            return Teacher()

factory = Factory()

worker = factory.get_person('w')

stu = factory.get_person('s')

teacher = factory.get_person('t')

worker = Worker()

stu = Student()

teacher = Teacher()

使用工厂类的get_person()方法去创建具体的类对象

优点:

大批量创建对象的时候有统一的入口,易于代码维护

当发生修改,仅修改工厂类的创建方法即可

符合现实世界的模式,即由工厂来制作产品(对象)

什么是工厂模式

将对象的创建由使用原生类本身创建

转换到由特定的工厂方法来创建

好处

大批量创建对象的时候有统一的入口,易于代码维护

当发生修改,仅修改工厂类的创建方法即可

符合现实世界的模式,即由工厂来制作产品(对象)

进程、线程

现代操作系统都是支持多任务的操作系统

进程:就是一个程序,运行在系统之上,那么便称这个程序为一个运行进程,并分配进程id方便系统管理

线程:线程是归属于进程的,一个进程可以开启多个线程,执行不同的工作,是进程的实际工作最小单位

进程好比一家公司,是操作系统对程序进行运行管理的单位

线程好比公司的员工,进程可以有多个线程(员工),是进程实际的工作者

操作系统可以运行多个进程,即多任务运行

一个进程内可以运行多个线程,即多线程运行

注意点:

进程之间是内存隔离的,即不同的进程拥有各自的内存空间。这就类似于不同的公司拥有不同的办公场所

线程之间是内存共享的,线程是属于进程的,一个进程内的多个线程之间是共享这个进程拥有的内存空间的,这就好比,公司员工之间是共享公司的办公场所

并行执行

并行执行的意思指的是同一时间做不同的工作

进程之间就是并行执行的,操作系统可以同时运行好多程序,这些程序都是并行在执行

除了进程外,线程也是可以并行执行的

比如一个python程序,完全可以做到

一个线程在输出:你好

一个线程在输出hello

像这样一个程序在同一时间做两件乃至多件不同的事情,我们就称之为:多线程并行执行

一个进程内的多个线程同时在运行,称之为:多线程并行执行

threading模块

绝大多数编程语言,都允许多线程编程,python也不例外

python的多线程可以通过threading模块来实现

import threading

thread_obj = threading.Thread([group[,target[,name[,args[,kwargs]]]]])

- group:暂时无用,未来功能的预留参数

- target:执行的目标任务名

- args:以元组的方式给执行任务传参

- kwargs:以字典方式给执行任务传参

- name:线程名,一般不用设置

需要传参的话可以通过:

args参数通过元组(按参数顺序)的方式传参

或使用kwargs参数用字典的形式传参

Socket

socket(简称 套接字)是进程之间通信的一个工具,好比现实生活中的插座,所有的家用电器想要工作都是基于插座进行,进程之间想要进行网络通信需要socket

socket负责进程之间的网络数据传输,好比数据的搬运工

2个进程之间通过socket进行相互通讯,就必须有服务端和客户端

socket服务端:等待其他进程的连接、可接受发来的消息,可以回复消息

socket客户端:主动连接服务端、可以发送消息、可以接收回复

socket服务端编程

1.创建socket对象

import socket

socket_server = socket.socket()

2.绑定socket_server到指定ip和地址

socket_server.bind(host,port)

3.服务器端开始监听端口

socket_server.listen(backlog)

# backlog为int整数,表示允许的连接数量,超出的会等待,可以不填,不填会自动设置一个合理值

4.接收客户端连接,获得连接对象

conn.address = socket_server.accept()

print(f"接收到客户端连接,连接来自:{address}")

# accept方法是阻塞方法,如果没有连接,会卡在当前这一行不向下执行代码

# accept返回的是一个二元元组,可以使用上述形式,用这两个变量接收二元元组的2个元素

5.客户端连接后,通过recv方法,接收客户端发送的消息

while True:

    data = conn.recv(1024).decode("UTF-8")

#recv方法的返回值是字节数组(Bytes),可以通过decode使用UTF-8解码为字符串

#recv方法的传参是buffsize,缓冲区大小,一般设置为1024

    if data == 'exit':

        break

    print("接收到发送来的数据:",data)

# 可以通过while True无限循环来持续和客户端进行数据交互

# 可以通过判定客户端发来的特殊标记,如exit,来退出无限循环

6.通过conn(客户端当次连接对象),调用send方法可以回复消息

while True:

    data = conn.recv(1024).decode("UTF-8")

    if data == 'exit':

        break

    print("接收到发送来的数据:",data)

    conn.send("你好呀哈哈哈".encode("UTF-8"))

7.conn(客户端当次连接对象)和socket_server对象调用close方法,关闭连接

socket客户端编程

主要分为如下几个步骤

1.创建socket对象

import socket

socket_client = socket.socket()

2.连接到服务器

socket_client.connect("localhost",8888))

3.发送消息

while True: #可以通过无限循环来确保持续的发送消息给服务端

    send_msg = input("请输入要发送的消息")

    if send_msg == 'exit':

        # 通过特殊标记来确保可以退出无限循环

        break

    socket_client.send(send_msg.encode("UTF-8"))  # 消息需要编码为字节数组(UTF-8编码)

4.接收返回消息:

while True:

    send_msg = input("请输入要发送的消息").encode("UTF-8")

    socket_client.send(semd_msg)

    recv_data = socket_client.recv(1024)   # 1024是缓冲区大小,一般1024即可

    # recv方法是阻塞式的,即不接收到返回,就卡在这里等待

   print("服务端回复消息为",recv_data,decode("UTF-8")) #接受的消息需要通过UTF-8解码为字符串

5.关闭连接

socket_client.close()   # 最后通过close关闭连接

学习目标:

了解什么是正则表达式

掌握re模块的基础使用

正则表达式:规则表达式,是使用单个字符串来描述、匹配某个句法规则的字符串,常常被用来检索、替换那些符合某个模式(规则)的文本

正则表达式就是使用:字符串定义规则,并通过规则去验证字符串是否匹配

正则的3个基础方法

python正则表达式,使用re模块,并基于re模块中三个基础方法来做正则匹配

分别是:match、search、findall三个基础方法

re.match(匹配规则,被匹配字符串)

从被匹配字符串开头进行匹配,匹配成功返回匹配对象(包含匹配的信息),匹配不成功返回空

re.search(匹配规则,被匹配字符串)

搜索整个字符串,找出匹配的。从前往后,找到第一个后,就停止,不会继续向后

正则的三个基础方法

findall(匹配规则,被匹配字符串)

匹配整个字符串,找出全部匹配项

找不到返回空列表

掌握正则表达式的各类元字符规则

了解字符串的r标记作用

.:匹配任意1个字符(除了\n,\.匹配点本身)

[]:匹配[]中列举的字符

\d:匹配数字,即0-9

\D:匹配非数字

\s:匹配空白,即空格、tab键

\S:匹配非空白

\w:匹配单词字符,即a-z、A-Z、0-9

\W:匹配非单词字符

示例:

* 匹配前一个规则的字符出现0至无数次

+ 匹配前一个规则的字符出现1至无数次

?匹配前一个规则的字符出现0次或1次

{m} 匹配前一个规则的字符出现m次

{m,}匹配前一个规则的字符吹西安最少m次

{m,n} 匹配前一个规则的字符出现m到n次

边界匹配:

^ 匹配字符串开头

$ 匹配字符串结尾

\b 匹配一个单词的边界

\B 匹配非单词边界

分组匹配

| 匹配左右任意一个表达式

() 将括号中字符作为一个分组

递归在编程中是一种非常重要的算法

递归:即方法(函数)自己调用自己的一种特殊编程写法

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值