|
|
@@ -13,6 +13,9 @@
|
|
|
```Python
|
|
|
"""
|
|
|
输入M和N计算C(M,N)
|
|
|
+
|
|
|
+Version: 0.1
|
|
|
+Author: 骆昊
|
|
|
"""
|
|
|
|
|
|
m = int(input('m = '))
|
|
|
@@ -41,12 +44,7 @@ print(fm // fn // fmn)
|
|
|
|
|
|
```Python
|
|
|
def factorial(num):
|
|
|
- """
|
|
|
- 求阶乘
|
|
|
-
|
|
|
- :param num: 非负整数
|
|
|
- :return: num的阶乘
|
|
|
- """
|
|
|
+ """求阶乘"""
|
|
|
result = 1
|
|
|
for n in range(1, num + 1):
|
|
|
result *= n
|
|
|
@@ -59,24 +57,19 @@ n = int(input('n = '))
|
|
|
print(factorial(m) // factorial(n) // factorial(m - n))
|
|
|
```
|
|
|
|
|
|
-> **说明:** Python的math模块中其实已经有一个factorial函数了,事实上要计算阶乘可以直接使用这个现成的函数而不用自己定义。下面例子中的某些函数其实Python中也是内置了,我们这里是为了讲解函数的定义和使用才把它们又实现了一遍,实际开发中不建议做这种低级的重复性的工作。
|
|
|
+> **说明:** Python的`math`模块中其实已经有一个`factoria`l函数了,事实上要计算阶乘可以直接使用这个现成的函数而不用自己定义。下面例子中的一些函数在Python中也都是现成的,我们这里是为了讲解函数的定义和使用才把它们又实现了一遍,实际开发中不建议做这种低级的重复性的工作。
|
|
|
|
|
|
|
|
|
### 函数的参数
|
|
|
|
|
|
-函数是绝大多数编程语言中都支持的一个代码的“构建块”,但是Python中的函数与其他语言中的函数还是有很多不太相同的地方,其中一个显著的区别就是Python对函数参数的处理。在Python中,函数的参数可以有默认值,也支持使用可变参数,所以Python并不需要像其他语言一样支持[函数的重载](https://zh.wikipedia.org/wiki/%E5%87%BD%E6%95%B0%E9%87%8D%E8%BD%BD),因为我们在定义一个函数的时候可以让它有多种不同的使用方式,下面是两个小例子。
|
|
|
+函数是绝大多数编程语言中都支持的一个代码的"构建块",但是Python中的函数与其他语言中的函数还是有很多不太相同的地方,其中一个显著的区别就是Python对函数参数的处理。在Python中,函数的参数可以有默认值,也支持使用可变参数,所以Python并不需要像其他语言一样支持[函数的重载](https://zh.wikipedia.org/wiki/%E5%87%BD%E6%95%B0%E9%87%8D%E8%BD%BD),因为我们在定义一个函数的时候可以让它有多种不同的使用方式,下面是两个小例子。
|
|
|
|
|
|
```Python
|
|
|
from random import randint
|
|
|
|
|
|
|
|
|
def roll_dice(n=2):
|
|
|
- """
|
|
|
- 摇色子
|
|
|
-
|
|
|
- :param n: 色子的个数
|
|
|
- :return: n颗色子点数之和
|
|
|
- """
|
|
|
+ """摇色子"""
|
|
|
total = 0
|
|
|
for _ in range(n):
|
|
|
total += randint(1, 6)
|
|
|
@@ -84,6 +77,7 @@ def roll_dice(n=2):
|
|
|
|
|
|
|
|
|
def add(a=0, b=0, c=0):
|
|
|
+ """三个数相加"""
|
|
|
return a + b + c
|
|
|
|
|
|
|
|
|
@@ -105,7 +99,6 @@ print(add(c=50, a=100, b=200))
|
|
|
|
|
|
```Python
|
|
|
# 在参数名前面的*表示args是一个可变参数
|
|
|
-# 即在调用add函数时可以传入0个或多个参数
|
|
|
def add(*args):
|
|
|
total = 0
|
|
|
for val in args:
|
|
|
@@ -113,6 +106,7 @@ def add(*args):
|
|
|
return total
|
|
|
|
|
|
|
|
|
+# 在调用add函数时可以传入0个或多个参数
|
|
|
print(add())
|
|
|
print(add(1))
|
|
|
print(add(1, 2))
|
|
|
@@ -139,21 +133,21 @@ foo()
|
|
|
|
|
|
当然上面的这种情况我们很容易就能避免,但是如果项目是由多人协作进行团队开发的时候,团队中可能有多个程序员都定义了名为`foo`的函数,那么怎么解决这种命名冲突呢?答案其实很简单,Python中每个文件就代表了一个模块(module),我们在不同的模块中可以有同名的函数,在使用函数的时候我们通过`import`关键字导入指定的模块就可以区分到底要使用的是哪个模块中的`foo`函数,代码如下所示。
|
|
|
|
|
|
-module1.py
|
|
|
+`module1.py`
|
|
|
|
|
|
```Python
|
|
|
def foo():
|
|
|
print('hello, world!')
|
|
|
```
|
|
|
|
|
|
-module2.py
|
|
|
+`module2.py`
|
|
|
|
|
|
```Python
|
|
|
def foo():
|
|
|
print('goodbye, world!')
|
|
|
```
|
|
|
|
|
|
-test.py
|
|
|
+`test.py`
|
|
|
|
|
|
```Python
|
|
|
from module1 import foo
|
|
|
@@ -169,7 +163,7 @@ foo()
|
|
|
|
|
|
也可以按照如下所示的方式来区分到底要使用哪一个`foo`函数。
|
|
|
|
|
|
-test.py
|
|
|
+`test.py`
|
|
|
|
|
|
```Python
|
|
|
import module1 as m1
|
|
|
@@ -181,7 +175,7 @@ m2.foo()
|
|
|
|
|
|
但是如果将代码写成了下面的样子,那么程序中调用的是最后导入的那个`foo`,因为后导入的foo覆盖了之前导入的`foo`。
|
|
|
|
|
|
-test.py
|
|
|
+`test.py`
|
|
|
|
|
|
```Python
|
|
|
from module1 import foo
|
|
|
@@ -191,7 +185,7 @@ from module2 import foo
|
|
|
foo()
|
|
|
```
|
|
|
|
|
|
-test.py
|
|
|
+`test.py`
|
|
|
|
|
|
```Python
|
|
|
from module2 import foo
|
|
|
@@ -201,9 +195,9 @@ from module1 import foo
|
|
|
foo()
|
|
|
```
|
|
|
|
|
|
-需要说明的是,如果我们导入的模块除了定义函数之外还中有可以执行代码,那么Python解释器在导入这个模块时就会执行这些代码,事实上我们可能并不希望如此,因此如果我们在模块中编写了执行代码,最好是将这些执行代码放入如下所示的条件中,这样的话除非直接运行该模块,if条件下的这些代码是不会执行的,因为只有直接执行的模块的名字才是“\_\_main\_\_”。
|
|
|
+需要说明的是,如果我们导入的模块除了定义函数之外还中有可以执行代码,那么Python解释器在导入这个模块时就会执行这些代码,事实上我们可能并不希望如此,因此如果我们在模块中编写了执行代码,最好是将这些执行代码放入如下所示的条件中,这样的话除非直接运行该模块,if条件下的这些代码是不会执行的,因为只有直接执行的模块的名字才是"\_\_main\_\_"。
|
|
|
|
|
|
-module3.py
|
|
|
+`module3.py`
|
|
|
|
|
|
```Python
|
|
|
def foo():
|
|
|
@@ -223,7 +217,7 @@ if __name__ == '__main__':
|
|
|
bar()
|
|
|
```
|
|
|
|
|
|
-test.py
|
|
|
+`test.py`
|
|
|
|
|
|
```Python
|
|
|
import module3
|
|
|
@@ -239,6 +233,7 @@ import module3
|
|
|
|
|
|
```Python
|
|
|
def gcd(x, y):
|
|
|
+ """求最大公约数"""
|
|
|
(x, y) = (y, x) if x > y else (x, y)
|
|
|
for factor in range(x, 0, -1):
|
|
|
if x % factor == 0 and y % factor == 0:
|
|
|
@@ -246,6 +241,7 @@ def gcd(x, y):
|
|
|
|
|
|
|
|
|
def lcm(x, y):
|
|
|
+ """求最小公倍数"""
|
|
|
return x * y // gcd(x, y)
|
|
|
```
|
|
|
|
|
|
@@ -255,6 +251,7 @@ def lcm(x, y):
|
|
|
|
|
|
```Python
|
|
|
def is_palindrome(num):
|
|
|
+ """判断一个数是不是回文数"""
|
|
|
temp = num
|
|
|
total = 0
|
|
|
while temp > 0:
|
|
|
@@ -269,6 +266,7 @@ def is_palindrome(num):
|
|
|
|
|
|
```Python
|
|
|
def is_prime(num):
|
|
|
+ """判断一个数是不是素数"""
|
|
|
for factor in range(2, num):
|
|
|
if num % factor == 0:
|
|
|
return False
|
|
|
@@ -286,7 +284,7 @@ if __name__ == '__main__':
|
|
|
print('%d是回文素数' % num)
|
|
|
```
|
|
|
|
|
|
-通过上面的程序可以看出,当我们将代码中重复出现的和相对独立的功能抽取成函数后,我们可以组合使用这些函数来解决更为复杂的问题,这也是我们为什么要定义和使用函数的一个非常重要的原因。
|
|
|
+> **注意**:通过上面的程序可以看出,当我们**将代码中重复出现的和相对独立的功能抽取成函数**后,我们可以**组合使用这些函数**来解决更为复杂的问题,这也是我们为什么要定义和使用函数的一个非常重要的原因。
|
|
|
|
|
|
最后,我们来讨论一下Python中有关变量作用域的问题。
|
|
|
|
|
|
@@ -294,7 +292,8 @@ if __name__ == '__main__':
|
|
|
def foo():
|
|
|
b = 'hello'
|
|
|
|
|
|
- def bar(): # Python中可以在函数内部再定义函数
|
|
|
+ # Python中可以在函数内部再定义函数
|
|
|
+ def bar():
|
|
|
c = True
|
|
|
print(a)
|
|
|
print(b)
|
|
|
@@ -310,7 +309,7 @@ if __name__ == '__main__':
|
|
|
foo()
|
|
|
```
|
|
|
|
|
|
-上面的代码能够顺利的执行并且打印出100和“hello”,但我们注意到了,在`bar`函数的内部并没有定义`a`和`b`两个变量,那么`a`和`b`是从哪里来的。我们在上面代码的`if`分支中定义了一个变量`a`,这是一个全局变量(global variable),属于全局作用域,因为它没有定义在任何一个函数中。在上面的`foo`函数中我们定义了变量`b`,这是一个定义在函数中的局部变量(local variable),属于局部作用域,在`foo`函数的外部并不能访问到它;但对于`foo`函数内部的`bar`函数来说,变量`b`属于嵌套作用域,在`bar`函数中我们是可以访问到它的。`bar`函数中的变量`c`属于局部作用域,在`bar`函数之外是无法访问的。事实上,Python查找一个变量时会按照“局部作用域”、“嵌套作用域”、“全局作用域”和“内置作用域”的顺序进行搜索,前三者我们在上面的代码中已经看到了,所谓的“内置作用域”就是Python内置的那些隐含标识符`min`、`len`等都属于内置作用域)。
|
|
|
+上面的代码能够顺利的执行并且打印出100、hello和True,但我们注意到了,在`bar`函数的内部并没有定义`a`和`b`两个变量,那么`a`和`b`是从哪里来的。我们在上面代码的`if`分支中定义了一个变量`a`,这是一个全局变量(global variable),属于全局作用域,因为它没有定义在任何一个函数中。在上面的`foo`函数中我们定义了变量`b`,这是一个定义在函数中的局部变量(local variable),属于局部作用域,在`foo`函数的外部并不能访问到它;但对于`foo`函数内部的`bar`函数来说,变量`b`属于嵌套作用域,在`bar`函数中我们是可以访问到它的。`bar`函数中的变量`c`属于局部作用域,在`bar`函数之外是无法访问的。事实上,Python查找一个变量时会按照“局部作用域”、“嵌套作用域”、“全局作用域”和“内置作用域”的顺序进行搜索,前三者我们在上面的代码中已经看到了,所谓的“内置作用域”就是Python内置的那些标识符,我们之前用过的`input`、`print`、`int`等都属于内置作用域。
|
|
|
|
|
|
再看看下面这段代码,我们希望通过函数调用修改全局变量`a`的值,但实际上下面的代码是做不到的。
|
|
|
|
|
|
@@ -343,7 +342,7 @@ if __name__ == '__main__':
|
|
|
|
|
|
我们可以使用`global`关键字来指示`foo`函数中的变量`a`来自于全局作用域,如果全局作用域中没有`a`,那么下面一行的代码就会定义变量`a`并将其置于全局作用域。同理,如果我们希望函数内部的函数能够修改嵌套作用域中的变量,可以使用`nonlocal`关键字来指示变量来自于嵌套作用域,请大家自行试验。
|
|
|
|
|
|
-在实际开发中,我们应该尽量减少对全局变量的使用,因为全局变量的作用域和影响过于广泛,可能会发生意料之外的修改和使用,除此之外全局变量比局部变量拥有更长的生命周期,可能导致对象占用的内存长时间无法被[垃圾回收](https://zh.wikipedia.org/wiki/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8))。事实上,减少对全局变量的使用,也是降低代码之间耦合度的一个重要举措,同时也是对[迪米特法则](https://zh.wikipedia.org/zh-hans/%E5%BE%97%E5%A2%A8%E5%BF%92%E8%80%B3%E5%AE%9A%E5%BE%8B)的践行。减少全局变量的使用就意味着我们应该尽量让变量的作用域在函数的内部,但是如果我们希望将一个局部变量的生命周期延长,使其在函数调用结束后依然可以访问,这时候就需要使用[闭包](https://zh.wikipedia.org/wiki/%E9%97%AD%E5%8C%85_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)),这个我们在后续的内容中进行讲解。
|
|
|
+在实际开发中,我们应该尽量减少对全局变量的使用,因为全局变量的作用域和影响过于广泛,可能会发生意料之外的修改和使用,除此之外全局变量比局部变量拥有更长的生命周期,可能导致对象占用的内存长时间无法被[垃圾回收](https://zh.wikipedia.org/wiki/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8))。事实上,减少对全局变量的使用,也是降低代码之间耦合度的一个重要举措,同时也是对[迪米特法则](https://zh.wikipedia.org/zh-hans/%E5%BE%97%E5%A2%A8%E5%BF%92%E8%80%B3%E5%AE%9A%E5%BE%8B)的践行。减少全局变量的使用就意味着我们应该尽量让变量的作用域在函数的内部,但是如果我们希望将一个局部变量的生命周期延长,使其在定义它的函数调用结束后依然可以使用它的值,这时候就需要使用[闭包](https://zh.wikipedia.org/wiki/%E9%97%AD%E5%8C%85_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)),这个我们在后续的内容中进行讲解。
|
|
|
|
|
|
> **说明:** 很多人经常会将“闭包”和[“匿名函数”](https://zh.wikipedia.org/wiki/%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0)混为一谈,但实际上它们是不同的概念,如果想提前了解这个概念,推荐看看[维基百科](https://zh.wikipedia.org/wiki/)或者[知乎](https://www.zhihu.com/)上对这个概念的讨论。
|
|
|
|