函数基础

函数的分类:内建函数,max(),reversed()等

库函数:math.ceil()等

 

函数的作用:封装,复用,简洁,美观易懂

 

函数定义,调用:定义需要在调用之前,也就是说调用时,已经被定义过了

否则,抛NameError异常

 

函数是可调用对象,callable()   True,False

 

函数的参数:

参数调用时,传入的参数要和定义的个数想匹配(可变参数例外)

位置参数

  • def f(x,y,x) 调用使用f(1,3,5)
  • 按照参数定义顺序传入实参

关键字参数

  • def f(x,y,z)调用使用f(x=1,y=3,z=5)
  • 使用形参的名字来传入实参,传参的顺序可以和定义的顺序不同

传参

  • 位置参数必须在关键字参数之前传入,位置参数是按位置对应的
def add(x, y):
    return x+y


print(add(5, 7))     ------> 12
print(add(x=6, y=9))   ------> 15
print(add(y=2, 8))   ------>  SyntaxError: positional argument follows keyword argument

参数默认值

定义时,在形参后跟上一个值

def add(x=4, y=5):

    return x+y

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


print(add(5, 7))
print(add(5, y=77))
print(add(5))
print(add(x=5))
print(add(y=8))
print(add())
print(add(y=5, x=7))

错误:
print(add(x=5, 7))
print(add(y=5, 7))

定义时的错误:
def add1(x=4, y):    # 应该为 def add1(y,x=4):

作用:可以未传入足够的实参的时候,对没有给定的参数赋值为默认值,有时不需要每次都要输入所有参数,可简化函数调用

 举例:

定义一个函数login,参数名为host,port,username,password

def login(host='127.0.0.1', port='8080', username='panqi', password='hehe'):
    print('{}:{}@{}/{}'.format(host, port, username, password))


login()
login('192.168.1.111', '80', 'tom', 'haha')
login('localhost', port='80', password='haha')
login(password='heihei', port='80', host='www')

可变参数 ---- 一个形参可以匹配任意个参数

有多个数,需要累加求和

def add(nums):
    sumnum = 0
    for x in nums:
        sumnum += x
    return sumnum


print(add([1, 3, 5]))
print(add((2, 4, 6)))

# 传入一个可迭代对象,迭代元素求和

位置参数的可变参数

def add(*nums):
    sumnum = 0
    for x in nums:
        sumnum += x
    return sumnum


print(add(1, 3, 5))

# 在形参前使用*表示该形参是可变参数,可以接收多个实参
# 收集多个实参为一个tuple

关键字参数的可变参数

def showconfig(**kwargs):
    for k, v in kwargs.items():
        print('{}={}'.format(k, v))


showconfig(host='127.0.0.1', port='8080', username='panqi', password='hehe')

# 形参前使用**符号,表示可以接收多个关键字参数
# 收集的实参名称和值组成一个字典

out:
port=8080
username=panqi
password=hehe
host=127.0.0.1

可变参数混合使用

def showconfig(username, password, **kwargs):
    print(username, password, kwargs)


showconfig(host='127.0.0.1', port='8080', username='panqi', password='hehe')

out:
panqi hehe {'host': '127.0.0.1', 'port': '8080'}

-----------------------------------------------------------------------------

def showconfig(username, *args, **kwargs):
    print(username, args, kwargs)


showconfig('panqi', '8080', '192.168.1.177', password='hehe')

out:
panqi ('8080', '192.168.1.177') {'password': 'hehe'}

-----------------------------------------------------------------------------

def showconfig(username, password, *args, **kwargs):
    print(username, password, args, kwargs)


out:
panqi hehe ('8080',) {'host': '192.168.1.177'}

可变参数总结:

  • 有位置可变参数和关键字可变参数
  • 位置可变参数在形参前使用一个*号
  • 关键字可变参数在形参前使用两个*号(**)
  • 位置可变参数和关键字可变参数都可收集若干个实参,位置可变参数收集形成一个tuple,关键字可变参数收集形成一个dict
  • 混合使用参数时,可变参数要放到参数列表的最后,普通参数要放到参数列表前面,位置可变参数要在关键字可变参数之前

举例:

def fn(x, y, *args, **kwargs):
    print(x)
    print(y)
    print(args)
    print(kwargs)


