这个夏天,你还可以和美丽的姑娘一起刷Python | Coding小组第4周学习笔记 — ScalersTalk成长会 – 持续行动,刻意学习 – ScalersTalk Wonderland

这个夏天,你还可以和美丽的姑娘一起刷Python | Coding小组第4周学习笔记

学术研究 scalerstalk 浏览 0条评论

7.15.jpg

前几天介绍了夏天一起虐口译活动,有许多童鞋来咨询并加入成长会开始行动,现在的交传小组成员来自国内、英国、美国,我觉得这已经算是一个全球化的训练小组了。

但是我们做的事情不止这一个。Scalers的公众号介绍是游走在口译世界的IT从业者,所以,你不仅会在这里看到口译,也会看到IT。所以今天的文章是要介绍我们正在进行的另外一个学习小组ScalersTalk成长会Coding小组

Coding小组其实就是在做编程方面的研习与训练。目前我们的节奏是共同刷一本书《Python核心编程》,每天刷10页,自行完成书上的练习,周末共同总结。现在已经进行了4周,所以今天和大家分享的是第4周的学习笔记。这其实就是集体学习了,我们小组共同学习一本书,共同实践,大家从各自地角度交流自己的理解,并且针对学习中的问题相互讨论。水平高一点的,就是在用经典教材做复习,扎实基础,水平入门的,就在用经典教材快速上手,并自行多投入时间自己开小灶。我们是从Python这门编程语言着手,在完成以后,会用这个语言做一些共同的项目,到后期会越来越好玩。

所以正好夏天到了,这个夏天除了可以在成长会一起虐口译,还可以在成长会一起刷Python。英语和计算机是现代社会的两大基础技能,把这两个搞好了,未来还是可以去想象一下的。

那为什么今天的标题叫《这个夏天,你还可以和美丽的姑娘一起刷Python》?我其实不是在用美女做营销话题,而是我的确注意到,在绝大多数和IT有关的社群或者公众号的读者中,男生的占比太高了,长期和机器与男生打交道,以至于有许多男生都不会好好说话了。我在的一些技术社群中,男女生比例差距如此之大,以至于群里只要有女生冒泡说话,男生们就像蜀犬吠日般狂热躁动……在这种环境下,身心都无法正常成长,何况学习技能和知识?

不过作为穿行在工科和文科世界的成长会而言,我们从来不存在这样的问题。我们的公众号性别比例非常均衡,而且女生甚至比男生多一些。所以哪怕是像编程、机器学习这样的小组,也有相当比例的美丽姑娘。由于大家是心向成长而相聚,所以阳光向上、积极乐观,也是社群的主旋律。我相信只有身心健康成长,我们才有全面的、稳定均衡的发展。

所以这个夏天,除了可以和成长会一起虐口译,还可以和美丽的姑娘一起刷Python,把你的视野充分打开,下次看到会Python又能做口译的姑娘,就不会汪汪汪了。

Coding小组目前仅面向成长会成员开放,要进入Coding小组,需要完成Codecademy.com上面的Python部分的全部学习课程与练习。

当然你也可以联系我,我相信你能找到我的QQ号码。

第4周Python学习笔记

四周前我们开启了Coding小组的Python学习活动。这是我们的第4周学习笔记,由成长会聆木整理汇总。目前的节奏是每天10Python核心编程,然后辅以相关的练习,周末线上总结讨论。以后你在ScalersTalk公众号,不仅仅能看到英语学习的进度,也能看到代码学习的进度。我们会把这件事情持续下去,用Python玩出更多的花样。如果你想来,可以加入成长会,和我们一起刷刷刷。

python中的集合类型

集合对象是一组无序排列的可哈希的值。集合支持innot in操作符检查成员,也支持len()方法得到集合基数的大小,用for循环迭代集合的成员。

集合有两种不同的类型,可变集合和不可变集合。可变结合可以添加和删除元素,不可变集合不能这样子做。

创建集合类型和给集合赋值

集合的工厂方法 set() frozenset()来创建集合。

