【python 让繁琐工作自动化】第4章 列表


Automate the Boring Stuff with Python: Practical Programming for Total Beginners (2nd Edition)
Written by Al Sweigart.
The second edition is available on 2019.10.29


4.1 列表数据类型

列表 (list) 是一个值,在有序序列中包含多个值。术语列表值 (list value) 指的是列表本身 (该值可以存储在变量中,也可以像其他值一样,可以传递给函数),而不是列表值中的值。
列表以左方括号开始,以右方括号结束,即 []。列表中的值也称为表项 (item)。表项之间用逗号分隔 (也就是说,它们是逗号分隔的 (comma-delimited))。

spam = ['cat', 'bat', 'rat', 'elephant']
用下标取得列表中的单个值
print('The ' + spam[1] + ' ate ' + spam[0] + '.')	# 打印 The bat ate cat.

使用的下标不能超出列表中值的个数。下标只能是整数,不能是浮点值。

列表也可以包含其他列表值。这些列表的列表中的值,可以通过多重下标来访问:

spam = [['cat', 'bat'], [10, 20, 30, 40, 50]]
print(spam[0]) # 打印 ['cat', 'bat']
print(spam[1][4]) # 打印 50
负数下标

整数值 −1 指的是列表中的最后一个下标,−2 指的是列表中倒数第二个下标,以此类推。

spam = ['cat', 'bat', 'rat', 'elephant']
print('The ' + spam[-1] + ' is afraid of the ' + spam[-3] + '.') # 打印 The elephant is afraid of the bat.
利用切片取得子列表

切片(slice)可以以新列表的形式从列表中取得多个值。切片输入在一对方括号中,像下标一样,但它有两个冒号分隔的整数。
在一个切片中,第一个整数是切片开始处的下标。第二个整数是切片结束处的下标。切片向上增长,直至第二个下标的值,但不包括它。

spam = ['cat', 'bat', 'rat', 'elephant']
print(spam[0:4]) # 打印 ['cat', 'bat', 'rat', 'elephant']
print(spam[1:3]) # 打印 ['bat', 'rat']
print(spam[0:-1]) # 打印 ['cat', 'bat', 'rat']

作为一种快捷方式,可以在切片中忽略冒号两边的一个或两个下表。省略第一个下表与使用 0 或列表的开头相同。省略第二个下表与使用列表的长度相同,它将切片到列表的末尾。

print(spam[:2]) # 打印 ['cat', 'bat']
print(spam[1:]) # 打印 ['bat', 'rat', 'elephant']
print(spam[:]) # 打印 ['cat', 'bat', 'rat', 'elephant']
用 len() 取得列表的长度
print(len(spam))	# 打印 4
用下标改变列表中的值
spam = ['cat', 'bat', 'rat', 'elephant']
spam[1] = 'aardvark'
print(spam) # 打印 ['cat', 'aardvark', 'rat', 'elephant']
spam[2] = spam[1]
print(spam) # 打印 ['cat', 'aardvark', 'aardvark', 'elephant']
spam[-1] = 12345
print(spam) # 打印 ['cat', 'aardvark', 'aardvark', 12345]
列表连接和列表复制

操作符 + 可以连接两个列表,得到一个新列表。操作符 * 可以用于一个列表和一个整数,实现列表的复制。

print([1, 2, 3] + ['A', 'B', 'C']) # 打印 [1, 2, 3, 'A', 'B', 'C']
print(['X', 'Y', 'Z'] * 3) # 打印 ['X', 'Y', 'Z', 'X', 'Y', 'Z', 'X', 'Y', 'Z']
用 del 语句从列表中删除值

del 语句将删除列表中下表处的值。删除值之后列表中的所有值将向前移动一个下标。

spam = ['cat', 'bat', 'rat', 'elephant']
del spam[2]
print(spam) # 打印 ['cat', 'bat', 'elephant']

del 语句也可用于删除一个简单变量,作用就像是“取消赋值”语句。如果在删除之后试图使用该变量,就会遇到 NameError 错误,因为该变量已不再存在。
在实践中,基本上不需要删除简单变量。del 语句几乎总是用于删除列表中的值。


4.2 使用列表