fn(3, 5, 7, 9, 10, a=1, b='python')
print('------------------------------')
fn(3, 5)
print('------------------------------')
fn(3, 5, 7)
print('------------------------------')
fn(3, 5, a=1, b='python')
# fn(7, 9, y=5, x=3, a=1, b='python')  # 错误 7,9分别赋值给x,y,又y=5,x=3,重复

out:
3
5
(7, 9, 10)
{'a': 1, 'b': 'python'}
------------------------------
3
5
()
{}
------------------------------
3
5
(7,)
{}
------------------------------
3
5
()
{'a': 1, 'b': 'python'}

keyword-only参数(python3加入)

如果在一个*号参数后,或者一个位置可变参数,出现的普通参数,不再是普通参数,而是keyword-only参数

def fn(*args, x, y, **kwargs):
    print(x)
    print(y)
    print(args)
    print(kwargs)


fn(7, 9, y=5, x=3, a=1, b='python')
# args可以看做已截获了所有的位置参数,y,x不使用关键字参数就不可能拿到实参

out:
3
5
(7, 9)
{'b': 'python', 'a': 1}

而def(**kwargs, x):

这种写法没有必要,kwagrs会截获所有关键字参数,就算写了x=9,x也永远得不到这个值,所以语法错误

 

keyword-only参数另一种形式

def fn(*, x, y):
    print(x, y)


fn(x=5, y=6)

# *号之后,普通形参都变成了必须给出keyword-only参数

可变参数和参数默认值 

def fn(*args, x=5):
    print(args)
    print(x)


fn()  # 等价于fn(x=5)
print('--------------------')
fn(5)
print('--------------------')
fn(x=6)
print('--------------------')
fn(1, 2, 3, x=10)

out:
()
5
--------------------
(5,)
5
--------------------
()
6
--------------------
(1, 2, 3)
10
def fn(y, *args, x=5):   # x是keyword-only参数
    print('x={}, y={}'.format(x, y))
    print(args)


# fn() # y没有传参
fn(5)  # 5传给y 可变位置参数没有,x为默认值
# fn(x=6) # y没有传参
fn(1, 2, 3, x=10)  # 1传给y 2,3为可变位置参数,x=10
# fn(y=17,2,3,x=10) # 位置参数跟在关键字参数后面
# fn(1,2,y=3,x=10) #1已经位置参数给了y,后面又关键字传参y=3,重复了

out:
x=5, y=5
()
x=10, y=1
(2, 3)
def fn(x=5, **kwargs):
    print('x={}'.format(x))
    print(kwargs)


fn()
print('--------------')
fn(5)
print('--------------')
fn(x=6)
print('--------------')
fn(y=3, x=10)
print('--------------')
fn(3, y=10)

out:
x=5
{}
--------------
x=5
{}
--------------
x=6
{}
--------------
x=10
{'y': 3}
--------------
x=3
{'y': 10}

参数规则

参数列表参数一般顺序是,普通参数---缺省参数---可变位置参数---keyword-only参(可带缺省省)---可变关键字参数

def fn(x, y, z=3, *arg(*), m=4, n, **kwargs)
 

def fn(x, y, z=3, *args, m=4, n, **kwargs):
    print(x, y, z)
    print(args)
    print(m)
    print(n)
    print(kwargs)


fn(2, 4, 6, 8, 10, n=16, k='hehe')

out:
2 4 6
(8, 10)
4
16
{'k': 'hehe'}
def connect(host='localhost', port='3306', user='admin', password='admin', **kwargs):
    print(host, port)
    print(user, password)
    print(kwargs)


connect(db='cmdb')
print('--------------------------')
connect(host='192.168.1.123', db='cmdb')
print('--------------------------')
connect(host='192.168.1.123', db='cmdb', password='mysql')

out:
localhost 3306
admin admin
{'db': 'cmdb'}
--------------------------
192.168.1.123 3306
admin admin
{'db': 'cmdb'}
--------------------------
192.168.1.123 3306
admin mysql
{'db': 'cmdb'}

参数解构

def add(x, y):
    return x+y


print(add(4, 5))
t = (4, 5)
print(add(t[0], t[1]))
print(add(*t))
print(add(*(4, 5)))
print(add(*[4, 5]))
print(add(*{4, 5}))
print(add(*range(4, 6)))