>>> s = set(‘cheeseshop’)
>>> s
set([
‘c’, ‘e’, ‘h’, ‘o’, ‘p’, ‘s’])
>>> t = frozenset(‘bookshop’)
>>> t
frozenset([
‘b’, ‘h’, ‘k’, ‘o’, ‘p’, ‘s’])
>>> frozenset([‘b’, ‘h’, ‘k’, ‘o’, ‘p’, ‘s’])
frozenset([
‘b’, ‘h’, ‘k’, ‘o’, ‘p’, ‘s’])
>>> type(s)
<type
‘set’>
>>> type(t)
<type
‘frozenset’>
>>> len(s)
6
>>>

访问集合的值

使用in或者是not in来访问集合中的值

>>> ‘k’ in s
False
>>> ‘k’ in t
True
>>> ‘c’ not in t
True
>>> for i in s:
  print i

c
e
h
o
p
s
>>>

更新集合

使用add,update,remove以及set等方法来更新,添加和删除集合的成员。比如:

>>> s.add(‘z’)
>>> s
set([
‘c’, ‘e’, ‘h’, ‘o’, ‘p’, ‘s’, ‘z’])
>>> s.update(‘pypi’)
>>> s
set([
‘c’, ‘e’, ‘i’, ‘h’, ‘o’, ‘p’, ‘s’, ‘y’, ‘z’])
>>> s.remove(‘z’)
>>> s
set([
‘c’, ‘e’, ‘i’, ‘h’, ‘o’, ‘p’, ‘s’, ‘y’])
>>> s -= set(‘pypi’)
>>> s
set([
‘c’, ‘e’, ‘h’, ‘o’, ‘s’])
>>>

只有可变集合能被修改。试图修改不可变集合会引发异常

>>> t.add(‘z’)
Traceback (most recent call last):
 File
“<stdin>”, line 1, in <module>
AttributeError:
‘frozenset’ object has no attribute ‘add’
>>>

del方法是用来删除集合中的成员而使用的。

>>> del s
>>> s

集合类型操作符

Python 中的 in not in 操作符决定某个元素是否是一个集合中的成员。

等价/不等价被用于在相同或不同的集合之间做比较。两个集合相等是指,对每个集合而言,当且仅当其中一个集合中的每个成员同时也是另一个集合中的成员。

>>> s = set(‘cheeshshop’)
>>> s
set([‘c’, ‘e’, ‘h’, ‘o’, ‘p’, ‘s’])
>>> u = set(‘cheap’)
>>> u
set([‘a’, ‘h’, ‘c’, ‘e’, ‘p’])
>>> s == u
False
>>> u = frozenset(s)
>>> u
frozenset([‘p’, ‘c’, ‘e’, ‘h’, ‘s’, ‘o’])
>>> s == u
True
>>>

子集/超集

SetsPython的比较操作符检查某集合是否是其他集合的超集或子集。小于符号( <,<=)用来判断子集,“大于符号( >, >= )用来判断超集。

小于大于意味着两个集合在比较时不能相等。等于号允许非严格定义的子集和超集。

>>> set(‘shop’) < set(‘cheeseshop’)
True
>>> set(‘bookshop’) >= set(‘shop’)
True
>>>

集合类型操作符

联合( | )

联合(union)操作和集合的 OR(又称可兼析取(inclusive disjunction))其实是等价的,两个集合的联合是一个新集合,该集合中的每个元素都至少是其中一个集合的成员,,属于两个集合其中之一的成员。联合符号有一个等价的方法,union()

>>> s | u
set([
‘c’, ‘e’, ‘h’, ‘o’, ‘p’, ‘s’])
>>>

交集( & )

两个集合的交集是一个新集合,该集合中的每个元素同时是两个集合中的成员,,属于两个集合的成员。交集符号有一个等价的方法,intersection()

>>> s & u
set([
‘c’, ‘e’, ‘h’, ‘o’, ‘p’, ‘s’])
>>>

差补/相对补集( – )

两个集合(s u)的差补或相对补集是指一个集合 C,该集合中的元素,只属于集合 s,而不属于集合 t。差符号有一个等价的方法,difference()

>>> s – u
set([])
>>>
对称差分( ^ )

两个集合(s u)的对称差分是指另外一个集合 C,该集合中的元素,只能是属于集合 s 或者集合 t的成员,不能同时属于两个集合。对称差分有一个等价的方法,symmetric_difference()