# 使用列表,可以保存用户输入的任意多的猫
catNames = []
while True:
    print('Enter the name of cat ' + str(len(catNames) + 1) + ' (Or enter nothing to stop.):')
    name = input()
    if name == '':
        break
    catNames = catNames + [name] # 列表连接
print('The cat names are:')
for name in catNames:
    print('  ' + name)
列表用于循环
for i in [0, 1, 2, 3]:
    print(i)

注意:在本书中使用术语类似列表(list-like),来指技术上称为序列(sequences)的数据类型。

一种常见的 Python 技术,是在 for 循环中使用使用 range(len(someList)) 来遍历列表的下标。

supplies = ['pens', 'staplers', 'flame-throwers', 'binders']
for i in range(len(supplies)):
    print('Index ' + str(i) + ' in supplies is: ' + supplies[i])
in 和 not in 操作符
# 检查输入的宠物名字是否在列表中
myPets = ['Zophie', 'Pooka', 'Fat-tail']
print('Enter a pet name:')
name = input()
if name not in myPets:
    print('I do not have a pet named ' + name)
else:
    print(name + ' is my pet.')
多重赋值技巧
cat = ['fat', 'orange', 'loud']
size, color, disposition = cat

变量的数量和列表的长度必须完全相等,否则 Python 会给出错误 ValueError。

多重赋值技巧也可以用来交换两个变量中的值:

a, b = 'Alice', 'Bob'
a, b = b, a # 交换两个变量中的值
print(a) # 打印 'Bob'
print(b) # 打印 'Alice'
使用 enumerate() 函数

在循环的每次迭代中,enumerate() 将返回两个值:列表中项目的索引和列表本身中的项目。

>>> supplies = ['pens', 'staplers', 'flamethrowers', 'binders']
>>> for index, item in enumerate(supplies):
...     print('Index ' + str(index) + ' in supplies is: ' + item)

Index 0 in supplies is: pens
Index 1 in supplies is: staplers
Index 2 in supplies is: flamethrowers
Index 3 in supplies is: binders
使用 random.choice() 函数和 random.shuffle() 函数

random.choice() 函数将从列表中返回一个随机选择的项目。

>>> import random
>>> pets = ['Dog', 'Cat', 'Moose']
>>> random.choice(pets)
'Dog'
>>> random.choice(pets)
'Cat'
>>> random.choice(pets)
'Cat'

可以认为 random.choice(someList)someList[random.randint(0, len(someList)– 1]) 的较短形式。

random.shuffle() 函数将对列表中的项目重新排序。该函数在原地修改列表,而不是返回新列表。

>>> import random
>>> people = ['Alice', 'Bob', 'Carol', 'David']
>>> random.shuffle(people)
>>> people
['Carol', 'David', 'Alice', 'Bob']
>>> random.shuffle(people)
>>> people
['Alice', 'David', 'Bob', 'Carol']

4.3 增强赋值操作符

表4-1 增强的赋值操作符

增强的赋值语句等价的赋值语句
spam += 1spam = spam + 1
spam -= 1spam = spam - 1
spam *= 1spam = spam * 1
spam /= 1spam = spam / 1
spam %= 1spam = spam % 1

操作符 += 还可以完成字符串和列表的连接,操作符 *= 可以完成字符串和列表的复制。

spam = 'Hello'
spam += ' world!'
print(spam) # 打印 'Hello world!'

bacon = ['Zophie']
bacon *= 3
print(bacon) # 打印 ['Zophie', 'Zophie', 'Zophie']

4.4 方法

方法(method)和函数是一样的,不同的是它被一个值“调用”。方法部分位于值之后,以句点分隔。
每种数据类型都有自己的一组方法。例如,列表数据类型有一些有用的方法,来查找、添加、删除和操作列表中的值。

使用 index() 方法在列表中查找值

列表值有一个 index() 方法,可以传入一个值,如果该值存在于列表中,就返回它的下标。如果该值不在列表中,Python 就报 ValueError。

spam = ['hello', 'hi', 'howdy', 'heyas']
print(spam.index('heyas')) # 打印 3
print(spam.index('howdy howdy howdy')) # 报错:ValueError: 'howdy howdy howdy' is not in list

当列表中有重复的值时,返回它第一次出现的下标。

spam = ['Zophie', 'Pooka', 'Fat-tail', 'Pooka']
print(spam.index('Pooka')) # 打印 1
使用 append() 和 insert() 方法向列表添加值