# 全部一个结果,为9
  • 给函数提供实参的时候,可以集合类型前使用*或者**,把集合类型的结构解开,并提取所有元素作为函数的实参
  • 非字典类型使用*解构成位置参数
  • 字典类型使用**解构成关键字参数
  • 提取出来的元素数目要和参数的要求匹配,也要和参数的类型匹配
def add(x, y):
    return x+y


print(add(*(4, 5)))
print('-------------')
print(add(*[4, 5]))
print('-------------')
print(add(*{4, 6}))
print('-------------')
d = {'x': 5, 'y': 6}
print(add(**d))
print('-------------')
print(add(**{'x': 5, 'y': 6}))
# add(**{'a': 5, 'b': 6}) 关键字名字不对


out:
9
-------------
9
-------------
10
-------------
11
-------------
11

参数解构和可变参数

  • 给函数提供实参的时候,可以在集合类型前使用*或者**,把集合类型的结构解开,提取出所有元素作为函数的实参
def add(*iterable):
    print(iterable)
    result = 0
    for x in iterable:
        result += x
    return result


print(add(1, 2, 3))
print(add(*[1, 2, 3]))
print(add(*range(10)))

out:
(1, 2, 3)
6
(1, 2, 3)
6
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
45

函数的返回值总结:

  • 函数使用return语句返回“返回值”
  • 所有函数都有返回值,如果没有return语句,会隐式调用return None
  • return语句并不一定是函数的语句块的最后一条语句
  • 一个函数可以存在多个return语句,但只有一条可以被执行,如果没有,会隐式调用return None
  • 如果有必要,可以显示调用return None,简写为return
  • 函数如果执行了return语句,函数就会返回,不再执行下去了
  • 作用:结束函数调用,返回值
def fn():
    return [1, 2, 3]  # 返回一个值(列表),此值有三个元素


def fn1():
    return 1, 2, 3   # 返回一个值(元组)


def fn2():
    return 1,   # 返回一个值(元组)


def fn3():
    return 1    # 返回一个单值,1



x, y, z = fn()   # 使用解构提取更为方便

 作用域 

一个标识符的可见范围,就是作用域,一般常说的是变量的作用域

如下:

x = 5


def fn():
    print(x)

    
fn()

#没问题,正常输出5

------------------------------------
x = 5


def fn():
    x += 1    ---> x = x + 1    赋值即定义,相当于重新定义了一个本地变量x,
    print(x)                    而此时,本地并没有x,报UnboundLocalError未绑定本地变量
                                
                                且x也覆盖不了外部的x,不管是局部还是全局的
fn()
 

特别重要


全局作用域:

  • 在整个程序运行环境中都可见

 

局部作用域:

  • 在函数、类等内部可见
  • 局部变量使用范围不能超过其所在的局部作用域

def fn():

     x = 1  # 局部作用域,在fn内

 

def fn1():
     print(x)   #  x不可见

 

def outer1():
    o = 65

    def inner():   # 先定义不执行,等第8条,调用才执行
        print('inner{}'.format(o))  # 此时,本地没有变量:o  向上一层找到o = 65  第二个输出
        print(chr(o))   # A   第三个输出
    print('outer{}'.format(o))  # outer65  第一个输出
    inner()  # 调用 执行inner函数


outer1()

out:
outer65
inner65
A
def outer2():
    o = 65

    def inner():   # 先定义不执行,等第9条,调用才执行
        o = 97
        print('inner{}'.format(o))  # 此时,本地有变量:o = 97  第二个输出 inner97
        print(chr(o))   # chr(97)   第三个输出 a
    print('outer{}'.format(o))  # 本地作用域内,有变量o = 65   第一个输出 outer65
    inner()  # 调用 执行inner函数


outer2()

out:
outer65
inner97
a

以上例子可以看出

  • 外层变量作用域在内层作用域内可见
  • 内层作用域inner中,如果定义了o=97,相当于当前作用域中重新定义了一个新的变量o,但这个o并没有覆盖外层作用域outer中的o

全局变量global

x = 5


def fn():
    global x
    x += 1
    return x


print(fn())

out:6