>>> u = set(‘cheap’)
>>> u
set([
‘a’, ‘h’, ‘c’, ‘e’, ‘p’])
>>> s = set(‘shopcheap’)
>>> s
set([
‘a’, ‘c’, ‘e’, ‘h’, ‘o’, ‘p’, ‘s’])
>>> u ^ s
set([
‘s’, ‘o’])
>>>

仅适用于可变集合的操作符

   1 (|=) 等价于 update()

>>> s = set(‘cheeseshop’)
>>> u = frozenset(s)
>>> s |= set(
‘pypi’)
>>> s
set([
‘c’, ‘e’, ‘i’, ‘h’, ‘o’, ‘p’, ‘s’, ‘y’])
>>>

2 (&=) 保留/交集更新 保留(或交集更新)操作保留与其他集合的共有成员

>>> s = set(u)
>>> s &= set(
‘shop’)
>>> s
set([
‘h’, ‘s’, ‘o’, ‘p’])
>>>

3 (-=) 差更新 对集合 s t 进行差更新操作 s-=t,差更新操作会返回一个集合,该集合中的成员是集合 s 去 除掉集合 t 中元素后剩余的元素。此方法和 difference_update()等价。

4 对称差分更新 ( ^= ) 返回一个集合,该集合中的成 员仅是原集合 s 或仅是另一集合 t 中的成员,剔除共有元素。

内建函数

len()

把集合作为参数传递给内建函数 len(),返回集合的基数(或元素的个数)

集合类型工厂函数

set() frozenset()工厂函数分别用来生成可变和不可变的集合。如果不提供任何参数,默认会生成空集合。如果提供一个参数,则该参数必须是可迭代的,,一个序列,或迭代器,或支持迭代的一个对象。

集合类型内建方法

内建方法 copy() 没有等价的操作符。和同名的字典方法一样,copy()方法比set(),frozenset(), dict()这样的工厂方法复制对象的副本要快。

条件和循环

if语句

由三部分组成: 关键字本身, 用于判断结果真假的条件表达式, 以及当表达式为真或者非零时执行的代码块。

if expression:
       
   expr_true_suite

多重条件表达式

单个 if 语句可以通过使用布尔操作符 and , or not实现多重判断条件或是否定判断条件。

单一语句的代码块

如果一个复合语句(例如 if 子句, while for 循环)的代码块仅仅包含一行代码, 那么它可以和前面的语句写在同一行上。

if make_hard_copy: send_data_to_printer()

else 语句

Python 提供了与 if 语句搭配使用的 else 语句。如果 if 语句的条件表达式的结果布尔值为假, 那么程序将执行 else 语句后的代码。

if expression:
       
   expr_true_suite
   
else:
       
   expr_false_suite

elif Python else-if 语句, 它检查多个表达式是否为真, 并在为真时执行特定代码块中的代码. else 一样, elif 声明是可选的, 然而不同的是, if 语句后最多只能有一个 else语句, 但可以有任意数量的elif 语句

if user.cmd == ‘create’:
     
   action =
“create item”
 
elif user.cmd ==
‘delete’:
     
   action =
‘delete item’
 
elif user.cmd ==
‘update’:
     
   action =
‘update item’
 
else:
     
   action =
‘invalid choice… try again!’

条件表达式(三元操作符“)

