def calculate_and print_stats(list_of_numbers):
sum = sum(list_of_numbers)
mean = statistics.mean(list_of_numbers)
median = statistics.median(list_of_numbers)
mode = statistics.mode(list_of_numbers)
print(‘-----------------Stats-----------------’)
print(‘SUM: {}’.format(sum) print(‘MEAN: {}’.format(mean)
print(‘MEDIAN: {}’.format(median)
print(‘MODE: {}’.format(mode)
这一函数做两件事:计算一组关于数字列表的统计数据,并将它们打印到 STDOUT。该函数违反了只有一个原因能让函数改变的原则。显然有两个原因可以让该函数做出改变:新的或不同的数据需要计算或输出的格式需要改变。最好将该函数写成两个独立的函数:一个用来执行并返回计算结果;另一个用来接收结果并将其打印出来。函数有多重功能的一个致命漏洞是函数名称中含有单词「and」
这种分离还可以简化针对函数行为的测试,而且它们不仅被分离成一个模块中的两个函数,还可能在适当情况下存在于不同的模块中。这使得测试更加清洁、维护更加简单。
只做两件事的函数其实非常罕见。更常见的情况是一个函数负责许多许多任务。再次强调一下,为可读性、可测试性起见,我们应该将这些「多面手」函数分成一个一个的小函数,每个小函数只负责一项任务。
**文档注释**
很多 Python 开发者都知道 PEP-8,它定义了 Python 编程的风格指南,但很少有人了解定义了文档注释风格的 PEP-257。在这里并不会详细介绍 PEP-257,读者可详细阅读该指南所约定的文档注释风格。
* PEP-8:https://www.python.org/dev/peps/pep-0008/
* PEP-257:https://www.python.org/dev/peps/pep-0257/
首先文档注释是在定义模块、函数、类或方法的第一段字符串声明,这一段字符串应该需要描述清楚函数的作用、输入参数和返回参数等。PEP-257 的主要信息如下:
* 每一个函数都需要一个文档描述;
* 使用合适的语法和标点,书写完整的句子;
* 最开始需要用一句话总结函数的主要作用;
* 使用规定性的语言而不是描述性的语言。
在编写函数时,遵循这些规则很容易。我们只需要养成编写文档注释的习惯,并在实际写函数主体之前完成它们。如果你不能清晰地描述这个函数的作用是什么,那么你需要更多地考虑为什么要写这个函数。
**返回值**
函数可以且应该被视为一个独立的小程序。它们以参数的形式获取一些输入,并返回一些输出值。当然,参数是可选的,但是从 Python 内部机制来看,返回值是不可选的。即使你尝试创建一个不会返回值的函数,我们也不能选择不在内部采用返回值,因为 Python 的解释器会强制返回一个 None。不相信的读者可以用以下代码测试:
❯ python3
Python 3.7.0 (default, Jul 23 2018, 20:22:55)
[Clang 9.1.0 (clang-902.0.39.2)] on darwin
Type “help”, “copyright”, “credits” or “license” *for *more information.
def add(a, b):
… print(a + b)
…
b = add(1, 2)
3
b
b is None
True
运行上面的代码,你会看到 b 的值确实是 None。所以即使我们编写一个不包含 return 语句的函数,它仍然会返回某些东西。不过函数也应该要返回一些东西,因为它也是一个小程序。没有输出的程序又会有多少用,我们又如何测试它呢?
我甚至希望发表以下声明:每一个函数都应该返回一个有用的值,即使这个值仅可用来测试。我们写的代码应该需要得到测试,而不带返回值的函数很难测试它的正确性,上面的函数可能需要重定向 I/O 才能得到测试。此外,返回值能改变方法的调用,如下代码展示了这种概念:
with open(‘foo.txt’, ‘r’) as input_file:
for line in input_file:
if line.strip().lower().endswith(‘cat’):
# … do something useful with these lines
代码行 if line.strip().lower().endswith('cat') 能够正常运行,因为字符串方法 (strip(), lower(), endswith()) 会返回一个字符串以作为调用函数的结果。
以下是人们在被问及为什么他们写的函数没有返回值时给出的一些常见原因:
>
> 「函数所做的就是类似 I/O 的操作,例如将一个值保存到数据库中,这种函数不能返回有用的输出。」
>
>
>
我并不同意这种观点,因为在操作成功完成时,函数可以返回 True。
>
> 「我需要返回多个值,因为只返回一个值并不能代表什么。」
>
>
>
当然也可以返回包含多个值的一个元组。简而言之,即使在现有的代码库中,从函数返回一个值肯定是一个好主意,并且不太可能破坏任何东西。
**函数长度**
函数的长度直接影响了可读性,因而会影响可维护性。因此要保证你的函数长度足够短。50 行的函数对我而言是个合理的长度。
如果函数遵循单一功能原则,一般而言其长度会非常短。如果函数是纯函数或幂等函数(下面会讨论),它的长度也会较短。这些想法对于构造简洁的代码很有帮助。
那么如果一个函数太长该怎么办?代码重构(refactor)!代码重构很可能是你写代码时一直在做的事情,即使你对这个术语并不熟悉。它的含义是:在不改变程序行为的前提下改变程序的结构。因此从一个长函数提取几行代码并转换为属于该函数的函数也是一种代码重构。这也是将长函数缩短最快和最常用的方法。只要适当给这些新函数命名,代码的阅读将变得更加容易。
**幂等性和函数纯度**
幂等函数(idempotent function)在给定相同变量参数集时会返回相同的值,无论它被调用多少次。函数的结果不依赖于非局部变量、参数的易变性或来自任何 I/O 流的数据。以下的 add\_three(number) 函数是幂等的:
def add_three(number):
“”“Return number + 3.”“”
return number + 3
无论何时调用 add\_three(7),其返回值都是 10。以下展示了非幂等的函数示例:
def add_three():
“”“Return 3 + the number entered by the user.”“”
number = int(input('Enter a number: '))
return number + 3
这函数不是幂等的,因为函数的返回值依赖于 I/O,即用户输入的数字。每次调用这个函数时,它都可能返回不同的值。如果它被调用两次,则用户可以第一次输入 3,第二次输入 7,使得对 add\_three() 的调用分别返回 6 和 10。
**为什么幂等很重要?**
可测试性和可维护性。幂等函数易于测试,因为它们在使用相同参数的情况下会返回同样的结果。测试就是检查对函数的不同调用所返回的值是否符合预期。此外,对幂等函数的测试很快,这在单元测试(Unit Testing)中非常重要,但经常被忽视。重构幂等函数也很简单。不管你如何改变函数以外的代码,使用同样的参数调用函数所返回的值都是一样的。
**什么是「纯」函数?**
在函数编程中,如果函数是幂等函数且没有明显的副作用(side effect),则它就是纯函数。记住,幂等函数表示在给定参数集的情况下该函数总是返回相同的结果,不能使用任何外部因素来计算结果。但是,这并不意味着幂等函数无法影响非局部变量(non-local variable)或 I/O stream 等。例如,如果上文中 add\_three(number) 的幂等版本在返回结果之前先输出了结果,它仍然是幂等的,因为它访问了 I/O stream,这不会影响函数的返回值。调用 print() 是副作用:除返回值以外,与程序或系统中其余部分的交互。
我们来扩展一下 add\_three(number) 这个例子。我们可以用以下代码片段来查看 add\_three(number) 函数被调用的次数:
add_three_calls = 0
def add_three(number):
“”“Return number + 3.”“”
global add_three_calls
print(f’Returning {number + 3}')
add_three_calls += 1
return number + 3
def num_calls():
“”“Return the number of times add_three was called.”“”
return add_three_calls
现在我们向控制台输出结果(一项副作用),并修改了非局部变量(又一项副作用),但是由于这些副作用不影响函数的返回值,因此该函数仍然是幂等的。
纯函数没有副作用。它不仅不使用任何「外来数据」来计算值,也不与系统/程序的其它部分进行交互,除了计算和返回值。因此,尽管我们新定义的 add\_three(number) 仍是幂等函数,但它不再是纯函数。
还有兄弟不知道网络安全面试可以提前刷题吗?费时一周整理的160+网络安全面试题,金九银十,做网络安全面试里的显眼包!
王岚嵚工程师面试题(附答案),只能帮兄弟们到这儿了!如果你能答对70%,找一个安全工作,问题不大。
对于有1-3年工作经验,想要跳槽的朋友来说,也是很好的温习资料!
【完整版领取方式在文末!!】
***93道网络安全面试题***
![](https://img-blog.csdnimg.cn/img_convert/6679c89ccd849f9504c48bb02882ef8d.png)
![](https://img-blog.csdnimg.cn/img_convert/07ce1a919614bde78921fb2f8ddf0c2f.png)
![](https://img-blog.csdnimg.cn/img_convert/44238619c3ba2d672b5b8dc4a529b01d.png)
内容实在太多,不一一截图了
### 黑客学习资源推荐
最后给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
#### 1️⃣零基础入门
##### ① 学习路线
对于从来没有接触过网络安全的同学,我们帮你准备了详细的**学习成长路线图**。可以说是**最科学最系统的学习路线**,大家跟着这个大的方向学习准没问题。
![image](https://img-blog.csdnimg.cn/img_convert/acb3c4714e29498573a58a3c79c775da.gif#pic_center)
##### ② 路线对应学习视频
同时每个成长路线对应的板块都有配套的视频提供:
![image-20231025112050764](https://img-blog.csdnimg.cn/874ad4fd3dbe4f6bb3bff17885655014.png#pic_center)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化资料的朋友,可以点击这里获取](https://bbs.csdn.net/topics/618540462)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**