append() 方法将参数添加到列表的末尾。
insert() 方法可以在列表中的任意下标处插入一个值。insert() 的第一个参数是新值的下标,第二个参数是要插入的新值。

spam = ['cat', 'dog', 'bat']
spam.append('moose')
print(spam) # 打印 ['cat', 'dog', 'bat', 'moose']
spam.insert(1, 'chicken')
print(spam) # 打印 ['cat', 'chicken', 'dog', 'bat', 'moose']

方法属于单一数据类型。append() 和 insert() 方法是列表方法,只能对列表值调用,而不能对字符串或整数等其他值调用;否则,Python 会给出 AttributeError 错误消息。

使用 remove() 方法从列表中删除值
spam = ['cat', 'bat', 'rat', 'elephant']
spam.remove('bat')
print(spam) # 打印 ['cat', 'rat', 'elephant']

试图删除列表中不存在的值将导致 ValueError 错误。

spam.remove('chicken') # 报错:ValueError: list.remove(x): x not in list

如果该值在列表中出现多次,则仅删除该值的第一个实例。

spam = ['cat', 'bat', 'rat', 'cat', 'hat', 'cat']
spam.remove('cat')
print(spam) # 打印 ['bat', 'rat', 'cat', 'hat', 'cat']

如果知道要从列表中删除的值的索引,可以使用 del 语句。如果知道要从列表中删除的值,使用 remove() 方法。

使用 sort() 方法对列表中的值进行排序

可以使用 sort() 方法对数值列表或字符串列表进行排序。

spam = [2, 5, 3.14, 1, -7]
spam.sort()
print(spam) # 打印 [-7, 1, 2, 3.14, 5]

spam = ['ants', 'cats', 'dogs', 'badgers', 'elephants']
spam.sort()
print(spam) # 打印 ['ants', 'badgers', 'cats', 'dogs', 'elephants']

可以传递 True 给 reverse 关键字参数,使 sort() 按逆序排序。

spam.sort(reverse=True)
print(spam) # 打印 ['elephants', 'dogs', 'cats', 'badgers', 'ants']

无法对同时包含数字值和字符串值的列表进行排序,因为 Python 不知道如何比较这些值。

spam = [1, 3, 2, 4, 'Alice', 'Bob']
spam.sort() # 报错:TypeError: unorderable types: str() < int()

在 Python2 中运行上面的代码,没有报错,得到结果 spam = [1, 2, 3, 4, ‘Alice’, ‘Bob’]

sort() 对字符串排序使用 “ASCII 字符顺序”,而不是实际的字母顺序。这意味着大写字母在小写字母之前。

spam = ['Alice', 'ants', 'Bob', 'badgers', 'Carol', 'cats']
spam.sort()
print(spam) # 打印 ['Alice', 'Bob', 'Carol', 'ants', 'badgers', 'cats']

如果需要按字母顺序对值进行排序,将关键字参数 key 设置为 str.lower

spam = ['a', 'z', 'A', 'Z']
spam.sort(key=str.lower)
print(spam) # 打印 ['a', 'A', 'z', 'Z']

这将导致 sort() 方法将列表中的所有项视为小写,但实际上不更改列表中的值。

使用 reverse() 方法反转列表中的值
>>> spam = ['cat', 'dog', 'moose']
>>> spam.reverse()
>>> spam
['moose', 'dog', 'cat']

4.5 示例程序:魔术 8 球与列表

import random

messages = ['It is certain',
    'It is decidedly so',
    'Yes definitely',
    'Reply hazy try again',
    'Ask again later',
    'Concentrate and ask again',
    'My reply is no',
    'Outlook not so good',
    'Very doubtful']

print(messages[random.randint(0, len(messages) - 1)])

Python 中缩进规则的例外
在大多数情况下,一行代码的缩进量告诉 Python 它在哪个块中。不过,这条规则也有一些例外。例如,列表实际上可以跨越源代码文件中的几行。这些行的缩进并不重要。Python知道,直到看到结束方括号时,列表才算完成。
大多数人利用 Python 的行为,使他们的列表看起来漂亮易读,就像神奇 8 球程序中的消息列表一样。
也可以在行末使用续行字符 \,将一条指令写成多行。可以把 \ 看成是“这条指令在下一行继续”。\ 续行字符之后的一行中,缩进并不重要。