前面提到x += 1,会报错,这里使用global,将fn内的x变量声明为使用外部的全局作用域中定义的x

全局作用域中必须有x的定义

def fn():
    global x
    x = 10
    x += 1
    return x


print(fn())
print(x)

out:
11
11

但是,x = 10 赋值即定义,x在内部作用域为一个外部作用域的变量赋值,这里的x的作用域还是全局的

global总结:

  • 外部作用域变量会内部作用域可见,但为了函数封装,尽量要函数传参

闭包

自由变量:未在本地作用域中定义的变量,例如定义在内层函数外的外层函数的作用域中的变量

闭包是一个概念,出现在嵌套函数中

指的是内层函数引用到了外层函数的自由变量,就形成了闭包

def fn():
    c = [0]

    def fn1():
        c[0] += 1   # 修改的是列表的元素   这里的c引用的是自由变量,也就是fn里的c
        return c[0]    #  python2中实现闭包的方式,python3还可以使用nonlocal关键字
    return fn1


foo = fn()
c = 111
print(foo(), foo())
print(foo())

out:
1 2
3

nonlocal关键字

使用后,将变量标记为不在本地作用域定义,而在上级的某一级局部作用域中定义,但不能是全局作用域中定义

def fn():
    count = 0

    def fn1():
        nonlocal count
        count += 1
        return count
    fn1()
    return fn1


foo = fn()
print(foo())
print(foo())
print(foo())


out:
2
3
4

count是外层函数的局部变量,被内部函数引用

内部函数使用nonlocal关键字声明count变量在上级作用域而非本地作用域中定义

上面代码且可形成闭包


默认值的作用域

def foo(xyz=1):
    print(xyz)


foo()   -------> 1
foo()   --------> 1
print(xyz)    ----------> NameError: name 'xyz' is not defined
def foo(xyz=[]):
    xyz.append(1)
    print(xyz)


foo()  -----> [1]
foo()   ------>  [1, 1]
print(xyz)  ------>   NameError: name 'xyz' is not defined  当前作用域没有xyz变量

第二次foo() 输出为[1, 1]

因为函数也是对象,python把函数的默认值放了在属性中,这个属性就伴随着这个函数对象的整个生命周期

查看foo.__defaults__属性

def foo(xyz=[], u='abc', z=123):
    xyz.append(1)
    return xyz


print(foo(), id(foo))
print(foo.__defaults__)
print(foo(), id(foo))
print(foo.__defaults__)

out:
[1] 2824230231712
([1], 'abc', 123)
[1, 1] 2824230231712
([1, 1], 'abc', 123)

函数地址没有变,就是说函数这个对象一直没有变是,调用它,它的属性__default__中使用元组保存默认值

xyz默认值是引用类型,引用类型的元素变动,并不是元组的变化

非引用类型例子:

def foo(w, u='abc', z=123):
    u = 'xyz'
    z = 789
    print(w, u, z)


print(foo.__defaults__)
foo('magedu')
print(foo.__defaults__)

out:
('abc', 123)
magedu xyz 789
('abc', 123)
  • 属性__default__中使用元组保存所有位置参数默认值,它不会因为在函数体内使用了它而发生变化
  • 元组不可变
def foo(w, u='abc', *, z=123, zz=[456]):  # z、zz 是keyword-only参数
    u = 'xyz'
    z = 789
    zz.append(1)
    print(w, u, z, zz)


print(foo.__defaults__)
foo('magedu')
print(foo.__kwdefaults__)


out:
('abc',) # 单元组
magedu xyz 789 [456, 1]
{'z': 123, 'zz': [456, 1]}
  • 属性__kwdefaults__中使用字典保存所有keyword-only参数的默认值

 

使用可变类型作为默认值,就可能修改这个默认值

有时候这个特性是好的,有的时候这种特性是不好的,有副作用

def foo(xyz=[], u='abc', z=123):
    xyz = xyz[:]  # 等同 a = xyz[:]   所以前面的这个xyz不是形参中的xyz
    xyz.append(1)  # 这里xyz.append 也类似a.append 与形参中的xyz无关,不影响其值改变
    print(xyz)