C ? X : Y 三元运算符. ( C 是条件表达式; X C True 时的结果, Y C False 时的结果。使用三元操作符只需要一行完成条件判断和赋值操作, 而不需要像使用 if-else 语句实现对数字 x y 的操作:

>>> x, y = 4, 3
>>> if x < y :
  smaller = x
else:
  smaller = y

>>> x,y = 4,3
>>> smaller
3
>>>

而使用三元操作符则更加简洁:

>>> smaller = x if x < y else y
>>> smaller
3
>>>

while 语句

if声明相比,如果 if 后的条件为真,就会执行一次相应的代码块。 而 while 中的代码块会一直循环执行,直到循环条件不再为真。

while expression:
       
   suite_to_repeat

例子:计数循环

count = 0
   
while (count <
9):
       
   print
‘the index is:’, count
       
   count +=
1

必须小心地使用 while 循环, 因为有可能 condition 永远不会为布尔假. 这样一来循环就永远不会结束。

for 语句

for 循环会访问一个可迭代对象(例如序列或是迭代器)中的所有元素,并在所有条目都处理过后结束循环。

for iter_var in iterable:
        suite_to_repeat

例子:用于序列类型

>>> for eachLetter in ‘Names’:
  print ‘current letter:’,eachLetter

current letter: N
current letter: a
current letter: m
current letter: e
current letter: s
>>>

迭代序列有三种基本方法

通过序列项迭代

>>> nameList = [‘Walter’, “Nicole”, ‘Steven’, ‘Henry’]
>>> for eachName in nameList:
  print eachName, “lim”

Walter lim
Nicole lim
Steven lim
Henry lim
>>>

通过序列索引迭代

>>> nameList = [‘Cathy’, “Terry”, ‘Joe’, ‘Heather’, ‘Lucy’]
>>> for nameIndex in range(len(nameList)):
  print “liu”,nameList[nameIndex]

liu Cathy
liu Terry
liu Joe
liu Heather
liu Lucy
>>>

使用了内建的 len() 函数获得序列长度, 使用 range() 函数创建了要迭代的序列。

使用项和索引迭代

>>> nameList = [‘Cathy’, “Terry”, ‘Joe’, ‘Heather’, ‘Lucy’]
>>> for i, eachLee in enumerate(nameList):
  print “%d %s Lee” %(i+1, eachLee)

1 Cathy Lee
2 Terry Lee
3 Joe Lee
4 Heather Lee
5 Lucy Lee
>>>

range() 内建函数

语法:range(start, end, step =1)range() 会返回一个包含所有 k 的列表, 这里 start <= k < end , start end , k 每次递增 step . step 不可以为零,否则将发生错误。

>>> range(2, 19, 3)
[
2, 5, 8, 11, 14, 17]
>>>

如果只给定两个参数,而省略 step, step 就使用默认值 1

range() 简略语法

range(end)

range(start, end)

>>> range(5)
[
0, 1, 2, 3, 4]
>>>

>>> for count in range(2, 5):
  print count

2
3
4
>>>
xrange() 可能更为适合,因为它不会在内存里创建列表的完整拷贝。而且范围比range大,缺点只被用到for中。

与序列相关的内建函数

sorted()reversed() enumerate()zip()

sorted():按从小到大顺序排列

reversed():按从大到小顺序排列

enumerate():返回下标 与 本身的值

在序列中循环时,索引位置和对应值可以使用 enumerate() 函数同时得到:

>>> for i,v in enumerate([‘tic’,‘janily’,‘reicky’]):
  print(i,v)

(0, ‘tic’)
(1, ‘janily’)
(2, ‘reicky’)
>>>

break 语句

Python 中的 break 语句可以结束当前循环然后跳转到下条语句,常用在当某个外部条件被触发(一般通过 if 语句检查),需要立即从循环中退出时。

continue 语句

它可以被用在 while for 循环里. while 循环是条件性的, for 循环是迭代的, 所以 continue 在开始下一次循环前要满足一些先决条件(前边的核心笔记中强调的), 否则循环会正常结束.

pass 语句

如果你在需要子语句块的地方不写任何语句,解释器会提示你语法错误。 因此,Python 提供了 pass 语句,它不做任何事情 NOP (No OPeration , 无操作 )

if user_choice == ‘do_calc’:
       
   pass else:
pass
pass 在任何需要语句块(一个或多个语句)的地方都可以使用(例如 elif , else , clasa , def , try , except , finally )

迭代器和 iter() 函数

它为类序列对象提供了一个类序列的接口。比如序列,它们是一组数据结构,你可以利用它们的索引从 0 开始一直迭代到序列的最后一个条目。

迭代方法

next() ,不是通过索引来计数。当你或是一个循环机制(例如 for 语句)需要下一个项时,调用迭代器的 next() 方法就可以获得它。条目全部取出后,会引发一个 StopIteration 异常,这并不表示错误发生,只是告诉外部调用者, 迭代完成。

reversed() 内建函数将返回一个反序访问的迭代器。enumerate() 内建函数同样也返回迭代器。

使用迭代器

>>> myTuple = (123,‘xyz’,45.67)
>>> i = iter(myTuple)
>>> i.next()
123
>>> i.next()
‘xyz’
>>> i.next()
45.67
>>> i.next()
Traceback (most recent call last):
 File
“<stdin>”, line 1, in <module>
StopIteration
>>>

字典与文件的迭代器

字典和文件是另外两个可迭代的 Python 数据类型. 字典的迭代器会遍历它的键(keys).语句 for eachKey in myDict.keys() 可以缩写为 for eachKey in myDict

d = {‘x’:1,‘y’:2,‘z’:3}
>>> for key in d:
  print key, ‘correspond to’,d[key]

y correspond to
2
x correspond to
1
z correspond to
3
>>>

文件迭代

文件对象生成的迭代器会自动调用 readline() 方法。这样, 循环就可以访问文本文件的所有行。或者使用更简单的 for eachLine in myFile 替换 for eachLine inmyFile.readlines() :

>>> myfile = open(‘/Users/chenweijie/Desktop/1.txt’)
>>> for eachline in myfile:
  print eachline, # \n

asfasdfasdgfasdsdgasdgsadgsagsdgsdgsa
asdfasdfsdfdfewqwetwe
>>>

可变对象和迭代器

迭代可变对象的时候修改它们并不是个好主意。这在迭代器出现之前就是一个问题。除列表外的其他序列都是不可变的,所以危险就发生在这里。一个序列的迭代器只是记录你当前到达第多少个元素,所以如果你在迭代时改变了元素,更新会立即反映到你所迭代的条目上。

如何创建迭代器

对一个对象调用 iter() 就可以得到它的迭代器. 它的语法如下:

iter(obj)

iter(func, sentinel )

如果传递一个参数给 iter() , 它会检查你传递的是不是一个序列, 如果是, 那么很简单:根据索引从 0 一直迭代到序列结束。

列表解析,用来动态地创建列表。

[expr for iter_var in iterable]

语句的核心是 for 循环, 它迭代 iterable 对象的所有条目。前边的 expr 应用于序列的每个成员,最后的结果值是该表达式产生的列表。迭代变量并不需要是表达式的一部分。

>>> [x ** 2 for x in range(6)]
[0, 1, 4, 9, 16, 25]
>>>

扩展语法

[expr for iter_var in iterable if cond_expr]

这个语法在迭代时会过滤/捕获满足条件表达式 cond_expr 的序列成员。

seq = [11, 10, 9, 9, 10, 10, 9, 8, 23, 9, 7, 18, 12, 11, 12]
>>> [x for x in seq if x % 2]
[11, 9, 9, 9, 23, 9, 7, 11]
>>>

生成器表达式

生成器是特定的函数, 允许你返回一个值, 然后暂停代码的执行, 稍后恢复。

它与列表解析非常相似,而且它们的基本语法基本相同;不过它并不真正创建数字列表, 而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目产生”(yield)出来. 生成器表达式使用了延迟计算“(lazy evaluation), 所以它在使用内存上更有效.

(expr for iter_var in iterable if cond_expr)

>>> myfile.seek(0)
>>> sum(len(word) for line in myfile for word in line.split())
58
>>>

文件

文件对象不仅可以用来访问普通的磁盘文件, 而且也可以访问任何其它类型抽象层面上的文 件“.处理类文件对象的情况. 例如实时地打开一个 URL”来读取 Web 页面,在另一个独立的进程中执行一个命令进行通讯, 就好像是两个同时打开的文件, 一个用于读取, 另个用于写入。

文件内建函数[open()file()]

内建函数 open() 的基本语法是:

file_object = open(file_name, access_mode=’r’, buffering=-1)

file_name 是包含要打开的文件名字的字符串, 它可以是相对路径或者绝对路径. 可选变量access_mode 也是一个字符串, 代表文件打开的模式. 通常, 文件使用模式 ‘r’, ‘w’, 或是 ‘a’模式来打开, 分别代表读取, 写入和追加. 还有个 ‘U’ 模式, 代表通用换行符支持。

使用 ‘r’ ‘U’ 模式打开的文件必须是已经存在的. 使用 ‘w’ 模式打开的文件若存在则首先清空, 然后(重新)创建. ‘a’ 模式打开的文件是为追加数据作准备的, 所有写入的数据都将追加到文件的末尾. 即使你 seek 到了其它的地方. 如果文件不存在, 将被自动创建, 类似以 ‘w’模式打开文件。

文件对象的访问模式

几种常见的模式:

r 以读方式打开

w   以写方式打开 (必要时清空)

a    以追加模式打开 ( EOF 开始, 必要时创建新文件)

r+ 以读写模式打开

w+ 以读写模式打开

a+  以读写模式打开

工厂函数 file()

open() file() 函数具有相同的功能, 可以任意替换。

一般说来, 建议使用 open() 来读写文件, 想说明在处理文件对象时使用 file() , 例如 if instance(f, file)

通用换行符支持(UNS)

当你使用 ‘U’ 标志打开文件的时候, 所有的行分割符(或行结束符, 无论它原来是什么)通过 Python 的输入方法(例如 read*() )返回时都会被替换为换行符 NEWLINE(\n). (‘rU’ 模式也支持 ‘rb’ 选项) .

编译 Python 的时候,UNS 默认是打开的.

文件内建方法

文件方法可以分为四类: 输入, 输出, 文件内移动, 以及杂项操作.

输入

read() 方法用来直接读取字节到字符串中, 最多读取给定数目个字节. 如果没有给定 size参数(默认值为 -1)或者 size 值为负, 文件将被读取直至末尾.

readline() 方法读取打开文件的一行(读取下个行结束符之前的所有字节). 然后整行,包括行结束符,作为字符串返回.

readlines() 方法并不像其它两个输入方法一样返回一个字符串. 它会读取所有(剩余的)行然后把它们作为一个字符串列表返回.

输出

write() 内建方法功能与 read() readline() 相反. 它把含有文本数据或二进制数据块的字符串写入到文件中去.

readlines() 一样,writelines() 方法是针对列表的操作, 它接受一个字符串列表作为参数, 将它们写入文件. 行结束符并不会被自动加入, 所以如果需要的话, 你必须在调用writelines()前给每行结尾加上行结束符.

当使用输入方法如 read() 或者 readlines() 从文件中读取行时, Python 并不会删除行结束符. 需要手动关闭文件。

f = open(‘myFile’, ‘r’)
data = [line.strip() for line in f.readlines()]
f.close()

文件内移动

seek() 方法可以在文件中移动文件指针到不同的位置. offset字节代表相对于某个位置偏移量. 位置的默认值为 0 , 代表从文件开头算起(即绝对偏移量), 1 代表从当前位置算起, 2 代表从文件末尾算起.

text() 方法是对 seek() 的补充; 它告诉你当前文件指针在文件中的位置从文件起始算起,单位为字节.

文件迭代

for eachLine in f:

close() 通过关闭文件来结束对它的访问.

fileno() 方法返回打开文件的描述符.

文件方法杂项

读取文件到时候,建议使用文件迭代器, 每次只读取和显示一行:

filename = raw_input(‘Enter file name: ‘)
   
f = open(filename,
‘r’)
   
for eachLine in f:
       
   print eachLine, f.close()

操作系统间的差异之一是它们所支持的行分隔符不同.当我们创建要跨这三个平台的应用的时候, 这些差异会让我们感觉非常麻烦(而且支持的平台越多越麻烦)

Python os 模块已经帮我们想到了这些问题. os 模块有五个很有用的属性可以来解决这些差异:

os 模块属性  描述

linesep          用于文件中分隔行的字符串

sep                用来分隔文件路径名字的字符串

pathsep         用于分隔文件路径的字符串

curdir             当前工作目录的字符串名称

pardir             当前工作目录父目录字符串名称

文件内建属性

file.encoding

文件所使用的编码 Unicode 字符串被写入数据时, 它们将自动使用 file.encoding 转换为字节字符串; file.encoding None 时使用系统默认编码.

file.mode

文件打开时使用的访问模式

file.name

文件名

文件系统

对文件系统的访问大多通过 Python os 模块实现.

os 模块还负责处理大部分的文件系统操作,删除/重命名文件, 遍历目录树, 以及管理文件访问权限等.

另一个模块 os.path 可以完成一些针对路径名的操作. 它提供的函数可以完成管理和操作文件路径名中的各个部分, 获取文件或子目录信息, 文件路径查询等操作.    

ScalersTalk ID:scalerstalk

本文原文 http://scalerstalk.com/559-python-week4,首发ScalersTalk

本文作者Scalers,游走在口译世界的IT从业者。微信公众号ScalersTalk,网站ScalersTalk.com

ScalersTalk成长会回复“VIP”查看.口译100小时训练计划群C 456036104

与本文相关的文章