master
/ 4.1 函数的定义、调用与返回值.ipynb

4.1 函数的定义、调用与返回值.ipynb @master

341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
d487d71
 
 
 
 
 
 
 
 
341084b
 
d487d71
 
 
 
341084b
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
d487d71
 
 
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
d487d71
 
 
 
 
 
 
 
 
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
d487d71
 
 
 
 
 
 
 
 
 
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
d487d71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
d487d71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4.2 函数的定义、调用和返回值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在前面的学习中,我们经常用到一些<font color=Red>__系统内置函数__</font>,如input()、print()、abs()等,它们把输入、输出和求绝对值等的语句封装起来,以函数的形式提供给用户使用。用户直接<font color=Red>__调用函数__</font>实现相应功能,而不需要再重复编写代码。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面的这段代码导入了<font color=Red>__math__</font>库,使用<font color=Red>__math.sqrt()__</font>函数来求P点到坐标原点的距离,而P点的坐标值x、y都通过<font color=Red>__input()__</font>函数读入并使用<font color=Red>__float()__</font>函数转换为浮点值的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math          #导入math库,后面要使用math.sqrt()函数\n",
    "\n",
    "\n",
    "x = float(input())   #输入P(x,y)点的横坐标\n",
    "y = float(input())   #输入P(x,y)点的纵坐标\n",
    "dist = math.sqrt(x ** 2 + y ** 2)  #求P点到坐标原点的距离\n",
    "print(f'{dist:.2f}')               #保留两位小数并输出"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "系统内置函数是系统预先定义好的函数,除了内置函数外,我们还能<font color=Red>__自定义函数数__</font>。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在实际的程序设计过程中,有很多操作都是相同或相似的,我们可以将执行相似操作的<font color=Red>__代码封装__</font>成函数,通过函数调用来实现特定的功能。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "将语句封装成函数就是<font color=Red>__定义函数__</font>,函数一经定义就可以多次调用,这个也叫<font color=Red>__代码复用__</font>。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在程序设计中,<font color=Red>__函数__</font>是指用于进行某种计算或具有某种功能的一系列语句的有名称的组合,这个名称就是<font color=Red>__函数名__</font>。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用函数不仅能减少代码量、让代码更为简洁,而且保证了代码的一致性、降低了维护成本。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python程序中函数的使用要遵循<font color=Red>__先定义后调用__</font>的规则。也就是说函数的调用必须位于函数定义之后,一般的作法是将函数的定义放在程序的开头部分,每个函数之间、函数与主程序之间各留一个空行。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4.2.1 函数的定义"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python中的函数定义格式如下:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def 函数名([参数列表]):\n",
    "    \"\"\"文档字符串\"\"\"\n",
    "    函数体\n",
    "    return 返回值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color=Red>__def__</font>是用于函数定义的关键字,def后的空格接函数名,括号()里的是参数列表,参数可以是0个或多个,该行语句必须以半角冒号:结束。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color=Red>__函数名__</font>必须由字母、下划线、数字组成,<font color=Red>__不能是关键字__</font>,也<font color=Red>__不能以数字开头__</font>,一般建议函数名要有一定的意义,能够简单说明函数的功能。\n",
    "函数名一般为小写字母,需要用多个单词时,使用下划线以增加可读性,如:add_number,find_number等。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hi\n"
     ]
    }
   ],
   "source": [
    "def say_hi():\n",
    "    print(\"Hi\")\n",
    "\n",
    "\n",
    "say_hi()    \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面的代码定义了一个名为<font color=Red>__say_hi__</font>的函数,函数名后的小括号里没有参数,这表示say_hi()是一个<font color=Red>__无参函数__</font>,但是小括号不可少。\n",
    "下面的代码调用函数来打印问候语。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "say_hi()         #调用函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数体内尽量写上<font color=Red>__文档注释字符串(文档注释)__</font>,以方便查看代码功能。\n",
    "\n",
    "文档注释是Python独有的注释方式,用三对双引号引起来的注释语句作为函数里的第一个语句,注释内容可以通过对象的__doc__成员被自动提取,并且被pydoc所用。\n",
    "\n",
    "文档注释的内容主要包括该函数的功能、可接受参数的个数和数据类型、返回值的个数和类型等。\n",
    "\n",
    "文档注释不是必须,但在函数的定义时加上一段用三对双引号引起来的注释,可以为用户提供更友好的提示和使用帮助。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以下是文档字符串内容和格式的约定。\r\n",
    "\r\n",
    "第一行应为对象用途的简短摘要。为保持简洁,不要在这里显式说明对象名或类型,因为可通过其他方式获取这些信息(除非该名称碰巧是描述函数操作的动词)。这一行应以大写字母开头,以句点结尾。\r\n",
    "\r\n",
    "文档字符串为多行时,第二行应为空白行,在视觉上将摘要与其余描述分开。后面的行可包含若干段落,描述对象的调用约定、副作用等。\r\n",
    "\r\n",
    "Python 解析器不会删除 Python 中多行字符串字面值的缩进,因此,文档处理工具应在必要时删除缩进。这项操作遵循以下约定:文档字符串第一行 之后 的第一个非空行决定了整个文档字符串的缩进量(第一行通常与字符串开头的引号相邻,其缩进在字符串中并不明显,因此,不能用第一行的缩进),然后,删除字符串中所有行开头处与此缩进“等价”的空白符。不能有比此缩进更少的行,但如果出现了缩进更少的行,应删除这些行的所有前导空白符。转化制表符后(通常为 8 个空格),应测试空白符的等效性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def fact(n):\n",
    "    \"\"\" 计算阶乘函数。\n",
    "    \n",
    "    接收一个非负整数n,计算返回n的阶乘,返回值为整型。\n",
    "    \"\"\"\n",
    "    result = 1\n",
    "\n",
    "    for i in range(1,n+1):\n",
    "        result = result * i\n",
    "    return result\n",
    "\n",
    "print(fact(9))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面的代码定义了fact()函数,并给出了文档注释。\n",
    "\n",
    "下面的gif动画演示了在IDLE里调用fact时,<font color=Red>__输入左侧括号后解释器会显示文档注释__</font>,提示用户该如何使用fact()函数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"images/ch4/2.gif\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color=Red>__函数体__</font>是程序语句,完成函数要实现的功能,相对于 def 关键字必须要<font color=Red>__保持一定数量的空格缩进__</font>。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4.2.2 函数的返回值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以将函数的处理结果<font color=Red>__返回__</font>给调用处以进行后续处理,例如fact函数希望返回计算得到的阶乘值,此时可以使用<font color=Red>__return语句__</font>。\n",
    "\n",
    "函数的返回值语句由 return 关键词开头,返回值没有类型限制,也没有个数限制,当函数返回值为多个时,以元组形式返回。当没有renturn语句时,表示没有返回值,此时返回<font color=Red>__None__</font>。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你好, 周小丫\n"
     ]
    }
   ],
   "source": [
    "#下面定义了一个无返回值的函数say_hi_to_you\n",
    "def say_hi_to_you(your_name):\n",
    "    print('你好,' , your_name)    #如果your_name里存储的是周小丫,则打印 你好,周小丫\n",
    "    \n",
    "say_hi_to_you('周小丫')    #say_hi_to_you()函数没有返回值,其处理结果直接被打印出来"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "return 是函数在调用过程中执行的最后一个语句,函数可以有多个 return 语句,但在执行过程中,<font color=Red>__只能有一个__</font>被执行到。\n",
    "\n",
    "一旦某个 return 语句被执行,函数调用即<font color=Red>__结束__</font>,返回值将被返回给函数被调用的位置。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "def is_odd(n):\n",
    "    \"\"\"输入整数n,如果是奇数则返回True,否则返回False\"\"\"\n",
    "    if n % 2 == 1:\n",
    "        return True    #当n为奇数时,返回True,函数结束,下面的语句不会被执行\n",
    "    else:\n",
    "        return False   #当n为偶数时,返回False\n",
    "\n",
    "print(is_odd(233)) #233为奇数,打印True\n",
    "print(is_odd(320)) #320为偶数,打印False\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面代码中定义了函数is_odd(n),<font color=Red>__函数体中有两处__</font>return语句,但实际执行时<font color=Red>__只有一处__</font>会被执行,函数一旦return则函数执行结束。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python的函数可以使用一个return语句<font color=Red>__同时返回多个值__</font>。\n",
    "下面的代码里定义了函数f(),其中return语句同时返回a和b,这两个值形成元组(元组的概念后续章节会有介绍)。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def f(x,y):\n",
    "    a = x // y   #计算x除以y的商\n",
    "    b = x % y    #计算x除以y的余数\n",
    "    return a,b\n",
    "\n",
    "c,d = f(13,3)    #以13和3做参数调用f,两个返回值按顺序依次赋值给c和d\n",
    "print(c,d)       #依次打印c和d的值\n",
    "print(f(13,3))   #输出函数的返回值,可以看到是元组 (4,1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4.2.3 函数的调用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数只有被调用才会运行,定义好的函数<font color=Red>__通过函数名__</font>来调用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数被调用时,括号里要给出和函数定义里<font color=Red>__同样多的__</font>的参数。\n",
    "函数定义时括号里的参数被称为<font color=Red>__形式参数__</font>,函数调用时括号里的参数被称为<font color=Red>__实际参数__</font>。\n",
    "实际参数必须有确定的值,这些值被传递给形式参数,这相当于一个<font color=Red>__赋值__</font>过程。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " <font face='楷体' color='red' size=5> 实例4.1 阶乘函数 </font>\n",
    " \n",
    " 定义一个计算整数n的阶乘的函数fact(n),接受一个非负整数n作为参数,返回其阶乘值。\n",
    " \n",
    " 调用fact()函数的实际参数来源于用户的输入,fact()函数的返回值即是阶乘值,打印输出该阶乘值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      " 12\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "479001600\n"
     ]
    }
   ],
   "source": [
    "#下面定义计算n的阶乘的函数\n",
    "def fact(n):\n",
    "    \"\"\"接受一个非负整数n,返回n的阶乘\"\"\"\n",
    "    result = 1          #保存n的阶乘\n",
    "    for i in range(1,n+1):\n",
    "        result = result * i     # 1 ~ n 累乘\n",
    "    return result       #返回\n",
    "\n",
    "#下面读入num后调用fact(),并输出阶乘\n",
    "num = int(input())    #输入一个正整数num\n",
    "print(fact(num))      #调用fact()函数,其返回值即是num的阶乘,打印输出该阶乘值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面的代码里输入num和打印阶乘值的语句都写在函数体之外,和函数定义处于<font color=Red>__同一层次__</font>。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如下修改一下fact()函数的定义,函数体内直接输出n的阶乘,函数<font color=Red>__没有返回值__</font>。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      " 12\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "479001600\n",
      "=============\n",
      "479001600\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "#下面定义计算n的阶乘的函数\n",
    "def fact(n):\n",
    "    \"\"\"接受一个非负整数n,直接输出n的阶乘,无返回值\"\"\"\n",
    "    result = 1          #保存n的阶乘\n",
    "    for i in range(1,n+1):\n",
    "        result = result * i     # 1 ~ n 累乘\n",
    "    print(result)       #直接输出计算结果\n",
    "\n",
    "#下面读入num后调用fact(),fact()函数里直接输出阶乘值\n",
    "num = int(input())      #输入一个正整数num\n",
    "fact(num)               #调用函数fact(),函数里直接输出结果\n",
    "\n",
    "print('=============')  #打印分隔\n",
    "#下面语句试图打印fact()函数的返回值\n",
    "print(fact(num))       #fact()被调用,打印出num的阶乘,但是fact()本身没有返回值,print()会打印出None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "另一种更常用的函数定义方法是将输入输出和函数调用语句写在 if __name__ == '__main__': 下面。\n",
    "\n",
    "注意,上面语句里的四处下划线都是<font color=Red>__双下划线__</font>。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def fact(n):\n",
    "    \"\"\"接受一个非负整数n,直接输出n的阶乘,无返回值\"\"\"\n",
    "    result = 1          #保存n的阶乘\n",
    "    for i in range(1,n+1):\n",
    "        result = result * i     # 1 ~ n 累乘\n",
    "    return result       #返回计算结果\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    num = int(input())    #输入一个正整数num\n",
    "    print(fact(num))      #调用fact()函数,,打印输出该阶乘值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "if \\_\\_name\\_\\_ == '\\_\\_main\\_\\_': 语句表明,其后的代码块只有在<font color=Red>__本文件作为脚本直接执行时__</font>才会被执行到。当本文件被其它程序用<font color=Red>__import导入__</font>时,其后的代码块不会被执行,即屏蔽了输入输出函数调用等语句,只使用其中定义的函数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当代码中函数和方法可能会被其它程序import并调用时,一般建议使用这种模式,此时代码既可以作为单独的程序使用,又可以作为模块被导入到其它程序使用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以发现定义为函数的代码比之前的版本更加简单优雅。更为重要的是,定义的求阶乘函数fact还可以在其他需要求阶乘的代码中重复使用。所以,使用函数可以帮助我们将功能上相对独立且会被重复使用的代码封装起来,当我们需要这些的代码,不是把重复的代码再编写一遍,而是通过调用函数实现对既有代码的复用。  \n",
    "事实上,Python 标准库的math模块中,已经有一个名为factorial()的函数实现了求阶乘的功能,我们可以直接用import math导入math模块,然后使用math.factorial来调用求阶乘的函数;我们也可以通过from math import factorial直接导入factorial函数来使用它,代码如下所示。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from math import factorial\n",
    "\n",
    "m = 10\n",
    "n = 6\n",
    "print(factorial(m) // factorial(n) // factorial(m - n))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "将来我们使用的函数,要么是自定义的函数,要么是 Python 标准库或者三方库中提供的函数,如果已经有现成的可用的函数,我们就没有必要自己去定义,重复性的工作是没有意义的。如果觉得上面代码中factorial这个名字太长,书写代码的时候不是特别方便,在导入函数的时候还可以通过as关键字为其命别名。在调用函数的时候,我们可以使用函数的别名。  \n",
    "需要注意的是,过于简洁的别名会失去函数名原有的语义,使代码的可读性变差。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from math import factorial as f\n",
    "\n",
    "m = 10\n",
    "n = 6\n",
    "\n",
    "print(f(m) // f(n) // f(m - n))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font face='楷体' color='red' size=5> 实例4.2 素数函数 </font>\n",
    "\n",
    "定义一个函数,接受一个正整数作为输入,判断其是否为素数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "素数又称<font color=Red>__质数__</font>。一个大于1的自然数,除了1和它自身以外,不能被其它自然数整除的数被称为素数。\n",
    "2是最小的素数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "素数的应用非常广泛,分解质因数、反素数、回文素数、哥德巴赫猜想以及密码学等很多问题都会用到素数的判定。\n",
    "\n",
    "对于这种广泛应用的程序,可以将其设计成一个函数,实现<font color=Red>__代码复用__</font>的目的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def is_prime(n):\n",
    "    \"\"\"接收非负整数n作为参数,判定n是否为素数,是素数则返回True,否则返回False\"\"\"\n",
    "    if n < 2:             # 0和1以及负数都不是素数\n",
    "        return False      # False为假,代表不是素数,此值返回给调用函数之处\n",
    "    for i in range(2, n): #遍历2~n-1范围内的数\n",
    "        if n % i == 0:    # 当存在n的因子时,n不是素数\n",
    "            return False  # False为假,代表不是素数,此值返回给调用函数之处\n",
    "    else:                 # else 子句与for 子句匹配,i == 2时也进入此语句块\n",
    "        return True       # True为真,代表是素数,此值返回给调用函数之处\n",
    "\n",
    "\n",
    "# 函数与主程序语句之间一般用1 到 2 个空行分隔\n",
    "if __name__ == '__main__':\n",
    "    num = int(input())     # 输入一个正整数\n",
    "    print(is_prime(num))   # 输出素数函数的判定结果"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "大素数的计算量还是比较大的,在应用时尽可能编写高效算法以减少运算时间。\n",
    "\n",
    "我们知道,约数是成对出现的。比如48,你找到约数6,那么一定有个约数8,这对约数里必然有一个小于等于$\\sqrt{n}$,另一个大于等于$\\sqrt{n}$。所以,在$\\sqrt{n}$之前找不到约数,在$\\sqrt{n}$之后也不会有。\n",
    "\n",
    "利用这个原理,可以<font color=Red>__缩小遍历范围__</font>为range(2, int(n ** 0.5) + 1),以提高算法效率。上述函数可以改写如下:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def is_prime(n):\n",
    "    \"\"\"接收非负整数n作为参数,判定n是否为素数,是素数则返回True,否则返回False\"\"\"\n",
    "    if n < 2:             # 0和1以及负数都不是素数\n",
    "        return False      # False为假,代表不是素数,此值返回给调用函数之处\n",
    "    for i in range(2, int(n ** 0.5) + 1):\n",
    "        if n % i == 0:    # 当存在能被整除的数时,不是素数\n",
    "            return False  # False为假,代表不是素数,此值返回给调用函数之处\n",
    "    else:                 # else 子句与for 子句匹配,i == 2时也进入此语句块\n",
    "        return True       # True为真,代表是素数,此值返回给调用函数之处\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    num = int(input())     # 输入一个正整数\n",
    "    print(is_prime(num))   # 输出素数函数判定结果"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font face='楷体' color='red' size=5> 实例4.3 幂函数 </font>\n",
    "\n",
    "定义一个函数,用于计算x的n次幂。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "进行函数定义时,可以定义<font color=Red>__2个或多个__</font>形式参数,函数调用时使用<font color=Red>__数量相同__</font>的实际参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义一个函数用于计算 x 的 n 次幂 \n",
    "def power(x, n):\n",
    "    \"\"\"接收两个整数参数x和n,其中n为非负整数,计算并返回x的n次幂\"\"\"\n",
    "    result = 1\n",
    "    for i in range(n):           # 循环n次         \n",
    "        result = result * x     \n",
    "    return result                # 返回计算结果 \n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    a, m = map(int,input().split())  # 输入用空格分隔的两个整数,切分并映射为整数后赋值给a和m\n",
    "    print(power(a, m))               # 调用函数计算a的m次幂 "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "power()函数定义时要求2个参数,那么调用这个函数时也要给出2个值a和m,按顺序分别传递给x和n。\n",
    "\n",
    "在函数调用时,由用户输入x和n的值,那么这个函数就可以计算任意整数的非负整数次幂了。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 类型提示"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "typing 是python3.5中开始新增的专用于类型注解(type hints)的模块,为python程序提供静态类型检查,如下面的greeting函数规定了参数name的类型是str,返回值的类型也是str。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def greeting(name: str) -> str:\n",
    "    \"\"\"接收表示名字的字符串为参数,返回一句问候语,字符串类型\"\"\"\n",
    "    return 'Hello ' + name\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    print(greeting(input()))  # 以输入的名字为参数,输出问候语"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "PEP 484 中建议每个函数的参数和返回值都加上类型提示。  \n",
    "参数的类型提示是在参数后加半角逗号和类型;  \n",
    "返回值的类型提示是在函数定义的冒号前加“->”和类型名,无返回值函数的返回值类型为“None”"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def is_prime(n: int)->bool:\n",
    "    \"\"\"接收非负整数n作为参数,判定n是否为素数,返回布尔值。是素数则返回True,否则返回False\"\"\"\n",
    "    if n < 2:             # 0和1以及负数都不是素数\n",
    "        return False      # False为假,代表不是素数,此值返回给调用函数之处\n",
    "    for i in range(2, int(n ** 0.5) + 1):\n",
    "        if n % i == 0:    # 当存在能被整除的数时,不是素数\n",
    "            return False  # False为假,代表不是素数,此值返回给调用函数之处\n",
    "    else:                 # else 子句与for 子句匹配,i == 2时也进入此语句块\n",
    "        return True       # True为真,代表是素数,此值返回给调用函数之处\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    num = int(input())     # 输入一个正整数\n",
    "    print(is_prime(num))   # 输出素数函数判定结果"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 温度转换、判定是否发烧、是否结冰"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def fahrenheit_to_celsius(num: str) -> float:\n",
    "    \"\"\"接受一个带单位的华氏温度,将其转为摄氏温度返回\"\"\"\n",
    "    temp = (float(num[:-1]) - 32) / 1.8\n",
    "    print(f'华氏{num[:-1]}℉为摄氏{temp:.2f}℃')\n",
    "    return temp\n",
    "\n",
    "\n",
    "def celsius_to_fahrenheit(num: str) -> float:\n",
    "    \"\"\"接受一个带单位的摄氏温度,将其转为华氏温度返回\"\"\"\n",
    "    temp = float(num[:-1]) * 1.8 + 32\n",
    "    print(f'摄氏{num[:-1]}℃为华氏{temp:.2f}℉')\n",
    "    return temp\n",
    "\n",
    "\n",
    "def have_a_fever(temp: str) -> bool:\n",
    "    \"\"\"接受一个带单位的温度,判定是否结冰(体温≥37.3℃),返回布尔值\"\"\"\n",
    "    if temp[-1] in 'Ff':\n",
    "        temp_c = fahrenheit_to_celsius(temp)\n",
    "    else:\n",
    "        temp_c = float(temp[:-1])\n",
    "    return temp_c >= 37.3\n",
    "\n",
    "\n",
    "def freeze(temp: str) -> bool:\n",
    "    \"\"\"接受一个带单位的温度,判定是否结冰(温度<0℃),返回布尔值\"\"\"\n",
    "    if temp[-1] in 'Ff':\n",
    "        temp_c = fahrenheit_to_celsius(temp)\n",
    "    else:\n",
    "        temp_c = float(temp[:-1])\n",
    "    return temp_c < 0\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    temperature = input()\n",
    "    if temperature[-1] in 'FfCc':\n",
    "        print(have_a_fever(temperature))\n",
    "    else:\n",
    "        print('data error')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "typing 模块定义了一些常用的注解类型,如 List、Tuple、Dict、Sequence、TextIO 等等,了解了每个类型的具体使用方法,我们可以得心应手的对任何变量进行声明了,在引入的时候就直接通过 typing 模块引入就好了。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面以List为例简单介绍,List、列表,是 list 的泛型,基本等同于 list,其后紧跟一个方括号,里面代表了构成这个列表的元素类型,如由字符串构成的列表可以声明为:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List, TextIO\n",
    "\n",
    "def csv_to_list(csv_file: TextIO) -> List[str]:\n",
    "    \"\"\"返回由字符串构成的列表,检查返回值类型如['2', '3.14', 'whut']\"\"\"\n",
    "    pass\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List, TextIO\n",
    "\n",
    "def csv_to_list(csv_file: TextIO) -> List[int or float]:\n",
    "    \"\"\"返回由整数或浮点数构成的列表,检查返回值类型如[2, 3.14]\"\"\"\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List, TextIO\n",
    "\n",
    "def csv_to_list(csv_file: TextIO) -> List[List[int]]:\n",
    "    \"\"\"返回由元素为整数的子列表构成的列表,检查返回值类型如[[1, 2], [3, 4]]\"\"\"\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List, TextIO\n",
    "\n",
    "def csv_to_list(csv_file: TextIO) -> List[List[str]]:\n",
    "    \"\"\"返回由元素为字符串的子列表构成的列表,\n",
    "    检查返回值类型如:\n",
    "    [['7090', 'Danforth Ave / Lamb Ave', '43.681991', '-79.329455', '15', '4', '10'],\n",
    "    ['7486', 'Gerrard St E / Ted Reeve Dr', '43.684261', '-79.299332', '24', '5', '19'],\n",
    "    ['7571', 'Highfield Rd / Gerrard St E - SMART', '43.671685', '-79.325176', '19', '14','5'],\n",
    "    ......\n",
    "    ]\n",
    "\"\"\"\n",
    "    pass\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 应用实例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "读示例数据文件中的数据到列表中:  \n",
    "<a href=\"https://data.educoder.net/api/attachments/4579493?type=office&disposition=inline\" target=\"_blank\">stations.csv</a>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def csv_to_list(csv_file: TextIO) -> List[List[str]]:\n",
    "    \"\"\"读取csv文件中的数据为元素为列表的列表,每个子列表容纳一行数据,每个数据项转为子列表的一个字符串元素\n",
    "    \"\"\"\n",
    "\n",
    "    # 读取并忽略标题行\n",
    "    csv_file.readline()    # 读文件的一行,未定义变量名,忽略不用\n",
    "\n",
    "    data = []              # 创建空列表\n",
    "    for line in csv_file:  # 遍历文件对象\n",
    "        data.append(line.strip().split(','))  # 每一行字符串去掉换行符根据逗号切分为列表增加到列表中\n",
    "    return data                               # 返回子列表元素为字符串的列表\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    bike_io = open('/data/bigfiles/stations.csv')  # 打开文件,创建TextIO对象\n",
    "    print(csv_to_list(bike_io))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def csv_to_list(csv_file: TextIO) -> List[List[str]]:\n",
    "    \"\"\"读取csv文件中的数据为元素为列表的列表,每个子列表容纳一行数据,每个数据项转为子列表的一个字符串元素\"\"\"\n",
    "    data = [line.strip().split(',') for line in csv_file]   # 列表推导式上述功能实现\n",
    "    return data[1:]                         # 返回子列表元素为字符串的列表,去掉标题行\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    bike_io = open('/data/bigfiles/stations.csv')  # 打开文件,创建TextIO对象\n",
    "    print(csv_to_list(bike_io))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 文档测试"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在函数的文档字符串中用引导符>>> 加函数调用,下一行写函数预期返回值,可以进行函数文档测试,这样运行函数时可以尽早发现问题。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def leap(year):\n",
    "    \"\"\"接收一个表示年份的整数,判定是否是闰年,返回布尔值\n",
    "    >>> leap(1900)\n",
    "    False\n",
    "    >>> leap(2000)\n",
    "    True\n",
    "    >>> leap(2002)\n",
    "    False\n",
    "    \"\"\"\n",
    "    return year % 400 == 0 or (year % 4 == 0 and year % 100 != 0)\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    test_year = int(input())\n",
    "    if leap(test_year):  # 以输入的名字为参数,输出问候语\n",
    "        print(f'{test_year}年是闰年')\n",
    "    else:\n",
    "        print(f'{test_year}年不是闰年')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在写代码尤其是开发商业项目的时候,一定要有意识的将相对独立且重复使用的功能封装成函数,这样不管是自己还是团队的其他成员都可以通过调用函数的方式来使用这些功能,减少工作中那些重复且乏味的劳动。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}