Quellcode durchsuchen

更新了部分文档

jackfrued vor 6 Jahren
Ursprung
Commit
8e719caa5f

+ 27 - 28
Day01-15/06.函数和模块的使用.md

@@ -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/)上对这个概念的讨论。
 

+ 49 - 28
Day01-15/07.字符串和常用数据结构.md

@@ -2,7 +2,7 @@
 
 ### 使用字符串
 
-第二次世界大战促使了现代电子计算机的诞生,最初的目的用计算机来快速的完成导弹弹道的计算,因此在计算机刚刚诞生的那个年代,计算机处理的信息基本上都是数值型的信息,而世界上的第一台电子计算机ENIAC每秒钟能够完成约5000次浮点运算。随着时间的推移,虽然数值运算仍然是计算机日常工作中最为重要的事情之一,但是今天的计算机更多的时间需要处理的数据可能都是以文本的方式存在的,如果我们希望通过Python程序操作本这些文本信息,就必须要先了解字符串类型以及与它相关的知识。
+第二次世界大战促使了现代电子计算机的诞生,最初计算机被应用于导弹弹道的计算,而在计算机诞生后的很多年时间里,计算机处理的信息基本上都是数值型的信息。世界上的第一台电子计算机叫ENIAC(电子数值积分计算机),诞生于美国的宾夕法尼亚大学,每秒钟能够完成约5000次浮点运算。随着时间的推移,虽然数值运算仍然是计算机日常工作中最为重要的事情之一,但是今天的计算机处理得更多的数据可能都是以文本的方式存在的,如果我们希望通过Python程序操作本这些文本信息,就必须要先了解字符串类型以及与它相关的知识。
 
 所谓**字符串**,就是由零个或多个字符组成的有限序列,一般记为![$${\displaystyle s=a_{1}a_{2}\dots a_{n}(0\leq n \leq \infty)}$$](./res/formula_5.png)。在Python程序中,如果我们把单个或多个字符用单引号或者双引号包围起来,就可以表示一个字符串。
 
@@ -128,38 +128,62 @@ print(f'{a} * {b} = {a * b}')
 
 ### 使用列表
 
-下面的代码演示了如何定义列表、使用下标访问列表元素以及添加和删除元素的操作。
+不知道大家是否注意到,刚才我们讲到的字符串类型(`str`)和之前我们讲到的数值类型(`int`和`float`)有一些区别。数值类型是标量类型,也就是说这种类型的对象没有可以访问的内部结构;而字符串类型是一种结构化的、非标量类型,所以才会有一系列的属性和方法。接下来我们要介绍的列表(`list`),也是一种结构化的、非标量类型,它是值的有序序列,每个值都可以通过索引进行标识,定义列表可以将列表的元素放在`[]`中,多个元素用`,`进行分隔,可以使用`for`循环对列表元素进行遍历,也可以使用`[]`或`[:]`运算符取出列表中的一个或多个元素。
+
+下面的代码演示了如何定义列表、如何遍历列表以及列表的下标运算。
 
 ```Python
 list1 = [1, 3, 5, 7, 100]
-print(list1)
-list2 = ['hello'] * 5
-print(list2)
+print(list1) # [1, 3, 5, 7, 100]
+# 乘号表示列表元素的重复
+list2 = ['hello'] * 3
+print(list2) # ['hello', 'hello', 'hello']
 # 计算列表长度(元素个数)
-print(len(list1))
+print(len(list1)) # 5
 # 下标(索引)运算
-print(list1[0])
-print(list1[4])
+print(list1[0]) # 1
+print(list1[4]) # 100
 # print(list1[5])  # IndexError: list index out of range
-print(list1[-1])
-print(list1[-3])
+print(list1[-1]) # 100
+print(list1[-3]) # 5
 list1[2] = 300
-print(list1)
+print(list1) # [1, 3, 300, 7, 100]
+# 通过循环用下标遍历列表元素
+for index in range(len(list1)):
+    print(list1[index])
+# 通过for循环遍历列表元素
+for elem in list1:
+    print(elem)
+# 通过enumerate函数处理列表之后再遍历可以同时获得元素索引和值
+for index, elem in enumerate(list1):
+    print(index, elem)
+```
+
+下面的代码演示了如何向列表中添加元素以及如何从列表中移除元素。
+
+```Python
+list1 = [1, 3, 5, 7, 100]
 # 添加元素
 list1.append(200)
 list1.insert(1, 400)
+# 合并两个列表
+# list1.extend([1000, 2000])
 list1 += [1000, 2000]
-print(list1)
-print(len(list1))
-# 删除元素
-list1.remove(3)
+print(list1) # [1, 400, 3, 5, 7, 100, 200, 1000, 2000]
+print(len(list1)) # 9
+# 先通过成员运算判断元素是否在列表中,如果存在就删除该元素
+if 3 in list1:
+	list1.remove(3)
 if 1234 in list1:
     list1.remove(1234)
-del list1[0]
-print(list1)
+print(list1) # [1, 400, 5, 7, 100, 200, 1000, 2000]
+# 从指定的位置删除元素
+list1.pop(0)
+list1.pop(len(list1) - 1)
+print(list1) # [400, 5, 7, 100, 200, 1000]
 # 清空列表元素
 list1.clear()
-print(list1)
+print(list1) # []
 ```
 
 和字符串一样,列表也可以做切片操作,通过切片操作我们可以实现对列表的复制或者将列表中的一部分取出来创建出新的列表,代码如下所示。
