3. 第三章 函数

3. 函数

在程序设计中, 函数是指用于进行某种计算的一系列语句的组合,
且为这个组合起一个名称, 该名称用于标识该函数.

定义一个函数时, 需要指定函数的名称并写下一系列程序语句.
之后, 就可以使用名称来'调用'这个函数.
3.1 函数调用
# 函数调用示例:
>>> type(1)
<class 'int'>

这个函数的名称是type, 括号中的表达式我们称之为函数的参数, 
这个函数调用的结果是求得参数的类型.
我们通常说函数'接收'参数, '返回'结果, 这个结果也称为返回值(return value).
Python提供了一些可以将某个值从一种类型转换为另一个类型的函数.
int()函数: 可以将'纯整型的字符串''浮点型'转为整型. 如果转换失败, 则会报错.
# 将纯数字字符串赋值给变量s, 查看s的类型为str字符串.
>>> s = '1'
>>> type(s)
<class 'str'>

# 使用int()函数将纯数字字符串转为整型.
i = int(s)
>>> type(i)
<class 'int'>

# 将浮点数转换为整数, 会直接舍弃小数部分, 而不是四舍五入.
>>> int(1.6)
1

# 浮点型字符串无法转换.
>>> int('1.1')

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '1.1'.
值错误: int()的文本无效, 基数为10: '1.1'.