foo()   # [1]
print(foo.__defaults__)  # ([], 'abc', 123)
foo()   # [1]
print(foo.__defaults__)  # ([], 'abc', 123)
foo([10])   # [10, 1]
print(foo.__defaults__)  # ([], 'abc', 123)
foo([10, 5])   # [10, 5, 1]
print(foo.__defaults__)  # ([], 'abc', 123)

# 后面为输出结果
# xyz都是传入参数或者默认参数的副本,如果想修改原参数,无能为力
def foo(xyz=None, u='abc', z=123):
    if xyz is None:
        xyz = []
    xyz.append(1)
    print(xyz)


foo()  # xyz为None,进if语句,xyz = [],   xyz.append(1)  xyz为[1]
print(foo.__defaults__)  # (None, 'abc', 123)
foo()  # xyz为None,进if语句,xyz = [],   xyz.append(1)  xyz为[1]
print(foo.__defaults__)  # (None, 'abc', 123)
foo([10])  # 不使用默认值,为[10],append(1),  xyz为[10,1]
print(foo.__defaults__)  # (None, 'abc', 123)
foo([10, 5])   # 不使用默认值,为[10, 5],append(1),  xyz为[10,5,1]
print(foo.__defaults__)  # (None, 'abc', 123) 

out:
[1]
(None, 'abc', 123)
[1]
(None, 'abc', 123)
[10, 1]
(None, 'abc', 123)
[10, 5, 1]
(None, 'abc', 123)

使用不可变类型默认值,如果使用缺省值None就创建一个列表,如果传入一个列表,就修改这个列表 


第一种方法:

  • 使用影子拷贝创建一个新的对象,永远不能改变传入的参数

第二种方法:

  • 通过值的判断就可灵活的选择创建或者修改传入对象
  • 这种方式灵活,应用广泛
  • 很多函数的定义,都可以看到使用None这个不可变的值作为默认参数,可以说是一种惯用法

函数的销毁

def foo(xyz=[], u='abc', z=123):
    xyz.append(1)
    z = 111
    return xyz, z


a = foo
print(id(a))   # 2240486465184
print(foo(), id(foo), foo.__defaults__)  # ([1], 111) 2240486465184 ([1], 'abc', 123)


def foo(xyz=[], u='abc', z=123):   # 重新定义过foo
    xyz.append(1)
    return xyz


print(foo(), id(foo), foo.__defaults__)   # [1] 2240486465320 ([1], 'abc', 123)
del foo  # 相当于对象的引用计数减1
print(a(), id(a), a.__defaults__)
# 但a还保留着foo函数对象所指向的地址  ([1, 1], 111) 2240486465184 ([1, 1], 'abc', 123)
# 注意这个foo是第一个的foo,与下面这个重新赋值定义的foo不一样,看从id就能看出来
# print(foo(), id(foo), foo.__defaults__) 这条会报下面的错误
# 而重新赋值定义的foo,又被del,引用计数减1,所以会报NameError: name 'foo' is not defined

全局函数的销毁

  • 重新定义同名函数
  • del语句删除函数对象,引用计数减1
  • 程序结束时
def foo(xyz=[], u='abc', z=123):
    xyz.append(1)

    def inner(a=10):
        pass
    print(id(inner))

    def inner(a=100):
        print(xyz, a)
    print(id(inner))
    return inner


print(id(foo))
bar = foo()  # 会首先输出第6行,接着第10行,可以看出两个inner已经不是同一个,第一个被覆盖了
# 而且它会得到foo函数的返回值,inner函数对象(第二个)
print(id(bar), foo.__defaults__, bar.__defaults__)
# 所以id(bar)应该和第10行一样,而从bar.__defaults__也能看出,为第二个inner,a的默认值
# del bar  # 只相当于inner的引用计数减1(有可能别地还有个kao = foo(),那就又引用到了),等到其引用计数为0,就得gc了,
# 但bar这个标识符就不能再调用什么东西了,所以最后两句就会报错
print(id(foo))   # 这是全局函数,得程序结束时,或重定义,或del时,才能删除,这样不为函数调用结束就消亡
print(foo.__defaults__)
print(id(bar))
print(bar.__defaults__)

out:
2258046377632
2258046377768
2258047541320
2258047541320 ([1], 'abc', 123) (100,)
2258046377632
([1], 'abc', 123)
2258047541320
(100,)