@@ -167,22 +191,17 @@ print(list1)
 ```Python
 fruits = ['grape', 'apple', 'strawberry', 'waxberry']
 fruits += ['pitaya', 'pear', 'mango']
-# 循环遍历列表元素
-for fruit in fruits:
-    print(fruit.title(), end=' ')
-print()
 # 列表切片
 fruits2 = fruits[1:4]
-print(fruits2)
-# fruit3 = fruits  # 没有复制列表只创建了新的引用
+print(fruits2) # apple strawberry waxberry
 # 可以通过完整切片操作来复制列表
 fruits3 = fruits[:]
-print(fruits3)
+print(fruits3) # ['grape', 'apple', 'strawberry', 'waxberry', 'pitaya', 'pear', 'mango']
 fruits4 = fruits[-3:-1]
-print(fruits4)
+print(fruits4) # ['pitaya', 'pear']
 # 可以通过反向切片操作来获得倒转后的列表的拷贝
 fruits5 = fruits[::-1]
-print(fruits5)
+print(fruits5) # ['mango', 'pear', 'pitaya', 'waxberry', 'strawberry', 'apple', 'grape']
 ```
 
 下面的代码实现了对列表的排序操作。
@@ -204,6 +223,8 @@ list1.sort(reverse=True)
 print(list1)
 ```
 
+### 生成式和生成器
+
 我们还可以使用列表的生成式语法来创建列表,代码如下所示。
 
 ```Python
@@ -255,7 +276,7 @@ if __name__ == '__main__':
 
 ### 使用元组
 
-Python 的元组与列表类似,不同之处在于元组的元素不能修改,在前面的代码中我们已经不止一次使用过元组了。顾名思义,我们把多个元素组合到一起就形成了一个元组,所以它和列表一样可以保存多条数据。下面的代码演示了如何定义和使用元组。
+Python 的元组与列表类似也是一种容器数据类型,可以用一个变量(对象)来存储多个数据,不同之处在于元组的元素不能修改,在前面的代码中我们已经不止一次使用过元组了。顾名思义,我们把多个元素组合到一起就形成了一个元组,所以它和列表一样可以保存多条数据。下面的代码演示了如何定义和使用元组。
 
 ```Python
 # 定义元组

+ 5 - 1
Day91-100/100.Python面试题集.md

@@ -221,4 +221,8 @@
 
 38. 说一下索引的原理和作用。
 
-39. 是否使用过Nginx实现负载均衡?用过哪些负载均衡算法?
+39. 是否使用过Nginx实现负载均衡?用过哪些负载均衡算法?
+
+40. 一个保存整数(int)的数组,除了一个元素出现过1次外,其他元素都出现过两次,请找出这个元素。
+
+41. 有12个外观相同的篮球,其中1个的重要和其他11个的重量不同(有可能轻有可能重),现在有一个天平可以使用,怎样才能通过最少的称重次数找出这颗与众不同的球。