# 普通字符串无法转换.
>>> int('a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'a'.
值错误: int()的文本无效, 基数为10: 'a'.

float()函数: 可以将'纯数字字符串''整型'转为浮点型.
>>> float(1)
1.0

>>> float('1')
1.0

>>> float('1.1')
1.1

# 普通字符串无法转换.
>>> float('a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: could not convert string to float: 'a'.
值错误: 无法将字符串转换为浮点数: 'a'.

str()函数: 可以将任意类型参数转换为字符串.
>>> str(1)
'1'

>>> str(1.1)
'1.1'

3.2 数学函数
Python有一个数学设计模块, 提供了大多数常用的数学函数.
模块(module): 是指包含一组相关的函数的文件.
想要使用模块中的函数, 需要先使用import语句将它导入运行环境, :
>>> import math

这个语句会创建一个名为math的模块对象(module object). 如果显示(打印)这个对象, 可以看到它的一些信息:
>>> import math
>>> math
<module 'math' (built-in)>

模块对象包含了该模块中定义的函数和变量, 若要访问其中的一个函数, 需要同时指定模块名称和函数名称,
用一个句点(.)分隔, 这个格式称为'句点表示法'(dot notation).
# 表达式math.pi的意思: 从math模块中获取变量pi, 这个变量的值是π的近似值, 大约精确到15位数.
>>> math.pi
3.141592653589793

# 返回 x/y 的余数.
>>> math.fmod(10, 3)
1.0

# 模块的其它功能省略, 有兴趣自己去查看.

3.3 组合
到现在为止, 我们已经分别了解了程序的基本元素-变量, 表达式, 语句,
但还没有接触如何将它们有机地组合起来.
程序设计语句最有用的特性之一就是可以将各种小的构建块(building block)组合起来.
例如: 函数的参数可以是任何类型的表达式, 包括算术操作符: 
# 1 + 2 + 3 就是一个组合.
print(1 + 2 + 3)  # 6

基本上, 在任何可以使用值的地方, 都可以使用任意表达式, 
只有一个例外, 赋值表达式的左边必须是变量名称, 在左边放置任何其它的表达式都是语法错误.
(后面我们还会看到这条规则的例外情况.)
# 正确示例. 分钟 = 小时 * 60
>>> hours = 6
>>> minutes = hours * 60
>>> minutes
360

# 错误示例. 
>>> 1 * 60 = minutes
  File "<stdin>", line 1
    1 * 60 = minutes
    ^
SyntaxError: cannot assign to operator.
语法错误:无法分配给运算符.

3.4 添加新函数
到此, 我们都只是在使用Python提供的函数, 其实我们也可以自己添加新的函数.
函数定义: 指定新函数的名称, 并提供一系列程序语句, 当函数被调用时, 这些语句会顺序执行.
# 定义函数.
def print_hello():
	print('hello_1')
	print('hello_2')
	print('hello_3')

    
# 调用函数, 开始执行函数内的语句.
print_hello() 

def是关键字, 表示接下来是一个函数定义.
这个函数的名字是print_hello, 函数名的书写规则和变量的名称一样使用: 字母, 数字, 下划线组成.
(开头第一个字符不能是数字, '避免与关键字', '已经定义变量名'同名.)

函数名后面的空括号表示它不接收任何参数.
函数定义的第一行称为函数头(header), 其它部分称为函数体(body).
函数头应该以冒号结束, 函数体则应当整体缩进一级.
依照惯例, 缩进总是使用4个空格(使用几个都可以, 推荐4), 函数体的代码语句行数不限. 
本例中print语句例的字符串使用单引号括起来, 单引号和双引号的作用相同. 
代码中所有的引号(包括单引号, 双引号)都必须是直引号, 而不是斜引号.

大多情况下, 人们使用单引号, 只在本例中这样的特殊情况下才使用双引号.
本例中的字符串本身就存在单引号, (这里的单引号作为缩略符号用, I'm是I am的缩写).
(想要在字符串内打印引号, 则包裹字符串的引号不能与其相同, 使用另一种引号.)
如果在交互模式例输入函数定义, 则解释器会输出省略号(...)提示用户当前的定义还没有结束.
想要要结束这个函数定义, 需要输入一个空行.
>>> def print_hello():
...     print('hello_1')
...     print('hello_2')
...     print('hello_3')
...
>>>

定义一个函数会创建一个函数对象, 其类型是'function'.
>>> print_hello
<function print_hello at 0x000001C5AE5CE040>

>>> type(print_hello)
<class 'function'>

调用新创建的函数的方式, 与调试内置函数时一样的, 函数名后面加上'括号'则调用函数.
>>> print_hello()
hello_1
hello_2
hello_3

定义好一个函数之后, 就可以在其它函数中调用它, 
另写一个函数, 将调用print_hello函数的代码写在这个函数内.
>>> def repeat_print():
...     print_hello()
...     print_hello()
...

# 调用函数.
>>> repeat_print()
hello_1
hello_2
hello_3
hello_1
hello_2
hello_3

3.5 定义和使用
将前面一节的代码的片整合起来, 整个程序如下:
>>> def print_hello():
...     print('hello_1')
...     print('hello_2')
...     print('hello_3')
>>>
>>> def repeat_print():
...     print_hello()
...     print_hello()
...
>>> repeat_print()

这个程序包含两个函数定义: print_hello和repeat_print.
函数定义的执行方式和其它语句一样, 不同的是, 执行后会创建函数对象, 函数体里面的语句并不会立即运行,
而是等到函数被调用时才执行. 函数定义不会产生任何输出.

必须先创建一个函数, 才能运行它, 换言之, 函数定义必须在函数调用之前先运行.
将调用函数的语句, 移动到首行, 让函数的调用会先于函数定义执行.
运行程序并查看会有什么样的错误信息.
# 在脚本模式下运行.
repeat_print()

def print_hello():
    print('hello_1')
    print('hello_2')
    print('hello_3')

    
def repeat_print():
    print_hello()
    print_hello()
    
运行终端显示:
Traceback (most recent call last):
  File "C:\Users\13600\PycharmProjects\test\test.py", line 1, in <module>
    print_hello()
NameError: name 'repeat_print' is not defined.
名称错误:未定义名称'repeat_print'.
将函数调用那一行放回到末尾, 并将函数print_hello的定义移动到函数repeat_print定义之后,
这个时候运行程序会发生什么?
def repeat_print():
    print_hello()
    print_hello()
    
    
def print_hello():
    print('hello_1')
    print('hello_2')
    print('hello_3')

 
repeat_print()

运行工具窗口显示:
hello_1
hello_2
hello_3
hello_1
hello_2
hello_3

定义repeat_print()时并不会执行里面的代码, 这个时候print_hello()函数还没有定义, 也会不会出错.
在调用repeat_print()函数时, print_hello()已经被定义, 执行print_hello()正常运行.
3.6 执行流程
为了保证函数的定义优先于其调用执行, 需要知道程序中语句运行的顺序, '执行流程'.

执行总是从程序的第一行开始, 语句按照从上到下的顺序逐一执行.
函数定义并不会改变程序的执行流程, 但应注意函数体中的语句并不立即执行, 而是等到函数被调用时运行.

函数调用可以看作程序运行流程中的一个迂回路径. 
遇到函数调用时, 并不会直接继续运行下一条语句, 而是跳到函数体的第一行, 
继续运行完函数体的所有语句, 再跳回到原来离开的地方, 往下执行.

函数体中可以调用其它函数, 当程序流程运行到一个函数之中时, 可能需要运行其它函数中的语句.
而后, 当运行那个函数中的语句时, 又可能再需要调用运行另一个函数的语句!
Python对于它运行到哪里有很好的记录, 所有每个函数执行结束后, 程序都能跳回到它离开的地方.
直到执行到整个程序的结尾, 才会结束程序.
总之, 在阅读代码时, 并不总因该按照代码书写的顺序一行行阅读, 
有时候, 按照程序执行的流程来阅读代码, 理解的效果可能会更好.
3.7 形参和实参
在函数调用阶段, 某些函数需要传入'参数'. 
有些需要一个参数, 有些需要多个参数, 这些参数被称为'实参'.
在函数内部, 实参会赋值给称为形参(parameter)的变量.

参数有两种: 函数定义里的形参(parameter), 以及调用函数时传入的实参(argument), 这两种是由区别的.
# 定义一个函数, 接收一个实参.
def print_message(message):
	print(message)
    
这个函数在调用时会把实参的值赋值到形参message上, 并打印message的值.
可以将任何值作为参数传递给形参.
# 定义函数.
def print_message(message):
    print(message)


# 调用函数, 传递实参.
print_message(1)  # 1

# 再次调用, 尝试传递不同的值.
print_message('hello')  # hello

内置函数的组合规则, 在用户自定义函数上也同样可用, 可以使用任何表达式作为实参.
作为实参的表达式会在函数正式调用前先执行, 计算好结果后才开始执行函数体代码.
# 定义打印信息的函数.
def print_message(message):
    print(message)


# 调用函数, 将表达式作为实参, 会先将计算表达式的值在进行传递.
print_message(1 * 2)  # 2
print_message('Hello' + ' ' + 'World!')  # Hello World!

# 可以使用变量作为实参.
num = 1
print_message(num)  # 1

作为实际传入到函数的变量的名称(num)和函数定义里形参的名称(message)没有关系.
函数内部只关心形参的值, 而不用关心它在调用前叫什么名字, 
在print_message()函数内部, 大家都叫它message.
3.8 变量和形参是局部的
在函数体内新建一个变量时, 这个变量时局部的(local), 即它只存在于这个函数之内.
# 定义一个字符拼接的函数.
def character_splicing(part1, part2):
    # 字符串拼接.
    cat = part1 + part2
    # 打印cat的值.
    print(cat)  # Hello World!


# 调用函数.
character_splicing('Hello', ' World!')

当character_splicing函数结束时, 变量cat会被销毁, 这时再尝试打印它的话, 会得到一个异常:
# 定义一个字符拼接的函数.
def character_splicing(part1, part2):
    # 字符串拼接.
    cat = part1 + part2
    # 打印cat的值.
    print(cat)  # Hello World!


# 调用函数.
character_splicing('Hello', ' World!')
print(cat)  # 报错

运行工具窗口显示:
Hello World!
Traceback (most recent call last):
  File "C:\Users\13600\PycharmProjects\test\test.py", line 11, in <module>
    print(cat)
NameError: name 'cat' is not defined.
名称错误: 未定义名称'cat'.
形参也是局部的,  在print_message函数外部不存在message这个变量.
def print_message(message):
    print(message)


print_message(1)
print(message)

运行工具窗口显示:
1
Traceback (most recent call last):
  File "C:\Users\13600\PycharmProjects\test\test.py", line 6, in <module>
    print(message)
NameError: name 'message' is not defined.
名称错误: 未定义名称'message'.
3.9 栈图
要跟踪哪些变量在地方使用, 有时候画一个栈图(stack diagram)会很方便.
和状态图一样, 栈图可以展示每个变量的值, 不同的是它会展示每个变量所属的函数.

每个函数使用的一个帧, 帧在栈图中就是一个带着函数名称的盒子, 里面有函数的参数和变量.
line1 = 'Hello'
line2 = ' World!'

# 定义打印信息的函数.
def print_message(message):
    print(message)
    
    
# 定义一个字符拼接的函数.
def character_splicing(part1, part2):
    # 字符串拼接.
    cat = part1 + part2
    # 调用print_message函数打印cat的值.
    print_message(cat)  # Hello World!


# 调用函数.
character_splicing(line1, line2)

2023-03-06_01735

图中各个帧从上到下安排成一个栈, 能够展示出哪个函数被哪个函数被调用了.
(: __main__中的值, 出现在character_splicing, 那么就说明在__main__中调用了character_splicing.)

上例中, print_message被character_splicing调用, 而Character_splicing被__main__调用.
__mina__是用于表示整个栈图的图框的特别名字, 在所有函数之外新建变量时, 它就是属于__main__的.

每个形参都指向其对应实参的值, : part1与line1的值相同, cat与message的值相同.
如果调用函数(f1)的过程中发生了错误, Python会打印出函数(f1)的名称, 调用它的函数(f2)的名称, 
以及调用这个调用者(f2)的函数名(f3), 以此类推, 一直到__main__.
例如: 在print_message中访问一个不存在的变量, 则会得到一个NameErroe.
# 定义打印信息的函数.
def print_message():
    # 访问不存在的变量.
    print(xx)


# 定义一个字符拼接的函数.
def character_splicing():
    print_message()


# 调用函数.
character_splicing()

运行工具窗口显示:
Traceback (most recent call last):
  File "C:\Users\13600\PycharmProjects\test\test.py", line 13, in <module>
    character_splicing()
  File "C:\Users\13600\PycharmProjects\test\test.py", line 9, in character_splicing
    print_message()
  File "C:\Users\13600\PycharmProjects\test\test.py", line 4, in print_message
    print(xx)
NameError: name 'xx' is not defined
上面这个函数列表被称为回溯(traceback), 它告诉你错误出现在哪个程序文件,
哪一行导致的错误, 以及哪些函数正在运行.

回溯中函数的顺序和栈图中图框的顺序一致, 当前正在执行的函数在最底层.
3.10 有返回值函数和无返回值函数
有返回值函数(fruitful function): 函数执行之后, 有返回结果().
调用一个有返回值的函数时, 大部分情况下你是想要对这个结果进行某些操作.
在交互模式中执行, Python会直接显示结果, 而脚本中, 如果没有使用变量存储或打印出来, 它就没有实际作用.
>>> import math
>>> math.fmod(10, 3)
1.0
>>>

import math

# 将返回值赋值给变量.
remainder = math.fmod(10, 3)

print(remainder)  # 1.0

没返回值函数(void function): 函数执行之后, 没有返回结果().
无返回值函数可能在屏幕上显示某些东西, 或者有其它效果, 但是它们没有返回值,
打印函数的执行结果会得到一个特殊的值None.
def print_hello():
    print('hello')  # None


print(print_hello())  # None

特殊值None有自己的属性: NoneType.
print(type(None))  # <class 'NoneType'>

到目前为止, 我们自定义的函数都是无返回值的函数, 之后会学习如何写有返回值的函数.
3.11 为什么要有函数
为什么要花功夫将程序拆分成函数?
也许刚开始编程的时候这其中的原因并不明晰, 下面解释都可以作为参考.
* 新建一个函数, 可以让你有机会给一组语句命名, 这样可以让代码更易读和更易调试.
* 函数可以通过减少重复代码使程序更短小, 后面如果需要修改代码, 也只要修改一个地方即可.
* 将一长段程序拆分成几个函数后, 可以对每一个函数单独进行调试, 再将它们组装起来成为完整的产品.
* 一个设计良好的函数, 可以在很多程序中使用, 书写一次, 复用无穷.
3.12 调试
你会掌握的一个最重要的技能就是调试, 虽然调用可能时有烦恼, 
但它的确实编程活动中最耗脑力, 最有挑战, 最有趣的部分.

在某种程度上, 调试和刑侦工作很像, 你会面对一些线索, 而且必须推导出事情发生的过程, 
以及导致现场结果的事件.

调试也像是一种实验科学, 一旦猜出错误的可能原因, 就可以修改程序, 再运行一次.
如果猜对了, 那么程序的运行结果会符合预测, 这样就离正确的程序更近一步, 如果猜错了, 则需要重新思考.
正如夏洛克・福尔摩斯所说: '当你排除掉所有的可能性, 那么剩下的, 不管多少不可能, 必定是真相'.

对某些人来说, 编程和调试是同一件事, 也就是说, 编程正是不断调试修改直到程序达到设计目的的过程.
这种想法的要旨是, 因该从一个能做某些时的程序开始, 然后做一点点修改, 并调试修改,
如此迭代, 以确保总有一个可以运行的程序.
例如: Linux是包含了数百万行代码的操作系统, 但最开始只是Linux Torvalds编写的用来研究Inter 80386
芯片的简单程序. 据Larry Greenfield所说: 'Linus最早的一个程序时交替打印AAAAA和BBBB',
后来这些程序演化成了Linux'.
3. 13 术语表
函数(function): 一个有名称的语句序列, 可以进行某种有用的操作.
	函数可以接收或不接收参数, 可以返回或不返回结果.

函数定义(function definition): 一个用来创建新函数的语句, 指定函数的名称, 参数以及它包含的语句序列.

函数对象(function object): 函数定义所创建的值. 
	函数名可以用作是变量, 来引用一个函数对象, (函数名是一个指向函数对象的变量). 

函数头(header): 函数定义的第一行.

函数体(body): 函数定义内的语句序列.

形参(parameter): 函数内使用的, 用来引用作为实参传入的值的名称.

函数调用(function call): 运行一个函数的语句. 它由函数名称和括号, 以及括号中的参数列表组成.

实参(argument): 当函数调用时, 提供给它的值. 这个值会被赋值给对应的形参.

局部变量(local variable): 函数内定义的变量. 局部变量只能在函数体内使用.

返回值(return value): 函数的结果. 如果函数被当作表达式调用, 返回值就是表达式的值.

有返回值函数(fruiful function): 返回一个值的函数.

无返回值函数(void function): 总是返回None的函数.

None: 由无返回值函数返回的一个特殊值.

模块(module): 一个包含相关函数, 以及其它定义的集合文件.

import语句(import statement): 读入一个模块文件, 并创建一个模块对象的语句.

模块对象(末dule object): 使用import语句时创建的对象, 提供对模块中定义的值的访问.

句点表示法(dot notation): 调用另一个模块中函数的语法, 使用模块名加上一个句点符号, 再加上函数名.

组合(composition): 使用一个表达式作为更大表达式的一部分, 或者使用语句作为更大语句的一部分.

执行流程(flow of execution): 语句运行的顺序.

栈图(stack diagram): 函数栈的图形表达式, 也展示它们的变量, 以及这些变量引用的值.

图框(frame): 栈图中的一个图框, 表达一个函数调用. 它包含了局部变量以及函数的参数.

回溯(traceback): 当异常发生时, 打印出正在执行的函数栈.

3.14 练习
1. 练习1
编写一个函数right_justify, 接收一个字符串形参s(值为: 'monty'), 并打印出足够的前导空白, 
以达到最后一个字符出现在地70列上(70 - 5 = 65, 需要65个空格). 
提示: 可以利用字符串的拼接和重复特性.
另外, Python提供了一个内置名称len的函数, 返回一个字符串的长度, 所有len('allen')的值是5.
def right_justify(s):
    # 65个空格.
    space = 65 * ' '
    # 拼接字符串.
    print_message = space + s
    # 统计字符串长度, 在打印结果.
    print(len(print_message))
    # 打印拼接的字符串.
    print(space + s)


right_justify('monty')

运行工具窗口显示:
70
                                                                 monty
2. 练习2
函数对象是一个值, 可以将它赋值给变量, 或者作为实参传递.
例如, do_twice是一个函数, 接收一个函数对象作为实参, 并调用它两次:
def do_twice(f):
	f()
	f()
    
下面一个使用do_twice来调用一个print_spam函数两次的实例:
def print_spam():
	print('spam')

    
do_twice(print_spam)

1. 将这个实例存入脚本中并测试它.
def do_twice(f):
    f()  # spam
    f()  # spam


def print_spam():
    print('spam')


do_twice(print_spam)

2. 修改do_twice, 让它接收连个实参, 一个实函数对象, 另一个是一个值, 
它会调用函数对象两次, 并传入哪个值最为实参.
def do_twice(f, spam):
    f(spam)  # spam
    f(spam)  # spam


def print_spam(spam):
    print(spam)


do_twice(print_spam, 'spam')

3. 将函数print_twice的定义复制到你的脚本中. 
def  print_twice(bruce):
	print(bruce)
	print(bruce)
	

def do_twice(f, spam):
    f(spam)  # spam
    f(spam)  # spam


def print_spam(spam):
    print(spam)


do_twice(print_spam, 'spam')

4. 使用修改版的do_teice来调用print_twice两次, 并传入实参'spam'.
def print_twice(bruce):
    print(bruce)  # spam
    print(bruce)  # spam


def do_twice(f, spam):
    f(spam)  # spam
    f(spam)  # spam

    print_twice(spam)
    print_twice(spam)


def print_spam(spam):
    print(spam)


do_twice(print_spam, 'spam')

5. 定义一个新的函数do_four, 接收一个函数对象, 与一个值, 使用这个值作为实参调用函数4.
这个函数的函数体因该只有两条语句, 而不是4.

解答: https://github.com/AllenDowney/ThinkPython2/tree/master/code/do_four.py
def do_twice(func, arg):
    func(arg)
    func(arg)


def do_four(func, arg):
    do_twice(func, arg)
    do_twice(func, arg)


do_four(print, 'spam')

终端工具显示:
spam
spam
spam
spam
3. 练习3
注意: 这个练习因该只用语句和我们已经学过的其它语言特性实现.
1. 编写一个函数, 绘制如下表格:
+ - - - - + - - - - +
|         |         |
|         |         |
|         |         |
|         |         |
+ - - - - + - - - - +
|         |         |
|         |         |
|         |         |
|         |         |
+ - - - - + - - - - +
提示: 要在用一行打印多个值, 可以使用逗号分隔不同的值:
print('+', '-')
这条语句输出的是'+' '-'.
不带参数的print语句会结束当前行并开始下一行.
默认情况下, print会自动换行, 如果你想改这一行为, 在结尾打印一个空格, 可以这样做:
print('+', end=' ')
print('-')
这两条语句输出的是'+  -'.
line1 = '+ - - - - + - - - - +'
line2 = '|         |         |'

# 打印一次line1.
def print_once(part):
    print(part)


# 打印三次次line2.
def print_triple(part):
    print(part)
    print(part)
    print(part)


def print_table(part1, part2):
    print_once(part1)
    print_triple(part2)

    print_once(part1)
    print_triple(part2)

    print_once(part1)


print_table(line1, line2)

2. 编写一个函数绘制类似的表格, 但是又44.
解答: https://github.com/AllenDowney/ThinkPython2/blob/master/code/grid.py
+ - - - - + - - - - + - - - - + - - - - + 
|         |         |         |         | 
|         |         |         |         | 
|         |         |         |         | 
|         |         |         |         | 
+ - - - - + - - - - + - - - - + - - - - + 
|         |         |         |         | 
|         |         |         |         | 
|         |         |         |         | 
|         |         |         |         | 
+ - - - - + - - - - + - - - - + - - - - + 
|         |         |         |         | 
|         |         |         |         | 
|         |         |         |         | 
|         |         |         |         | 
+ - - - - + - - - - + - - - - + - - - - + 
|         |         |         |         | 
|         |         |         |         | 
|         |         |         |         | 
|         |         |         |         | 
+ - - - - + - - - - + - - - - + - - - - + 
line1 = '+ - - - - + - - - - + - - - - + - - - - + '
line2 = '|         |         |         |         | '


# 打印一次line1.
def print_once(part):
    print(part)


# 打印三次次line2.
def print_triple(part):
    print(part)
    print(part)
    print(part)


def print_table(part1, part2):
    print_once(part1)
    print_triple(part2)

    print_once(part1)
    print_triple(part2)

    print_once(part1)
    print_triple(part2)

    print_once(part1)
    print_triple(part2)

    print_once(part1)


print_table(line1, line2)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值