print('Four score and seven ' + \
      'years ago...')

4.6 类似列表的类型:字符串和元组

列表并不是唯一表示序列值的数据类型。例如,若将字符串视为单个文本字符的 “列表”,则字符串和列表实际上是相似的。对列表的许多操作,也可以作用于字符串:按下标取值、切片、用于 for 循环、用于 len(),以及用于 in 和 not in 操作符。

name = 'Zophie'
print(name[0]) 		# 打印 'Z'
print(name[-2]) 	# 打印 'i'
print(name[0:4]) 	# 打印 'Zoph'
print(len(name)) 	# 打印 6
print('Zo' in name) # 打印 True
print('p' not in name) # 打印 False
for i in name: # i 的取值依次为 'Z', 'o', 'p', 'h', 'i', 'e'
	print('* * * ' + i + ' * * *')
可变和不可变数据类型

但是列表和字符串在一个重要的方面是不同的。列表值是一种可变的(mutable)数据类型:它可以添加、删除或更改值。然而,字符串是不可变的(immutable):它不能被改变。尝试对字符串中的一个字符重新赋值,会导致 TypeError 错误。

name = 'Zophie a cat'
name[7] = 'the' # 报错:TypeError: 'str' object does not support item assignment

“改变”字符串的正确方法,是使用切片和连接,通过复制旧字符串的部分来构建新字符串。

name = 'Zophie a cat'
newName = name[0:7] + 'the' + name[8:12]
print(name) # 打印 'Zophie a cat'
print(newName) # 打印 'Zophie the cat'

重新赋值并不能修改原来的列表。只是新的列表值覆盖了旧的列表值。

eggs = [1, 2, 3]
print(id(eggs))
eggs = [4, 5, 6]
print(id(eggs))

运行结果可能为:

140643064299336
140643064316040

两者的内存地址并不相同,表示名称为 eggs 的列表是两个不同的列表。

修改 eggs 中原来的列表,让它包含 [4, 5, 6]:

eggs = [1, 2, 3]
print(id(eggs))
del eggs[2]
del eggs[1]
del eggs[0]
eggs.append(4)
eggs.append(5)
eggs.append(6)
print(id(eggs))

运行结果可能为:

140239384989512
140239384989512

两者的内存地址相同,表示名称为 eggs 的列表是同一个。

元组数据类型

元组输入时用圆括号 ()

eggs = ('hello', 42, 0.5)
print(eggs[0]) # 打印 'hello'
print(eggs[1:3]) # 打印 (42, 0.5)
print(len(eggs)) # 打印 3

元组像字符串一样,是不可变的。元组的值不能被修改、添加或删除。否则会导致 TypeError 错误。

如果元组中只有一个值,可以通过在圆括号内的值后面加上逗号来表示。否则,Python 会认为您只是在常规括号内键入了一个值。逗号让 Python 知道这是一个元组值。(与其他一些编程语言不同,在 Python 中,在列表或元组的最后一项后面加上逗号是可以的。)

>>> type(('hello',))
<class 'tuple'>
>>> type(('hello'))
<class 'str'>

可以用元组告诉所有读代码的人,不打算改变这个序列的值。
因为元组是不可变的,所以 Python 可以实现一些优化,使得使用元组的代码比使用列表的代码稍微快一些。

使用 list() 和 tuple() 函数来转换类型

在 IDLE 中输入下面内容:

>>> tuple(['cat', 'dog', 5])
('cat', 'dog', 5)
>>> list(('cat', 'dog', 5))
['cat', 'dog', 5]
>>> list('hello')
['h', 'e', 'l', 'l', 'o']

4.7 引用

a = 10
b = a
print(id(a))
print(id(b))
a = 12
print(id(a))
print(b)

上面的代码运行的一种结果为:

10853920
10853920
10853984
10

将 a 赋值给 b 时,a 和 b 指向同一个内存地址。当运行到 a = 12 时,a 指向了另一个内存地址。b 不变。

将一个列表赋值给一个变量时,实际上是将一个列表引用(reference)赋值给该变量。引用是指向一些数据的值,列表引用是指向列表的值。