局部函数的销毁

  • 重新在上级作用域定义同名函数
  • del语句删除函数对象
  • 上级作用域销毁时

这里的上级作用域销毁:

# del bar  如果不销毁bar

del foo   # 销毁foo,虽然inner的引用计数减1,但还是能输出下面两行
print(id(bar))
print(bar.__defaults__)

# 因为bar还在引用inner


函数的执行流程

def foo1(b, b1=3):
    print("foo1 called", b, b1)


def foo2(c):
    foo3(c)
    print("foo2 called", c)


def foo3(d):
    print("foo3 called", d)


def main():
    print("main called")
    foo1(100, 101)
    foo2(200)
    print("main ending")


main()


out:
main called
foo1 called 100 101
foo3 called 200
foo2 called 200
main ending

1、全局帧中生成foo1,foo2,foo3,main函数对象
2、main函数调用
3、main中查找内建函数print压栈,将常量字符串压栈,调用函数,弹出栈顶
4、main中全局查找函数foo1压栈,将常100,101压栈,调用函数foo1,创建栈帧,
   print函数压栈,字符串和变量b,b1压栈,调用函数,弹出栈顶,返回值
5、main中全局查找foo2函数压栈,将常量200压栈,调用foo2,创建栈帧,
   foo3函数压栈,变量c引用压栈,调用foo3,创建栈帧,foo3完成print函数调用后返回,
   foo2恢复调用,执行print后,返回值。
6、main中foo2函数调用结束后,弹出栈顶,main继续执行下一条print函数调用,弹出栈顶,main函数返回

递归

  • 递归一定要有边界条件
  • 当边界条件不满足时,递归前进
  • 当边界条件满足时,递归返回
# Loop
num = 8
per = 0
cur = 1
for i in range(num):
    per, cur = cur, per+cur
    print(per, end=' ')

print()


def fb(n):
    """婓波那契数列:F(n)=F(n-1)+F(n-2)"""
    return 1 if n < 2 else fb(n-1) + fb(n-2)


# 1 1 2 3 5 8 13 21
for i in range(8):
    print(fb(i), end=' ')


out:
1 1 2 3 5 8 13 21 
1 1 2 3 5 8 13 21 

超过递归深度限制,抛RecursionError:maxinum recursion depth exceeded

查看sys.getrecursionlimit()

效率比较

import datetime
start = datetime.datetime.now()
pre = 0
cur = 1
num = 35
for i in range(num-1):
    pre, cur = cur, pre + cur
    print(pre, end=' ')
delta = (datetime.datetime.now() - start).total_seconds()
print(delta)

print()


num = 35
start = datetime.datetime.now()


def fib(n):
    return 1 if n < 2 else fib(n-1) + fib(n-2)


for i in range(num):
    print(fib(i), end=' ')
delta = (datetime.datetime.now() - start).total_seconds()
print(delta)


out:
1 1 2 3 5 8 13 21 34 55 89 ..... 2178309 3524578 5702887 0.0

1 1 2 3 5 8 13 21 34 55 89 ..... 2178309 3524578 5702887 9227465 7.147847

循环时间忽略,而递归要7.14秒

婓波拉契数列的改进:

import datetime
start = datetime.datetime.now()


def fib(n, pre=0, cur=1):  # recursion
    pre, cur = cur, pre + cur
    print(pre, end=' ')
    if n == 2:
        delta = (datetime.datetime.now() - start).total_seconds()
        print(delta)
        return
    fib(n-1, pre, cur)


fib(35)

out:
1 1 2 3 5 8 13 21 34 55 89 ..... 2178309 3524578 5702887 0.0

间接递归:

def foo1():
    foo2()


def foo2():
    foo1()


foo1()

就是通过别的函数调用了函数自身

如果构成循环递归调用是非常危险的,但是往往这种情况在代码复杂的情况下,还是可能会发生调用

只能让代码规范来避免

总结:

  • 递归是一种很自然的表达,符合逻辑思维
  • 递归相对运行效率低,每一次调用函数都要开辟栈帧
  • 递归有深度限制,太深,函数反复压栈,栈内存很快就会溢出
  • 如果是有限次数的递归,可以使用递归调用,或者循环代替
  • 绝大多数递归,都可以使用循环实现
  • 能不用则不用递归

练习:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值