1
2
3
4
5
6 """Code for dealing with sequence alignments.
7 """
8
9 from Bio.Seq import Seq
10 from Bio.SeqRecord import SeqRecord
11 from Bio import Alphabet
12
13
14 from Bio.Align.Generic import Alignment as _Alignment
16 """"Represents a classical multiple sequence alignment (MSA).
17
18 By this we mean a collection of sequences (usually shown as rows) which
19 are all the same length (usually with gap characters for insertions of
20 padding). The data can then be regarded as a matrix of letters, with well
21 defined columns.
22
23 You would typically create an MSA by loading an alignment file with the
24 AlignIO module:
25
26 >>> from Bio import AlignIO
27 >>> align = AlignIO.read("Clustalw/opuntia.aln", "clustal")
28 >>> print align
29 SingleLetterAlphabet() alignment with 7 rows and 156 columns
30 TATACATTAAAGAAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273285|gb|AF191659.1|AF191
31 TATACATTAAAGAAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273284|gb|AF191658.1|AF191
32 TATACATTAAAGAAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273287|gb|AF191661.1|AF191
33 TATACATAAAAGAAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273286|gb|AF191660.1|AF191
34 TATACATTAAAGGAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273290|gb|AF191664.1|AF191
35 TATACATTAAAGGAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273289|gb|AF191663.1|AF191
36 TATACATTAAAGGAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273291|gb|AF191665.1|AF191
37
38 In some respects you can treat these objects as lists of SeqRecord objects,
39 each representing a row of the alignment. Iterating over an alignment gives
40 the SeqRecord object for each row:
41
42 >>> len(align)
43 7
44 >>> for record in align:
45 ... print record.id, len(record)
46 gi|6273285|gb|AF191659.1|AF191 156
47 gi|6273284|gb|AF191658.1|AF191 156
48 gi|6273287|gb|AF191661.1|AF191 156
49 gi|6273286|gb|AF191660.1|AF191 156
50 gi|6273290|gb|AF191664.1|AF191 156
51 gi|6273289|gb|AF191663.1|AF191 156
52 gi|6273291|gb|AF191665.1|AF191 156
53
54 You can also access individual rows as SeqRecord objects via their index:
55
56 >>> print align[0].id
57 gi|6273285|gb|AF191659.1|AF191
58 >>> print align[-1].id
59 gi|6273291|gb|AF191665.1|AF191
60
61 And extract columns as strings:
62
63 >>> print align[:,1]
64 AAAAAAA
65
66 Or, take just the first ten columns as a sub-alignment:
67
68 >>> print align[:,:10]
69 SingleLetterAlphabet() alignment with 7 rows and 10 columns
70 TATACATTAA gi|6273285|gb|AF191659.1|AF191
71 TATACATTAA gi|6273284|gb|AF191658.1|AF191
72 TATACATTAA gi|6273287|gb|AF191661.1|AF191
73 TATACATAAA gi|6273286|gb|AF191660.1|AF191
74 TATACATTAA gi|6273290|gb|AF191664.1|AF191
75 TATACATTAA gi|6273289|gb|AF191663.1|AF191
76 TATACATTAA gi|6273291|gb|AF191665.1|AF191
77
78 Combining this alignment slicing with alignment addition allows you to
79 remove a section of the alignment. For example, taking just the first
80 and last ten columns:
81
82 >>> print align[:,:10] + align[:,-10:]
83 SingleLetterAlphabet() alignment with 7 rows and 20 columns
84 TATACATTAAGTGTACCAGA gi|6273285|gb|AF191659.1|AF191
85 TATACATTAAGTGTACCAGA gi|6273284|gb|AF191658.1|AF191
86 TATACATTAAGTGTACCAGA gi|6273287|gb|AF191661.1|AF191
87 TATACATAAAGTGTACCAGA gi|6273286|gb|AF191660.1|AF191
88 TATACATTAAGTGTACCAGA gi|6273290|gb|AF191664.1|AF191
89 TATACATTAAGTATACCAGA gi|6273289|gb|AF191663.1|AF191
90 TATACATTAAGTGTACCAGA gi|6273291|gb|AF191665.1|AF191
91
92 Note - This object is intended to replace the existing Alignment object
93 defined in module Bio.Align.Generic but is not fully backwards compatible
94 with it.
95
96 Note - This object does NOT attempt to model the kind of alignments used
97 in next generation sequencing with multiple sequencing reads which are
98 much shorter than the alignment, and where there is usually a consensus or
99 reference sequence with special status.
100 """
101
102 - def __init__(self, records, alphabet=None):
103 """Initialize a new MultipleSeqAlignment object.
104
105 Arguments:
106 records - A list (or iterator) of SeqRecord objects, whose sequences
107 are all the same length. This may be an be an empty list.
108 alphabet - The alphabet for the whole alignment, typically a gapped
109 alphabet, which should be a super-set of the individual
110 record alphabets. If omitted, a consensus alphabet is used.
111
112 You would normally load a MSA from a file using Bio.AlignIO, but you
113 can do this from a list of SeqRecord objects too:
114
115 >>> from Bio.Alphabet import generic_dna
116 >>> from Bio.Seq import Seq
117 >>> from Bio.SeqRecord import SeqRecord
118 >>> a = SeqRecord(Seq("AAAACGT", generic_dna), id="Alpha")
119 >>> b = SeqRecord(Seq("AAA-CGT", generic_dna), id="Beta")
120 >>> c = SeqRecord(Seq("AAAAGGT", generic_dna), id="Gamma")
121 >>> align = MultipleSeqAlignment([a, b, c])
122 >>> print align
123 DNAAlphabet() alignment with 3 rows and 7 columns
124 AAAACGT Alpha
125 AAA-CGT Beta
126 AAAAGGT Gamma
127
128 NOTE - The older Bio.Align.Generic.Alignment class only accepted a
129 single argument, an alphabet. This is still supported via a backwards
130 compatible "hack" so as not to disrupt existing scripts and users, but
131 is deprecated and will be removed in a future release.
132 """
133 if isinstance(records, Alphabet.Alphabet) \
134 or isinstance(records, Alphabet.AlphabetEncoder):
135 if alphabet is None:
136
137 alphabet = records
138 records = []
139 import warnings
140 warnings.warn("Invalid records argument: While the old "
141 "Bio.Align.Generic.Alignment class only "
142 "accepted a single argument (the alphabet), the "
143 "newer Bio.Align.MultipleSeqAlignment class "
144 "expects a list/iterator of SeqRecord objects "
145 "(which can be an empty list) and an optional "
146 "alphabet argument")
147 else :
148 raise ValueError("Invalid records argument")
149 if alphabet is not None :
150 if not (isinstance(alphabet, Alphabet.Alphabet) \
151 or isinstance(alphabet, Alphabet.AlphabetEncoder)):
152 raise ValueError("Invalid alphabet argument")
153 self._alphabet = alphabet
154 else :
155
156 self._alphabet = Alphabet.single_letter_alphabet
157
158 self._records = []
159 if records:
160 self.extend(records)
161 if alphabet is None:
162
163 self._alphabet = Alphabet._consensus_alphabet(rec.seq.alphabet for \
164 rec in self._records \
165 if rec.seq is not None)
166
168 """Add more SeqRecord objects to the alignment as rows.
169
170 They must all have the same length as the original alignment, and have
171 alphabets compatible with the alignment's alphabet. For example,
172
173 >>> from Bio.Alphabet import generic_dna
174 >>> from Bio.Seq import Seq
175 >>> from Bio.SeqRecord import SeqRecord
176 >>> from Bio.Align import MultipleSeqAlignment
177 >>> a = SeqRecord(Seq("AAAACGT", generic_dna), id="Alpha")
178 >>> b = SeqRecord(Seq("AAA-CGT", generic_dna), id="Beta")
179 >>> c = SeqRecord(Seq("AAAAGGT", generic_dna), id="Gamma")
180 >>> d = SeqRecord(Seq("AAAACGT", generic_dna), id="Delta")
181 >>> e = SeqRecord(Seq("AAA-GGT", generic_dna), id="Epsilon")
182
183 First we create a small alignment (three rows):
184
185 >>> align = MultipleSeqAlignment([a, b, c])
186 >>> print align
187 DNAAlphabet() alignment with 3 rows and 7 columns
188 AAAACGT Alpha
189 AAA-CGT Beta
190 AAAAGGT Gamma
191
192 Now we can extend this alignment with another two rows:
193
194 >>> align.extend([d, e])
195 >>> print align
196 DNAAlphabet() alignment with 5 rows and 7 columns
197 AAAACGT Alpha
198 AAA-CGT Beta
199 AAAAGGT Gamma
200 AAAACGT Delta
201 AAA-GGT Epsilon
202
203 Because the alignment object allows iteration over the rows as
204 SeqRecords, you can use the extend method with a second alignment
205 (provided its sequences have the same length as the original alignment).
206 """
207 if len(self):
208
209 expected_length = self.get_alignment_length()
210 else:
211
212 records = iter(records)
213 try:
214 rec = records.next()
215 except StopIteration:
216
217 return
218 expected_length = len(rec)
219 self.append(rec, expected_length)
220
221
222 for rec in records:
223 self._append(rec, expected_length)
224
225 - def append(self, record, _private_expected_length=None):
226 """Add one more SeqRecord object to the alignment as a new row.
227
228 This must have the same length as the original alignment (unless this is
229 the first record), and have an alphabet compatible with the alignment's
230 alphabet.
231
232 >>> from Bio import AlignIO
233 >>> align = AlignIO.read("Clustalw/opuntia.aln", "clustal")
234 >>> print align
235 SingleLetterAlphabet() alignment with 7 rows and 156 columns
236 TATACATTAAAGAAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273285|gb|AF191659.1|AF191
237 TATACATTAAAGAAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273284|gb|AF191658.1|AF191
238 TATACATTAAAGAAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273287|gb|AF191661.1|AF191
239 TATACATAAAAGAAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273286|gb|AF191660.1|AF191
240 TATACATTAAAGGAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273290|gb|AF191664.1|AF191
241 TATACATTAAAGGAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273289|gb|AF191663.1|AF191
242 TATACATTAAAGGAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273291|gb|AF191665.1|AF191
243 >>> len(align)
244 7
245
246 We'll now construct a dummy record to append as an example:
247
248 >>> from Bio.Seq import Seq
249 >>> from Bio.SeqRecord import SeqRecord
250 >>> dummy = SeqRecord(Seq("N"*156), id="dummy")
251
252 Now append this to the alignment,
253
254 >>> align.append(dummy)
255 >>> print align
256 SingleLetterAlphabet() alignment with 8 rows and 156 columns
257 TATACATTAAAGAAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273285|gb|AF191659.1|AF191
258 TATACATTAAAGAAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273284|gb|AF191658.1|AF191
259 TATACATTAAAGAAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273287|gb|AF191661.1|AF191
260 TATACATAAAAGAAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273286|gb|AF191660.1|AF191
261 TATACATTAAAGGAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273290|gb|AF191664.1|AF191
262 TATACATTAAAGGAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273289|gb|AF191663.1|AF191
263 TATACATTAAAGGAGGGGGATGCGGATAAATGGAAAGGCGAAAG...AGA gi|6273291|gb|AF191665.1|AF191
264 NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN...NNN dummy
265 >>> len(align)
266 8
267
268 """
269 if self._records:
270 self._append(record, self.get_alignment_length())
271 else:
272 self._append(record)
273
274 - def _append(self, record, expected_length=None):
275 """Helper function (PRIVATE)."""
276 if not isinstance(record, SeqRecord):
277 raise TypeError("New sequence is not a SeqRecord object")
278
279
280
281
282 if expected_length is not None and len(record) != expected_length:
283
284
285
286 raise ValueError("Sequences must all be the same length")
287
288
289
290 if not Alphabet._check_type_compatible([self._alphabet, record.seq.alphabet]):
291 raise ValueError("New sequence's alphabet is incompatible")
292 self._records.append(record)
293
295 """Combines to alignments with the same number of rows by adding them.
296
297 If you have two multiple sequence alignments (MSAs), there are two ways to think
298 about adding them - by row or by column. Using the extend method adds by row.
299 Using the addition operator adds by column. For example,
300
301 >>> from Bio.Alphabet import generic_dna
302 >>> from Bio.Seq import Seq
303 >>> from Bio.SeqRecord import SeqRecord
304 >>> from Bio.Align import MultipleSeqAlignment
305 >>> a1 = SeqRecord(Seq("AAAAC", generic_dna), id="Alpha")
306 >>> b1 = SeqRecord(Seq("AAA-C", generic_dna), id="Beta")
307 >>> c1 = SeqRecord(Seq("AAAAG", generic_dna), id="Gamma")
308 >>> a2 = SeqRecord(Seq("GT", generic_dna), id="Alpha")
309 >>> b2 = SeqRecord(Seq("GT", generic_dna), id="Beta")
310 >>> c2 = SeqRecord(Seq("GT", generic_dna), id="Gamma")
311 >>> left = MultipleSeqAlignment([a1, b1, c1])
312 >>> right = MultipleSeqAlignment([a2, b2, c2])
313
314 Now, let's look at these two alignments:
315
316 >>> print left
317 DNAAlphabet() alignment with 3 rows and 5 columns
318 AAAAC Alpha
319 AAA-C Beta
320 AAAAG Gamma
321 >>> print right
322 DNAAlphabet() alignment with 3 rows and 2 columns
323 GT Alpha
324 GT Beta
325 GT Gamma
326
327 And add them:
328
329 >>> print left + right
330 DNAAlphabet() alignment with 3 rows and 7 columns
331 AAAACGT Alpha
332 AAA-CGT Beta
333 AAAAGGT Gamma
334
335 For this to work, both alignments must have the same number of records (here
336 they both have 3 rows):
337
338 >>> len(left)
339 3
340 >>> len(right)
341 3
342
343 The individual rows are SeqRecord objects, and these can be added together. Refer
344 to the SeqRecord documentation for details of how the annotation is handled. This
345 example is a special case in that both original alignments shared the same names,
346 meaning when the rows are added they also get the same name.
347 """
348 if not isinstance(other, MultipleSeqAlignment):
349 raise NotImplementedError
350 if len(self) != len(other):
351 raise ValueError("When adding two alignments they must have the same length"
352 " (i.e. same number or rows)")
353 alpha = Alphabet._consensus_alphabet([self._alphabet, other._alphabet])
354 merged = (left+right for left,right in zip(self, other))
355 return MultipleSeqAlignment(merged, alpha)
356
358 """Access part of the alignment.
359
360 Depending on the indices, you can get a SeqRecord object
361 (representing a single row), a Seq object (for a single columns),
362 a string (for a single characters) or another alignment
363 (representing some part or all of the alignment).
364
365 align[r,c] gives a single character as a string
366 align[r] gives a row as a SeqRecord
367 align[r,:] gives a row as a SeqRecord
368 align[:,c] gives a column as a Seq (using the alignment's alphabet)
369
370 align[:] and align[:,:] give a copy of the alignment
371
372 Anything else gives a sub alignment, e.g.
373 align[0:2] or align[0:2,:] uses only row 0 and 1
374 align[:,1:3] uses only columns 1 and 2
375 align[0:2,1:3] uses only rows 0 & 1 and only cols 1 & 2
376
377 We'll use the following example alignment here for illustration:
378
379 >>> from Bio.Alphabet import generic_dna
380 >>> from Bio.Seq import Seq
381 >>> from Bio.SeqRecord import SeqRecord
382 >>> from Bio.Align import MultipleSeqAlignment
383 >>> a = SeqRecord(Seq("AAAACGT", generic_dna), id="Alpha")
384 >>> b = SeqRecord(Seq("AAA-CGT", generic_dna), id="Beta")
385 >>> c = SeqRecord(Seq("AAAAGGT", generic_dna), id="Gamma")
386 >>> d = SeqRecord(Seq("AAAACGT", generic_dna), id="Delta")
387 >>> e = SeqRecord(Seq("AAA-GGT", generic_dna), id="Epsilon")
388 >>> align = MultipleSeqAlignment([a, b, c, d, e], generic_dna)
389
390 You can access a row of the alignment as a SeqRecord using an integer
391 index (think of the alignment as a list of SeqRecord objects here):
392
393 >>> first_record = align[0]
394 >>> print first_record.id, first_record.seq
395 Alpha AAAACGT
396 >>> last_record = align[-1]
397 >>> print last_record.id, last_record.seq
398 Epsilon AAA-GGT
399
400 You can also access use python's slice notation to create a sub-alignment
401 containing only some of the SeqRecord objects:
402
403 >>> sub_alignment = align[2:5]
404 >>> print sub_alignment
405 DNAAlphabet() alignment with 3 rows and 7 columns
406 AAAAGGT Gamma
407 AAAACGT Delta
408 AAA-GGT Epsilon
409
410 This includes support for a step, i.e. align[start:end:step], which
411 can be used to select every second sequence:
412
413 >>> sub_alignment = align[::2]
414 >>> print sub_alignment
415 DNAAlphabet() alignment with 3 rows and 7 columns
416 AAAACGT Alpha
417 AAAAGGT Gamma
418 AAA-GGT Epsilon
419
420 Or to get a copy of the alignment with the rows in reverse order:
421
422 >>> rev_alignment = align[::-1]
423 >>> print rev_alignment
424 DNAAlphabet() alignment with 5 rows and 7 columns
425 AAA-GGT Epsilon
426 AAAACGT Delta
427 AAAAGGT Gamma
428 AAA-CGT Beta
429 AAAACGT Alpha
430
431 You can also use two indices to specify both rows and columns. Using simple
432 integers gives you the entry as a single character string. e.g.
433
434 >>> align[3,4]
435 'C'
436
437 This is equivalent to:
438
439 >>> align[3][4]
440 'C'
441
442 or:
443
444 >>> align[3].seq[4]
445 'C'
446
447 To get a single column (as a string) use this syntax:
448
449 >>> align[:,4]
450 'CCGCG'
451
452 Or, to get part of a column,
453
454 >>> align[1:3,4]
455 'CG'
456
457 However, in general you get a sub-alignment,
458
459 >>> print align[1:5,3:6]
460 DNAAlphabet() alignment with 4 rows and 3 columns
461 -CG Beta
462 AGG Gamma
463 ACG Delta
464 -GG Epsilon
465
466 This should all seem familiar to anyone who has used the NumPy
467 array or matrix objects.
468 """
469 if isinstance(index, int):
470
471
472 return self._records[index]
473 elif isinstance(index, slice):
474
475 return MultipleSeqAlignment(self._records[index], self._alphabet)
476 elif len(index)!=2:
477 raise TypeError("Invalid index type.")
478
479
480 row_index, col_index = index
481 if isinstance(row_index, int):
482
483 return self._records[row_index][col_index]
484 elif isinstance(col_index, int):
485
486 return "".join(rec[col_index] for rec in self._records[row_index])
487 else:
488
489 return MultipleSeqAlignment((rec[col_index] for rec in self._records[row_index]),
490 self._alphabet)
491
493 """Sort the rows (SeqRecord objects) of the alignment in place.
494
495 This sorts the rows alphabetically using the SeqRecord object id.
496 Currently no advanced sort options are available, although this may
497 be added in a future release of Biopython.
498
499 This is useful if you want to add two alignments which use the same
500 record identifiers, but in a different order. For example,
501
502 >>> from Bio.Alphabet import generic_dna
503 >>> from Bio.Seq import Seq
504 >>> from Bio.SeqRecord import SeqRecord
505 >>> from Bio.Align import MultipleSeqAlignment
506 >>> align1 = MultipleSeqAlignment([
507 ... SeqRecord(Seq("ACGT", generic_dna), id="Human"),
508 ... SeqRecord(Seq("ACGG", generic_dna), id="Mouse"),
509 ... SeqRecord(Seq("ACGC", generic_dna), id="Chicken"),
510 ... ])
511 >>> align2 = MultipleSeqAlignment([
512 ... SeqRecord(Seq("CGGT", generic_dna), id="Mouse"),
513 ... SeqRecord(Seq("CGTT", generic_dna), id="Human"),
514 ... SeqRecord(Seq("CGCT", generic_dna), id="Chicken"),
515 ... ])
516
517 If you simple try and add these without sorting, you get this:
518
519 >>> print align1 + align2
520 DNAAlphabet() alignment with 3 rows and 8 columns
521 ACGTCGGT <unknown id>
522 ACGGCGTT <unknown id>
523 ACGCCGCT Chicken
524
525 Consult the SeqRecord documentation which explains why you get a
526 default value when annotation like the identifier doesn't match up.
527 However, if we sort the alignments first, then add them we get the
528 desired result:
529
530 >>> align1.sort()
531 >>> align2.sort()
532 >>> print align1 + align2
533 DNAAlphabet() alignment with 3 rows and 8 columns
534 ACGCCGCT Chicken
535 ACGTCGTT Human
536 ACGGCGGT Mouse
537
538 """
539 self._records.sort(key = lambda r: r.id)
540
542 """Returns a string containing a given column (OBSOLETE).
543
544 This is a method provided for backwards compatibility with the old
545 Bio.Align.Generic.Alignment object. You are encouraged to use the
546 slice notation instead.
547 """
548 import warnings
549 warnings.warn("This is a method provided for backwards compatibility with the old Bio.Align.Generic.Alignment object. You are encouraged to use the slice notation instead.", PendingDeprecationWarning)
550 return _Alignment.get_column(self, col)
551
552 - def add_sequence(self, descriptor, sequence, start = None, end = None,
553 weight = 1.0):
554 """Add a sequence to the alignment (OBSOLETE).
555
556 The start, end, and weight arguments are not supported! This method
557 only provides limited backwards compatibility with the old
558 Bio.Align.Generic.Alignment object. You are encouraged to use the
559 append method with a SeqRecord instead.
560 """
561 import warnings
562 warnings.warn("The start, end, and weight arguments are not supported! This method only provides limited backwards compatibility with the old Bio.Align.Generic.Alignment object. You are encouraged to use the append method with a SeqRecord instead.", PendingDeprecationWarning)
563
564
565 if start is not None or end is not None or weight != 1.0:
566 raise ValueError("The add_Sequence method is obsolete, and only "
567 "provides limited backwards compatibily. The"
568 "start, end and weight arguments are not "
569 "supported.")
570 self.append(SeqRecord(Seq(sequence, self._alphabet),
571 id = descriptor, description = descriptor))
572
573
575 """Run the Bio.Align module's doctests.
576
577 This will try and locate the unit tests directory, and run the doctests
578 from there in order that the relative paths used in the examples work.
579 """
580 import doctest
581 import os
582 if os.path.isdir(os.path.join("..", "..", "Tests", "Clustalw")):
583 print "Runing doctests..."
584 cur_dir = os.path.abspath(os.curdir)
585 os.chdir(os.path.join("..", "..", "Tests"))
586 doctest.testmod()
587 os.chdir(cur_dir)
588 del cur_dir
589 print "Done"
590 elif os.path.isdir(os.path.join("Tests", "Clustalw")):
591 print "Runing doctests..."
592 cur_dir = os.path.abspath(os.curdir)
593 os.chdir(os.path.join("Tests"))
594 doctest.testmod()
595 os.chdir(cur_dir)
596 del cur_dir
597 print "Done"
598
599 if __name__ == "__main__":
600
601 _test()
602