spam = [0, 1, 2, 3, 4, 5]
cheese = spam
print(id(spam))
print(id(cheese))
cheese[1] = 'Hello!'
print(spam) 	# 打印 [0, 'Hello!', 2, 3, 4, 5]
print(cheese) 	# 打印 [0, 'Hello!', 2, 3, 4, 5]
print(id(spam))
print(id(cheese))

上面的代码运行的一种结果为:

139703613665096
139703613665096
[0, 'Hello!', 2, 3, 4, 5]
[0, 'Hello!', 2, 3, 4, 5]
139703613665096
139703613665096

列表变量 spam 和 cheese 指向内存地址相同,且没有改变。两者指向同一个列表,列表本身实际从未复制。

传递引用
def eggs(someParameter):
    someParameter.append('Hello')

spam = [1, 2, 3]
eggs(spam)
print(spam) # 打印 [1, 2, 3, 'Hello']

尽管 spam 和 someParameter 包含不同的引用,但它们都指向同一个列表。这就是为什么函数内部的append(‘Hello’) 方法调用即使在函数调用返回之后也会影响列表的原因。
请记住这种行为:忘记 Python 以这种方式处理列表和字典变量,可能会导致令人困惑的 bug。

copy 模块的 copy() 和 deepcopy() 函数

copy.copy() 可以用来复制可变值,比如列表或字典,而不仅仅是复制引用。

import copy
spam = ['A', 'B', 'C', 'D']
cheese = copy.copy(spam)
print(id(spam))
print(id(cheese))
cheese[1] = 42
print(spam) # ['A', 'B', 'C', 'D']
print(cheese) # ['A', 42, 'C', 'D']

上面的代码运行的一种结果是:

140716989992840
140717008873672
['A', 'B', 'C', 'D']
['A', 42, 'C', 'D']

列表变量 spam 和 cheese 指向不同的内存地址,指向不同的列表。

如果需要复制的列表包含列表,那么使用 copy.deepcopy() 函数代替 copy.copy()deepcopy() 函数将同时复制它们内部的列表。

import copy
list1 = [1, 2, [3, 4]]
list2 = copy.copy(list1)
list3 = copy.deepcopy(list1)
list1[2][0] = 7
print(list1)
print(list2)
print(list3)
print(id(list1[2]))
print(id(list2[2]))
print(id(list3[2]))

上面的代码运行的结果为:

[1, 2, [7, 4]]
[1, 2, [7, 4]]
[1, 2, [3, 4]]
139733085488008
139733085488008
139733085488072

4.8 康威的生命游戏

# conway.py - Conway's Game of Life
import random, time, copy
WIDTH = 60
HEIGHT = 20

# Create a list of list for the cells:
nextCells = []
for x in range(WIDTH):
    column = [] # Create a new column.
    for y in range(HEIGHT):
        if random.randint(0, 1) == 0:
            column.append('#') # Add a living cell.
        else:
            column.append(' ') # Add a dead cell.
    nextCells.append(column) # nextCells is a list of column lists.

while True: # Main program loop.
    print('\n\n\n\n\n') # Separate each step with newlines.
    currentCells = copy.deepcopy(nextCells)

    # Print currentCells on the screen:
    for y in range(HEIGHT):
        for x in range(WIDTH):
            print(currentCells[x][y], end='') # Print the # or space.
        print() # Print a newline at the end of the row.

    # Calculate the next step's cells based on current step's cells:
    for x in range(WIDTH):
        for y in range(HEIGHT):
            # Get neighboring coordinates:
            # `% WIDTH` ensures leftCoord is always between 0 and WIDTH - 1
            leftCoord  = (x - 1) % WIDTH
            rightCoord = (x + 1) % WIDTH
            aboveCoord = (y - 1) % HEIGHT
            belowCoord = (y + 1) % HEIGHT

            # Count number of living neighbors:
            numNeighbors = 0
            if currentCells[leftCoord][aboveCoord] == '#':
                numNeighbors += 1 # Top-left neighbor is alive.
            if currentCells[x][aboveCoord] == '#':
                numNeighbors += 1 # Top neighbor is alive.
            if currentCells[rightCoord][aboveCoord] == '#':
                numNeighbors += 1 # Top-right neighbor is alive.
            if currentCells[leftCoord][y] == '#':
                numNeighbors += 1 # Left neighbor is alive.
            if currentCells[rightCoord][y] == '#':
                numNeighbors += 1 # Right neighbor is alive.
            if currentCells[leftCoord][belowCoord] == '#':
                numNeighbors += 1 # Bottom-left neighbor is alive.
            if currentCells[x][belowCoord] == '#':
                numNeighbors += 1 # Bottom neighbor is alive.
            if currentCells[rightCoord][belowCoord] == '#':
                numNeighbors += 1 # Bottom-right neighbor is alive.

            # Set cell based on Conway's Game of Life rules:
            if currentCells[x][y] == '#' and (numNeighbors == 2 or numNeighbors == 3):
                # Living cells with 2 or 3 neighbors stay alive:
                nextCells[x][y] = '#'
            elif currentCells[x][y] == ' ' and numNeighbors == 3:
                # Dead cells with 3 neighbors become alive:
                nextCells[x][y] = '#'
            else:
                # Everything else dies or stays dead:
                nextCells[x][y] = ' '
    time.sleep(1) # Add a 1-second pause to reduce flickering.

4.9 实践项目

逗号代码

假设有一个这样的列表值:
spam = [‘apples’, ‘bananas’, ‘tofu’, ‘cats’]
编写一个函数,该函数接受一个列表值作为参数,并返回一个字符串,该字符串包含所有表项,表项之间由逗号和空格分隔,最后一个表项前插入 and。例如,将前面的 spam 列表传递给函数,将返回 ‘apples, bananas, tofu, and cats’。但是你的函数应该能够处理传递给它的任何列表值。

def commaCode(listExam):
	if len(listExam) == 0:
		return ''
	if len(listExam) == 1:
		return str(listExam[0])
	result = ''
	for i in range(len(listExam) - 2):
		result += str(listExam[i]);
		result += ', '
	result += str(listExam[-2])
	result += ' and '
	result += str(listExam[-1])
	return result

spam = ['apples', 'bananas', 'tofu', 'cats']
print(commaCode(spam))
字符图网格

假设有一个列表的列表,其中内部列表中的每个值都是包含一个字符的字符串,如下所示:

grid = [['.', '.', '.', '.', '.', '.'],
        ['.', 'O', 'O', '.', '.', '.'],
        ['O', 'O', 'O', 'O', '.', '.'],
        ['O', 'O', 'O', 'O', 'O', '.'],
        ['.', 'O', 'O', 'O', 'O', 'O'],
        ['O', 'O', 'O', 'O', 'O', '.'],
        ['O', 'O', 'O', 'O', '.', '.'],
        ['.', 'O', 'O', '.', '.', '.'],
        ['.', '.', '.', '.', '.', '.']]

可以认为 grid[x][y] 是一幅“图”在 x、y 坐标处的字符,该图由文本字符组成。原点 (0, 0) 在左上角,向右 x 坐标增加,向下 y 坐标增加。
复制前面的网格值,编写代码用它打印出图像。

..OO.OO..
.OOOOOOO.
.OOOOOOO.
..OOOOO..
...OOO...
....O....

提示:需要使用循环嵌套循环,打印出 grid[0][0],然后 grid[1][0],然后 grid[2][0],以此类推,直到 grid[8][0]。这就完成第一行,所以接下来打印换行。然后程序将打印出 grid[0][1],然后 grid[1][1],然后 grid[2][1],以此类推。程序最后将打印出 grid[8][5]。
另外,如果不想在每次 print() 调用后自动打印换行,请记住将 end 关键字参数传递给 print()。

def picture(grid):
	m = len(grid)
	n = len(grid[0])
	for y in range(n):
		for x in range(m):
			print(grid[x][y], end = '')
		print() # 打印换行

grid = [['.', '.', '.', '.', '.', '.'],
        ['.', 'O', 'O', '.', '.', '.'],
        ['O', 'O', 'O', 'O', '.', '.'],
        ['O', 'O', 'O', 'O', 'O', '.'],
        ['.', 'O', 'O', 'O', 'O', 'O'],
        ['O', 'O', 'O', 'O', 'O', '.'],
        ['O', 'O', 'O', 'O', '.', '.'],
        ['.', 'O', 'O', '.', '.', '.'],
        ['.', '.', '.', '.', '.', '.']]
picture(grid)

【python 让繁琐工作自动化】目录
学习网站:https://automatetheboringstuff.com/chapter4/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值