diff --git a/TEST/TEST01.ipynb b/TEST/TEST01.ipynb new file mode 100644 index 000000000..790ac74be --- /dev/null +++ b/TEST/TEST01.ipynb @@ -0,0 +1,66 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "数字1和数字2相加的结果为3.0。\n" + ] + } + ], + "source": [ + "# 数字求和:通过用户输入两个数字,并计算两个数字之和:\n", + "num1 = input(\"请输入第一个数字:\")\n", + "num2 = input(\"请输入第二个数字:\")\n", + "sum = float(num1) + float(num2)\n", + "print(f\"数字{num1}和数字{num2}相加的结果为{sum}。\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "数字-8.0的平方根是(1.7319121124709868e-16+2.8284271247461903j)。\n" + ] + } + ], + "source": [ + "# 平方根:用户输入一个数字,并计算这个数的平方根\n", + "num = float(input(\"请输入一个数字:\"))\n", + "num_sqrt = num ** 0.5\n", + "print(f\"数字{num}的平方根是{num_sqrt}。\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "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.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/new-test-20251106.txt b/new-test-20251106.txt new file mode 100644 index 000000000..2b7e60900 --- /dev/null +++ b/new-test-20251106.txt @@ -0,0 +1 @@ +1232 \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\254\224\350\256\260/5A.txt" "b/\345\255\246\344\271\240\347\254\224\350\256\260/5A.txt" new file mode 100644 index 000000000..e7d495816 --- /dev/null +++ "b/\345\255\246\344\271\240\347\254\224\350\256\260/5A.txt" @@ -0,0 +1,10 @@ +1. 北京 +2. 上海 +3. 广州 +4. 深圳 +5. 成都 +6. 杭州 +7. 西安 +8. 重庆 +9. 南京 +10. 苏州 \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\254\224\350\256\260/Pyhton\345\270\270\347\224\250\346\250\241\345\235\227\345\217\212\345\207\275\346\225\260.ipynb" "b/\345\255\246\344\271\240\347\254\224\350\256\260/Pyhton\345\270\270\347\224\250\346\250\241\345\235\227\345\217\212\345\207\275\346\225\260.ipynb" new file mode 100644 index 000000000..8e156661f --- /dev/null +++ "b/\345\255\246\344\271\240\347\254\224\350\256\260/Pyhton\345\270\270\347\224\250\346\250\241\345\235\227\345\217\212\345\207\275\346\225\260.ipynb" @@ -0,0 +1,221 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "time 模块\n", + "print()\n", + "str()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### print()函数\n", + "\n", + "```python\n", + "print('a','b','c') # 输出a b c,因为print参数sep默认为空格' '\n", + "\n", + "i = 1\n", + "print('i=',i)\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on built-in function print in module builtins:\n", + "\n", + "print(*args, sep=' ', end='\\n', file=None, flush=False)\n", + " Prints the values to a stream, or to sys.stdout by default.\n", + "\n", + " sep\n", + " string inserted between values, default a space.\n", + " end\n", + " string appended after the last value, default a newline.\n", + " file\n", + " a file-like object (stream); defaults to the current sys.stdout.\n", + " flush\n", + " whether to forcibly flush the stream.\n", + "\n" + ] + } + ], + "source": [ + "help(print)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 静态对象\n", + "静态对象一般指程序运行期间不会发生改变的对象,或者不需要实例化就可以直接使用的对象。包括:\n", + "- 模块:导入后可以直接使用的对象。\n", + "- 类的静态成员:无需实例化类即可访问的属性或方法。\n", + "- 不可变对象:值在创建后不会改变的对象。" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 数学函数\n", + "| 函数 | 返回值 (描述) | 注意事项 |\n", + "|------|---------------|----------|\n", + "| abs(x) | 返回数字的绝对值,如 `abs(-10)` 返回 `10` | 内置函数,复数则返回其大小 |\n", + "| fabs(x) | 以浮点数形式返回数字的绝对值,如 `math.fabs(-10)` 返回 `10.0` | |\n", + "| ceil(x) | 返回数字的上入整数,如 `math.ceil(4.1)` 返回 `5` | `math.ceil(-4.1)` 返回 `-4` |\n", + "| floor(x) | 返回数字的下舍整数,如 `math.floor(4.9)` 返回 `4` | |\n", + "| exp(x) | 返回 e 的 x 次幂,如 `math.exp(1)` 返回 `2.718281828459045` | |\n", + "| log(x) | 如 `math.log(math.e)` 返回 `1.0`,`math.log(100,10)` 返回 `2.0` | |\n", + "| sqrt(x) | 返回数字 x 的平方根,如 `math.sqrt(100)` 返回 `10.0` | |\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### xx函数\n", + "| round(x [,n]) | 返回浮点数 x 的四舍五入值,如给出 n 值,则代表舍入到小数点后的位数。|不适合精度要求高的情况;round(2.675,2)会返回2.67,“四舍六入”|\n", + "| sqrt(x) | 返回数字 x 的平方根。|math.sqrt(100)返回10.0|" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 随机数函数\n", + "随机数可以用于数学,游戏,安全等领域中,还经常被嵌入到算法中,用以提高算法效率,并提高程序的安全性。\n", + "需要先导入random模块\n", + "| 函数 | 描述 |注意事项|\n", + "|---------------|-------------|--------|\n", + "| `choice(seq)` | 从序列的元素中随机挑选一个元素,比如 `random.choice(range(10))`,从 0 到 9 中随机挑选一个整数。|seq可以是列表、元组或者字符串|\n", + "| `randrange([start,] stop [,step])` | 从指定范围内,按指定基数递增的集合中获取一个随机数,基数默认值为 1。||\n", + "| `random()` | 随机生成下一个实数,它在 `[0,1)` 范围内。 ||\n", + "| `seed([x])` | 改变随机数生成器的种子 `seed`。如果你不了解其原理,你不必特别去设定 `seed`,Python 会帮你选择 `seed`。 ||\n", + "| `shuffle(lst)`| 将序列的所有元素随机排序。|random.shuffle ()返回值是None|\n", + "| `uniform(x, y)` | 随机生成下一个实数,它在 `[x, y]` 范围内。|返回浮点数,闭区间|\n", + "\n", + "range()返回的是一个range对象,不是一个具体的列表或者元组。" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 三角函数\n", + "\n", + "| 函数 | 描述 |\n", + "|---------------|--------------------------|\n", + "| `acos(x)` | 返回 `x` 的反余弦弧度值。 |\n", + "| `asin(x)` | 返回 `x` 的反正弦弧度值。 |\n", + "| `atan(x)` | 返回 `x` 的反正切弧度值。 |\n", + "| `atan2(y, x)` | 返回给定的 `x` 和 `y` 坐标值的反正切值。 |\n", + "| `cos(x)` | 返回 `x` 弧度的余弦值。 |\n", + "| `hypot(x, y)` | 返回欧几里德范数 `sqrt(x*x + y*y)`。 |\n", + "| `sin(x)` | 返回 `x` 弧度的正弦值。 |\n", + "| `tan(x)` | 返回 `x` 弧度的正切值。 |\n", + "| `degrees(x)` | 将弧度转换为角度,如 `degrees(math.pi/2)` 返回 `90.0`。 |\n", + "| `radians(x)` | 将角度转换为弧度。 |\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "range 函数:\n", + "Python3 range() 函数返回的是一个可迭代对象(类型是对象),而不是列表类型, 所以打印的时候不会打印列表。" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## operator模块" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## map函数\n", + "把“同一个函数”依次作用到“可迭代对象的每一个元素”上,生成一个新的迭代器。\n", + "map(function,iterable)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on class map in module builtins:\n", + "\n", + "class map(object)\n", + " | map(func, *iterables) --> map object\n", + " |\n", + " | Make an iterator that computes the function using arguments from\n", + " | each of the iterables. Stops when the shortest iterable is exhausted.\n", + " |\n", + " | Methods defined here:\n", + " |\n", + " | __getattribute__(self, name, /)\n", + " | Return getattr(self, name).\n", + " |\n", + " | __iter__(self, /)\n", + " | Implement iter(self).\n", + " |\n", + " | __next__(self, /)\n", + " | Implement next(self).\n", + " |\n", + " | __reduce__(...)\n", + " | Return state information for pickling.\n", + " |\n", + " | ----------------------------------------------------------------------\n", + " | Static methods defined here:\n", + " |\n", + " | __new__(*args, **kwargs)\n", + " | Create and return a new object. See help(type) for accurate signature.\n", + "\n" + ] + } + ], + "source": [ + "help(map)\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "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.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git "a/\345\255\246\344\271\240\347\254\224\350\256\260/Python\345\237\272\347\241\200\347\237\245\350\257\206.ipynb" "b/\345\255\246\344\271\240\347\254\224\350\256\260/Python\345\237\272\347\241\200\347\237\245\350\257\206.ipynb" new file mode 100644 index 000000000..7310c184d --- /dev/null +++ "b/\345\255\246\344\271\240\347\254\224\350\256\260/Python\345\237\272\347\241\200\347\237\245\350\257\206.ipynb" @@ -0,0 +1,3289 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "59f1ad7d", + "metadata": {}, + "source": [ + "> **记笔记的目的**: \n", + "> \n", + "> ★笔记不是“完整教程”,而是理解+易错点+示例+练习 \n", + "> \n", + "> 每一节:\n", + "> - “一句话总结(核心)”\n", + "> - “示例(能运行、最简单)”\n", + "> - “易错点(踩过的坑)”\n", + "> - “应用场景(什么时候用)”" + ] + }, + { + "cell_type": "markdown", + "id": "8a126022", + "metadata": {}, + "source": [ + "> 什么是“一句话总结”: \n", + "> - 它是什么\n", + "> - 为什么存在(解决什么问题?)\n", + "> - 什么时候用" + ] + }, + { + "cell_type": "markdown", + "id": "08c8aa7f", + "metadata": {}, + "source": [ + "> Python基础知识结构划分: \n", + "> - Python的特点\n", + "> - 环境搭建\n", + "> - 基础语法 \n", + "> - 变量 \n", + "> - 基本数据类型(数据结构) \n", + "> - 分支、循环结构\n", + "> - 函数和模块\n", + "> - 面向对象编程" + ] + }, + { + "cell_type": "markdown", + "id": "7b990156", + "metadata": {}, + "source": [ + "### Python的特点\n", + "1.大小写敏感。\n", + "2.对象(Object)、类(Class)、实例(Instance)三者的区别和联系\n", + "|名词|Python中的角色|举例|\n", + "|----|-----|----|\n", + "|对象(Object)|一切都是对象,包括数字、字符串、函数、类、模块|1、\"ABC\"、len、int|\n", + "|类(Class)|描述对象结构的模板,本质也是对象|int、list、dict|\n", + "|实例(Instance)|由类创建出来的对象|1 是 int 的实例|" + ] + }, + { + "cell_type": "markdown", + "id": "e43860bf", + "metadata": {}, + "source": [ + "### 环境搭建\n", + "为了能够在机器上运行Python,要处理的问题:\n", + "- 环境变量的问题 \n", + "- 二进制、八进制、十进制、十六进制\n", + "二进制的补码问题。\n", + "\n", + "- ASCII码、Unicode码\n", + "Python中所有的字符串都是Unicode字符串。\n", + "#### 命令行参数\n", + "交互模式中,最后被输出的表达式结果被赋值给变量_,例如:\n", + "```shell\n", + ">>> tax = 12.5 / 100\n", + ">>> price = 100.50\n", + ">>> price * tax\n", + "12.5625\n", + ">>> price + _\n", + "113.0625\n", + ">>> round(_, 2)\n", + "113.06\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "d9525ae6", + "metadata": {}, + "source": [ + "### Python基础语法\n", + "即为书写Python必须遵循的规则。\n", + "#### 标识符\n", + "标识符即为变量的命名,第一个字符必须以字母(a-z, A-Z)或下划线 _ ,其他部分由字母、数字和下划线组成。 \n", + "注意:标识符不能包含特殊字符(如 - ¥ $),不能使用数字开头,不能使用关键字。\n", + "##### 保留关键字\n", + "保留关键字在Python中有特定用处,不能作为标识符使用。\n", + "一旦用了,就会报错。\n", + "#### 注释\n", + "```\n", + "# 单行注释\n", + "# 第二个注释\n", + "\n", + "'''\n", + "第三行注释\n", + "第四行注释\n", + "'''\n", + "\n", + "\"\"\"\n", + "第五行注释\n", + "第六行注释\n", + "\"\"\"\n", + "```\n", + "#### 行与缩进\n", + "Python中用缩进表示代码块,而不是大括号`{}`。 \n", + "Python中一条语句很长,可以用反斜杠`\\`来实现换行。\n", + "```\n", + "total = item_one + \\\n", + " item_two + \\\n", + " item_three\n", + "``` \n", + "Python中同一行也可以有多个语句,用分号;分隔。 \n", + "在类或函数之间一般会留一个空行,便于日后的维护和重构。 \n", + "当写示例时,可以使用pass这个占位语句,表示什么都不做,以避免语法错误。\n", + "\n", + "#### 常用基本功能\n", + "1.接收用户用户输入,`str1=input()` \n", + "接收到的str1类型为string,还是`` \n", + "2.导入模块,用import与from...import \n", + "导入模块后,Python要知道使用的函数、类、变量来自哪个模块。 \n", + "`from...import`直接引入目标名称,调用更短,但是容易发生命名冲突。\n", + "`from...import *`会导入模块总的所有函数、变量,除了_开头的名字,但是会污染当前代码的命名空间。 \n", + "3.print格式化输出 \n", + "print()默认输出是带换行符的,即其中参数end默认值为'\\n'。\n", + "\n", + "#### 变量\n", + "1.Python中所有变量都是“名字→对象”的引用(变量存的是对象的地址)。 \n", + "变量没有类型,只有对象有类型。变量本质上等同于“自动管理内存的安全指针”。 \n", + "\n", + "2.一般说的变量类型指的是“变量当前指向的对象的类型”。\n", + "\n", + "3.衍生问题: \n", + "(1)如果变量存的是内存的地址,那么为什么print(变量)会返回对象的值? \n", + "print()调用的是变量所指向对象的`__str__()、__repr__()`方法,而不是打印地址。 \n", + "如果要打印地址,可以用print(id(a))返回变量a在内存中的地址id。 \n", + "(2)`a=1`和`a+1`中,变量a是地址,那为什么可以计算a+1? \n", + "先找到变量a指向的对象(1),调用该对象的__add__()方法(a.__add__(1))生成一个新的对象(2),然后把这个新的对象返回。原先的a和1都不会被修改。 \n", + "如果是`a=a+1`呢,这时候a引用的对象会变成2。 \n", + "(3)不可变对象(int,str,tuple)不能改,只能换对象; \n", + "可变对象(list,dict,set)能改,引用不变。 \n", + "不可变对象不允许修改内部值,修改只能创新新对象,旧对象如果无人引用则会被自动回收。 或者用del语句删除? \n", + "```python\n", + "a = 10 # int类型不可变\n", + "b = a\n", + "a = 20 # 修改a的值,不会影响b的值,10,20是两个独立的对象\n", + "# b 指的还是原先的整数10对象\n", + "\n", + "c = [1,2,3] # list类型可变\n", + "d = c\n", + "d.append(4) # 修改d的值,c的值也会变化,c和d是同个引用\n", + "```\n", + "(4)type(变量a)的时候,将变量a引用的对象作为参数传入内置函数type(),由type()查看该对象内部的`__class__属性`\n", + "\n", + "4.多变量赋值的问题:\n", + "`a=b=c=1`\n", + "`a,b,c=1,2,'runoob'`\n", + "\n", + "5.调换两个变量的值\n", + "`a,b=b,a`" + ] + }, + { + "cell_type": "markdown", + "id": "828e1f34", + "metadata": {}, + "source": [ + "### 数据类型\n", + "#### 数据类型简介\n", + "1.常见数据类型:\n", + "- Number(数字、数值):包含int、float、bool、complex(复数,例如4+3j)\n", + "- String(字符串)\n", + "- bool(布尔类型)\n", + "- List(列表)\n", + "- Tuple(元组)\n", + "- Set(集合)\n", + "- Dictionary(字典) \n", + "\n", + "2.数据类型分类可以按照是否可变,划分为: \n", + "不可变数据类型:Number、String、Tuple \n", + "可变数据类型:List、Dictionary、Set\n", + "\n", + "3.判断数据类型 \n", + "(1)type(obj)用来查对象的实际类型(class),例如`type(a)`,isinstance()用来判断对象(实例)是不是某个类型(或其子类)的实例,例如`isinstance(a,int)` \n", + "(2)区别:type()严格匹配类型,不考虑继承;isinstance()会考虑继承关系。 \n", + "```python\n", + "class A:pass # pass占位语句\n", + "class B(A):pass\n", + "\n", + "b = B()\n", + "print(type(b) is B) # True\n", + "print(type(b) is A) # False\n", + "print(isinstance(b,B)) # True\n", + "print(isinstance(b,A)) # True\n", + "print(issubclass(B,A)) # 判断类的继承关系,True\n", + "```\n", + "(3)特殊情况:\n", + "- `type(a) == int`和`type(a) is int`有什么区别? \n", + "`==` 是比较两个对象的“值”是否相等,不推荐,原因在==依赖类的`__eq__`,不是很严格; \n", + "`is` 是判断两个对象是否是“同一个对象”(id一致),推荐。\n", + "补充说明:int在Python中也是一个对象(类对象)\n", + "- `type(A)`返回的是啥?\n", + "返回type,类也是对象,Python中所有类的类型都是type\n", + "- `isinstance(B,A)`为啥是False?\n", + "`isinstance()`是用于判断实例是否属于某个类或者某个父类。\n", + "如果要判断两个类的继承关系,可以用`issubclass()`\n", + "\n", + "\n", + "#### 各个数据类型介绍与使用\n", + "##### 数字\n", + "1.数字注意事项:\n", + "数字是不可变类型。\n", + "\n", + "2.数字运算 \n", + "常规运算(加减乘除),除法或者混合运算时(包含浮点数和整数)运算结果一定为浮点数 \n", + "特殊运算:取余(%)、整除(//)、乘方(**) \n", + "`10%3`返回余数1 \n", + "`10//3`返回整数3,即10按照3来分,可以分成3个整份 \n", + "`10**3`返回1000 `4**0.5`返回2.0 \n", + "\n", + "3.运算符\n", + "注意事项:\n", + "(1)//取整是向小的方向取整数,例如9//2等于4,-9//2等于-5\n", + "(2):=海象运算符,用于在表达式中同时进行赋值,并返回赋值的值。Python3.8版本新增。\n", + "用法举例:\n", + "```python\n", + "#传统写法\n", + "n = 10\n", + "if n > 5:\n", + " print(n)\n", + "#使用海象运算符\n", + "if (n:=10) > 5: # 现把n赋值为10,再返回这个赋值结果\n", + " print(n)\n", + "```\n", + "(3)位运算符\n", + "位运算符把数字看做二进制来进行计算。\n", + "```python\n", + "a = 60\n", + "print(f'{a:08b}') # a的二进制数为 00111100\n", + "b = 13\n", + "print(f'{b:08b}') # b的二进制数为 00001101\n", + "\n", + "# & 按位与:两个对应位置都是1,则结果位为1,否则为0\n", + "print(a&b) #输出12 # a&b为 00001100\n", + "\n", + "# | 按位或:两个对应位置有一个是1,则结果位为1,否则为0\n", + "print(a|b) #输出61 # a|b为 00111101\n", + "\n", + "# ^ 按位异或:两个对应位置不同时,结果为1,否则为0\n", + "print(a^b) #输出49 # a|b为 00110001\n", + "\n", + "# ~ 按位取反:把0变成1,把1变成0\n", + "print(~a) #输出-61 # ~a为 -0111101?\n", + "\n", + "# << 左移动若干位\n", + "print(a<<2) #输出240,相当于放大了2的2次方倍 # <> 左移动若干位\n", + "print(a>>2) #输出15,相当于缩小了2的2次方倍 # <>`、`<<` | 右移、左移 |\n", + "| `&` | 按位与 |\n", + "| `^`、`\\|` | 按位异或、按位或 |\n", + "| `<=`、`<`、`>`、`>=` | 小于等于、小于、大于、大于等于 |\n", + "| `==`、`!=` | 等于、不等于 |\n", + "| `is`、`is not` | 身份运算符 |\n", + "| `in`、`not in` | 成员运算符 |\n", + "| `not`、`or`、`and` | 逻辑运算符 |\n", + "| `=`、`+=`、`-=`、`*=`、`/=`、`%=`、`//=`、`**=`、`&=`、`\\|=`、`^=`、`>>=`、`<<=` | 赋值运算符|\n", + "\n", + "补充说明:\n", + "not是小写。\n", + "逻辑运算符有阻断。\n", + "\n", + "2.数值不同表示形式 \n", + "(1)整数:十进制、二进制、八进制、十六进制 \n", + "```python\n", + "print(0o12) #八进制,0o或者0O\n", + "print(0b1010) #二进制,0b或者0B\n", + "print(0xA) #十六进制,0x或者0X\n", + "#十六进制的数A B C D E F 分别表示十进制中的 10 11 12 13 14 15\n", + "\n", + "# print()默认输出数值是转换为十进制显示。\n", + "print(oct(97)) #oct()十进制转为八进制字符串,输出0o141\n", + "print(hex(97)) #hex()十进制转为十六进制字符串,输出0x61\n", + "print(bin(97)) #bin()十进制转为二进制字符串,输出0b1100001\n", + "```\n", + "(2)浮点数:主要是显示格式(小数点后几位)的问题 \n", + "见字符串部分输出格式表 \n", + "(3)复数:复数由实数部分和虚数部分构成,可以用a + bj,或者complex(a,b) 表示,复数的实部a和虚部b都是浮点型。如`3e+26j` " + ] + }, + { + "cell_type": "markdown", + "id": "3fb23002", + "metadata": {}, + "source": [ + "##### 字符串\n", + "1.字符串表示相关问题 \n", + "(1)反斜杠`\\`转义特殊字符,可以在字符串前面加一个`r`表示原始字符。\n", + "| 转义符 | 描述 | 示例代码 | 输出结果 |\n", + "|--------|----------|---------------|------------|\n", + "| `\\` | 续行符 | `print(\"line1 \\\\\\nline2\")` | `line1 line2` |\n", + "| `\\\\` | 反斜杠 | `print(\"\\\\\")` | `\\` |\n", + "| `\\'` | 单引号 | `print('\\'')` | `'` |\n", + "| `\\\"` | 双引号 | `print(\"\\\"\")` | `\"` |\n", + "| `\\a` | 响铃 | `print(\"\\a\")` | 响声 |\n", + "| `\\b` | 退格 | `print(\"Hello \\bWorld!\")` | `HelloWorld!` |\n", + "| `\\000` | 空字符 | `print(\"\\000\")` | 空(什么都不输出) |\n", + "| `\\n` | 换行 | `print(\"\\n\")` | 换行 |\n", + "| `\\v` | 纵表符 | `print(\"Hello\\vWorld!\")` | `Hello` 换行 `World!` |\n", + "| `\\t` | 横表符 | `print(\"Hello\\tWorld!\")` | `Hello World!` |\n", + "| `\\r` | 回车,将 `\\r` 后的内容移到字符串开头,并逐一替换开头部分的字符| `print(\"Hello\\rWorld!\")` | `World!` |\n", + "| `\\f` | 换页 | `print(\"Hello\\fWorld!\")` | `Hello` 换页 `World!` |\n", + "| `\\yyy` | 八进制 | `print(\"\\110\\145\")` | `He` |\n", + "| `\\xYY` | 十六进制,\\x开头,YY代表的字符 | `print(\"\\x48\\x65\")` | `He` |\n", + "| `\\other` | 普通字符 | 无特殊处理 | 无特殊处理 |\n", + "\n", + "例子:用\\r实现百分比进度\n", + "```python\n", + "import time\n", + "\n", + "for i in range(101): # 添加进度条图形,例如[#### ] 和百分比,例如30%\n", + " bar = '[' + '#' * (i) + ' ' * (100-i) + ']' # 100个\n", + " print(f'\\r{bar} {i:3}%',end='',flush=True) # flush选项的作用\n", + " time.sleep(0.02)\n", + "print()\n", + "```\n", + "(2)字符串的索引和截取:\n", + "区域是左闭右开,从0开始,-1为从末尾开始的位置。 \n", + "```python\n", + "str = 'Runoob'\n", + "print(str[0:-1]) # 'Runoob'\n", + "print(str[0]) # 'R'\n", + "print(str[2:5]) # 'noo'\n", + "print(str[2:]) # 'noob'\n", + "print(str[:2]) # 'Ru'\n", + "print(str[::-1]) # 'boonuR',默认步长为1,步长为-1时会反转\n", + "print(str[-1::-1]) # 'boonuR',默认步长为1,截取内的参数[start,end,step],当start为-1,end为空时,默认从右到左\n", + "```\n", + "\n", + "(3)字符串的运算符\n", + "```python\n", + "print(str * 2) # 'RunoobRunoob',*实现重复\n", + "print(str+'abc') # 'Runoobabc',+实现多个字符串连接\n", + "```\n", + "\n", + "(4)格式化输出:\n", + "有几种格式化方法\n", + "(a)用字符串格式符,例如%s(格式化字符串)、%d(格式化整数)\n", + "`print (\"我叫 %s 今年 %d 岁!\" % ('小明', 10))`\n", + "(b)用str.format(),用{}和:来代替以前的%\n", + "`{1} {0} {1}\".format(\"hello\", \"world\") # 设置指定位置`\n", + "(c)f-string,字面量格式化字符串,Python3.6之后的版本新增的格式化字符串\n", + "f后面跟着字符串,字符串中的表达式用大括号{}抱起来,会将表达式的结算结果带进输出的字符串里面\n", + "| 变量值 | 占位符 | 格式化结果 | 说明 |\n", + "| ----------- | ---------- | ------------| ---- |\n", + "| `3.1415926` | `{:.2f}` | `'3.14'` | 保留小数点后两位 |\n", + "| `3.1415926` | `{:+.2f}` | `'+3.14'` | 带符号保留小数点后两位 |\n", + "| `-1` | `{:+.2f}` | `'-1.00'` | 带符号保留小数点后两位 |\n", + "| `3.1415926` | `{:.0f}` | `'3'` | 不带小数 |\n", + "| `123` | `{:0>10d}` | `'0000000123'` | 左边补`0`,补够10位 |\n", + "| `123` | `{:x<10d}` | `'123xxxxxxx'` | 右边补`x` ,补够10位 |\n", + "| `123` | `{:>10d}` | `' 123'` | 左边补空格,补够10位 |\n", + "| `123` | `{:<10d}` | `'123 '` | 右边补空格,补够10位 |\n", + "| `123456789` | `{:,}` | `'123,456,789'` | 逗号分隔格式 |\n", + "| `0.123` | `{:.2%}` | `'12.30%'` | 百分比格式 |\n", + "| `123456789` | `{:.2e}` | `'1.23e+08'` | 科学计数法格式 |\n", + "\n", + "^, <, > 分别是居中、左对齐、右对齐,后面带宽度, : 号后面带填充的字符,只能是一个字符,不指定则默认是用空格填充。+ 表示在正数前显示 +,负数前显示 -; (空格)表示在正数前加空格\n", + "b、d、o、x 分别是二进制、十进制、八进制、十六进制。\n", + "此外我们可以使用大括号 {} 来转义大括号。\n", + "\n", + "(5)字符串不是特殊的列表,也不是特殊的元组;它是 Python 里独立的一种不可变序列类型(str),专门为文本处理而优化。\n", + "\n", + "(6)字符串用三引号,无需转义特殊字符,同时可以保持字符格式,所见即所得\n", + "\n", + "2.字符串内置的方法\n", + "字符串连接:`+` 、`''.join()`\n", + "| 分类 | 方法 | 描述 | 示例代码 |\n", + "|--------|-------------------------------|--------------------------------------|------------------------------------|\n", + "| 格式化方法 | capitalize() | 将字符串的第一个字符转换为大写。 | `'abc'.capitalize()` |\n", + "| | center(width, fillchar) | 返回一个指定宽度 `width` 居中的字符串,`fillchar` 为填充字符,默认为空格。 | `'abc'.center(10, '-')` |\n", + "| | ljust(width, fillchar) | 返回一个左对齐的字符串,使用 `fillchar` 填充至长度 `width`。 | `'abc'.ljust(10, '-')` |\n", + "| | rjust(width, fillchar) | 返回一个右对齐的字符串,使用 `fillchar` 填充至长度 `width`。 | `'abc'.rjust(10, '-')` |\n", + "| | zfill(width) | 返回长度为 `width` 的字符串,原字符串右对齐,前面填充 `0`。 | `'abc'.zfill(5)` |\n", + "| | title() | 返回标题化的字符串,即每个单词首字母大写,其余字母小写。 | `'hello world'.title()` |\n", + "| 查找方法 | find(str, beg=0, end=len()) | 检测 `str` 是否包含在字符串中,返回索引值,若不存在返回 -1。 | `'abc'.find('b', 0, 3)` |\n", + "| | index(str, beg=0, end=len()) | 类似于 `find()`,但若 `str` 不存在则抛出异常。 | `'abc'.index('b', 0, 3)` |\n", + "| | rfind(str, beg, end) | 类似于 `find()`,但从右边开始查找。 | `'abcabc'.rfind('a', 0, 6)` |\n", + "| | rindex(str, beg, end) | 类似于 `index()`,但从右边开始查找。 | `'abcabc'.rindex('a', 0, 6)` |\n", + "| 检查方法 | isalnum() | 检查字符串是否只由字母和数字组成,若是则返回 `True`,否则返回 `False`。 | `'abc123'.isalnum()` |\n", + "| | isalpha() | 检查字符串是否只由字母组成,若是则返回 `True`,否则返回 `False`。 | `'abc'.isalpha()` |\n", + "| | isdigit() | 检查字符串是否只包含数字,若是则返回 `True`,否则返回 `False`。 | `'123'.isdigit()` |\n", + "| | islower() | 检查字符串是否全为小写字母,若是则返回 `True`,否则返回 `False`。 | `'abc'.islower()` |\n", + "| | isupper() | 检查字符串是否全为大写字母,若是则返回 `True`,否则返回 `False`。 | `'ABC'.isupper()` |\n", + "| | isnumeric() | 检查字符串是否只包含数字字符,若是则返回 `True`,否则返回 `False`。 | `'123'.isnumeric()` |\n", + "| | isspace() | 检查字符串是否只包含空白字符,若是则返回 `True`,否则返回 `False`。 | `' '.isspace()` |\n", + "| | istitle() | 检查字符串是否是标题化的,若是则返回 `True`,否则返回 `False`。 | `'Hello World'.istitle()` |\n", + "| 替换方法 | replace(old, new, max) | 将字符串中的 `old` 替换为 `new`,若指定 `max` 则替换不超过 `max` 次。| `'abcabc'.replace('a', 'x', 1)`|\n", + "| 分割方法 | split(str, num) | 以 `str` 为分隔符截取字符串,若指定 `num`,则仅截取 `num+1` 个子字符串。 | `'a,b,c'.split(',')` |\n", + "| | splitlines(keepends) | 按行分隔字符串,若 `keepends=True`,则保留换行符。 | `'a\\\\nb'.splitlines()` |\n", + "| 去空格方法 | strip(chars) | 截掉字符串两端的空格或指定字符。 | `' abc '.strip()` |\n", + "| | lstrip(chars) | 截掉字符串左边的空格或指定字符。 | `' abc'.lstrip()` |\n", + "| | rstrip(chars) | 截掉字符串右边的空格或指定字符。 | `'abc '.rstrip()` |\n", + "| 连接方法 | join(seq) | 以指定字符串作为分隔符,将 `seq` 中的元素合并为一个新的字符串。 | `','.join(['a', 'b', 'c'])` |\n", + "| 大小写方法 | lower() | 将字符串中的所有大写字符转换为小写。 | `'ABC'.lower()` |\n", + "| | upper() | 将字符串中的所有小写字母转换为大写。 | `'abc'.upper()` |\n", + "| | swapcase() | 将字符串中的大写转换为小写,小写转换为大写。 | `'AbC'.swapcase()` |\n", + "| 映射方法 | maketrans(x, y) | 创建字符映射表,`x` 表示需要转换的字符,`y` 表示转换的目标字符。 | `str.maketrans('a', 'b')` |\n", + "| | translate(table, deletechars) | 根据 `table` 转换字符串的字符,`deletechars` 指定要过滤掉的字符。 | `'abc'.translate({97: 98})` |\n", + "| 其他方法 | len(string) | 返回字符串的长度。 | `len('abc')` |\n", + "| | expandtabs(tabsize=8) | 将字符串中的 tab 符号替换为空格,`tabsize` 指定空格数,默认为 8。 | `'\\tabc'.expandtabs(4)` |\n", + "| | max(str) | 返回字符串中最大的字符。 | `max('abc')` |\n", + "| | min(str) | 返回字符串中最小的字符。 | `min('abc')` |\n", + "s.strip() 会去掉字符串两端所有“空白字符”(whitespace),包括:\n", + "空格 ' ' 制表符 '\\t' 换行 '\\n' 回车 '\\r' 以及其他空白字符\n", + "3.字符串中的常量\n", + "`string.ascii_letters`:包含所有英文字母(大小写)。\n", + "`string.digits`:包含所有数字字符(0-9)。\n", + "`string.punctuation`:包含所有标点符号。" + ] + }, + { + "cell_type": "markdown", + "id": "4d473164", + "metadata": {}, + "source": [ + "##### 列表([])\n", + "1.列表也可以被索引和截取,截取后返回一个新列表,也可以用+、*运算。\n", + "2.列表中的元素可以改变,字符串中的元素不可以改变。\n", + "3.字符串和元组都属于序列(Sequence)类型,如下图所示:\n", + "Python有6个序列的内置类型,最常见的是列表和元组。\n", + "```python\n", + "object\n", + " └── collections.abc.Iterable\n", + " └── collections.abc.Sequence\n", + " ├── list(可变序列)\n", + " ├── tuple(不可变序列)\n", + " └── str(不可变序列,但元素必须是字符)\n", + "\n", + "```\n", + "4.列表内的元素不需要具有相同的类型。\n", + "5.列表支持嵌套\n", + "5.列表内置的方法\n", + "列表的数据可以修改或更新:\n", + "新增:append()方法可以添加列表项;\n", + "删除:del语句可以删除列表中的指定元素,按照索引制定。\n", + "pop()\n", + "6.列表脚本运算符\n", + "和字符串一样,+ * in \n", + "\n", + "7.列表的函数\n", + "len()列表元素个数\n", + "max(list)、min(list)返回列表元素最大值、最小值\n", + "list(seq)将序列转化为列表\n", + "seq -- 元素列表,可以是列表、元组、集合、字典,若为字典,则仅会将键(key)作为元素依次添加至原列表的末尾。\n", + "\n", + "8.列表的方法\n", + "| 序号 | 语法 | 功能解释 |\n", + "|------|-----------------------------|---------------------------------------------|\n", + "| 1 | `list.append(obj)` | 在列表末尾添加新的对象 |\n", + "| 2 | `list.count(obj)` | 统计某个元素在列表中出现的次数 |\n", + "| 3 | `list.extend(seq)` | 在列表末尾一次性追加另一个序列中的多个值 |\n", + "| 4 | `list.index(obj)` | 从列表中找出某个值第一个匹配项的索引位置 |\n", + "| 5 | `list.insert(index, obj)` | 将对象插入列表,index是要插入对象的索引位置|\n", + "| 6 | `list.pop([index=-1])` | 移除列表中的一个元素(默认最后一个),并返回该元素 |\n", + "| 7 | `list.remove(obj)` | 移除列表中某个值的第一个匹配项 |\n", + "| 8 | `list.reverse()` | 反向列表中元素 |\n", + "| 9 | `list.sort(key=None, reverse=False)` | 对原列表进行排序 |\n", + "| 10 | `list.clear()` | 清空列表,类似于del a[:] |\n", + "| 11 | `list.copy()` | 复制列表,并返回复制后的新列表 |\n", + "\n", + "`list.sort(key=None, reverse=False)`单独解释:\n", + "(1)默认排序规则:默认升序(从小到大)`reverse=False`,`reverse=True`的时候是降序(从大到小)。字母以及特殊字符的降序规则基于字符的 ASCII/Unicode 值。例如升序情况下,a(ASCII值为97)大于A(ASCII值为65),A排在a之前\n", + "(2)key是一个函数,该函数会作用于可迭代对象的每个元素,返回一个值,再以这个值对可迭代对象中的元素进行排序。\n", + "按字符串长度排序:key=len\n", + "按绝对值排序:key=abs\n", + "忽略大小写排序:key=str.lower\n", + "按元组的某个元素排序:key=lambda x: x[1]\n", + "\n", + "9.列表的函数和方法的区别\n", + "| 区别 | 列表的函数 | 列表的方法 |\n", + "|--------|---------------------|--------------------------------|\n", + "| 定义 | 全局函数,作用于列表或其他可迭代对象。通常为Python的内置函数 | 列表对象的成员函数,仅作用于列表。 |\n", + "| 调用方式 | `函数(列表)` | `列表.方法()` |\n", + "| 作用范围 | 可以作用于其他可迭代对象(如元组、集合)。 | 仅适用于列表。 |\n", + "| 示例 | `len(lst)`、`max(lst)` | `lst.append(4)`、`lst.sort()` |\n", + "\n", + "10.嵌套列表\n", + "```python\n", + "matrix = [ # 3X4的矩阵列表\n", + "[1, 2, 3, 4],\n", + "[5, 6, 7, 8],\n", + "[9, 10, 11, 12],\n", + "]\n", + "# 3X4 转为 4X3:\n", + "m2 = [[row(i) for row in matrix] for i in range(4)]\n", + "# 或者\n", + "transposed = []\n", + "for i in range(4):\n", + " transpoed.append([row[i] for row in matrix]) \n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d37fae74", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]\n", + "[1, 2, 3, 4]\n", + "[5, 6, 7, 8]\n", + "[9, 10, 11, 12]\n", + "[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]\n" + ] + } + ], + "source": [ + "matrix = [\n", + "[1, 2, 3, 4],\n", + "[5, 6, 7, 8],\n", + "[9, 10, 11, 12],\n", + "]\n", + "print(matrix)\n", + "for row in matrix:\n", + " print(row)\n", + "m2 = [[row[i] for row in matrix] for i in range(4)]\n", + "print(m2)" + ] + }, + { + "cell_type": "markdown", + "id": "6663e0af", + "metadata": {}, + "source": [ + "##### 元组(())\n", + "1.元组不能修改(修改删除个别元素),元组内的元素类型可以不同。元组支持切片和索引。\n", + "2.元组可以包含list列表这种可变的对象。\n", + "3.特殊表示\n", + "`tup1 = () # 空元组`\n", + "`tup2 = (20,) # 一个元素,需要在元素后添加逗号,否则就会成为数学运算中的括号`\n", + "4.元组支持 + += * in 迭代\n", + "5.元组的内置函数与list基本相同,除了 tuple(iterable),该函数可将可迭代系列转换为元组" + ] + }, + { + "cell_type": "markdown", + "id": "2597e067", + "metadata": {}, + "source": [ + "##### 字典({})\n", + "1.列表是有序的对象集合,字典是无序的对象集合。两者之间的区别在于:字典当中的元素是通过键来存取的(所以键必须唯一),而不是通过偏移存取。\n", + "2.字典是一种映射类型,是一个无序的键(唯一key) : 值(value) 的集合。\n", + "```python\n", + "dict1 = {}\n", + "dict1['one'] = \"1 - 菜鸟教程\"\n", + "dict1[2] = \"2 - 菜鸟工具\"\n", + "tinydict = {'name': 'runoob','code':1, 'site': 'www.runoob.com'}\n", + "\n", + "print (dict['one']) # 输出键为 'one' 的值,'1 - 菜鸟教程'\n", + "print (dict[2]) # 输出键为 2 的值,'2 - 菜鸟工具'\n", + "print (tinydict) # 输出完整的字典,'{'name': 'runoob', 'code': 1, 'site': 'www.runoob.com'}'\n", + "print (tinydict.keys()) # 输出所有键,'dict_keys(['name', 'code', 'site'])'\n", + "print (tinydict.values()) # 输出所有值,'dict_values(['runoob', 1, 'www.runoob.com'])'\n", + "```\n", + "3.字典的构造函数dict()\n", + "```python\n", + "dict([('Runoob', 1), ('Google', 2), ('Taobao', 3)]) # 列表形式\n", + "{'Runoob': 1, 'Google': 2, 'Taobao': 3}\n", + ">>> {x: x**2 for x in (2, 4, 6)} # 字典推导式形式\n", + "{2: 4, 4: 16, 6: 36}\n", + ">>> dict(Runoob=1, Google=2, Taobao=3) # a=1形式\n", + "{'Runoob': 1, 'Google': 2, 'Taobao': 3}\n", + "```\n", + "4.字典内置函数?内置方法?\n", + "len(dict1) 返回字典的键值对个数\n", + "str(dict1) 返回字典的字符串表示\n", + "内置方法:\n", + "| 序号 | 语法 | 功能解释 |\n", + "|------|-------------------------------|--------------------------------------------------------------------------|\n", + "| 1 | `dict.clear()` | 删除字典内所有元素,删除后字典对象还在 |\n", + "| 2 | `dict.copy()` | 返回一个字典的浅复制 |\n", + "| 3 | `dict.fromkeys(seq, val)` | 创建一个新字典,以序列 `seq` 中元素做字典的键,`val` 为字典所有键对应的初始值 |\n", + "| 4 | `dict.get(key, default=None)` | 返回指定键的值,如果键不在字典中返回 `default` 设置的默认值 |\n", + "| 5 | `key in dict` | 如果键在字典 `dict` 里返回 `True`,否则返回 `False` |\n", + "| 6 | `dict.items()` | 以列表返回一个视图对象,包含字典的键值对 |\n", + "| 7 | `dict.keys()` | 返回一个视图对象,包含字典的所有键 |\n", + "| 8 | `dict.setdefault(key, default=None)` | 和 `get()` 类似,但如果键不存在于字典中,将会添加键并将值设为 `default` |\n", + "| 9 | `dict.update(dict2)` | 把字典 `dict2` 的键/值对更新到 `dict` 里 |\n", + "| 10 | `dict.values()` | 返回一个视图对象,包含字典的所有值 |\n", + "| 11 | `dict.pop(key[, default])` | 删除字典 `key` 所对应的值,返回被删除的值 |\n", + "| 12 | `dict.popitem()` | 返回并删除字典中的最后一对键和值 |\n", + "\n", + "dict.items()、dict.keys()、dict.values()返回的是一个视图对象,是什么意思?视图就是给个窗口看到当前字典的内容\n", + "好像生成的是一个元组?\n", + "\n", + "5.字典的key是什么类型?\n", + "key必须可哈希(不可变类型,如str、int、tuple)\n", + "6.删除字典元素\n", + "del tinydict['key1'] 删除指定的键值对\n", + "tinydict.clear() 清空字典\n", + "del tinydict 删除字典\n", + "\n", + "7.遍历技巧\n", + "(1)字典的关键字和对应值,可以使用items()方法同时解读出来\n", + "`for k,v in dict1.items()`\n", + "(2)序列中遍历时,索引位置和对应值可以用enumerate()函数同时得到\n", + "`for i,v in enumerate(list1)`,`print(i,v)`i,v分别是索引位置和对应值\n", + "(3)同时遍历多个序列,可以用zip组合\n", + "for q,a in zip(list1,list2)\n", + "(4)反向遍历一个序列,先制定这个序列,然后调用reversed()函数\n", + "`for i in reversed(list)`\n", + "要顺序遍历一个序列,可以用sorted,sorted(list),不会修改原值" + ] + }, + { + "cell_type": "markdown", + "id": "58078555", + "metadata": {}, + "source": [ + "##### 集合({})\n", + "1.集合(Set)是一种无序、可变的数据类型,用于存储唯一的元素。\n", + "2.集合可以进行集合操作(交集、并集、差集)\n", + "3.创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。\n", + "```python\n", + "sites = {'Google', 'Taobao', 'Runoob', 'Facebook', 'Zhihu', 'Baidu'}\n", + "# 也可以用site()创建 sites = srt(('Google', 'Taobao', 'Runoob', 'Facebook', 'Zhihu', 'Baidu'))\n", + "print(sites) # 输出集合,重复的元素被自动去掉\n", + "\n", + "# 成员测试\n", + "if 'Runoob' in sites :\n", + " print('Runoob 在集合中')\n", + "else :\n", + " print('Runoob 不在集合中')\n", + "\n", + "# set可以将其他类型转换为集合\n", + "a = set('abracadabra')\n", + "b = set('alacazam')\n", + "\n", + "print(a) # 重复元素会被去掉,{'b', 'c', 'r', 'a', 'd'}\n", + "print(a - b) # a 和 b 的差集,{'r', 'b', 'd'}\n", + "print(a | b) # a 和 b 的并集,{'b', 'c', 'a', 'z', 'm', 'r', 'l', 'd'}\n", + "print(a & b) # a 和 b 的交集,{'c', 'a'}\n", + "print(a ^ b) # a 和 b 中不同时存在的元素,{'z', 'b', 'm', 'r', 'l', 'd'}\n", + "```\n", + "4.集合的基本操作\n", + "\n", + "| 方法 | 描述 |\n", + "|-------------------------------|----------------------------------------------------------------------|\n", + "| `add()` | 为集合添加元素 |\n", + "| `clear()` | 移除集合中的所有元素 |\n", + "| `copy()` | 拷贝一个集合 |\n", + "| `difference()` | 返回多个集合的差集,例如x.difference(y),返回在x中,不在y中的元素 |\n", + "| `difference_update()` | 移除两个集合中都存在的元素,没有返回值 |\n", + "| `discard()` | 删除集合中指定的元素 |\n", + "| `intersection()` | 返回集合的交集,两个集合都有的元素 |\n", + "| `intersection_update()` | 返回集合的交集,并更新当前集合 |\n", + "| `isdisjoint()` | 判断两个集合是否包含相同的元素,如果没有返回 `True`,否则返回 `False` |\n", + "| `issubset()` | 判断是否为子集,x.issubset(y),x中的所有元素是否都在y中|\n", + "| `issuperset()` | 判断是否为父集,x.issuperset(y),y中的所有元素都在x中 |\n", + "| `pop()` | 随机移除元素 |\n", + "| `remove()` | 移除指定元素 |\n", + "| `symmetric_difference()` | 返回两个集合中不重复的元素集合 |\n", + "| `symmetric_difference_update()` | 移除当前集合中在另外一个指定集合相同的元素,并将另外一个指定集合中不同的元素插入到当前集合中 |\n", + "| `union()` | 返回两个集合的并集 |\n", + "| `update()` | 给集合添加元素 |\n", + "| `len()` | 计算集合元素个数 |" + ] + }, + { + "cell_type": "markdown", + "id": "537eaaee", + "metadata": {}, + "source": [ + "#### 推导式\n", + "推导式可以从一个数据序列构建另一个数据序列的结构体,用于生成列表、元组、字典、集合和生成器。\n", + "\n", + "1.列表推导式\n", + "[表达式 for 变量 in 列表 if 条件]\n", + "`[out_exp_res for out_exp in input_list if condition]`\n", + "out_exp_res:列表生成元素表达式,可以是有返回值的函数。\n", + "for out_exp in input_list:迭代 input_list 将 out_exp 传入到 out_exp_res 表达式中。\n", + "if condition:条件语句,可以过滤列表中不符合条件的值。\n", + "\n", + "集合推导式、元组推导式与列表推导式基本相同。\n", + "元组推导式返回的结果是一个生成器对象,要用tuple()函数转换为元组。\n", + "\n", + "也可以判断两个条件,结果2选1:\n", + "结果值1 if 判断条件 else 结果2 for 变量名 in 原列表\n", + "```python\n", + "list1 = ['python', 'test1', 'test2']\n", + "list2 = [word.title() if word.startswith('p') else word.upper() for word in list1]\n", + "print(list2) # 输出['Python', 'TEST1', 'TEST2']\n", + "```\n", + "\n", + "2.字典推导式\n", + "`{key_expr: value_expr for value in collection if condition}`\n", + "key_expr:每次迭代,生成一个字典的键\n", + "value_expr:每次迭代,生成一个字典的值\n", + "collection:可迭代对象\n", + "\n", + "如果字典的键和值分别来自两个可迭代对象,可以使用 zip() 函数(将两个可迭代对象一一配对,生成键值对)结合字典推导式来生成字典:\n", + "`{key: value for key, value in zip(keys_iterable, values_iterable)}`\n", + "备注:两个可迭代对象长度不同,zip()会以较短的可迭代对象为准。" + ] + }, + { + "cell_type": "markdown", + "id": "f72a7ee2", + "metadata": {}, + "source": [ + "#### 迭代器\n", + "1.迭代器是一个可以记住遍历的位置的对象。\n", + "2.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。详细解释:\n", + "(1)迭代器是一个惰性对象,它不会一次性生成所有元素,而是按需生成。\n", + "(2)每次调用 next() 或在 for 循环中迭代时,迭代器会返回下一个元素,并将内部指针移动到下一个位置。\n", + "(3)一旦某个元素被访问过,迭代器的指针就不会再回到之前的位置。\n", + "\n", + "3.迭代器有两个基本的方法:iter() 和 next()。\n", + "\n", + "4.字符串,列表或元组对象都可用于创建迭代器:\n", + "```python\n", + "list1 = [1, 2, 3, 4]\n", + "it = iter(list1) # 创建迭代器对象\n", + "print(type(it)) # 输出: \n", + "\n", + "print(it) # 输出迭代器对象的内存地址\n", + "print(next(it)) # 输出: 1,指针移动到下一个元素\n", + "print(next(it)) # 输出: 2,指针移动到下一个元素\n", + "print(next(it)) # 输出: 3,指针移动到下一个元素\n", + "\n", + "for x in it: # 从当前指针位置开始迭代\n", + " print('迭代:', x) # 输出: 迭代: 4\n", + "```\n", + "\n", + "5.把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__() 与 __next__() 。\n", + "\n", + "6.StopIteration用于标识迭代的完成。\n" + ] + }, + { + "cell_type": "markdown", + "id": "0be4e849", + "metadata": {}, + "source": [ + "#### 生成器\n", + "使用了yield的函数称为生成器。\n" + ] + }, + { + "cell_type": "markdown", + "id": "cbf50712", + "metadata": {}, + "source": [ + "#### 函数\n", + "1.python 函数的参数传递:\n", + "不可变类型:类似 C++ 的值传递,如整数、字符串、元组。如 fun(a),传递的只是 a 的值,没有影响 a 对象本身。如果在 fun(a) 内部修改 a 的值,则是新生成一个 a 的对象。\n", + "可变类型:类似 C++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后 fun 外部的 la 也会受影响\n", + "\n", + "2.函数的参数类型:\n", + "必需参数:\n", + "`def printme(str):pass` 必须以正确的顺序,传入相应类型、数量的参数\n", + "关键字参数:\n", + "`def printme(age,name):pass`,可以通过`printme(name='Allen',age=16)`,不按照指定顺序来调用 \n", + "默认参数:\n", + "`def printme(name,age = 35):pass`,35是age参数的默认值,没有传age参数就用默认值\n", + "不定长参数:\n", + "`def printinfo(arg1, *vartuple)`,*vartuple,arg1之外的参数,可以用元组的形式导入,存在所有未命名的变量参数\n", + "参数带一个*是元组,两个**是字典,例如`def printinfo(arg1, **vardict)`调用时,用`printinfo(arg1,a=1,b=2)`\n", + "*的其他作用,单独出现星号时,其后的参数必须用关键字传入,如`def f(a,b,*,c)`里面的c,必须用关键字c=1传入\n", + "Python3.8的强制位置参数,`def f(a, b, /, c, d, *, e, f)`,形参 a 和 b 必须使用指定位置参数,c 或 d 可以是位置形参或关键字形参,而 e 和 f 要求为关键字形参。\n", + "\n", + "3.匿名函数\n", + "匿名的含义:不再使用def标准形式来定义一个函数。匿名的另一个意思是,lambda函数没有函数名称,只能通过赋值给变量或参数。\n", + "用lambda表达式来创建匿名函数,语法为:`lambda [arg1 [,arg2,.....argn]]:expression`,`expression`用于计算并返回函数的结果\n", + "匿名函数可以封装在一个函数内,可以使用同样的代码来创建多个匿名函数。\n", + "lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数?lambda里面的x的范围仅限于lambda语句本身。\n", + "\n", + "lambda函数通常与map()、filter()、reduce()一起使用,便于在集合上操作,例如:\n", + "把列表的元素翻倍:`list(map(lambda x:x*2,list1))`\n", + "筛选列表中的偶数:`list(filter(lambda x:x%2==0,list1))`,filter筛选,返回True的元素才留下\n", + "计算一个序列的累积乘积:`reduce(lambda x,y:x*y,list1)`,reduce合并(先算1、2,然后用1和2的合并结果与3计算,以此类推),先导入functools模块才可以用。\n", + "\n", + "4.return语句\n", + "return表达式用于退出函数,return不带参数值,调用函数会返回None。" + ] + }, + { + "cell_type": "markdown", + "id": "387f8138", + "metadata": {}, + "source": [ + "#### 装饰器\n", + "含义:装饰器用于动态地修改函数或类的行为,一般用于日志记录、性能分析、权限控制、缓存\n", + "基本语法:\n", + "```python\n", + "# 装饰器本质上是一个接收函数作为输入并返回一个新的包装过后的函数的对象。\n", + "def decorator_function(original_function):\n", + " def wrapper(*args, **kwargs):\n", + " \n", + " before_call_code() # 这里是在调用原始函数前添加的新功能\n", + " \n", + " result = original_function(*args, **kwargs)\n", + " \n", + " after_call_code()# 这里是在调用原始函数后添加的新功能\n", + " \n", + " return result\n", + " return wrapper\n", + "\n", + "# 使用装饰器\n", + "@decorator_function\n", + "def target_function(arg1, arg2):\n", + " pass # 原始函数的实现\n", + "\n", + "'''\n", + "等价于 target_function = time_logger(target_function)\n", + "'''\n", + "```\n", + "解释:使用装饰器时,会将目标函数作为参数传递给装饰器函数,装饰器函数除了会原原本本输出原始函数的原有输出,还会在调用原始函数前、后增加一些功能。\n", + "其中的wrapper函数,是实际会被调用的函数,既包含了原始函数的调用,也在后面增加了额外的行为。\n", + "\n", + "内置装饰器:\n", + "@staticmethod: 将方法定义为静态方法,不需要实例化类即可调用。\n", + "(这个方法/函数和这个类有关,所以就放在里面,而且不用实例化具体对象后再用,方便管理)\n", + "Python默认放在类里面的函数,都是给“对象”用的!\n", + "\n", + "@classmethod: 将方法定义为类方法,第一个参数是类本身(通常命名为 cls)。即站爱\n", + "(当我想操作整个类,而不是某个对象时)\n", + "\n", + "@property: 将方法转换为属性,使其可以像属性一样访问。就是不用加括号了。\n", + "\n", + "##### 类装饰器(后续再看)\n", + "可以动态修改类的行为。它接收一个类作为参数,并返回一个新的类或修改后的类。" + ] + }, + { + "cell_type": "markdown", + "id": "052f3e5c", + "metadata": {}, + "source": [ + "#### Python数据结构\n", + "\n", + "1.栈\n", + "(1)栈是一种后进先出(LIFO, Last-In-First-Out)数据结构,意味着最后添加的元素最先被移除。\n", + "(2)可以用列表来实现栈的功能。\n", + "(3)栈操作:\n", + "压入(Push): 将一个元素添加到栈的顶端。list1.append(x)\n", + "弹出(Pop): 移除并返回栈顶元素。list1.pop()\n", + "查看栈顶元素(Peek/Top): 返回栈顶元素而不移除它。list1[-1]\n", + "检查是否为空(IsEmpty): 检查栈是否为空。判断len(list1)==0\n", + "获取栈的大小(Size): 获取栈中元素的数量。len(list1)\n", + "\n", + "2.队列\n", + "队列是一种先进先出(FIFO, First-In-First-Out)的数据结构,意味着最早添加的元素最先被移除。\n", + "使用列表时,如果频繁地在列表的开头插入或删除元素,性能会受到影响,因为这些操作的时间复杂度是 O(n)。为了解决这个问题,Python 提供了 collections.deque,它是双端队列,可以在两端高效地添加和删除元素。\n", + "```python\n", + "from collections import deque\n", + "\n", + "# 创建一个空队列\n", + "queue = deque()\n", + "\n", + "# 向队尾添加元素\n", + "queue.append('a')\n", + "queue.append('b')\n", + "queue.append('c')\n", + "\n", + "print(\"队列状态:\", queue) # 输出: 队列状态: deque(['a', 'b', 'c'])\n", + "\n", + "# 从队首移除元素\n", + "first_element = queue.popleft()\n", + "print(\"移除的元素:\", first_element) # 输出: 移除的元素: a\n", + "print(\"队列状态:\", queue) # 输出: 队列状态: deque(['b', 'c'])\n", + "\n", + "# 查看队首元素(不移除)\n", + "front_element = queue[0]\n", + "print(\"队首元素:\", front_element) # 输出: 队首元素: b\n", + "\n", + "# 检查队列是否为空\n", + "is_empty = len(queue) == 0\n", + "print(\"队列是否为空:\", is_empty) # 输出: 队列是否为空: False\n", + "\n", + "# 获取队列大小\n", + "size = len(queue)\n", + "print(\"队列大小:\", size) # 输出: 队列大小: 2\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "9f7a2be8", + "metadata": {}, + "source": [ + "#### 模块\n", + "1.模块用于长久保存方法及变量。\n", + "2.模块可以用于代码复用、命名空间管理、重新组织代码(使程序结构清晰)\n", + "\n", + "3.`import`用于引入模块:\n", + "一个模块只会被导入一次。\n", + "可以设置模块的别名,`import modname as md`\n", + "import语句执行时,Python的查找路径为sys.path,即为:\n", + "(1)当前目录。\n", + "(2)环境变量 PYTHONPATH 指定的目录。\n", + "(3)Python 标准库目录。\n", + "(4).pth 文件中指定的目录。\n", + "\n", + "4.模块里面除了方法定义,还可以包括可执行代码。\n", + "可执行代码在引入模块的时候(仅第一次模块被导入时)会执行。\n", + "\n", + "5.每个某块都有自己的符号表(即命名空间),即为模块内的函数、变量只属于这个模块,不会影响外面的世界。\n", + "\n", + "6.__name__属性:一个模块被另一个程序第一次引入时,其主程序(指的是顶层代码,即未被函数或类封装的代码)将运行。\n", + "如果不想模块内的主程序在模块被引入时自动执行,可以在模块内的主程序设置条件:`if __name__ == '__main__':主程序代码`\n", + "原理为:每个模块都有一个 __name__ 属性。\n", + "如果模块是被直接运行,__name__ 的值为 __main__。\n", + "如果模块是被导入的,__name__ 的值为模块名。\n", + "\n", + "7.dir()函数\n", + "内置函数,可以找到对应模块所有的名称,以字符串列表形式返回。\n", + "\n", + "8.包\n", + "包可以理解为包含很多某块的文件夹,目的是减少模块间的命名冲突,方便代码管理和后续维护。\n", + "导入包时,Python会根据sys.path中的目录来寻找这个包中包含的子目录。目录只有包含一个叫做 __init__.py 的文件才会被认作是一个包。\n", + "如果包定义文件 __init__.py 存在一个叫做 __all__ 的列表变量,那么在使用 from package import * 的时候就把这个列表中的所有名字作为包内容导入。\n", + "使用 from Package import specific_submodule 这种方法永远不会有错。" + ] + }, + { + "cell_type": "markdown", + "id": "31e01658", + "metadata": {}, + "source": [ + "#### 标准模块\n", + "1.模块(module)、包(package)、库(library)\n", + "模块(module)∈包(package)∈库(library)\n", + "模块一般指单个.py文件;包指的是多个.py文件组成的文件夹,一般包含__init__.py文件;库指的是为某类功能提供的一整套可复用代码集合,例如requests(HTTP库)和pandas(数据分析库)等\n", + "2.标准库指的是按照python时自带的,不用pip install,直接import就可以用。\n", + "3.常见标准模块\n", + "| 模块名 | 功能描述 | 常用函数/变量 | 举例说明 | 用法说明 |\n", + "|--------------|-----------------------------------|----------------------------------------|---------------------------------------|---------------------------------------|\n", + "| `math` | 数学运算(如平方根、三角函数等) | `sqrt`, `sin`, `cos`, `pi`, `factorial`| `import math; print(math.sqrt(16))` | `sqrt`: 计算平方根;`sin`, `cos`: 计算正弦、余弦;`pi`: 圆周率;`factorial`: 计算阶乘 |\n", + "| `os` | 操作系统相关功能(如文件、目录操作)| `listdir`, `mkdir`, `remove`, `getcwd` | `import os; print(os.getcwd())` | `listdir`: 列出目录内容;`mkdir`: 创建目录;`remove`: 删除文件;`getcwd`: 获取当前工作目录 |\n", + "| `sys` | 系统相关的参数和函数 | `argv`, `path`, `exit`, `platform` | `import sys; print(sys.platform)` | `argv`: 命令行参数列表;`path`: 模块搜索路径;`exit`: 退出程序;`platform`: 获取操作系统名称 |\n", + "| `random` | 生成随机数 | `randint`, `choice`, `shuffle`, `random`| `import random; print(random.randint(1, 10))` | `randint`: 生成指定范围内的随机整数;`choice`: 随机选择一个元素;`shuffle`: 打乱序列;`random`: 生成0到1之间的随机浮点数 |\n", + "| `datetime` | 处理日期和时间 | `datetime`, `date`, `timedelta`, `now` | `from datetime import datetime; print(datetime.now())` | `datetime`: 日期时间类;`date`: 日期类;`timedelta`: 时间差类;`now`: 获取当前时间 |\n", + "| `json` | 处理 JSON 数据 | `load`, `loads`, `dump`, `dumps` | `import json; print(json.dumps({'a': 1}))` | `load`: 从文件加载JSON;`loads`: 从字符串加载JSON;`dump`: 将JSON写入文件;`dumps`: 将对象转换为JSON字符串 |\n", + "| `re` | 正则表达式操作 | `match`, `search`, `findall`, `sub` | `import re; print(re.findall(r'\\\\d+', 'abc123'))` | `match`: 从字符串开头匹配;`search`: 搜索匹配;`findall`: 查找所有匹配;`sub`: 替换匹配内容 |\n", + "| `collections`| 提供额外的数据结构(如 defaultdict、deque)| `Counter`, `defaultdict`, `deque`, `OrderedDict` | `from collections import Counter; print(Counter('abcabc'))` | `Counter`: 统计元素出现次数;`defaultdict`: 带默认值的字典;`deque`: 双端队列;`OrderedDict`: 有序字典 |\n", + "| `itertools` | 提供迭代器工具 | `count`, `cycle`, `permutations`, `combinations` | `import itertools; print(list(itertools.permutations('abc', 2)))` | `count`: 无限计数器;`cycle`: 无限循环迭代;`permutations`: 排列;`combinations`: 组合 |\n", + "| `functools` | 高阶函数工具(如 reduce、lru_cache)| `reduce`, `lru_cache`, `partial`, `wraps` | `from functools import reduce; print(reduce(lambda x, y: x + y, [1, 2, 3]))` | `reduce`: 累积计算;`lru_cache`: 缓存函数结果;`partial`: 创建偏函数;`wraps`: 保留原函数元信息 |\n", + "\n", + "4.标准模块的使用举例(业务需求出发)\n", + "(1)创建\\查找文件\n", + "os.getcwd() 获取当前工作目录\n", + "os.listdif(指定目录) 列出目录下的文件(返回列表)\n", + "glob.glob('*.py') 查找当前工作目录下,所有以'.py结尾'的文件\n", + "(2)在命令行运行Python脚本时,脚本文件名及后面跟的一些“额外文字”,会被Python收集起来放到sys.argv里,方便后续在代码里面读取并决定程序行为。\n", + "(3)字符串匹配,例如匹配是否\n", + "```python\n", + "import re\n", + "re.findall(r'\\bf[a-z]*', 'which foot or hand fell fastest') # 输出['foot', 'fell', 'fastest']\n", + "# 匹配规则解释:\n", + "# r'\\bf[a-z]*':匹配所有“以 f 开头的单词”(只考虑小写字母)。\n", + "# \\b单词边界(word boundary)。要求匹配从一个“单词的开始位置”开始(前面不是单词字符,后面是单词字符)。\n", + "# f:字母 f。\n", + "# [a-z]*:后面跟着 0 个或多个小写字母(a 到 z)。\n", + "\n", + "re.sub(r'(\\b[a-z]+) \\1', r'\\1', 'cat in the the hat') # 输出 'cat in the hat'\n", + "# 匹配规则解释:\n", + "# r'(\\b[a-z]+) \\1':是在找 重复单词(比如 \"the the\"),并把它替换成一个。\n", + "'''\n", + "(\\b[a-z]+):第 1 个捕获组\n", + "\\b:单词边界(从单词开始)\n", + "[a-z]+:一个或多个小写字母\n", + "用括号 (...) 包住表示“把这一段记下来”,编号为组 1\n", + "\n", + "空格 ' ':匹配一个空格\n", + "\n", + "\\1:反向引用(backreference)\n", + "表示“必须再出现一次 和第 1 组完全相同的内容”\n", + "也就是:前面捕获到什么单词,后面必须重复同一个单词\n", + "\n", + "替换串:r'\\1'\n", + "表示用“捕获组 1 的内容”替换整个匹配结果\n", + "所以 \"the the\" 被替换为 \"the\"\n", + "最终得到:'cat in the hat'。\n", + "'''\n", + "```\n", + "(4)放回抽样random.choices()和不放回抽样random.sample()\n", + "(5)用于处理从 urls 接收的数据的 urllib.request\n", + "```python\n", + "import json # json模块提供json的序列化\n", + "from urllib.request import urlopen, Request\n", + "# urlopen 发送请求并返回响应对象(response)\n", + "# Request:用来构造 HTTP 请求对象(URL、方法、headers等)\n", + "url = \"https://httpbin.org/json\" # 要访问的url,示例接口,会返回 JSON\n", + "\n", + "# 构造一个请求对象,里面包含请求目标url和请求头headers\n", + "req = Request( \n", + " url,\n", + " # 请求头是客户端发给服务器的一些元信息\n", + " headers={\n", + " \"User-Agent\": \"Mozilla/5.0\" # 有些网站不加 UA 会拒绝\n", + " },\n", + ")\n", + "# Request(...)默认是GET请求,如果要用POST,会传data\n", + "\n", + "# 然后发起请求并拿到响应对象 resp,并确保用完自动关闭连接/资源。\n", + "with urlopen(req, timeout=10) as resp: # resp是一个类文件对象\n", + " raw = resp.read() # 读取响应体的全部内容,返回bytes(网络传输的“原始内容”是字节序列,即为bytes)。不带参数的.read()会读取到EOF(响应结束),适用于较小的效应。\n", + " text = raw.decode(\"utf-8\") # 把字节raw按照UTF-8编码规则,解码成字符串str(因为JSON文本是一种文本协议,本质上是字符串)\n", + " data = json.loads(text) # 把JSON字符串转换为Python对象,这里面是dict\n", + "\n", + "print(data.keys()) # 返回的是dict_keys视图对象(不是列表),但是可迭代,如果想看到列表形式,可以print(list(data.keys()))\n", + "```\n", + "\n", + "JSON 数据类型映射到 Python 规则:\n", + "JSON object(花括号 {})→ Python dict\n", + "JSON array(方括号 [])→ Python list\n", + "JSON string → Python str\n", + "JSON number → Python int 或 float\n", + "JSON true/false → Python True/False\n", + "JSON null → Python None\n", + "(6)用于发送电子邮件的 smtplib:\n", + "```python\n", + "import smtplib # 标准库smtplib 提供 SMTP 协议客户端,用于“把邮件提交给 SMTP 服务器”。\n", + "from email.mime.text import MIMEText # 导入MIMEText类,邮件不是随便字符串,要符合MIME格式,MIMEText 用于构造纯文本/HTML邮件的正文部分。\n", + "from email.header import Header # 邮件头(如 Subject)里出现中文时,需要按 RFC 规范编码;Header(\"中文\", \"utf-8\") 会生成正确的头部编码形式,避免乱码\n", + "\n", + "# 以下为SMTP的基本配置(服务器、端口、账号)\n", + "smtp_host = \"smtp.163.com\" # SMTP服务器地址\n", + "smtp_port = 465 # SSL 端口(常见)\n", + "sender = \"13427527534@163.com\" # 发件人的邮箱账号 \n", + "password = \"\" # 注意:通常不是登录密码,是“授权码”\n", + "receiver = \"547651488@qq.com\" # 收件人的邮箱账号\n", + "\n", + "# 以下构造邮件内容\n", + "msg = MIMEText(\"这是一封测试邮件\", \"plain\", \"utf-8\") \n", + "# plain内容类型是纯文本,若是HTML就用html,utf-8为正文编码\n", + "msg[\"From\"] = sender \n", + "msg[\"To\"] = receiver # 干啥用的?\n", + "# msg[\"From\"]和msg[\"To\"]用处主要是 “邮件内容层面的发件人\\收件人信息(展示/语义/规范)”,不是 SMTP 投递层面的发件人\\收件人列表。\n", + "msg[\"Subject\"] = Header(\"Python smtplib 测试\", \"utf-8\") # 邮件主题\\标题\n", + "\n", + "# 建立到SMTP服务的SSL连接,并绑定为server\n", + "with smtplib.SMTP_SSL(smtp_host, smtp_port) as server:\n", + " server.login(sender, password) # 登录SMTP服务器\n", + " server.sendmail(sender, [receiver], msg.as_string())\n", + " # sender,SMTP层面的发件人\n", + " # [receiver]为收件人列表\n", + " # msg.as_string()把MIME对象序列化为RFC格式的原始邮件文本\n", + "print(\"sent\")\n", + "```\n", + "\n", + "(7)测试,单元测试\n", + "```python\n", + "# 待测试代码文件,calc.py\n", + "def add(a,b):\n", + "\treturn a+b\n", + "def div(a,b):\n", + "\treturn a/b\n", + "```\n", + "```python\n", + "# 测试用代码,test_calc.py\n", + "import unittest # unittest是标准库的单元测试框架\n", + "import calc\n", + "\n", + "class TestCalc(unittest.TestCase): # 定义一个测试类,该类继承自unittest.TestCase\n", + "\t# unittest.TestCase提供了各种断言方法(assertEqual等)和测试运行机制\n", + "\n", + " # 以下定义测试用例(测试用例方法名以test_开头,unittest会自动发现所有以test_开头的方法并执行)\n", + " # 注意:每个测试方法都自动建立了一个TestCalc实例(就是self)\n", + " def test_add(self): # 这里面的self指的是什么?self指的是当前这个测试类的一个实例对象,有这个对象才能调用从父类继承的方法\n", + "\t\tself.assertEqual(calc.add(1,2),3) # 必须相等的断言\n", + "\tdef test_div(self):\n", + "\t\tself.assertAlmostEqual(calc.div(1,3),1/3) # 基本相等,一般到小数点几位之后的精度\n", + " \tdef test_div_zero(self):\n", + "\t\twith self.assertRaises(ZeroDivisionError):\n", + "\t\t\tcalc.div(1,0) # 期望该代码会抛出ZeroDivisionError,否则就报错\n", + "if __name__ == \"__main__\":\n", + "\tunittest.main()\n", + "```\n", + "\n", + "(8)日期和时间\n", + "```python\n", + "import datetime\n", + "# 获取日期\n", + "current_datetime = datetime.datetime.now()\n", + "print(current_datetime) # 输出 2026-01-04 17:25:57.469107\n", + "current_date = datetime.date.today()\n", + "print(current_date) # 输出 2026-01-04\n", + "formatted_datetime = current_datetime.strftime(\"%Y-%m-%d %H:%M:%S\")\n", + "print(formatted_datetime) # 输出 2026-01-04 17:25:57\n", + "# %Y %m %d %H %M %S 这些都代表啥?\n", + "\n", + "# 创建具体日期\n", + "birthday = datetime.date(1997,3,17)\n", + "print(birthday) # 输出 1997-03-17\n", + "```\n", + "\n", + "(9)数据压缩\n", + "模块直接支持通用的数据打包和压缩格式:zlib,gzip,bz2,zipfile,以及 tarfile。\n", + "**“压缩”**的核心意思是:把数据用某种算法重新编码,让它占用的字节数更少(节省磁盘/传输带宽)。\n", + "**“打包”**的核心意思是:把多个文件/目录按一种容器格式组织到一起(变成一个“包”)。\n", + "\n", + "(10)性能度量\n", + "```python\n", + "from timeit import Timer\n", + "print(Timer('t=a; a=b; b=t', 'a=1; b=2').timeit()) # 输出 0.016925900003116112\n", + "print(Timer('a,b = b,a', 'a=1; b=2').timeit())\n", + "# 输出 0.01278959999763174\n", + "```\n", + "\n", + "以上我们看到的只是 Python3 标准库中的一部分模块,还有很多其他模块可以在官方文档中查看完整的标准库文档:https://docs.python.org/zh-cn/3/library/index.html" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "959e1bc4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.016925900003116112\n", + "0.01278959999763174\n" + ] + } + ], + "source": [ + "from timeit import Timer\n", + "print(Timer('t=a; a=b; b=t', 'a=1; b=2').timeit())\n", + "\n", + "print(Timer('a,b = b,a', 'a=1; b=2').timeit())" + ] + }, + { + "cell_type": "markdown", + "id": "e94367dc", + "metadata": {}, + "source": [ + "#### Python魔法方法\n", + "魔法方法也成双下划线方法。\n", + "本质:魔法方法不是给你“直接调用”的,而是让Python在“特定时机”自动调用的。\n", + "例如,执行`a+b`时,Python自动调用了`a.__add__(b)`\n", + "目的:为了让“自定义对象”表现得像内置类型一样自然,可以直接(+ - * /、用len()、for...in、print()、==等,普通方法无法做到)\n", + "注意事项:\n", + "(1)魔法方法是Python解释器和对象之间的“协议接口”,实现了这个接口,Python才知道什么时候能for、能+、能len、能print\n", + "(2)\n", + "分类(四大类魔法方法):\n", + "1.对象创建与生命周期\n", + "(1)__new__:负责创建对象,返回一个实例\n", + "例如`obj = MyClass()`,Python做的其实是`obj=MyClass.__new__(MyClass)`,参数为cls\n", + "(2)__init__:不创建对象,而是“配置已经创建好的对象”,初始化对象的属性,`MyClass.__init__(obj)`,参数为self\n", + "(3)__del__:在对象被垃圾回收前调用,资源释放一般用with\n", + "2.对象表现(打印/转字符串)\n", + "Python很多地方需要用字符串(print、日志等),所以对象必须能够变成字符串。\n", + "(1)__str__:给“人”看的字符串。\n", + "(2)__repr__:给“程序员/调试”看的字符串。定义对象“真实、准确、不模糊”的表示。\n", + "__str__与__repr__的区别:\n", + "pirnt(obj),优先用__str__;\n", + "如果没有__str__,就用__repr__\n", + "如都没有,输出<__main__.Class object at 0x...>\n", + "3.容器/迭代协议\n", + "容器可以看做是一组行为,一个对象支持这些操作之一就会像个容器:len、for x in obj、obj[i]、x in obj\n", + "__len__\n", + "__iter__\n", + "__next__\n", + "__getitem__\n", + "4.运算符与比较\n", + "__add__\n", + "__eq__\n", + "__lt__\n", + "...\n", + "\n", + "#### 魔法变量" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5f0d4d4f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "<__main__.User at 0x22ba7c7b4a0>" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "class User:\n", + " def __repr__(self):\n", + " return 'User(id=1)'\n", + "u = User()\n", + "u" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cf54385", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2\n" + ] + } + ], + "source": [ + "class MyNumbers:\n", + " def __iter__(self):\n", + " self.a = 1\n", + " return self\n", + "\n", + " def __next__(self):\n", + " x = self.a\n", + " self.a += 1\n", + " return self.a\n", + "\n", + "myclass = MyNumbers()\n", + "myiter = iter(myclass) # iter()是一个内置函数,内置函数的工作规则:如果对象有__iter__方法,就用对象的方法;否则尝试__getitem__(0),__getitem__(1)\n", + "# 所以,这一步等价于 myclass.__iter__,用的是我自己定义的Mynumbers.__iter__\n", + "\n", + "print(next(myiter))\n", + "\n", + "# 我这样做的含义是什么,我重写了__iter__()和__next()方法吗? 那myiter = iter(myclass)这一步用的是哪个iter方法?" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "9c5a47ca", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'a': 1, 'b': 2, 'c': 3}\n", + "\n", + "('g', 's', 'i', 'n', 't')\n" + ] + } + ], + "source": [ + "keys = ['a','b','c']\n", + "values = [1,2,3]\n", + "dicts1 = {key:value for key,value in zip(keys,values)}\n", + "tup1 = tuple({x for x in 'sitting'})\n", + "print(dicts1)\n", + "print(type(dicts1))\n", + "print(tup1)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6f292d99", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[2, 4, 6]\n" + ] + } + ], + "source": [ + "names = [x for x in range(1,7) if x%2 == 0 ]\n", + "print(names)" + ] + }, + { + "cell_type": "markdown", + "id": "a1e3ef90", + "metadata": {}, + "source": [ + "#### 条件控制\n", + "1.if语句\n", + "```python\n", + "if condition_1: # condition_1 为True时,执行block_1\n", + " statement_block_1\n", + "elif condition_2: # condition_1 为False时,condition_2 为 False时,执行block_2\n", + " statement_block_2\n", + "else:\n", + " statement_block_3 # 否则执行block_3\n", + "```\n", + "\n", + "2.match...case条件判断(Python3.10版后引入的)\n", + "支持匹配值(多个值用|)、匹配类型、匹配结构(list、dict、class)、匹配并绑定变量、支持守卫条件(case if ...)\n", + "(1)基础语法\n", + "```python\n", + "match expression:\n", + " case pattern1:\n", + " # 处理pattern1的逻辑\n", + " case pattern2 if condition:\n", + " # 处理pattern2并且满足condition的逻辑\n", + " case _:\n", + " # 处理其他情况的逻辑\n", + "```\n", + "(2)可以匹配并绑定变量\n", + "```python\n", + "def match_example(item):\n", + " match item:\n", + " case (x, y) if x == y:\n", + " print(f\"匹配到相等的元组: {item}\")\n", + " case (x, y):\n", + " print(f\"匹配到元组: {item}\")\n", + " case _:\n", + " print(\"匹配到其他情况\")\n", + "\n", + "match_example((1, 1)) # 输出: 匹配到相等的元组: (1, 1)\n", + "match_example((1, 2)) # 输出: 匹配到元组: (1, 2)\n", + "match_example(\"other\") # 输出: 匹配到其他情况\n", + "```\n", + "\n", + "```python\n", + "# 复杂的接口返回,想取得关联关系(relation)是什么\n", + "resp = {\n", + " 'code':200,\n", + " 'data':{\n", + " 'company':{\n", + " 'id':'A123',\n", + " 'relationship':'控股'\n", + " }\n", + " }\n", + "}\n", + "# 传统写法\n", + "if resp.get('code') == 200:\n", + " if 'company' in resp['data']:\n", + " relation = resp['data']['company']['relationship']\n", + "# match:\n", + "match resp:\n", + " case {'code':200,'data':{'company':{'id':'A123','relationship':rel}}}: # 匹配并绑定变量\n", + " print('关联关系',rel)\n", + "```\n", + "\n", + "(3)可以匹配类型:\n", + "```python\n", + "class Circle:\n", + " def __init__(self, radius):\n", + " self.radius = radius\n", + "\n", + "class Rectangle:\n", + " def __init__(self, width, height):\n", + " self.width = width\n", + " self.height = height\n", + "\n", + "def match_shape(shape):\n", + " match shape:\n", + " case Circle(radius=1):\n", + " print(\"匹配到半径为1的圆\")\n", + " case Rectangle(width=1, height=2):\n", + " print(\"匹配到宽度为1,高度为2的矩形\")\n", + " case _:\n", + " print(\"匹配到其他形状\")\n", + "\n", + "match_shape(Circle(radius=1)) # 输出: 匹配到半径为1的圆\n", + "match_shape(Rectangle(width=1, height=2)) # 输出: 匹配到宽度为1,高度为2的矩形\n", + "match_shape(\"other\") # 输出: 匹配到其他形状\n", + "```\n", + "\n", + "(4)匹配对象类型:检查对象是不是某种类型,类似于isinstance;\n", + "匹配类对象:不仅检查类型,还要检查对象内部属性结构,并允许提取(绑定)内部的值\n" + ] + }, + { + "cell_type": "markdown", + "id": "f646ca2a", + "metadata": {}, + "source": [ + "#### 循环语句\n", + "1.for循环,用于遍历任何可迭代对象\n", + "\n", + "2.while循环,Python中没有do while循环\n", + "简单循环:while True:print('a')\n", + "\n", + "3.break,跳出当前循环\n", + "\n", + "4.continue,跳出当前循环块中的剩余语句,进行下一次循环" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e1924434", + "metadata": {}, + "outputs": [], + "source": [ + "n = 5\n", + "while n > 0:\n", + " n -= 1\n", + " if n == 2:\n", + " continue\n", + " print(n)\n", + "print('end!')" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "8a6179b9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "关联关系 控股\n", + "控股\n" + ] + } + ], + "source": [ + "resp = {\n", + " 'code':200,\n", + " 'data':{\n", + " 'company':{\n", + " 'id':'A123',\n", + " 'relationship':'控股'\n", + " }\n", + " }\n", + "}\n", + "# 传统写法\n", + "if resp.get('code') == 200:\n", + " if 'company' in resp['data']:\n", + " relation = resp['data']['company']['relationship']\n", + "# match:\n", + "match resp:\n", + " case {'code':200,'data':{'company':{'id':'A123','relationship':rel}}}:\n", + " print('关联关系',rel)\n", + " \n", + "\n", + "print(relation)" + ] + }, + { + "cell_type": "markdown", + "id": "429b4e33", + "metadata": {}, + "source": [ + "#### Python 直接赋值、浅拷贝和深度拷贝解析:\n", + "(1)直接赋值:其实就是对象的引用(别名)。\n", + "b=a,即a和b都指向同一个对象。\n", + "(2)浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象。\n", + "b=a.copy(),a和b指向不同的对象,但是他们的子对象指向的还是一个对象(是引用)。\n", + "(3)深拷贝(deepcopy):copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。\n", + "b = copy.deepcopy(a),a和b是独立的对象,其中的子对象也是不同的对象。\n", + "```python\n", + "# 字典浅拷贝\n", + "a = {1:[1,2,3]}\n", + "b = a.copy() \n", + "print(a,b) # 输出 {1: [1, 2, 3]} {1: [1, 2, 3]}\n", + "a[1].append(4) # a的子对象和b的子对象是同一个\n", + "print(a,b) # 输出 {1: [1, 2, 3, 4]} {1: [1, 2, 3, 4]}\n", + "\n", + "# 深拷贝\n", + "import copy\n", + "c = copy.deepcopy(a)\n", + "print(a,c) # 输出 {1: [1, 2, 3, 4]} {1: [1, 2, 3, 4]}\n", + "a[1].append(5)\n", + "print(a,c) # 输出 {1: [1, 2, 3, 4, 5]} {1: [1, 2, 3, 4]}\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6e71df9a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{1: [1, 2, 3]} {1: [1, 2, 3]}\n", + "{1: [1, 2, 3, 4]} {1: [1, 2, 3, 4]}\n", + "{1: [1, 2, 3, 4]} {1: [1, 2, 3, 4]}\n", + "{1: [1, 2, 3, 4, 5]} {1: [1, 2, 3, 4]}\n" + ] + } + ], + "source": [ + "a = {1:[1,2,3]}\n", + "b = a.copy() \n", + "print(a,b) # 输出 {1: [1, 2, 3]} {1: [1, 2, 3]}\n", + "a[1].append(4) # a的子对象和b的子对象是同一个\n", + "print(a,b) # 输出 {1: [1, 2, 3, 4]} {1: [1, 2, 3, 4]}\n", + "# 深拷贝\n", + "import copy\n", + "c = copy.deepcopy(a)\n", + "print(a,c) # 输出 {1: [1, 2, 3, 4]} {1: [1, 2, 3, 4]}\n", + "a[1].append(5)\n", + "print(a,c) # 输出 {1: [1, 2, 3, 4, 5]} {1: [1, 2, 3, 4]}" + ] + }, + { + "cell_type": "markdown", + "id": "7480e222", + "metadata": {}, + "source": [ + "##### bytes类型\n", + "bytes类型表示不可变的二进制序列,其中的元素是整数值(0-255之间的整数)。\n", + "常用于处理二进制数据(图像、音频、视频),网络编程中也用来传输二进制数据。\n", + "bytes与字符串类型一样,也支持切片、拼接、查找、替换等。\n", + "可以使用`b`前缀,或者`bytes()`函数来创建bytes类型对象。\n", + "\n", + "```python\n", + "x = b\"hello\"\n", + "y = bytes('world',encoding='utf-8')\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "60672bf2", + "metadata": {}, + "source": [ + "\n", + "\n", + "##### bool(布尔类型)\n", + "bool是int的子类,`True==1,False==0,issubclass(bool,int)`都返回True。 \n", + "布尔值可以被看作整数来使用,True等价于1,False等价于0。\n", + "`bool()`函数可以将其他类型的值转为布尔值,下面的值会被转为False:\n", + "None、False、零 (0、0.0、0j)、空序列(如 ''、()、[])和空映射(如 {})。其余均为True。 \n", + "常用示例:\n", + "```python\n", + "print(True and False) # False\n", + "print(True or False) # True\n", + "print(not True) # False\n", + "print(not False) # True\n", + "\n", + "x = 10\n", + "if x:\n", + " print(\"x is non-zero and thus True in a boolean context\")\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "9cf899d0", + "metadata": {}, + "source": [ + "#### 数据类型转换\n", + "##### 隐式类型转换\n", + "一般是较低数据类型(如整数)转换为较高数据类型(如浮点数),以避免数据丢失。\n", + "较低:一般指的是表示范围小,表示精度低,内存占用较小,存储结构简单;较高反之。\n", + "特殊://的结果不一定是整数,例如`7.0//2`返回`3.0`。\n", + "##### 显式类型转换\n", + "将对象的数据类型做强制转换。\n", + "```python\n", + "# int():将一个字符串或者数字转换为整型\n", + "print(int('25',base = 10)) # base为进制,默认十进制,明确进制时只能转换字符串\n", + "print(int(2)) # 输出2\n", + "print(int(0b10010) )#输出18,默认输出10进制\n", + "\n", + "# float():将一个数或者字符串转换为浮点数\n", + "print(float(25)) # 输出25.0\n", + "print(float('280')) # 输出280.0\n", + "\n", + "# str():将对象转为字符串,即返回一个对象的string格式\n", + "dict3 = {'runoob': 'runoob.com', 'google': 'google.com'}\n", + "print(dict3) # 输出{'runoob': 'runoob.com', 'google': 'google.com'}\n", + "\n", + "# complex():创建一个复数\n", + "print(complex(1,2)) # 输出(1+2j)\n", + "print(complex(1)) # 输出(1+0j)\n", + "print(complex('1')) # 输出(1+0j),1当做实部处理\n", + "print(complex('1+2j')) # 输出(1+2j)\n", + "\n", + "\n", + "\n", + "# repr():将对象转换为供解释器读取的形式?\n", + "# 将读取到的格式字符,比如换行符、制表符,转化为其相应的转义字符\n", + "s=\"物品\\t单价\\t数量\\n包子\\t1\\t2\"\n", + "print(str(s))\n", + "print(repr(s)) # 转义字符变为正常的字符\n", + "\n", + "# eval():用于执行一个字符串表达式,并返回表达式的值(有风险)\n", + "print(eval('1+2+3')) # 输出6\n", + "\n", + "# tuple(s)、list(s):将可迭代系列s(列表)转换为元组、列表\n", + "list1 = ['Google', 'Taobao', 'Runoob', 2]\n", + "tuple1 = tuple(list1)\n", + "print(tuple1) # 输出('Google', 'Taobao', 'Runoob', 2)\n", + "\n", + "# set():将一个可迭代对象对象转换为集合,可以用于去重、关系测试\n", + "x = set('runoob')\n", + "print(x) # 输出{'n', 'u', 'b', 'r', 'o'}\n", + "\n", + "# frozenset():返回一个冻结的集合,该集合不能添加或删除任何元素。\n", + "a = frozenset(range(10))\n", + "print(a) # 输出 frozenset({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})\n", + "b = frozenset('runoob')\n", + "print(b) # 输出 frozenset({'r', 'o', 'n', 'b', 'u'})\n", + "\n", + "# chr(x):将一个整数转换为一个ASCII字符,x可以是十进制也可以是十六进制。\n", + "print(chr(10)) # 输出的是换行符(\\n),相当于print('\\n')\n", + "print(repr(chr(10))) # 输出'\\n'\n", + "\n", + "# ord():将一个字符转换为它的整数值。\n", + "# ord() 函数是 chr() 函数(对于8位的ASCII字符串)或 unichr() 函数(对于Unicode对象)的配对函数。\n", + "print(ord('a')) # 输出97\n", + "print(ord('A')) # 输出65\n", + "print(ord('\\n')) # 输出10\n", + "\n", + "# hex():将一个十进制整数转换为十六进制字符串。\n", + "print(hex(255)) # 输出0xff\n", + "\n", + "# oct():将一个整数转换为八进制字符串。\n", + "print(oct(10)) # 输出0o12\n", + "```\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "9e9d4622", + "metadata": {}, + "source": [ + "dict()构造字典的本质是:能够传入键值对(key,value),就能生成字典。\n", + "传入键值对的方式是:\n", + "1.直接传入关键字:\n", + "```python\n", + "dict3 = dict(a='a',b='b',t='t') # 直接传入关键字构造\n", + "# 等价于 dict3 = (a='a',b='b',t='t')\n", + "print(dict3) # 输出{'a': 'a', 'b': 'b', 't': 't'}\n", + "```\n", + "\n", + "2.用可迭代对象,可迭代对象里面是(key,value)的二元序列即可\n", + "```python\n", + "dict4 = dict([('one',1),('two',2),('three',3)])\n", + "print(dict4) # {'one': 1, 'two': 2, 'three': 3}\n", + "```\n", + "可迭代对象是其他形式(元组、列表、生成器)也可以。\n", + "```python\n", + "dict4 = dict((['a',1],['b',2]))\n", + "dict4 = dict(((1,'x'x),(2,'y')))\n", + "dict4 = dict((f'key{i}',i) for i in range(3))\n", + "```\n", + "原因:dict()会遍历这个列表,每个元素都可以拆分成一个键和一个值。\n", + "\n", + "3.用zip,zip(key,values)会生成一堆(key,value)原则,符合dict需要的格式\n", + "\n", + "4.用映射类型(Mapping)构造。\n", + "映射类型指的是:能通过“键→值”的方式存储和访问数据的容器。\n", + "字典是最典型的映射类型,其他映射类型包括collections.OrderedDict(有序字典)等\n", + "所有映射类型都要能够实现__getitem__、__iter__、__len__\n", + "dict()函数可以接受任何Mapping类型,例如:\n", + "```python\n", + "mapping1 = {'a':1,'b':2}\n", + "d = dict(mapping1)\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "id": "890c875d", + "metadata": {}, + "source": [ + "### 输入和输出\n", + "1.Python两种输出值的方式: 表达式语句和 print() 函数。\n", + "第三种方式是使用文件对象的 write() 方法,标准输出文件可以用 sys.stdout 引用。\n", + "如果你希望输出的形式更加多样,可以使用 str.format() 函数来格式化输出值。\n", + "如果你希望将输出的值转成字符串,可以使用 repr() 或 str() 函数来实现。\n", + "str(): 函数返回一个用户易读的表达形式。\n", + "repr(): 产生一个解释器易读的表达形式。\n", + "str.format() 的基本使用如下:\n", + "`print('{}网址: \"{}!\"'.format('菜鸟教程', 'www.runoob.com'))`,输出`菜鸟教程网址: \"www.runoob.com!\"`\n", + "{}内可以设置关键字,或者序号\n", + "\n", + "2.input() 内置函数从标准输入读入一行文本,默认的标准输入是键盘。\n", + "\n", + "3.读和写文件\n", + "(1)open()将会返回一个file对象,`f = open(filename, mode)`\n", + "filename指的是文件名字符串,含文件路径;\n", + "mode是打开文件的模式,具体如下:\n", + "| 模式 | 读 | 写 | 创建 | 覆盖 | 指针在开始 | 指针在结尾 |\n", + "|---|---|---|---|---|---|---|\n", + "| `r` | + | | | | + | |\n", + "| `r+` | + | + | | | + | |\n", + "| `w` | | + | + | + | + | |\n", + "| `w+` | + | + | + | + | + | |\n", + "| `a` | | + | + | | | + |\n", + "| `a+` | + | + | + | | | + |\n", + "一般用r代表只读,w用于写入(存在同名文件会被删除),用a来追加内容\n", + "还有一个常用参数是encoding,指的是以什么编码规则打开文件。\n", + "(2)f.readline()从文件中读取单独的一行(单独一行指的是读取到下一个换行符'\\n'之前的内容),返回一个字符串。当返回空字符串时,说明已经读取到最后一行,这时候如果想从文件开头重新读,那么可以使用f.seek(0)。\n", + "f.readlines()返回该文件中包含的所有行。\n", + "f.readline()和f.readlines()都是从文件对象的当前指针位置开始读取。\n", + "也可以用for line in f:print(line,end='')。\n", + "(3)f.write()将字符串(不是字符串要转为字符串)写到文件中,然后返回写入的字符串数(\\n换行符算一个字符数量)。\n", + "(4)为什么最后一定要f.close():目的是为了让操作系统知道,这个文件关闭了,不然这个文件一直都显示被占用状态,即无法被删除或重命名。\n", + "也是为了确保数据真的写入磁盘,而非仅保存在缓冲区(有的OS在缓冲区慢、或者执行.flush()、.close()、或程序正常结束时才会写入磁盘)\n", + "文件句柄一直被占用?文件句柄指的是,操作系统给程序的一张“文件使用许可证编号”,程序想读文件时,要先向操作系统申请,取得一个整数,这个整数代表对应资源。\n", + "可以用with关键字打开:在with代码块结束后,会自动关闭文件。\n", + "(5)f.tell()可以确认文件当前读写的位置文件指针的位置,返回一个整数,这个整数代表从文件开头字节数的偏移量。\n", + "(6)f.seek()可以用于改变文件指针的位置,如f.seek(0)用于返回文件开头。\n", + "f.seek(offset,whence),offset表示相对于whence参数的偏移量,whence的默认值为0,表示开头;值为1,表示当前位置;值为2,表示文件的结尾\n", + "seek(x,0) :从起始位置即文件首行首字符开始移动 x 个字符,seek(0)中的0是偏移量为0,默认的whence值为0,所以是从文件开头移动0个位置,即为指针回到文件开头\n", + "seek(x,1) :表示从当前位置往后移动x个字符\n", + "seek(-x,2):表示从文件的结尾往前移动x个字符\n", + "(7)file对象常用函数\n", + "| 序号 | 方法 | 描述 |\n", + "|---:|---|---|\n", + "| 1 | `file.close()` | 关闭文件。关闭后文件不能再进行读写操作。 |\n", + "| 2 | `file.flush()` | 刷新文件内部缓冲,立即把缓冲区数据写入文件(而不是等待缓冲区自动写入)。 |\n", + "| 3 | `file.fileno()` | 返回整型文件描述符(FD),可用于 `os.read` 等底层操作。 |\n", + "| 4 | `file.isatty()` | 如果文件连接到终端设备返回 `True`,否则返回 `False`。 |\n", + "| 5 | `file.next()` | Python 3 的文件对象不支持 `next()` 方法;读取下一行应使用内置 `next(file)` 或迭代文件对象。 |\n", + "| 6 | `file.read([size])` | 用于从文件读取指定的字符数(文本模式 t)或字节数(二进制模式 b),如果未给定参数 size 或 size 为负数则读取文件所有内容。 |\n", + "| 7 | `file.readline([size])` | 读取一整行(包含 `\\n`)。可指定最多读取的字节数。 |\n", + "| 8 | `file.readlines([sizeint])` | 读取所有行并返回列表;若 `sizeint>0`,读取“约” `sizeint` 字节的行(可能略大,因为要填充缓冲区)。 |\n", + "| 9 | `file.seek(offset[, whence])` | 移动文件读写指针到指定位置。 |\n", + "| 10 | `file.tell()` | 返回文件当前位置(位置标记)。 |\n", + "| 11 | `file.truncate([size])` | 截断文件为 `size` 个字符/字节(未提供则从当前位置截断);截断后后续内容被删除。 |\n", + "| 12 | `file.write(str)` | 写入字符串,返回写入的字符数。 |\n", + "| 13 | `file.writelines(sequence)` | 写入一个字符串序列;不会自动添加换行符,需要自行在元素中加 `\\n`。 |\n", + "\n", + "4.pickle模块\n", + "用途:实现基本的数据序列化(dump\\dumps)和反序列化(load\\loads),序列化操作的含义指的是将程序中运行的对象信息保存到文件中,永久存储;反序列化操作指的是能够从文件中创建上一次程序保存的对象。\n", + "通俗的讲就是为了永久保存对象,但是对象在内存中结构复杂,dump可以把对象转换为一串字符串保存(不是简单的文本输出),而且还可以直接从文件恢复这个对象。\n", + "```python\n", + "# 存入对象\n", + "import pickle\n", + "data = {\n", + " 'name':'Tome',\n", + " 'age':18,\n", + " 'scores':[90,95,88]\n", + "}\n", + "\n", + "with open('data.pkl','wb') as f: # wb 指的是二进制写,该文件原有内容会被删除\n", + " pickle.dump(data,f) # dump(需要保存的对象,文件对象)\n", + "\n", + "# 读取对象\n", + "with open('data.pkl','rb') as f: # rb 指的是二进制打开一个文件并设置只读\n", + " data2 = pickle.load(f)\n", + "print(data2)\n", + "print(type(data2))\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "id": "bd7e776b", + "metadata": {}, + "source": [ + "### OS模块\n", + "可以通过OS模块来执行 跨平台的 文件操作、目录操作、环境变量管理、进程管理等任务。\n", + "常用操作包含目录操作:\n", + "(1)获取当前工作目录:os.getcwd()\n", + "(2)改变当前工作目录:os.chdir(path)\n", + "(3)列出目录内容:os.listdir(path)\n", + "(4)创建目录:os.mkdir(path)\n", + "(5)删除目录:os.rmdir(path),目录不为空时,会抛出异常\n", + "(6)删除文件:os.remove(path)用于删除一个文件,文件不存在会异常\n", + "(7)重命名文件或目录:os.rename(src,dst),src为原始路径,dst是新的路径。\n", + "(8)获取环境变量:os.getenv(key)用于获取指定的环境变量。\n", + "(9)执行系统命令:用于在操作系统的shell中执行命令,命令执行后,返回命令的退出状态。" + ] + }, + { + "cell_type": "markdown", + "id": "6fab01dc", + "metadata": {}, + "source": [ + "### 错误和异常\n", + "1.语法错误(解析错)\n", + "2.异常\n", + "(1)含义:运行期间检测到的错误\n", + "(2)类型:异常有不同的类型\n", + "(3)异常处理:\n", + "try/except语法格式:\n", + "```python\n", + "try:\n", + " #执行的代码,发生异常时剩下的部分将被忽略\n", + "except 异常类型:\n", + " #异常类型相符时,发生异常时执行的代码,没有异常时忽略这段\n", + "else: #可选项,在try子句没有发生任何异常时执行,比直接和try子句放在一起好一些\n", + "finally:#不管有没有异常都会执行的代码\n", + "```\n", + "内层异常接不住,就传递给上层的try,如果一直没有任何外层捕获,异常最终就会冒泡到最外层,程序报 Traceback 并终止。\n", + "可以有多个except子句,一个子句也可以有多个异常,这些异常被放在一个括号里面成为一个元组,例如`except (RuntimeError, TypeError, NameError)`\n", + "最后一个except子句可以忽略异常的名称,它将被当作通配符使用,即为匹配所有异常。你可以使用这种方法打印一个错误信息,然后再次把异常抛出。\n", + "```python\n", + "import sys\n", + "try:\n", + " f = open('myfile.txt')\n", + " s = f.readline()\n", + " i = int(s.strip())\n", + "except OSError as err:\n", + " print(\"OS error: {0}\".format(err))\n", + "except ValueError:\n", + " print(\"Could not convert data to an integer.\")\n", + "except:\n", + " print(\"Unexpected error:\", sys.exc_info()[0])\n", + " raise # 将当前捕获到的异常,原样重新抛出\n", + "```\n", + "sys.exc_info()是一个三元组:(异常类型,异常对象,traceback)\n", + "\n", + "raise语句常用于抛出一个指定的异常,主动抛出。\n", + "raise(没有参数)的作用是,将当前捕获到的异常,原样重新抛出。意思是这个except只是记录一下新的异常类型,不吞掉错误。\n", + "\n", + "可以通过继承Exception类来自定义异常。\n", + "\n", + "3.assert断言\n", + "用于判断一个表达式,表达式为false时,触发异常。\n", + "`assert expression`等价于`if not expression:raise AssertionError`\n", + "\n", + "# 常见错误类型\n", + "UnboundLocalError:你在函数的局部作用域里引用了一个局部变量,但这个局部变量在引用前还没有被赋值(绑定值)。" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "44b882dd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "执行到了下一步\n", + "Could not convert data to an integer.\n" + ] + } + ], + "source": [ + "import sys\n", + "try:\n", + " f = open('myfile.txt')\n", + " print('执行到了下一步')\n", + " s = f.readline()\n", + " i = int(s.strip())\n", + "except OSError as err:\n", + " print(\"OS error: {0}\".format(err))\n", + "except ValueError:\n", + " print(\"Could not convert data to an integer.\")\n", + "except:\n", + " print(\"Unexpected error:\", sys.exc_info()[0])\n", + " raise" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "f946ed11", + "metadata": {}, + "outputs": [], + "source": [ + "while True:\n", + " try:\n", + " x = input('请输入一个数字:')\n", + " break\n", + " except ValueError:\n", + " print('您输入的不是数字,请再次尝试输入!')" + ] + }, + { + "cell_type": "markdown", + "id": "580ec0e6", + "metadata": {}, + "source": [ + "#### 字符、字节与编码规则\n", + "1.字符是人看到的符号,字节是计算机实际存的长度,UTF-8编码规则下:\n", + "英文为1字节,常见中文字符为3字节,Emoji字符为4字节。\n", + "\n", + "2.编码规则是字符和字节之间的桥梁,常见的编码规则有UTF-8、UTF-16、GBK:\n", + "(1)UTF-8最常用,变长编码,英文省空间,向下兼容ASCII;\n", + "(2)UTF-16以2字节为基础,内部处理友好;\n", + "(3)GBK中文优先的本地编码,适合中文环境。\n" + ] + }, + { + "cell_type": "markdown", + "id": "fddce543", + "metadata": {}, + "source": [ + "#### with\n", + "with实际上封装了 try…except…finally 编码范式,便于使用。\n", + "实现原理:建立在上下文管理器之上。\n", + "上下文管理器是一个实现__enter__和__exit__方法的类,使用with语句会在嵌套模块末尾调用__exit__方法。\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "2c895b89", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "旅游计划城市: ['0# 北京', '1# 上海', '2# 广州', '3# 深圳', '4# 成都', '5# 杭州', '6# 西安', '7# 重庆', '8# 南京', '9# 苏州']\n", + "请输入有效的整数。\n", + "请输入有效的整数\n", + "最后的旅游城市为: ['8# 南京', '6# 西安', '3# 深圳', '9# 苏州', '0# 北京']\n" + ] + } + ], + "source": [ + "import random\n", + "import sys\n", + "try:\n", + " with open('5A.txt','r') as fp:\n", + " i = [line for line in fp if line[0].isdigit()]\n", + "except FileNotFoundError:\n", + " print(r\"文件'5A.txt'未找到,请检查文件路径\")\n", + " sys.exit()\n", + "\n", + "travelList = []\n", + "\n", + "# 读取城市列表\n", + "for index,item in enumerate(i):\n", + " temp_1 = item.strip()[item.index('.')+1:]\n", + " temp_1 = f\"{index}#{temp_1}\"\n", + " travelList.append(temp_1)\n", + "\n", + "print(\"旅游计划城市:\",travelList)\n", + "\n", + "# 删除旅游城市\n", + "city_num = input('输入不想旅游城市的个数:')\n", + "try:\n", + " for _ in range(int(city_num)):\n", + " index = int(input('输入不想旅游城市的序号(第1个城市索引为0)'))\n", + " if 0 <= index < len(travelList):\n", + " travelList.pop(index)\n", + " print(\"旅游计划城市:\",travelList)\n", + " else:\n", + " print(\"输入的序号超出范围。\")\n", + "except ValueError:\n", + " print(\"请输入有效的整数。\")\n", + "\n", + "# 修改旅游城市\n", + "city_num = input(\"请输入想修改计划旅游的城市个数:\")\n", + "try:\n", + " for _ in range(int(city_num)):\n", + " index = int(input(\"请输入想要修改计划旅游城市的序号:\"))\n", + " if 0 <= index < len(travelList):\n", + " new_name = input(f\"原名称为{travelList[index]},请输入要修改城市的名称:\")\n", + " travelList[index] = new_name\n", + " print(\"旅游计划城市:\",travelList)\n", + " else:\n", + " print(\"输入的序号超出范围!\")\n", + "except ValueError:\n", + " print(\"请输入有效的整数\")\n", + "\n", + "\n", + "random.shuffle(travelList)\n", + "print(\"最后的旅游城市为:\",travelList[:5])" + ] + }, + { + "cell_type": "markdown", + "id": "2c04f3f1", + "metadata": {}, + "source": [ + "### Python3 面向对象\n", + "1.类的构造方法(__init__)不是创建对 象的方法,而是初始化对象的方法,如果不主动写,Python也会进行自动调用一个默认的__init__。创造对象的方法是(__new__)。\n", + "使用场景:有需求时写,例如想要在对象生成时候就有相应的属性,事后加属性不是很规范;例如想强制传参,即不给特定的参数就不能创建这个对象。\n", + "\n", + "2.self代表类的实例(对象)本身。\n", + "\n", + "3.类的属性与方法\n", + "私有的含义,就是在类的外部不能直接访问或者使用。\n", + "(1)类的私有属性\n", + "在类里定义的、以双下划线开头(但不以双下划线结尾)的属性,比如 __name、__age。\n", + "它的核心作用是:让类外部“不能直接用原名字访问”这个属性,从而降低被误用/误改的风险(更接近“封装”的效果)。\n", + "实现机制:通过名称改写来实现,例如self.__name 在类 Student 内部会被解释器改写成:self._Student__name。所以类内部访问self.__name没问题,类外面写obj.__name就会报AttributeError\n", + "要访问的话,需要obj._Student__name\n", + "(2)类的方法\n", + "def来定义类的方法,类方法的参数必带self,self代表类的实例。\n", + "如果类中定义的函数,第一个参数不是self,那就不是实例方法。\n", + "(3)类的私有方法\n", + "__private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类的外部调用。self.__private_methods。\n", + "(4)类的专有方法:\n", + "类的专有方法:\n", + "__init__ : 构造函数,在生成对象时调用\n", + "__del__ : 析构函数,释放对象时使用\n", + "__repr__ : 打印,转换\n", + "__setitem__ : 按照索引赋值\n", + "__getitem__: 按照索引获取值\n", + "__len__: 获得长度\n", + "__cmp__: 比较运算\n", + "__call__: 函数调用\n", + "__add__: 加运算\n", + "__sub__: 减运算\n", + "__mul__: 乘运算\n", + "__truediv__: 除运算\n", + "__mod__: 求余运算\n", + "__pow__: 乘方\n", + "\n", + "为什么要有专用方法:普通方法只能 obj.method() 这样调用;\n", + "但如果你想让自定义对象也能:print(obj)、len(obj)、obj1 + obj2、obj[i]、for x in obj、obj == other 这些“语法级”的行为就需要通过专有方法来完成。\n", + "4.继承\n", + "多继承:继承多个父类\n", + "继承的方法,子类继承父类的方法。\n", + "\n", + "5.子类继承父类构造函数\n", + "情况一:子类需要自动调用父类的方法:子类不重写__init__()方法,实例化子类后,会自动调用父类的__init__()的方法。 这种情况在实例化子类对象的时候,需要传入父类__init__()方法所需要的参数。\n", + "情况二:子类不需要自动调用父类的方法:子类重写__init__()方法,实例化子类后,将不会自动调用父类的__init__()的方法。\n", + "情况三:子类重写__init__()方法又需要调用父类的方法:使用super关键词:\n", + "```python\n", + "super(子类,self).__init__(参数1,参数2,....)\n", + "class Son(Father):\n", + " def __init__(self, name): \n", + " super(Son, self).__init__(name)\n", + "```\n", + "\n", + "6.super()函数用于调用父类的方法\n", + "```python\n", + "class FooParent(object):\n", + " def __init__(self):\n", + " self.parent = 'I\\' the parent.'\n", + " print('Parent')\n", + " def bar(self,message):\n", + " print(f'{message} from Parent.')\n", + "\n", + "class FooChild(FooParent):\n", + " def __init__(self):\n", + " super().__init__() # 这一步不需要再传self\n", + " # super()已经知道当前的实例是谁,会自动把self传给父类方法\n", + " print('Child')\n", + "\n", + " def bar(self,message):\n", + " super().bar(message)\n", + " print('Child bar function')\n", + " print(self.parent)\n", + "\n", + "fooChild = FooChild()\n", + "fooChild.bar('Hello Wrold!')\n", + "```\n", + "\n", + "易错点:什么时候要self?\n", + "1.self有什么用?\n", + "self的作用:指代当前对象;存数据到对象里面;让不同对象互不干扰。\n", + "理解:`a = [1,2,3]` `a.append(4)`执行append的时候,实际上做的是`list.append(a,4)`,有了a,Python就知道往哪个对象里面添加值。这个a是被自动塞入的,在方法定义里面,就叫self。\n", + "\n", + "```python\n", + "# 有了self会怎么样?\n", + "class MyList:\n", + " def append(self,value):\n", + " pass\n", + "x = MyList()\n", + "x.append(10) # 我写的是这样,Python实际执行的是MyList.append(x,10),x被当成第一个参数自动传入了\n", + "\n", + "# 不用self,会怎么样?\n", + "class BadList:\n", + " def append(value):\n", + " pass\n", + "a = BadList()\n", + "b = BadList()\n", + "a.append(1)\n", + "b.append(2)\n", + "# Python区分不了,1和2到底加给谁,BadList(?,1)\n", + "```\n", + "需要记忆的点:self就是当前对象本身、调用对象.方法()时,Python会自动把对象当做self传进去。\n", + "\n", + "2.调用父类方法的时候,要不要self\n", + "调用父类方法时,不要self,原因在于:\n", + "super()会自动把self传给父类方法,如果再传就会产生2个self。\n", + "\n", + "如何理解 super()会自动把self传给父类方法 ?\n", + "super不是“父类”,不是单纯的简化父类名称,而是已绑定当前实例的、按照MRO规则定位到的下一个类的方法代理对象。\n", + "用处:多继承的情况下,一定要用super(),此时还是靠父类.方法的话,会破坏多继承的关系。\n", + "什么是MRO规则?Python在调用方法时,查找类的固定顺序表。\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "375c789c", + "metadata": {}, + "source": [ + "### 命名空间\n", + "原因:为了解决项目中命名冲突的问题\n", + "含义:命名空间(Namespace)是从名称到对象的映射,大部分的命名空间都是通过 Python 字典来实现的。\n", + "特点:同个命名空间内不能有重名,各个命名空间相互独立,没有关系。\n", + "\n", + "命名空间的种类及查找顺序:函数或类的局部命名空间→模块中的全局命名空间→Python语言内置的名称(如abs、char和异常名称等)\n", + "嵌套函数中外部函数的作用域属于一个非局部、非全局的作用域。\n", + "\n", + "命名空间的生命周期:取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束。\n", + "\n", + "局部变量的优先级高于全局变量。\n", + "当内部作用域想修改外部作用域的变量时,可以用global关键字和nonlocal关键字。其中global影响的是全局,nonlocal影响的是外层非全局作用域。\n", + "```python\n", + "num = 1\n", + "def fun1():\n", + " global num # 让程序不用创建局部变量,而是使用全局变量。\n", + " # 并非声明一个内部变量(nonlocal功能类似)\n", + " print(num) # 输出 1\n", + " num = 123\n", + " print(num) # 输出 123\n", + "fun1()\n", + "print(num) # 输出 123\n", + "```\n", + "\n", + "易错点:只要一个变量名在函数体内出现了赋值语句,Python 就会把它判定为该函数的“局部变量”(除非你显式声明 global num 或 nonlocal num)。以下代码会有错误:\n", + "```python\n", + "num = 1\n", + "def fun1():\n", + " num1 = num\n", + " print(num1) \n", + " num = 123 \n", + " print(num) \n", + "fun1()\n", + "print(num) \n", + "\n", + "# 函数体里出现对 num 的赋值,就会让 num 在该函数里变成局部变量\n", + "# 所以在调用num1 = num的时候,这句里面的num是局部变量,但是没有赋值,所以就会报错UnboundLocalError(先读后赋值)。\n", + "```\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "682ef5d8", + "metadata": {}, + "source": [ + "### Python虚拟环境创建\n", + "含义:独立运行的Python环境,允许在同一台机器上为不同的项目创建隔离的Python环境。每个虚拟环境都有自己的Python解释器、安装的包/库、环境变量。\n", + "作用:创建虚拟环境,实现项目隔离(不同项目需要不同版本的Python或第三方库)、避免冲突(防止全局Python环境被污染)、依赖管理(方便记录和分享项目的依赖关系)、安全测试项目新包\n", + "创建虚拟环境:\n", + "python -m venv .venv\n", + "\n", + "激活(PowerShell):\n", + ".\\.venv\\Scripts\\Activate.ps1\n", + "\n", + "激活(CMD):\n", + ".\\.venv\\Scripts\\activate.bat\n", + "\n", + "退出虚拟环境:\n", + "deactivate\n", + "\n", + "看当前 python/pip 是否在虚拟环境里:\n", + "where python\n", + "where pip\n", + "python -c \"import sys; print(sys.executable)\"\n", + "pip -V\n", + "\n", + "安装 / 卸载 / 查看包:\n", + "pip install requests\n", + "pip uninstall requests\n", + "pip list\n", + "pip show requests\n", + "\n", + "固化依赖 / 按依赖安装:\n", + "pip freeze > requirements.txt\n", + "pip install -r requirements.txt\n", + "\n", + "升级 pip(建议用这种写法)\n", + "python -m pip install --upgrade pip" + ] + }, + { + "cell_type": "markdown", + "id": "f0f47880", + "metadata": {}, + "source": [ + "### 类型注解\n", + "1.变量注解:\n", + "```python\n", + "name: str = \"Alice\" # 注解为字符串 (str)\n", + "age: int = 30 # 注解为整数 (int)\n", + "is_student: bool = False # 注解为布尔值 (bool)\n", + "scores: list = [95, 88, 91] # 注解为列表 (list)\n", + "```\n", + "2.函数注解:\n", + "```python\n", + "# 有类型注解的函数\n", + "def greet(first_name: str, last_name: str) -> str:\n", + "# first_name、last_name参数类型应为字符串\n", + "# ->str,这个函数执行后会返回一个字符串\n", + " full_name = first_name + \" \" + last_name\n", + " return \"Hello, \" + full_name\n", + "```\n", + "3.复杂类型注解\n", + "(1)容器类型\n", + "```python\n", + "from typing import List, Dict, Tuple, Set\n", + "# List[int] 表示这是一个只包含整数的列表\n", + "numbers: List[int] = [1, 2, 3, 4, 5]\n", + "# Dict[str, int] 表示这是一个键为字符串、值为整数的字典\n", + "student_scores: Dict[str, int] = {\"Alice\": 95, \"Bob\": 88}\n", + "# Tuple[int, str, bool] 表示这是一个包含整数、字符串、布尔值的元组\n", + "person_info: Tuple[int, str, bool] = (25, \"Alice\", True)\n", + "# Set[str] 表示这是一个只包含字符串的集合\n", + "unique_names: Set[str] = {\"Alice\", \"Bob\", \"Charlie\"}\n", + "```\n", + "(2)可选类型\n", + "```python\n", + "from typing import Optional\n", + "def find_student(name: str) -> Optional[str]:\n", + " \"\"\"根据名字查找学生,可能找到也可能返回None\"\"\"\n", + " students = {\"Alice\": \"A001\", \"Bob\": \"B002\"}\n", + " return students.get(name) # 可能返回字符串或None\n", + "# 等价于 Union[str, None]\n", + "# Optional表示,可能是某种类型,或者是None\n", + "```\n", + "(3)联合类型(Union)\n", + "可能是多种类型之一。\n", + "`def process_input(data: Union[str, int, List[int]]) -> None:`\n", + "可能是字符串、整数或者整数列表的输入\n", + "\n", + "可以使用Mypy进行静态类型检查,pip install mypy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3837d1cb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Parent\n", + "Child\n", + "Hello Wrold! from Parent.\n", + "Child bar function\n", + "I' the parent.\n" + ] + } + ], + "source": [ + "class FooParent(object):\n", + " def __init__(self):\n", + " self.parent = 'I\\' the parent.'\n", + " print('Parent')\n", + " def bar(self,message):\n", + " print(f'{message} from Parent.')\n", + "\n", + "class FooChild(FooParent):\n", + " def __init__(self):\n", + " super().__init__() # 这一步不需要再传self\n", + " # super()已经知道当前的实例是谁,会自动把self传给父类方法\n", + " print('Child')\n", + "\n", + " def bar(self,message):\n", + " super().bar(message)\n", + " print('Child bar function')\n", + " print(self.parent)\n", + "\n", + "fooChild = FooChild()\n", + "fooChild.bar('Hello Wrold!')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "edeac874", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "FooParent.__init__() takes 1 positional argument but 2 were given", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[11], line 18\u001b[0m\n\u001b[0;32m 15\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mChild bar function\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m 16\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mparent)\n\u001b[1;32m---> 18\u001b[0m fooChild \u001b[38;5;241m=\u001b[39m FooChild()\n\u001b[0;32m 19\u001b[0m fooChild\u001b[38;5;241m.\u001b[39mbar(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mHello Wrold!\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", + "Cell \u001b[1;32mIn[11], line 10\u001b[0m, in \u001b[0;36mFooChild.__init__\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 9\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[1;32m---> 10\u001b[0m \u001b[38;5;28msuper\u001b[39m()\u001b[38;5;241m.\u001b[39m\u001b[38;5;21m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m)\n\u001b[0;32m 11\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mChild\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", + "\u001b[1;31mTypeError\u001b[0m: FooParent.__init__() takes 1 positional argument but 2 were given" + ] + } + ], + "source": [ + "class FooParent(object):\n", + " def __init__(self):\n", + " self.parent = 'I\\' the parent.'\n", + " print('Parent')\n", + " def bar(self,message):\n", + " print(f'{message} from Parent.')\n", + "\n", + "class FooChild(FooParent):\n", + " def __init__(self):\n", + " super().__init__(self)\n", + " print('Child')\n", + "\n", + " def bar(self,message):\n", + " super().bar(self,message)\n", + " print('Child bar function')\n", + " print(self.parent)\n", + "\n", + "fooChild = FooChild()\n", + "fooChild.bar('Hello Wrold!')" + ] + }, + { + "cell_type": "markdown", + "id": "4b514b92", + "metadata": {}, + "source": [ + "### 函数\n", + "静态方法\n", + "### 模块\n", + "Python中的模块是独立的命名空间,模块中的函数和变量不会自动导入到全局命名空间。\n", + "模块,例如random模块,本身可以看做一个静态对象,可以通过random.choice()来调用random模块中的choice()函数,这种调用方式可以避免命名冲突。\n", + "\n", + "1.静态对象\n", + "(1)含义:静态对象是指在程序运行期间,其状态或行为不会发生改变,或者不需要实例化就可以直接使用的对象。\n", + "(2)常见类型:模块、类的静态成员、不可变对象\n", + "(3)应用场景:静态对象无需实例化即可使用,而动态对象是需要实例化之后才可以使用的对象。而且静态对象创建后不可变。\n", + "\n", + "类的方法\n" + ] + }, + { + "cell_type": "markdown", + "id": "32f5df29", + "metadata": {}, + "source": [ + "### 其他知识点\n", + "1.在 Jupyter Notebook 中执行过的变量会一直保留在当前 Kernel 的内存里 \n", + "可以运行`%whos`(显示变量名、类型、值)、`%who`(只显示变量名)、`dir()`(会把魔法变量也列出来)。 \n", + "可以用`del`语句,删除不想要的变量;或者重启Jupyter Kernel(最彻底)。 \n", + "原因:因为 Jupyter Notebook 的执行模式是 stateful(有状态): \n", + "前面 cell 定义的变量,后面 cell 可以继续用,就算你删除 cell,变量仍然保留,只有重启 Kernel 才会清掉所有变量。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1d24a5a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Variable Type Data/Info\n", + "-----------------------------\n", + "dict2 dict n=0\n", + "dict3 dict n=2\n", + "list1 list n=4\n", + "s str 物品\t单价\t数量\\n包子\t1\t2\n", + "tuple1 tuple n=4\n", + "x set {'n', 'u', 'b', 'r', 'o'}\n" + ] + } + ], + "source": [ + "%whos" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0bd8ee68", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Runoo\n", + "R\n", + "noo\n", + "noob\n", + "Ru\n", + "boonuR\n", + "boonuR\n" + ] + } + ], + "source": [ + "str = 'Runoob'\n", + "print(str[0:-1]) # 'Runoob'\n", + "print(str[0]) # 'R'\n", + "print(str[2:5]) # 'noo'\n", + "print(str[2:]) # 'noob'\n", + "print(str[:2]) # 'Ru'\n", + "print(str[::-1]) # 'boonuR',默认步长为1,步长为-1时会反转\n", + "print(str[-1::-1]) # 'boonuR',默认步长为1,步长为-1时会反转" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3ecd9167", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'Runoob': 1, 'Google': 2, 'Taobao': 3}\n", + "{2: 4, 4: 16, 6: 36}\n", + "{'Runoob': 1, 'Google': 2, 'Taobao': 3}\n" + ] + } + ], + "source": [ + "del dict\n", + "\n", + "dict1 = dict([('Runoob', 1), ('Google', 2), ('Taobao', 3)])\n", + "print(dict1) # {'Runoob': 1, 'Google': 2, 'Taobao': 3}\n", + "dict2 = {x: x**2 for x in (2, 4, 6)}\n", + "print(dict2)# {2: 4, 4: 16, 6: 36}\n", + "dict3 = dict(Runoob=1, Google=2, Taobao=3)\n", + "print(dict3) # {'Runoob': 1, 'Google': 2, 'Taobao': 3}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "040e17dd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'Zhihu', 'Facebook', 'Runoob', 'Taobao', 'Baidu', 'Google'}\n", + "Runoob 在集合中\n", + "{'b', 'c', 'r', 'a', 'd'}\n", + "{'b', 'd', 'r'}\n", + "{'m', 'b', 'c', 'r', 'a', 'd', 'z', 'l'}\n", + "{'a', 'c'}\n", + "{'m', 'b', 'z', 'l', 'r', 'd'}\n" + ] + } + ], + "source": [ + "sites = {'Google', 'Taobao', 'Runoob', 'Facebook', 'Zhihu', 'Baidu'}\n", + "print(sites) # 输出集合,重复的元素被自动去掉\n", + "\n", + "# 成员测试\n", + "if 'Runoob' in sites :\n", + " print('Runoob 在集合中')\n", + "else :\n", + " print('Runoob 不在集合中')\n", + "\n", + "# set可以进行集合运算\n", + "a = set('abracadabra')\n", + "b = set('alacazam')\n", + "\n", + "print(a)\n", + "print(a - b) # a 和 b 的差集\n", + "print(a | b) # a 和 b 的并集\n", + "print(a & b) # a 和 b 的交集\n", + "print(a ^ b) # a 和 b 中不同时存在的元素" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc91bb2b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "False\n", + "True\n", + "True\n", + "True\n" + ] + } + ], + "source": [ + "class A:pass\n", + "class B(A):pass\n", + "\n", + "b = B()\n", + "print(type(b) is B) # True\n", + "print(type(b) is A) # False\n", + "print(isinstance(b,B)) # True\n", + "print(isinstance(b,A)) # True\n", + "print(issubclass(B,A)) # True" + ] + }, + { + "cell_type": "markdown", + "id": "73c336d4", + "metadata": {}, + "source": [ + "### Python错误和警告\n", + "1.`print(1 is True)`会有警告:\n", + "SyntaxWarning:\"is\" with 'int' literal. Did you mean \"==\"?\n", + "2.TypeError: 'dict' object is not callable\n", + "是因为先前的代码把dict类型覆盖成了一个字典对象,导致dict变为一个普通的对象,故报错不可调用。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ae936f2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "人会说话\n", + "Allen is learnning 英语\n", + "\n", + "\n", + "False\n", + "True\n", + "True\n", + "False\n", + "False\n" + ] + } + ], + "source": [ + "class Person:\n", + "\n", + " def __init__(self,name,age):\n", + " self.name = name\n", + " self.age = age\n", + " \n", + " def speak(self):\n", + " print(\"人会说话\")\n", + "\n", + "class Student(Person):\n", + "\n", + " def __init__(self,name,age):\n", + " super().__init__(name,age)\n", + " \n", + " def learn(self,course):\n", + " print(f'{self.name} is learnning {course}')\n", + "\n", + "stu1 = Student('Allen',26)\n", + "stu1.speak()\n", + "stu1.learn('英语')\n", + "\n", + "print(type(stu1))\n", + "print(type(Student))\n", + "print(type(stu1) is Person)\n", + "print(isinstance(stu1,Person))\n", + "print(isinstance(stu1,Student))\n", + "print(isinstance(Student,Person))\n", + "print(isinstance(bool,int))" + ] + }, + { + "cell_type": "markdown", + "id": "bc2e5920", + "metadata": {}, + "source": [ + "- 运算符(优先级从高到低)\n", + " - 比较运算符:> >= < <= == !=\n", + " - 赋值运算符:= += *=\n", + " - 逻辑预算符:and or not\n", + " - 数学运算符: + - * / % // **" + ] + }, + { + "cell_type": "markdown", + "id": "1779142d", + "metadata": {}, + "source": [ + "> 字符串属于数据还是数据结构 \n", + "> 数字属于数据还是数据结构 \n", + "> 数据和数据结构有什么关系" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1181ca0f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hello\n" + ] + } + ], + "source": [ + "a =10\n", + "a = 'hello'\n", + "print(a)" + ] + }, + { + "cell_type": "markdown", + "id": "b30f7a39", + "metadata": {}, + "source": [ + "### 变量\n", + "- 变量含义\n", + " Python中的变量代表内存中的一个位置?\n", + "- 变量命名规则 \n", + " - Python变量允许以数字+字符(理解为英文字母)+符号,如涉及多个单词之间建议用下划线 \n", + " - 常量为大写\n", + "- 变量类型(需要整合为表格)\n", + " - 可变类型\n", + " - 整数(int)\n", + " - 浮点数(float)\n", + " - 列表(list)\n", + " - \n", + " - 不可变类型\n", + " - 字符串(str)\n", + " - 元组(tuple)\n", + " - 字典(dict)\n", + " - 变量类型的转换\n", + " - 类型转换函数(应该是区分了强制转换和自动转换?)\n", + " - 转换为整数 `int(x)`,转换时可带进制\n", + " - 转换为浮点数`float(x)`\n", + " - 转换为字符串`str(x)`\n", + " - 转换为列表`list(x)`\n", + " - 转换为元组`tuple(x)`\n", + " - 转换为字典?\n", + " - 自动转换\n", + " - 带有浮点数的数学运算的结果,均为float类型 2/1 = 2.0\n", + " - \n", + "- 变量的声明\n", + " - 元组的声明,`tup1 = (1,)`,必须带括号,否则仅为仅为用于确定运算顺序的括号\n", + " - 字典的声明,\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "ec409422", + "metadata": {}, + "source": [ + "### 基本数据类型\n", + "数据类型对应的方法\n", + "string.split()?" + ] + }, + { + "cell_type": "markdown", + "id": "bcbe2001", + "metadata": {}, + "source": [ + "### 分支循环结构\n", + "- 分支结构 \n", + " if elif else" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c7644079", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "成绩优秀\n" + ] + } + ], + "source": [ + "a = 100\n", + "if a>90:\n", + " print('成绩优秀')\n", + "elif a>60:\n", + " print('成绩合格')\n", + "else:\n", + " print('成绩不合格')" + ] + }, + { + "cell_type": "markdown", + "id": "202e48e0", + "metadata": {}, + "source": [ + "- 循环结构 \n", + " while循环\n", + " for循环\n", + " range函数的问题" + ] + }, + { + "cell_type": "markdown", + "id": "a573f908", + "metadata": {}, + "source": [ + "### 函数\n", + "- 函数的定义\n", + " Python内的函数为\n", + "- 内置函数\n", + "- 装饰器\n", + "- 递归调用" + ] + }, + { + "cell_type": "markdown", + "id": "ff4a1328", + "metadata": {}, + "source": [ + "### 模块\n", + "- 标准库?" + ] + }, + { + "cell_type": "markdown", + "id": "6392017b", + "metadata": {}, + "source": [ + "### 面向对象\n", + "#### 面向对象的含义 \n", + "面向对象编程是一种编程范式(即程序设计的方法论)。\n", + "在面向对象编程的世界里,程序中的**数据和操作数据的函数是一个逻辑上的整体**,称之为**对象**。 \n", + "> 还有指令式变成、函数式编程等编程范式。\n", + "\n", + "对象可以接受消息,解决问题的方法就是创建对象并向对象发出各种各样的消息;通过消息传递,程序中的多个对象可以协同工作,这样就能构造出复杂的系统并解决现实中的问题。\n", + "> 面向对象编程:把一组数据和处理数据的方法组成**对象**,把行为相同的对象归纳为**类**,通过**封装**隐藏对象的内部细节,通过**继承**实现类的特化和泛化,通过**多态**实现基于对象类型的动态分派。\n", + "> - 封装:隐藏内部实现细节,往外暴露简单调用接口。例如在类中定义对象的方法就是封装,给对象发一个消息(即不用明确具体实现)就可以执行方法中的代码。\n", + "#### 面向对象编程三步走\n", + "第一步:定义类 \n", + "定义类就是要找到对象的公共属性或者方法 \n", + "第二步:创建对象 \n", + "第三步:给对象发消息 \n", + "Python内置的list、set、dict都是提前定义好的类,可以跳过第一步; \n", + "如果第一步、第二部都提前走了,就是某些内置对象,可以“开箱即用”。" + ] + }, + { + "cell_type": "markdown", + "id": "cd6c2b68", + "metadata": {}, + "source": [ + "#### 类和对象\n", + "含义:类是对象的蓝图和模板(例如人类),对象是类的实例(例如具体的人),是可以接受消息的实体。 \n", + "> 一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类。 \n", + "> 接收消息是什么意思?\n", + "\n", + "属性:属性是对象的静态特征,行为(方法)是对象的动态特征。具有共同特征的对象和属性和行为抽取出来就可以定义一个类。" + ] + }, + { + "cell_type": "markdown", + "id": "cb0d8728", + "metadata": {}, + "source": [ + "类的定义和使用:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "04fdc6f2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "<__main__.Student object at 0x0000024596C1A2D0>\n", + "<__main__.Student object at 0x00000245968E7D40>\n", + "0x24596c1a2d0 0x245968e7d40\n", + "王克章正在学习Python程序设计.\n", + "王大锤学生正在玩游戏.\n", + "王克章正在学习Python程序设计.\n", + "王大锤学生正在玩游戏.\n" + ] + } + ], + "source": [ + "### 类的创建 ###\n", + "class Student:\n", + "\n", + " def __init__(self,name,age): # 初始化方法,即常见对象时,\n", + " #首先会在内存中获取对象的所需的内存空间,然后会自动执行该方法,完成对内存的初始化操作\n", + " self.name = name # 对象属性赋初始化值\n", + " self.age = age # = 右侧的age是传入的参数\n", + "\n", + " def study(self,course_name):\n", + " print(f'{self.name}正在学习{course_name}.')\n", + "\n", + " def play(self):\n", + " print(f'{self.name}学生正在玩游戏.')\n", + "\n", + "### 类的使用 ### \n", + "stu1 = Student('王克章',28) # 构造器语法创建类的对象,此时会传入类的初始化参数\n", + "stu2 = Student('王大锤',25)\n", + "print(stu1) \n", + "# 输出 <__main__.Student object at 0x000002459692B2C0>\n", + "# 上面这个输出代表是一个Student类的对象,以及这个对象的以十六进制表示的内存地址\n", + "# __main__表示Student类定义在主模块中(当前脚本)\n", + "print(stu2) \n", + "# 输出 <__main__.Student object at 0x000002459692B2C0>\n", + "print(hex(id(stu1)),hex(id(stu2)))\n", + "# 函数hex()将整数转为十六进制字符串\n", + "# 函数id()返回对象在内存中的唯一标识(一般是内存地址),该标识在对象的全生命周期内保持一致\n", + "stu3 = stu2 # 并没有创建新的对象\n", + "# 变量存的是一个对象在内存中的逻辑地址(位置)\n", + "\n", + "### 调用对象的方法 ###\n", + "Student.study(stu1,'Python程序设计')# 类.方法(接收消息的对象,方法入参) 来调用\n", + "Student.play(stu2)\n", + "stu1.study('Python程序设计')# 对象.方法(仅为方法入参)来调用\n", + "stu2.play()" + ] + }, + { + "cell_type": "markdown", + "id": "52825732", + "metadata": {}, + "source": [ + "> 写在类里面的函数我们通常称之为方法,方法就是对象的行为,也就是对象可以接收的消息。方法的第一个参数通常都是self,它代表了接收这个消息的对象本身。 \n", + "> 这个self怎么理解?\n", + "> 是不是类中定义的所有函数的第一个参数都要是self?" + ] + }, + { + "cell_type": "markdown", + "id": "aa224ddb", + "metadata": {}, + "source": [ + "#### 面向对象案例" + ] + }, + { + "cell_type": "markdown", + "id": "b339fc8b", + "metadata": {}, + "source": [ + "##### 例子1:时钟\n", + "> 要求:定义一个类描述数字时钟,提供走字和显示时间的功能。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4922cdf1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "12:26:34\n", + "12:26:35\n", + "12:26:36\n", + "12:26:37\n" + ] + }, + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[5], line 25\u001b[0m\n\u001b[0;32m 23\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[0;32m 24\u001b[0m \u001b[38;5;28mprint\u001b[39m(clock1\u001b[38;5;241m.\u001b[39mshow())\n\u001b[1;32m---> 25\u001b[0m time\u001b[38;5;241m.\u001b[39msleep(\u001b[38;5;241m1\u001b[39m)\n\u001b[0;32m 26\u001b[0m clock1\u001b[38;5;241m.\u001b[39mrun()\n", + "\u001b[1;31mKeyboardInterrupt\u001b[0m: " + ] + } + ], + "source": [ + "import time\n", + "\n", + "class Clock: # 定义时钟类\n", + " def __init__(self,hour=0,minute=0,second=0):\n", + " self.hour = hour\n", + " self.min = minute\n", + " self.sec = second\n", + " \n", + " def run(self): # 走字\n", + " self.sec += 1\n", + " if self.sec == 60:\n", + " self.sec = 0\n", + " self.min += 1\n", + " if self.min == 60:\n", + " self.min = 0\n", + " self.hour += 1\n", + " if self.hour == 24:\n", + " self.hour = 0\n", + " def show(self):\n", + " return f'{self.hour:>2d}:{self.min:0>2d}:{self.sec:>02d}'\n", + "\n", + "clock1 = Clock(12,26) # 创建时钟对象\n", + "while True:\n", + " print(clock1.show())\n", + " time.sleep(1)\n", + " clock1.run() " + ] + }, + { + "cell_type": "markdown", + "id": "c22975b2", + "metadata": {}, + "source": [ + "##### 例2:平面上的点\n", + "> 要求:定义一个类描述平面上的点,提供计算到另一个点距离的方法。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dca8b607", + "metadata": {}, + "outputs": [], + "source": [ + "class Point:\n", + " \"\"\"平面上的点\"\"\"\n", + "\n", + " def __init__(self,x=0,y=0):\n", + " self.x,self.y = x,y\n", + " \n", + " def distance_to(self,other):# other表示另一个对象\n", + " dx = self.x - other.x\n", + " dy = self.y - other.y\n", + " return (dx*dx + dy*dy)**0.5\n", + " \n", + " def __str__(self): # 这个是什么意思?\n", + " # 可以理解为对象的字符串表示形式,就是调整print(对象)显示的内容\n", + " return f'({self.x},{self.y})'\n", + "\n", + "p1 = Point(3,5)\n", + "p2 = Point(6,9)\n", + "print(p1)\n", + "print(p2)\n", + "print(p1.distance_to(p2))\n" + ] + }, + { + "cell_type": "markdown", + "id": "c5cdb3b0", + "metadata": {}, + "source": [ + "#### 可见性和属性装饰器" + ] + }, + { + "cell_type": "markdown", + "id": "207cad3b", + "metadata": {}, + "source": [ + "对象的属性被设置成私有(private,属性前加__)或受保护(protected,属性前加_),以避免外界直接访问这些属性。\n", + "> 为什么要不允许外界直接访问?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a32d115d", + "metadata": {}, + "outputs": [], + "source": [ + "class Student:\n", + " __slots__ = ('name','age')\n", + " def __init__(self,name,age):\n", + " self.__name = name\n", + " self.__age = age\n", + " def study(self,course_name):\n", + " print(f'{self.__name}正在学习{course_name}.')\n", + "stu = Student('王大锤',20)\n", + "stu.sex = '男' # 给stu这个对象动态添加sex属性\n", + "print(stu.sex)\n", + "stu.study('Python程序设计') # 可以间接访问,不能直接访问\n", + "print(stu.__name) # 报错'Student' object has no attribute '__name'" + ] + }, + { + "cell_type": "markdown", + "id": "82abe644", + "metadata": {}, + "source": [ + "#### 动态属性\n", + "Python在运行时可以改变其结构,属于动态语言。\n", + "可以动态为对象添加属性(有点类似于可变类型数据)" + ] + }, + { + "cell_type": "markdown", + "id": "cec875c5", + "metadata": {}, + "source": [ + "> 名称改写机制(Name Mangling)是 Python 中的一种机制,用于避免子类覆盖父类的私有属性或方法。它会将以双下划线 __ 开头但不以双下划线结尾的属性或方法名,自动改写为 _类名__属性名 的形式。 \n", + "> 名称改写的主要目的是为了实现类的封装,避免子类意外覆盖父类的私有属性或方法。\n" + ] + }, + { + "cell_type": "markdown", + "id": "b944aefb", + "metadata": {}, + "source": [ + "class Triangle(object): 这里面加不加object有什么区别 " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "391876e3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "print(Student)" + ] + }, + { + "cell_type": "markdown", + "id": "2093e7f3", + "metadata": {}, + "source": [ + "#### 继承和多态\n", + "例如Teacher类继承自Person类" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "158e54f0", + "metadata": {}, + "outputs": [], + "source": [ + "class Person:\n", + " def __init__(self,name,age):\n", + " self.name = name\n", + " self.age = age\n", + " def eat(self):\n", + " print(f'{self.name}正在吃饭。')\n", + " def sleep(self):\n", + " print(f'{self.name}正在睡觉。')\n", + "\n", + "class Teacher(Person): # Teacher类继承自Person类\n", + " def __init__(self,name,age,title): \n", + " super().__init__(name,age) \n", + " # 子类中通过super().__init__()来调用父类初始化方法\n", + " self.title = title\n", + " # 子类定义自己特有的属性和方法\n", + " def teach(self,course_name):\n", + " print(f'{self.name}{self.titel}正在讲授{course_name}.')\n" + ] + }, + { + "cell_type": "markdown", + "id": "a6374de7", + "metadata": {}, + "source": [ + "#### 面向对象编程举例" + ] + }, + { + "cell_type": "markdown", + "id": "75cb9380", + "metadata": {}, + "source": [ + "##### 例子1:扑克牌游戏\n", + "说明:简单起见,我们的扑克只有52张牌(没有大小王),游戏需要将 52 张牌发到 4 个玩家的手上,每个玩家手上有 13 张牌,按照黑桃、红心、草花、方块的顺序和点数从小到大排列,暂时不实现其他的功能。" + ] + }, + { + "cell_type": "markdown", + "id": "cc705878", + "metadata": {}, + "source": [ + "> 发现不止一个对象或者类,要考虑类之间的关系: \n", + "> - is-a(继承)\n", + "> - has-a(关联)\n", + "> - use-a(依赖) \n", + "> 以以上例子为例,因为一副扑克有(has-a)52张牌,所以扑克和牌是has-a关系(关联);因为玩家手上有(has-a)牌,而且玩家使用了(use-a)牌,所以玩家和牌之间不仅有关联关系,还有依赖关系。\n", + ">\n", + "> 疑问:分这三类有什么具体用处?\n", + ">\n", + "> 类的属性和方法: \n", + "> 牌:属性包括花色和点数\n", + "> 扑克:属性包括牌剩余牌数;方法包括被抓牌然后减少牌数\n", + "> 玩家:属性包括手里牌的张数;方法包括抓牌、排序\n", + "\n", + "5 行话理解如何设计类(非常关键) \n", + "① 圈名词 → 找类、属性 \n", + "不是所有名词都是类,现实中独立存在的实体、有自己的状态和行为、在系统中是会变化的(这种变化应该是相对复杂的,用类可以保存状态并提供操作状态的方法,这种状态的变化不是单一变量可以处理的)、会在系统中被多个对象使用(需要复用)的才可能成为类\n", + "② 圈动词 → 找方法 \n", + "③ 基于“现实世界职责”分给合适的类 \n", + "④ 让每个类只负责“它自己能做的事” \n", + "⑤ 用组合(has-a)连接类,用方法调用(use-a)产生交互\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5d5d6ca", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Suite.SPADE:0\n", + "Suite.HEART:1\n", + "Suite.CLUB:2\n", + "Suite.DIAMOND:3\n", + "♠5\n", + "♥K\n", + "东邪:[♠2, ♠3, ♠7, ♠8, ♠10, ♠J, ♠Q, ♥9, ♣A, ♣2, ♣5, ♣6, ♦K]\n", + "西毒:[♠9, ♥A, ♥5, ♥7, ♥8, ♥Q, ♣3, ♣9, ♣Q, ♦2, ♦4, ♦7, ♦9]\n", + "南帝:[♠5, ♠6, ♠K, ♥2, ♥3, ♥10, ♥J, ♦A, ♦3, ♦5, ♦8, ♦10, ♦J]\n", + "北丐:[♠A, ♠4, ♥4, ♥6, ♥K, ♣4, ♣7, ♣8, ♣10, ♣J, ♣K, ♦6, ♦Q]\n" + ] + } + ], + "source": [ + "from enum import Enum # Python中创建枚举值要引入enum模块\n", + "\n", + "class Suite(Enum): # 新建一个Suite类来继承Enum类\n", + " \"\"\"牌花色的枚举值\"\"\"\n", + " SPADE,HEART,CLUB,DIAMOND = range(4)\n", + "\n", + "for suite in Suite: # Python中的枚举值可以迭代\n", + " print(f'{suite}:{suite.value}')\n", + "\n", + "# 定义牌这个类\n", + "# 牌类初始化时,牌一定是有指定的花色和大小\n", + "\n", + "class Card:\n", + " \"\"\"牌\"\"\"\n", + "\n", + " def __init__(self,suite,face):\n", + " self.suite = suite\n", + " self.face = face\n", + " \n", + " # __repr__(self):定义对象的开发者友好字符串表示,\n", + " # repr(obj) 或直接在交互式环境中输入对象时会调用。\n", + " def __repr__(self):\n", + " suites = '♠♥♣♦'\n", + " faces = ['','A','2','3','4','5','6',\n", + " '7','8','9','10','J','Q','K']\n", + " return f'{suites[self.suite.value]}{faces[self.face]}'\n", + " # 返回牌的花色和点数\n", + " \n", + " def __lt__(self,other):\n", + " if self.suite == other.suite:\n", + " return self.face < other.face # 花色相同比较点数大小\n", + " return self.suite.value < other.suite.value # 花色不同比较花色对应值\n", + "\n", + "card1 = Card(Suite.SPADE,5)\n", + "card2 = Card(Suite.HEART,13)\n", + "print(card1)\n", + "print(card2)\n", + "\n", + "# 定义扑克类\n", + "# 扑克类初始化时一定是指定的52张牌,\n", + "# 另外还有洗牌、发牌的方法,有还剩多少张牌的属性\n", + "import random\n", + "\n", + "class Poker:\n", + " \"\"\"扑克\"\"\"\n", + "\n", + " def __init__(self):# 52张牌构成的列表\n", + " self.cards = [Card(suite,face)\n", + " for suite in Suite\n", + " for face in range(1,14)]\n", + " self.current = 0 # 记录发牌位置的属性\n", + "\n", + " def shuffle(self):\n", + " \"\"\"洗牌\"\"\"\n", + " self.current = 0\n", + " random.shuffle(self.cards)\n", + " # 通过random模块的shuffle函数实现随机乱序\n", + " \n", + " def deal(self):\n", + " \"\"\"发牌\"\"\"\n", + " card = self.cards[self.current]\n", + " self.current += 1\n", + " return card\n", + " \n", + " @property\n", + " def has_nex(self):\n", + " \"\"\"还有没有牌可以发\"\"\"\n", + " return self.current < len(self.cards)\n", + " \n", + "# 定义玩家类\n", + "# 玩家类有摸牌、整理手牌的方法\n", + "class Player:\n", + " \"\"\"玩家\"\"\"\n", + "\n", + " def __init__(self,name):\n", + " self.name = name\n", + " self.cards = [] # 玩家手里的牌\n", + "\n", + " def get_one(self,card):\n", + " \"\"\"摸牌\"\"\"\n", + " self.cards.append(card)\n", + " \n", + " def arrange(self):\n", + " \"\"\"整理手上的牌\"\"\"\n", + " self.cards.sort()\n", + " \n", + "# 创建四个玩家,并发牌到玩家手上\n", + "poker = Poker()\n", + "poker.shuffle()\n", + "players = [Player('东邪'),Player('西毒'),Player('南帝'),Player('北丐')]\n", + "# 将牌轮流发到每个玩家手上每人13张牌\n", + "for _ in range(13):\n", + " for player in players:\n", + " player.get_one(poker.deal())\n", + "# 玩家整理手上的牌输出名字和手牌\n", + "for player in players:\n", + " player.arrange()\n", + " print(f'{player.name}:',end='')\n", + " print(player.cards)\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "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.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git "a/\345\255\246\344\271\240\347\254\224\350\256\260/data.pkl" "b/\345\255\246\344\271\240\347\254\224\350\256\260/data.pkl" new file mode 100644 index 000000000..b48525e06 Binary files /dev/null and "b/\345\255\246\344\271\240\347\254\224\350\256\260/data.pkl" differ diff --git "a/\345\255\246\344\271\240\347\254\224\350\256\260/git\345\267\245\344\275\234\346\265\201\347\250\213.md" "b/\345\255\246\344\271\240\347\254\224\350\256\260/git\345\267\245\344\275\234\346\265\201\347\250\213.md" new file mode 100644 index 000000000..a16b892f3 --- /dev/null +++ "b/\345\255\246\344\271\240\347\254\224\350\256\260/git\345\267\245\344\275\234\346\265\201\347\250\213.md" @@ -0,0 +1,60 @@ +## Git 工作流程 +本章节我们将为大家介绍 Git 的工作流程。 + +下图展示了 Git 的工作流程: +![Git 的工作流程](image.png) + +### 1、克隆仓库 +如果你要参与一个已有的项目,首先需要将远程仓库克隆到本地: +``` +git clone https://github.com/username/repo.git +cd repo +``` +> 问题1:如何只下载某个文件,不是克隆整个仓库 + +#### 如果确定远程为准,直接覆盖掉本地文件 +`git status`可以看未提交的修改(工作区+暂存区) +`nothing to commit,working tree clean`则为干干净净,表示无任何修改 +`git pull`只看提交记录是否一致,不管本地是否修改了文件。 +`git pull = git fetch + git merge`,git会比较本地当前分支的commit ID和远程当前分支的commit ID,如果两个ID一样,就会认为是`already up to date` +即使执行`git commit`,也是将文件在本地做了提交,需要再执行`git push origin master`才可以将本地的提交,提交到远程仓库 + +### 2、创建新分支 +为了避免直接在 main 或 master 分支上进行开发,通常会创建一个新的分支: +`git checkout -b new-feature` +### 3、工作目录 +在工作目录中进行代码编辑、添加新文件或删除不需要的文件。 +### 4、暂存文件 +将修改过的文件添加到暂存区,以便进行下一步的提交操作: +`git add filename` +或者添加所有修改的文件 +`git add .` +### 5、提交更改 +将暂存区的更改提交到本地仓库,并添加提交信息: +`git commit -m "Add new feature"` +### 6、拉取最新更改 +在推送本地更改之前,最好从远程仓库拉取最新的更改,以避免冲突: +`git pull origin main` +或者如果在新的分支上工作 +`git pull origin new-feature` +> 最新的更改,指的是最新远程仓库上面的信息吗? +### 7、推送更改 +将本地的提交推送到远程仓库: +`git push origin new-feature` +### 8、创建 Pull Request(PR) +在 GitHub 或其他托管平台上创建 Pull Request,邀请团队成员进行代码审查。PR 合并后,你的更改就会合并到主分支。 +### 9、合并更改 +在 PR 审核通过并合并后,可以将远程仓库的主分支合并到本地分支: +``` +git checkout main +git pull origin main +git merge new-feature +``` +### 10、删除分支 +如果不再需要新功能分支,可以将其删除: +`git branch -d new-feature` +或者从远程仓库删除分支: +`git push origin --delete new-feature` + +### 如果需要从github往本地下载文件怎么办, +只下载部分文件而且这些文件已经在本地了, \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\254\224\350\256\260/image.png" "b/\345\255\246\344\271\240\347\254\224\350\256\260/image.png" new file mode 100644 index 000000000..768579417 Binary files /dev/null and "b/\345\255\246\344\271\240\347\254\224\350\256\260/image.png" differ diff --git "a/\345\255\246\344\271\240\347\254\224\350\256\260/markdown\350\257\255\346\263\225.ipynb" "b/\345\255\246\344\271\240\347\254\224\350\256\260/markdown\350\257\255\346\263\225.ipynb" new file mode 100644 index 000000000..0e62a396e --- /dev/null +++ "b/\345\255\246\344\271\240\347\254\224\350\256\260/markdown\350\257\255\346\263\225.ipynb" @@ -0,0 +1,68 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "44897e58", + "metadata": {}, + "source": [ + "## 一、标题(#)\n", + "### 三级标题\n", + "#### 四级标题\n", + "\n", + "## 二、段落与换行\n", + "这是第一段。(空一行=新段落)\n", + "\n", + "这是第二段(行末两个空格=换行) \n", + "换行后的内容\n", + "## 三、粗体\n", + "**粗体** \n", + "*斜体* \n", + "~~删除线~~ \n", + "也可以组合 **加粗*内部斜体*组合**\n", + "## 四、列表\n", + "无序列表: \n", + "- 项目1 \n", + "- 项目2 \n", + " - 项目2.1 \n", + "\n", + "有序列表: \n", + "1. 项目1 \n", + "2. 项目2 \n", + "2.1 项目2.1 \n", + "2.2 项目2.2 \n", + "## 五、引用(>)\n", + "> 这是引用 \n", + "> 可以多行\n", + "## 六、代码\n", + "请执行 `print('Hello World')`\n", + "代码块: \n", + "```python\n", + "ded add(a,b):\n", + " return a + b\n", + "```\n", + "## 七、表格\n", + "|姓名|年龄|城市|\n", + "|------|------|------|\n", + "|张三|20|北京|\n", + "|李四|25|上海|\n", + "## 八、链接与图片\n", + "[超链接名称](http://www/baidu.com) \n", + "![超链接名称](https://www.baidu.com/img/flexible/logo/pc/result.png) \n", + "或者使用本地图片也可以\n", + "## 九、分割线(---或者***)\n", + "---\n", + "---\n", + "## 十、任务列表\n", + "- [x] 已完成任务 \n", + "- [ ] 待办任务\n" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git "a/\345\255\246\344\271\240\347\254\224\350\256\260/myfile.txt" "b/\345\255\246\344\271\240\347\254\224\350\256\260/myfile.txt" new file mode 100644 index 000000000..f2ba8f84a --- /dev/null +++ "b/\345\255\246\344\271\240\347\254\224\350\256\260/myfile.txt" @@ -0,0 +1 @@ +abc \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\254\224\350\256\260/python_object_system_full.md" "b/\345\255\246\344\271\240\347\254\224\350\256\260/python_object_system_full.md" new file mode 100644 index 000000000..7dd27b44c --- /dev/null +++ "b/\345\255\246\344\271\240\347\254\224\350\256\260/python_object_system_full.md" @@ -0,0 +1,252 @@ +# Python 对象体系全景图(完整版本) + +## 📑 目录(TOC) + +- [1. Python 中的 object 是一切的根](#1-python-中的-object-是一切的根) +- [2. object → collections.abc + 行为体系](#2-object--collectionsabc-行为体系) + - [2.1 Iterable(可迭代)](#21-iterable可迭代) + - [2.2 Sequence(序列)](#22-sequence序列) + - [2.3 Set(集合)](#23-set集合) + - [2.4 Mapping(映射)](#24-mapping映射) + - [2.5 Iterator(迭代器)](#25-iterator迭代器) + - [2.6 Generator(生成器)](#26-generator生成器) + - [2.7 Hashable(可哈希)](#27-hashable可哈希) + - [2.8 Callable(可调用)](#28-callable可调用) +- [3. Python 数值体系(numbers 模块)](#3-python-数值体系numbers-模块) +- [4. I/O 类型](#4-io-类型) +- [5. 模块对象 module](#5-模块对象-module) +- [6. 类与实例体系](#6-类与实例体系) +- [7. 上下文管理器](#7-上下文管理器) +- [8. 特殊类型](#8-特殊类型) +- [9. 完整树形结构总结](#9-完整树形结构总结) +- [10. 终极总结](#10-终极总结) + +------------------------------------------------------------------------ + +# 1. Python 中的 object 是一切的根 + +Python 采用统一对象模型(Everything is an object)。 + +所有内容(int、list、dict、class、function...)都继承自 object。 + +------------------------------------------------------------------------ + +# 2. object → collections.abc 行为体系 + +collections.abc 定义了容器、序列、映射等行为规范。 + +------------------------------------------------------------------------ + +# 2.1 Iterable(可迭代) + + Iterable + ├── Container + ├── Sized + └── Sequence + +Iterable 典型类型: + +- list / tuple / dict / set\ +- str / bytes\ +- range\ +- file 对象\ +- generator + +------------------------------------------------------------------------ + +# 2.2 Sequence(序列) + +序列 = 有序 + 可索引。 + + Sequence + ├── list(可变) + ├── tuple(不可变) + ├── range(不可变) + ├── str(不可变字符序列) + └── bytes(不可变字节序列) + +可变序列: + + MutableSequence + └── list + +------------------------------------------------------------------------ + +# 2.3 Set(集合) + + Set + ├── set(可变) + └── frozenset(不可变) + +------------------------------------------------------------------------ + +# 2.4 Mapping(映射) + + Mapping + ├── dict(可变) + └── MappingProxyType(只读映射) + +------------------------------------------------------------------------ + +# 2.5 Iterator(迭代器) + + Iterator + ├── list_iterator + ├── tuple_iterator + ├── range_iterator + └── dict_*iterator + +只要实现了 **next** 和 **iter**,就是迭代器。 + +------------------------------------------------------------------------ + +# 2.6 Generator(生成器) + + Generator + └── generator(yield) + +------------------------------------------------------------------------ + +# 2.7 Hashable(可哈希) + +可作为字典 key 或 set 元素。 + + Hashable + ├── int + ├── float + ├── str + ├── bytes + ├── tuple(内部全可哈希) + └── frozenset + +------------------------------------------------------------------------ + +# 2.8 Callable(可调用) + +能执行 "()" 的就是 Callable。 + + Callable + ├── function + ├── method + ├── class(可实例化) + └── 实现 __call__ 的对象 + +示例: + +``` python +class A: + def __call__(self): + print("callable") +``` + +------------------------------------------------------------------------ + +# 3. Python 数值体系(numbers 模块) + + Number + ├── Integral → int + ├── Real → float + ├── Complex → complex + └── Rational → Fraction + +------------------------------------------------------------------------ + +# 4. I/O 类型 + + TextIOWrapper(文本文件) + BufferedReader + BufferedWriter + BytesIO + StringIO + +------------------------------------------------------------------------ + +# 5. 模块对象 module + + module(如 math、os、sys) + +模块也是对象。 + +------------------------------------------------------------------------ + +# 6. 类与实例体系 + + type(类的类型) + └── class A + └── A 实例对象 + +------------------------------------------------------------------------ + +# 7. 上下文管理器 + +只要实现了: + + __enter__() + __exit__() + +即可用于 with 语句。 + +典型例子:文件、锁、数据库连接等。 + +------------------------------------------------------------------------ + +# 8. 特殊类型 + + NoneType(None) + bool + ellipsis(...) + NotImplementedType + memoryview + bytearray(可变字节序列) + +------------------------------------------------------------------------ + +# 9. 完整树形结构总结 + + object + ├── Iterable + │ ├── Sequence(list, tuple, range, str, bytes) + │ ├── MutableSequence(list) + │ ├── Set(set, frozenset) + │ ├── Mapping(dict) + │ ├── Iterator(所有迭代器) + │ └── Generator + │ + ├── Hashable(int, str, tuple, frozenset, bytes) + │ + ├── Callable(函数、类、方法、__call__ 对象) + │ + ├── Number(int, float, complex, Fraction) + │ + ├── IO(文件对象、缓冲区) + │ + ├── module(math, os...) + │ + ├── type(所有类的类型) + │ + ├── 自定义 class 与实例 + │ + └── 特殊类型(NoneType, bool, ellipsis 等) + +------------------------------------------------------------------------ + +# 10. 终极总结 + +Python 的体系本质是: + +> **行为驱动(Protocol / ABC)+ 鸭子类型(Duck Typing)**\ +> 只要对象实现了对应方法(协议),就被视为该类。 + +例如: + +- 有 `__iter__` → Iterable\ +- 有 `__getitem__` → Sequence 行为\ +- 有 `__next__` → Iterator\ +- 有 `__call__` → Callable + +Python 是通过行为判断,而不是类型名字判断。 + +------------------------------------------------------------------------ + +(全文结束) diff --git "a/\345\255\246\344\271\240\347\254\224\350\256\260/\345\255\246\344\271\240\346\226\271\346\263\225\351\227\256\351\242\230.md" "b/\345\255\246\344\271\240\347\254\224\350\256\260/\345\255\246\344\271\240\346\226\271\346\263\225\351\227\256\351\242\230.md" new file mode 100644 index 000000000..e7528c2cd --- /dev/null +++ "b/\345\255\246\344\271\240\347\254\224\350\256\260/\345\255\246\344\271\240\346\226\271\346\263\225\351\227\256\351\242\230.md" @@ -0,0 +1,270 @@ + # 什么是“一句话总结”: + - 它是什么 + - 为什么存在(解决什么问题?) + - 什么时候用 + + +# 我的Python越学越乱的原因: +- 记得是“细节”,而不是“框架” +- 我学到的每一个知识点在脑中没有一个“抽象层级” +- 我不知道这个知识点解决什么问题 +- 新出现的类型、新的语法,对我来说都是孤立的 + + +# 核心观念:Python 的所有东西,都可以归为三类 +① 数据 +② 行为(函数) +③ 容器 + 协议(数据如何被操作) + +我看到的所有“新类型”“新用法”其实都只是: +- 一种新的数据结构 +- 一个函数/方法 +- 一种操作规则(协议,如迭代、上下文、可哈希、序列类型) +- 一个标准库里封装好的工具 + +# Python 三大核心概念:数据、行为、协议 + +本文件系统性地解释 Python 世界中所有“看似无穷无尽的知识点”背后的统一抽象模型。 +帮助你用底层原理建立“不会乱、不怕新知识”的学习体系。 + +--- + +# 📍 第一类:数据(Data) +Python 的一切基础都是“数据”。 +你看到的所有类型,本质上都是 **数据结构**。 + +## 🧩 常见基础数据类型(一句话总结) +| 数据类型 | 一句话总结 | +|---------|------------| +| **int** | 一个整数值 | +| **float** | 一个小数 | +| **str** | 一段文本(不可变) | +| **list** | 可变序列 | +| **tuple** | 不可变序列 | +| **dict** | 哈希表,用 key 查 value | +| **set** | 去重 + 集合运算 | +| **bytes** | 二进制序列 | +| **bool** | True / False | +| **class** | 用户自定义的数据结构 | + +--- + +## 🧩 你未来会看到的“新类型”(其实也是数据) +例如: + +- datetime +- Fraction +- Decimal +- pathlib.Path +- re.Match +- MySQLCursor +- Response(requests) +- BeautifulSoup +- numpy.ndarray +- pandas.DataFrame + +它们都不是“新奇怪物”,它们都是: + +> Python 内置或第三方库定义的 **新的数据结构(结构不同,本质相同)** + +### ✔ 结论 +**所有“新数据类型”都是“数据”,不是新概念,只是新结构。** + +--- + +# 📍 第二类:行为(Function) +数据能做什么,由“行为”决定。 + +行为的统一定义: + +## ⭐ 行为 = 函数 + 方法(绑定到对象的函数) + +--- + +## 🎯 示例(你看到的一切操作,都是行为) +| 代码 | 类型 | 说明 | +|------|------|-------| +| `len(a)` | 函数 | 获取长度 | +| `a.append(3)` | 方法 | list 的行为 | +| `d.get("name")` | 方法 | dict 的行为 | +| `re.match()` | 函数 | 正则匹配 | +| `random.randint()` | 函数 | 取随机数 | + +本质上它们都是: + +> 输入数据 → 行为处理 → 输出结果 +> (方法还会改变对象内部状态) + +--- + +## ✔ 为什么这能消除你的焦虑? +当你知道: + +- list 的操作是 list 定义的行为 +- dict 的操作是 dict 定义的行为 +- 库里的方法是库的作者定义的行为 + +你看到“新方法、新函数、新 API” +就不会觉得那是“新知识点”,它只是“行为”。 + +--- + +# 📍 第三类:容器 + 协议(规则) +这是 Python 最强大也最容易让初学者困惑的部分。 + +--- + +# 1️⃣ 什么是容器? +容器就是“能装数据的结构”。 + +常见容器: + +- list +- tuple +- dict +- set +- generator +- iterator +- file +- pandas.DataFrame +- numpy.ndarray + +它们结构不同,但本质相同: +**装数据 + 对外提供统一操作行为(协议)** + +--- + +# 2️⃣ 什么是协议? +协议 ≠ 网络协议 +协议 = **规定对象必须具备的能力(钩子方法)** + +只要该能力被实现,对象就能表现出某种“行为”。 + +--- + +# 🔥 Python 常见协议(必须掌握) + +## ① 可迭代协议(Iterable) +只要对象实现: + +``` +__iter__() +``` + +它就能: + +- 被 for 循环 +- 被 list() 转换 +- 被展开:`*obj` +- 被 sum()、max() 等函数使用 + +### 🔥 所有这些都能 for,因为它们实现了 iterable: +- list +- tuple +- dict +- str +- range +- pandas.Series +- generator + +它们长得不一样,但行为一样,是因为遵守了 **同一个协议**。 + +--- + +## ② 上下文管理协议(Context Manager) +只要对象实现: + +``` +__enter__() +__exit__() +``` + +它就能被 `with` 使用。 + +### 这些都能用 with: +- 文件对象 +- 数据库连接 +- 锁 +- Selenium 浏览器 +- Requests Session +- TensorFlow Session +- contextlib 生成的对象 + +它们毫不相关,却都能: + +```python +with obj: + ... +``` + +因为它们遵守同一个协议。 + +--- + +## ③ 可调用协议(Callable) +如果一个对象实现: + +``` +__call__() +``` + +那么它就可以作为函数调用: + +```python +obj() +``` + +Python 的装饰器、回调机制、类实例当函数,都是用这个协议。 + +--- + +## ④ 数字协议(算术运算) +对象实现这些方法后,就能进行数学运算: + +``` +__add__() → + +__sub__() → - +__mul__() → * +``` + +numpy / pandas 的底层,就是依靠这些协议。 + +--- + +## ⑤ 序列协议(像数组一样) +只要实现: + +``` +__getitem__() +__len__() +``` + +它就能: + +- 被索引:`obj[0]` +- 被切片:`obj[1:3]` +- 被遍历 + +--- + +# 📌 最终总结(非常关键) + +## ⭐ Python 所有看似复杂的东西,都能归类为三件事: + +### **① 数据(Data)—— 类型本质是数据结构** +### **② 行为(Function/Method)—— 数据能做什么** +### **③ 协议(Protocol)—— 对象具备哪些能力** + +--- + +# ✔ 这意味着: + +- 新类型?→ 属于“数据” +- 新方法?→ 属于“行为” +- 能被 for?→ 遵守迭代协议 +- 能被 with?→ 遵守上下文协议 +- 能被调用?→ 遵守可调用协议 + +所有新东西都“有位置”,不会让你困惑。 + +--- diff --git "a/\345\255\246\344\271\240\347\254\224\350\256\260/\347\254\224\350\256\260\346\225\264\347\220\206.md" "b/\345\255\246\344\271\240\347\254\224\350\256\260/\347\254\224\350\256\260\346\225\264\347\220\206.md" new file mode 100644 index 000000000..b61c78452 --- /dev/null +++ "b/\345\255\246\344\271\240\347\254\224\350\256\260/\347\254\224\350\256\260\346\225\264\347\220\206.md" @@ -0,0 +1,1649 @@ +## 前言(原文保留) + +> **记笔记的目的**: +> +> ★笔记不是“完整教程”,而是理解+易错点+示例+练习 +> +> 每一节: +> - “一句话总结(核心)” +> - “示例(能运行、最简单)” +> - “易错点(踩过的坑)” +> - “应用场景(什么时候用)” + +> 什么是“一句话总结”: +> - 它是什么 +> - 为什么存在(解决什么问题?) +> - 什么时候用 + + +## 一、环境与运行 +### (一)安装与版本 +#### 1.Python 安装、解释器选择 +##### 环境变量的含义: + +##### 解释器是啥: +解释器(Interpreter)就是用来运行 Python 代码的程序。你写的 .py 文件本身不能直接被 Windows 执行,需要由解释器读取并执行。在 Python 里,“解释器”通常指你电脑上的 python.exe(Windows)这一套运行环境。 + +解释器具体做什么: +- 读取你的代码(源代码) +- 解析语法、编译成字节码(.pyc) +- 交给 Python 虚拟机执行 +- 提供标准库、管理内存、异常处理等运行时能力 + +多个解释器的情况(`where python`查看当前解释器路径): +- 系统 Python +- Anaconda/Miniconda 的 Python +- VS Code 选择的虚拟环境里的 Python(.venv\Scripts\python.exe) +它们都是“解释器”,只是路径和安装环境不同。 + +#### 2.版本差异(Python3.x) +### (二)开发工具 +#### 1.VS Code 基础配置、常用快捷键 +- 如何修改配置 +- 如何正确使用侧边栏AI +- 常用快捷键 +#### 2.代码格式化与提示(pyright/pylance) +#### 3.VSCode里面的KerNel内核 +### (三)虚拟环境 +#### 1.含义及作用 +含义:独立运行的Python环境,允许在同一台机器上为不同的项目创建隔离的Python环境。每个虚拟环境都有自己的Python解释器、安装的包/库、环境变量。 + +作用:创建虚拟环境,实现项目隔离(不同项目需要不同版本的Python或第三方库)、避免冲突(防止全局Python环境被污染)、依赖管理(方便记录和分享项目的依赖关系)、安全测试项目新包 +#### 2.虚拟环境管理 +```python +# 创建虚拟环境: +python -m venv .venv + +# 激活(PowerShell): +.\.venv\Scripts\Activate.ps1 +# 激活(CMD): +.\.venv\Scripts\activate.bat + +# 退出虚拟环境: +deactivate + +# 看当前 python/pip 是否在虚拟环境里: +where python +where pip +python -c "import sys; print(sys.executable)" +pip -V + +# 安装 / 卸载 / 查看包: +pip install requests +pip uninstall requests +pip list +pip show requests + +# 固化依赖 / 按依赖安装: +pip freeze > requirements.txt +pip install -r requirements.txt + +# 升级 pip(建议用这种写法) +python -m pip install --upgrade pip +``` +### (四)运行方式 +#### 1.脚本运行 +`python xxx.py` +#### 2.模块运行 +`python -m package.module` +#### 3.交互式 +- Jupyter Notebook +- shell等 +交互模式中,最后被输出的表达式结果被赋值给变量_,例如: +```shell +>>> tax = 12.5 / 100 +>>> price = 100.50 +>>> price * tax +12.5625 +>>> price + _ +113.0625 +>>> round(_, 2) +113.06 +``` +#### 4.常见命令行窗口 +| 命令行窗口/工具 | 类型 | 主要作用/特点 | 适合场景 | 常见打开方式(Windows) | +|---|---|---|---|---| +| 命令提示符(cmd.exe) | Shell | 传统 Windows 命令行,兼容性最好,适合基本命令与批处理 | 基础文件操作、运行 `.bat`、执行系统命令 | 开始菜单搜索 `cmd` | +| PowerShell(Windows PowerShell / PowerShell 7) | Shell | 更强的脚本与自动化能力,“对象管道”,命令功能更丰富 | 自动化脚本、系统管理、批量处理 | 开始菜单搜索 `PowerShell`(建议 PowerShell 7) | +| Windows Terminal(wt) | 终端宿主/容器 | 多标签/分屏终端,可同时承载 cmd/PowerShell/WSL 等 | 统一管理多个终端会话 | 开始菜单搜索 “Windows Terminal” 或运行 `wt` | +| Git Bash | Shell(类 Unix) | 提供 bash 环境与常见 Unix 命令(如 `ls/cp/mv/grep`),方便配合 Git | 使用类 Linux 命令、Git 操作 | 安装 Git for Windows 后打开 “Git Bash” | +| WSL 终端(Ubuntu 等) | Linux Shell 环境 | 在 Windows 上运行 Linux 用户态,命令链更贴近服务器环境 | Linux 开发/部署一致性、使用原生 Linux 工具链 | Windows Terminal 选择 Ubuntu 或运行 `wsl` | +| VS Code 集成终端 | IDE 内终端 | 在 VS Code 内运行 cmd/PowerShell/Git Bash/WSL,工作目录与项目联动 | 边写代码边运行、项目内调试 | VS Code:`Ctrl + \`` 打开,右上角选择 shell | + +--- + +#### 4.常见命令速查(按用途汇总) + +| 用途 | cmd(命令提示符) | PowerShell | bash/WSL(Git Bash / Linux) | +|---|---|---|---| +| 查看当前位置/切换目录/列目录 | `cd` / `cd /d d:\github` / `dir` | `Get-Location` / `Set-Location D:\github` / `ls` | `pwd` / `cd /d/github` / `ls` | +| 创建目录 | `mkdir test` | `mkdir test` | `mkdir test` | +| 查看文件内容 | `type a.txt` | `cat a.txt` | `cat a.txt` | +| 复制文件 | `copy a.txt b.txt` | `Copy-Item a.txt b.txt` | `cp a.txt b.txt` | +| 移动文件 | `move a.txt .\sub\` | `Move-Item a.txt .\sub\` | `mv a.txt sub/` | +| 删除文件 | `del a.txt` | `Remove-Item a.txt` | `rm a.txt` | +| 删除目录(慎用) | `rmdir /s /q folder` | `Remove-Item folder -Recurse -Force` | `rm -rf folder` | +| 文本搜索 | `findstr /s /n "keyword" *.py` | `Select-String -Path *.py -Pattern "keyword"` | `grep -RIn "keyword" .` | +| 网络/端口排查 | `ping github.com` / `ipconfig` / `netstat -ano \| findstr :8000` | `Test-NetConnection github.com -Port 443` +(也可用左列命令) | `ping` / `ip a`(或 `ifconfig`)/ `ss -lntp`(WSL) | +| 进程查看/结束 | `tasklist` / `taskkill /PID 1234 /F` | `Get-Process` / `Stop-Process -Id 1234 -Force` | `ps aux` / `kill -9 1234` | +| Python 常用 | `python --version` / `python -m venv .venv` / `.\.venv\Scripts\activate` / `python -m pip install -U pip` / `pip install requests` / `python xxx.py` / `python -m package.module` | 同 cmd(激活命令同样适用) | `python3 --version` / `python3 -m venv .venv` / `source .venv/bin/activate` / `python3 -m pip install -U pip` / `pip install requests` / `python3 xxx.py` | +| Git 常用 | `git status` / `git add .` / `git commit -m "msg"` / `git pull` / `git push` / `git branch` / `git checkout -b new-branch` | 同 cmd | 同 cmd | + +--- + +#### 4.VS Code 里选择终端类型 +| 操作 | 说明 | +|---|---| +| 打开集成终端 | `Ctrl + \``(反引号) | +| 切换 shell | 终端右上角下拉:PowerShell / cmd / Git Bash / WSL | + +--- + +#### 5.存在问题 +如何知道当前使用的Python是哪个版本,对应的包是什么? + +--- + +## 二、基础语法与编码规范 +### (一)基本语法 +#### 1.缩进与代码块、注释 +##### (1)缩进与代码块 +- Python中用缩进表示代码块,而不是大括号`{}`。 +- Python中一条语句很长,可以用反斜杠`\`来实现换行。 +```python +total = item_one + \ + item_two + \ + item_three +``` +- Python中同一行也可以有多个语句,用分号;分隔。 +- 在类或函数之间一般会留一个空行,便于日后的维护和重构。 +- 当写示例时,可以使用pass这个占位语句,表示什么都不做,以避免语法错误。 +##### (2)注释 +``` +# 单行注释 +# 第二个注释 + +''' +第三行注释 +第四行注释 +''' + +""" +第五行注释 +第六行注释 +""" +``` +#### 2.命名与规范 +##### (1)变量命名规则 +语法规则(硬性) +- 只能由 字母、数字、下划线 _ 组成 +- 不能以数字开头:1a 不行,a1 可以 +- 区分大小写:name 和 Name 是两个变量 +- 不能用关键字:如 class、def、for、None 等。可用 keyword.kwlist 查看关键字列表 +- 避免用内置名当变量:比如 list = [] 会覆盖内置 list 类型名(不推荐) + +常见命名习惯(约定): +- 用有意义的名字:total_price 比 tp 更清晰 +- 多个单词用下划线分隔:user_name(snake_case) + +##### (2)最佳实践PEP8 +PEP8是 Python 官方的代码风格规范(写代码的“排版/命名/布局”建议),目的是让代码更统一、更好读,最常见的几条: +- 变量/函数名:小写 + 下划线 snake_case。例:get_user_info +- 类名:大驼峰 CapWords。例:UserProfile +- 常量:全大写 + 下划线。例:MAX_RETRY = 3 +- 缩进:4 个空格(不要 tab) +- 每行长度:通常建议不超过 79/99(工具会自动处理) +- 运算符两边空格:a = b + c +- 导入顺序:标准库、第三方库、本地代码分组导入 + +### (二)理解变量 +#### 1.变量的含义 +含义:Python中所有变量都是“名字→对象”的引用(变量存的是对象的地址)。 + +变量没有类型: +- 变量没有类型,只有对象有类型。 +- 变量本质上等同于“自动管理内存的安全指针”。 +- 一般说的变量类型指的是“变量当前指向的对象的类型”。 + + +- 几个问题(大部分使用了Python的魔法方法): + +a.如果变量存的是内存的地址,那么为什么print(变量)会返回对象的值? +print()调用的是变量所指向对象的`__str__()、__repr__()`方法,而不是打印地址。 +如果要打印地址,可以用print(id(a))返回变量a在内存中的地址id。 + +b.`a=1`和`a+1`中,变量a是地址,那为什么可以计算a+1? +先找到变量a指向的对象(1),调用该对象的__add__()方法(a.__add__(1))生成一个新的对象(2),然后把这个新的对象返回。原先的a和1都不会被修改。 +如果是`a=a+1`呢,这时候a引用的对象会变成2。 + +c.不可变对象(int,str,tuple)不能改,只能换对象; +可变对象(list,dict,set)能改,引用不变。 +不可变对象不允许修改内部值,修改只能创建新对象,旧对象如果无人引用则会被自动回收。 + +```python +a = 10 # int类型不可变 +b = a +a = 20 # 修改a的值,不会影响b的值,10,20是两个独立的对象 +# b 指的还是原先的整数10对象 + +c = [1,2,3] # list类型可变 +d = c +d.append(4) # 修改d的值,c的值也会变化,c和d是同个引用 +``` + +d.用del语句删除的是什么?是将对象从内存中直接删除吗? +del 删除的是名字(引用),不是“直接把对象从内存里抹掉”。而对象是否被释放取决于:它还有没有其他引用。 + +e.type(变量a)的时候,将变量a引用的对象作为参数传入内置函数type(),由type()查看该对象内部的`__class__属性` + +#### 2.变量声明 +- 元组的声明,`tup1 = (1,)`,必须带括号,否则仅为仅为用于确定运算顺序的括号 +- 字典的声明? + +#### 3.变量类型 +可变类型和不可变类型 +变量类型的转换: +- 类型转换函数(应该是区分了强制转换和自动转换?) +- 转换为整数 `int(x)`,转换时可带进制 +- 转换为浮点数`float(x)` +- 转换为字符串`str(x)` +- 转换为列表`list(x)` +- 转换为元组`tuple(x)` +- 转换为字典? + +- 自动转换 +- 带有浮点数的数学运算的结果,均为float类型 2/1 = 2.0 + +4.多变量赋值的问题: +`a=b=c=1` +`a,b,c=1,2,'runoob'` + +5.调换两个变量的值 +`a,b=b,a` + +### (三)命名空间与作用域 +作用:为了解决项目中命名冲突的问题。 + +含义:命名空间(Namespace)是从名称到对象的映射,大部分的命名空间都是通过 Python 字典来实现的。 + +特点:同个命名空间内不能有重名,各个命名空间相互独立,没有关系。 + +命名空间的种类及查找顺序:函数或类的局部命名空间→模块中的全局命名空间→外部函数中外层非全局命名空间→Python语言内置的名称(如abs、char和异常名称等) + +命名空间和作用域的关系: +- 命名空间(namespace):“名字 → 对象”的映射表(字典一样的东西),用来存放/管理变量名。 +- 作用域(scope):代码中某个名字“能在哪些位置被访问/查找”的范围规则。 +- 二者关系:作用域决定查哪个命名空间;命名空间负责“存了什么名字”。 + +修改命名空间的方法:局部变量的优先级高于全局变量。 +当内部作用域想修改外部作用域的变量时,可以用global关键字和nonlocal关键字。其中global影响的是全局,nonlocal影响的是外层非全局作用域。 + +```python +num = 1 +def fun1(): + global num # 让程序不用创建局部变量,而是使用全局变量。 + # 并非声明一个内部变量(nonlocal功能类似) + print(num) # 输出 1 + num = 123 + print(num) # 输出 123 +fun1() +print(num) # 输出 123 +``` + +易错点:赋值会改变作用域判定。 +只要一个变量名在函数体内出现了赋值语句,Python 就会把它判定为该函数的“局部变量”(除非你显式声明 global num 或 nonlocal num)。以下代码会有错误: +```python +num = 1 +def fun1(): + num1 = num + print(num1) + num = 123 + print(num) +fun1() +print(num) + +# 函数体里出现对 num 的赋值,就会让 num 在该函数里变成局部变量 +# 所以在调用num1 = num的时候,这句里面的num是局部变量,但是没有赋值,所以就会报错UnboundLocalError(先读后赋值)。 +``` +(待确认) +- 输入与输出 + - `input()` / `print()`(sep/end) + - 格式化输出:f-string、`format()` +- 常见易错点 + - 缩进错误、编码问题、字符串引号混用 + + +--- + +## 03 数据类型与数据结构 +### 3.1 类型体系总览 +#### 3.1.1 数据类型、数据结构、变量类型、对象类型之间的区别和联系? +- 数据类型(data type):值/对象的类别,如 int、str、list、dict。决定能做什么操作/有哪些方法。 +- 对象类型(object type):某个具体对象的类型,基本等同“数据类型”,用 type(obj) 查看。 +- 变量类型(variable type):Python 里变量名本身没固定类型;通常指“变量当前指向的对象类型”。 +- 数据结构(data structure):组织数据的方式与常见操作/效率。Python 的 list/dict/set 既是类型,也代表典型数据结构。 +> 联系:变量(名字)引用对象;对象有类型(数据类型/对象类型);某些类型实现了特定数据结构来组织数据。 + +#### 3.1.2 数据类型的用处? +- 知道能用哪些操作:str.split()、list.append()、dict.get() 等。 +- 减少错误、便于调试:避免类型不匹配(如 input() 是 str)。 +- 选对结构更高效:set/dict 查找通常比 list 快。 +- 提升可读性与可维护性:配合类型注解让接口更清晰。 + +#### 3.1.3 动态类型、强类型(strongly typed) +动态类型:类型不绑定在变量名上,而是绑定在对象上;同一个变量名运行过程中可以指向不同类型的对象。 +强类型:不同类型之间不会被“偷偷自动转换”来完成运算;类型不匹配通常直接报错,要求你显式转换。 + +#### 3.1.4 `type()` / `isinstance()` 的区别与使用场景 +- type(obj)用来查对象的实际类型(class),例如`type(a)`。 +- isinstance()用来判断对象(实例)是不是某个类型(或其子类)的实例,例如`isinstance(a,int)` +> 区别:type()严格匹配类型,不考虑继承;isinstance()会考虑继承关系。 +```python +class A:pass # pass占位语句 +class B(A):pass + +b = B() +print(type(b) is B) # True +print(type(b) is A) # False +print(isinstance(b,B)) # True +print(isinstance(b,A)) # True +print(issubclass(B,A)) # 判断类的继承关系,True +``` + +几个问题: +- `type(a) == int`和`type(a) is int`有什么区别? +`==` 是比较两个对象的“值”是否相等,不推荐,原因在`==`依赖类的`__eq__`,不是很严格; +`is` 是判断两个对象是否是“同一个对象”(id一致),推荐。 +补充说明:int在Python中也是一个对象(类对象) +- `type(A)`返回的是啥?这里面的A是类 +返回type,类也是对象,Python中所有类的类型都是type +- `isinstance(B,A)`为啥是False? +`isinstance()`是用于判断实例是否属于某个类或者某个父类。 +如果要判断两个类的继承关系,可以用`issubclass()` + +#### 3.1.4 真值测试(truthiness) +真值测试(truthiness)指的是:在 if、while、and/or/not 这类需要“判断真/假”的场景里,Python 会把任何对象自动当成 True 或 False 来用(不一定非得是 True/False)。哪些值为False: +- None +- 数值的零:0、0.0、0j、Decimal(0) 等 +- 空的容器/序列:''、[]、()、{}、set() +- 空的 range:range(0) + + +### 3.2 数值类型(Numbers) +#### 3.2.1 数值类型分类 +##### 3.2.1.1 int 整数 +十进制、二进制、八进制、十六进制 +```python +# print()默认输出数值是转换为十进制显示。 +print(0o12) #八进制,0o或者0O +print(0b1010) #二进制,0b或者0B +print(0xA) #十六进制,0x或者0X +#十六进制的数A B C D E F 分别表示十进制中的 10 11 12 13 14 15 + +print(oct(97)) #oct()十进制转为八进制字符串,输出0o141 +print(hex(97)) #hex()十进制转为十六进制字符串,输出0x61 +print(bin(97)) #bin()十进制转为二进制字符串,输出0b1100001 +``` +##### 3.2.1.2 float 浮点数 +1)浮点数本质:Python 的 `float` 基本等同于 **IEEE 754 双精度浮点数**(64 位) +很多十进制小数无法用二进制有限表示(类似 1/3 不能用有限小数表示一样) +0.1表示成二进制是: + +2)浮点数误差: +- 某些看似简单的计算会出现微小误差,例如: +```python +0.1 + 0.2 # 0.30000000000000004 +``` +- 比较的话,`a==b`会失败,可以用`math.isclose()`来做近似比较 +```python +import math +math.isclose(0.1 + 0.2, 0.3) # True +``` +- 金额场景避免用float: + - 用 `decimal.Decimal`(高精度十进制) + - 或使用“整数分”(把金额乘 100 变成 int 存储) + +3)round规则简介: +- `round(x, n)`:把 `x` 四舍五入到小数点后 `n` 位,返回结果类型通常仍是 `float`(或 `int` 当 n 省略时)。 +- 关键点:Python使用“ties to even”(银行家舍入) +即 当“正好在中间”(末位是 5 且后面全是 0 的理想情况)时,会舍入到**最近的偶数**: +```python +round(1.5) # 2 +round(2.5) # 2 (2 是偶数) +round(3.5) # 4 +``` +- round 错觉 +```python +round(2.675,2) # 2.67而不是2.68 +``` +原因在于浮点误差,所以财务/报表类需求,建议用 `Decimal` 并指定舍入模式。 +4)输出格式 +```python +# (1)print 默认显示 +x = 0.1 + 0.2 +print(x) # 0.30000000000000004 + +# (2)用 f-string 控制小数位(保留 2 位::.2f) +x = 1 / 3 +print(f"{x:.2f}") # 0.33 + +# (3)科学计数法输出(e) +x = 123456789.0 +print(f"{x:e}") # 1.234568e+08 +print(f"{x:.3e}") # 1.235e+08 + +# (4)百分比格式(:% 会乘以 100 并加 %) +ratio = 0.1234 +print(f"{ratio:.2%}") # 12.34% + +# (5)对齐与宽度(总宽度 8,保留 2 位:8.2f) +x = 12.3456 +print(f"|{x:8.2f}|") # | 12.35| + +# (6)更完整/可复现表示:repr +x = 0.1 + 0.2 +print(repr(x)) # '0.30000000000000004' +``` + +##### 3.2.1.3 complex 复数 +复数由实数部分和虚数部分构成,可以用a + bj,或者complex(a,b) 表示,复数的实部a和虚部b都是浮点型。如`3e+26j` + +##### 3.2.1.3 decimal.Decimal 高精度小数 +1)Decimal解决的问题: +float有二进制误差`0.1+0.2!=0.3`,0.1转化为二进制是一个无限循环数。 +Decimal用十进制表示存储和计算,可以精确表示0.1这种十进制小数,适合: +- 金额/财务/报表 +- 需要可控舍入规则(例如“四舍五入”“银行家舍入”“向上取整”等) +- 需要固定小数位(如保留 2 位) + +2)基本创建方式,建议从字符串或int构建: +```python +from decimal import Decimal +Decimal("0.1") + Decimal("0.2") # Decimal('0.3') +``` +从float创建,会将float的误差也带进来。因为float里面的0.1本身就是不准确的。 + +3)Decimal不能和float混算,会报错TypeError + +4)精度与上下文(context) +- Decimal 的运算精度/舍入等由 **上下文**控制:`getcontext()` +- 常见:设置有效数字精度 +```python +from decimal import getcontext +getcontext().prec = 28 # 默认通常就是 28(有效数字) +``` +> `prec` 是“有效数字位数”,不是“小数点后位数”。 + + +5)舍入( rounding )与 `quantize()`(最常用) +- 保留小数位、按指定规则舍入:用 `quantize()` +- 例:保留 2 位小数(常用于金额) +```python +from decimal import Decimal, ROUND_HALF_UP +x = Decimal("2.675") +x.quantize(Decimal("0.00"), rounding=ROUND_HALF_UP) # Decimal('2.68') +``` + +常见舍入模式(了解并会用 1~2 个): +- `ROUND_HALF_UP`:传统四舍五入(0.5 进位) +- `ROUND_HALF_EVEN`:银行家舍入(ties to even) +- `ROUND_DOWN`:向 0 方向截断 +- `ROUND_FLOOR`:向负无穷取整 + +##### 其他类型 +- `fractions.Fraction`(可选):分数 +#### 3.2.2 数值运算 +##### 3.2.2.1 运算符 +1)运算符分类总览 +- 算术运算符:`+ - * / // % **` +- 比较运算符:`== != < <= > >=` +- 逻辑运算符:`and or not` +- 赋值运算符:`= += -= *= /= //= %= **= :=` +- 位运算符:`& | ^ ~ << >>` +- 成员运算符:`in not in` +- 身份运算符:`is is not` +- (了解)条件表达式:`x if cond else y` + +2)算数运算符 +- `/` 真除法:结果一定是 `float` +- `//` 地板除:向下取整(对负数要注意),-9//2等于-5 +- `%` 取模:和 `//` 的关系:`a == (a//b)*b + (a%b)` +- `**` 幂 +- `+` 在不同类型上的含义(运算符重载) + - `int/float`:加法 + - `str/list/tuple`:拼接 +- 数值类型的混合运算(int + float -> float) + +3)比较运算符与链式比较 +- 基本比较:`== != < <= > >=` +- 链式比较:`0 < x < 10` 等价于 `0 < x and x < 10` +- 不同类型比较:Python3 通常不允许随便比较(如 `1 < "2"` 会报错) + +4)逻辑运算符(短路与返回值) +- 短路:`and/or` 可能不执行右边表达式 +展开说说:当左边结果已经足够决定整体结果,右边就不会执行。 +`a and b`,当a为True时,会继续计算b;当a为False时,不会计算b,结果肯定为False +`a or b`,当a为True,结果肯定为True,不会计算b +- 返回值不是布尔:返回“最后计算的那个操作数” +and/or是表达式运算符,会返回参与运算的某个操作数本身,而不是强制返回True/False。 + - `a or b`:返回第一个 truthy(真值),否则返回 b +```python +print("" or "default") # "default"("" 是 falsy) +print("hello" or "default") # "hello"(第一个 truthy) +print(0 or 10) # 10 +print([] or [1, 2]) # [1, 2] +``` +一般用于提供默认值,例如: +```python +name = input().strip() +name = name or "匿名用户" # 不输入名称就默认为“匿名用户” +``` + - `a and b`:返回第一个 falsy(假值),否则返回 b +```python +print(0 and 10) # 0(第一个 falsy) +print(5 and 10) # 10(a 为 truthy,所以返回 b) +print("hi" and "") # ""(b 是最后计算的操作数) +print([1] and [2, 3]) # [2, 3] +``` +一般用于前置条件判断: +```python +user = {"name": "Tom"} +print(user and user.get("name")) # "Tom" + +user = None +print(user and user.get("name")) # None(短路,不会执行右边,避免报错) +``` +- `not x`:总是返回 bool +not 是逻辑非运算,它会把 x 先做真值测试,然后取反,结果一定是 True 或 False。 +- 真值测试(truthiness):空容器/0/None 为 False + +5)赋值运算符与“重新绑定” +- `=` 是绑定名字到对象,不是“把值塞进变量” +- 增量赋值:`+=` 等 + - 对不可变类型:通常会创建新对象再绑定 + - 对可变类型:可能就地修改(例如 list 的 `+=`) +- 海象运算符:`:=`(表达式内赋值,Python 3.8+),用法举例: +```python +#传统写法 +n = 10 +if n > 5: + print(n) +#使用海象运算符 +if (n:=10) > 5: # 现把n赋值为10,再返回这个赋值结果 + print(n) +``` + +6)身份 vs 相等(非常重要) +- `==`:值是否相等(`__eq__`) +- `is`:是否同一个对象(比较 `id`) +`id()`能辅助理解,但不要把它当“内存地址”的语义依赖,`id(obj)` 在 CPython 上通常对应对象的内存地址(实现细节),你可以用它观察对象身份是否相同,但不要写依赖 `id` 的逻辑。 +- `None` 比较:用 `is None` / `is not None` +- 小整数/字符串驻留:不要依赖 `is` 做值比较 +Python 为了性能,可能会把某些常用对象(小整数、字符串)复用(实现细节),导致看起来像值比较。值比较永远用“==” +```python +a = 256 +b = 256 +print(a == b) # True +print(a is b) # 可能 True(实现细节:小整数缓存) +``` +```python +a = 1000 +b = 1000 +print(a is b) # 可能 False(不要依赖) +``` + +7)成员运算符(in / not in) +- 序列:按元素查找 +- 字典:`in` 查的是 key(不是 value) +- 自定义对象可通过 `__contains__` 定义行为 + +8)位运算符(知道含义与应用场景) +- 与/或/异或:`& | ^` +- 取反:`~x == -(x+1)` +- 左移/右移:`<< >>` +- 应用:掩码、权限位、二进制处理(了解) + +位运算符把数字看做二进制来进行计算。 +```python +a = 60 +print(f'{a:08b}') # a的二进制数为 00111100 +b = 13 +print(f'{b:08b}') # b的二进制数为 00001101 + +# & 按位与:两个对应位置都是1,则结果位为1,否则为0 +print(a&b) #输出12 # a&b为 00001100 + +# | 按位或:两个对应位置有一个是1,则结果位为1,否则为0 +print(a|b) #输出61 # a|b为 00111101 + +# ^ 按位异或:两个对应位置不同时,结果为1,否则为0 +print(a^b) #输出49 # a|b为 00110001 + +# ~ 按位取反:把0变成1,把1变成0 +print(~a) #输出-61 # ~a为 -0111101? + +# << 左移动若干位 +print(a<<2) #输出240,相当于放大了2的2次方倍 # <> 左移动若干位 +print(a>>2) #输出15,相当于缩小了2的2次方倍 # <>`、`<<` | 右移、左移 | +| `&` | 按位与 | +| `^`、`\|` | 按位异或、按位或 | +| `<=`、`<`、`>`、`>=` | 小于等于、小于、大于、大于等于 | +| `==`、`!=` | 等于、不等于 | +| `is`、`is not` | 身份运算符 | +| `in`、`not in` | 成员运算符 | +| `not`、`or`、`and` | 逻辑运算符 | +| `=`、`+=`、`-=`、`*=`、`/=`、`%=`、`//=`、`**=`、`&=`、`\|=`、`^=`、`>>=`、`<<=` | 赋值运算符| + + +10)可选扩展:运算符重载(面向对象相关) +运算符重载=“把运算符变成方法调用”,让你的类像内置类型一样自然地参与 `+`、比较、`abs()` 等操作。 +通过实现魔法方法(dunder methods)让自定义类支持 `+ - * < ==` 等运算符。例如你写 `a + b`,Python 实际会尝试调用:`a.__add__(b)`(加法) +- 例如 `__add__`、`__mul__`、`__lt__` 等 +- 让自定义类支持 `+`、比较等操作 + +常见运算符与对应魔法方法: +- 算术: + - `+` → `__add__`(反向:`__radd__`) + - `-` → `__sub__`(反向:`__rsub__`) + - `*` → `__mul__`(反向:`__rmul__`) + - `/` → `__truediv__` + - `//` → `__floordiv__` + - `%` → `__mod__` + - `**` → `__pow__` +- 比较: + - `==` → `__eq__` + - `<` → `__lt__` + - `<=` → `__le__` + - `>` → `__gt__` + - `>=` → `__ge__` +- 一元: + - `-x` → `__neg__` + - `+x` → `__pos__` + - `abs(x)` → `__abs__` +- 增量赋值(可选): + - `+=` → `__iadd__`(若未实现,退回到 `__add__` 并重新绑定) + + +最小示例:让自定义“向量”支持 `+` 和比较。 +```python +from dataclasses import dataclass +import math + +# @dataclass 是标准库dataclasses提供的装饰器,用来自动生成样板代码。 +# forzen = True 表示这个数据类是不可变的(immutable): +# ·创建实例后,不能再修改字段值(不能给 x、y 重新赋值) +# ·更像“值对象”(value object),适合表示坐标、配置、常量数据等 +# ·也更安全:避免被意外修改 +# 此外,@dataclass的另外一个作用就是:根据类里写的“类型注解字段”自动帮你生成 __init__(以及 __repr__、__eq__ 等)。所以你虽然没手写初始化方法,但类在定义完成后已经“被补上”了初始化逻辑。 +@dataclass(frozen=True) +class Vec2: + x: float + y: float + + # v1 + v2 + def __add__(self, other): + if not isinstance(other, Vec2): + return NotImplemented # 类型不匹配时返回`NotImplemented` + return Vec2(self.x + other.x, self.y + other.y) + + # v1 * 3(标量乘) + def __mul__(self, k): + if not isinstance(k, (int, float)): + return NotImplemented + return Vec2(self.x * k, self.y * k) + + # 3 * v1(反向乘) + def __rmul__(self, k): + return self.__mul__(k) + + # v 的“大小”(模长) + def norm(self) -> float: + return math.hypot(self.x, self.y) + # math.hypot(x, y) 用来计算直角三角形斜边长度,也就是二维向量的欧几里得范数(模长),math.hypot(3,4)为5 + + # v1 < v2(按模长比较) + def __lt__(self, other): + if not isinstance(other, Vec2): + return NotImplemented + return self.norm() < other.norm() + +v1 = Vec2(1, 2) +v2 = Vec2(3, 4) + +print(v1 + v2) # Vec2(x=4, y=6) +print(v1 * 2) # Vec2(x=2, y=4) +print(2 * v1) # Vec2(x=2, y=4) +print(v1 < v2) # True(按 norm 比较) + +``` +##### 3.2.2.2 常用函数 +常用函数: +- 绝对值`abs()`、四舍五入`round()` + +- 幂运算`pow(x,y,m)`,先做幂运算,再取模 +等价于(x**y)%m,区别在于pow(x,y,m)会用更高效的算法,在y很大时速度和内容都好很多 + +- `divmod(a,b)`一次得到`(商,余数)`,等价`//`和`%`的组合 +```python +divmod(17, 5) # (3, 2) 因为 17 = 3*5 + 2 +q, r = divmod(17, 5) +``` + +### 3.3 布尔类型(bool) +- `True/False` 与 `1/0` 的关系(`bool` 是 `int` 的子类) +所以布尔值可以参与整数运算,`True` 在数值上等于 `1`,`False` 在数值上等于 `0`。 + +- 逻辑运算:`and/or/not` 的短路规则与返回值特点 +见3.2.2.1运算符4)逻辑运算符(短路与返回值)。 + +### 3.4 空值(NoneType) +- `None` 是一个特殊的单例对象(singleton),类型是 `NoneType`: +```python +type(None) # +``` +- 语义通常表示: + - **缺省值/未提供**(没有传参数、没有配置) + - **不存在/找不到**(查询失败、没有结果) + - **尚未初始化**(占位) + - **函数未返回值**(默认返回 None) +- 比较:用 `is None` / `is not None`(不要用 `== None`) +is 比较对象身份,语义最准确。是不是None本质是判断是否指向同一个对象。 + +### 3.5 字符串(str)与文本处理 +#### 3.5.1 str 本质与关键特性 +1)`str` 是**不可变类型**(immutable):对字符串的“修改”会生成新字符串 +2)序列类型:支持下标、切片、遍历、len()、in 判断 +#### 3.5.3 编码基础:Unicode、UTF-8(概念) +1)Python中所有的字符串都是Unicode字符串。 + +2)**Unicode**是一套字符集合+编号规则,每个“字符”分配统一编号(码点),例如: + - `"A"` 的码点是 `U+0041`,`U+`表示这是Unicode码点,`0041`是十六进制,等于十进制65 + - `"中"` 的码点是 `U+4E2D` + +3)**UTF-8**:把Unicode码点编码(例如`A`的字符编码`U+0041`)成**字节序列(bytes)**的规则(可变长度 1~4 字节)。 + +4)**字节序列**是一个字符(或一段文本)在存储/传输时真正落到介质的那串8位一组的数据(也就是很多个byte)。不是我看到的字形,而是文件里/内存里/网络包里面的原始数据。 + +5)进一步理解: +我看见的字符`"A"`,Unicode给字符`"A"`分配的统一编号(码点)为`U+0041`,按照UTF-8编码规则,转换成对应的字节序列是`0x41`。 +我们说的 0x41、65、01000001 只是不同的“写法”(十六进制/十进制/二进制文本表示),本质上是同一份比特。 + +6)再进一步理解: + - 磁盘/网络层:只有bytes(字节序列) + - 文本抽象层:Unicode码点(字符是谁) + - 显示层:字体渲染成你看到的字形(glyph) +同一个字符在不同字体下长的不一样,但是码点一样; +同一个码点用UTF-8/UTF-16编出来的字节序列也不一样。 + +7)业务场景 +- 编码(Unicode 字符 → UTF-8 字节)场景: +当你把字符串写到文件/网络/数据库时会发生,比如: + • 你保存一个.md 文件为 UTF-8 + • print() 输出到终端(终端也需要字节流) + • 发 HTTP 请求 body +这时会把字符 A (U+0041) 编成字节 0x41。 +- 解码(UTF-8 字节 → Unicode 字符)场景: +当你从文件/网络读入字节并要当作文本使用时会发生,比如: + • 打开文件读取内容 + • 浏览器收到网页字节流并显示 + • 程序读取 socket 的数据并当作字符串处理 +这时把字节 0x41 解成字符 A (U+0041)。 +```python +s = "A中" +b = s.encode("utf-8") # str -> bytes +print(b) # b'A\xe4\xb8\xad' +# b的类型是字节串(bytes),print(b)时,Python会把这些原始字节用一种“人能读/能复制回去”的方式显示出来: +# 可打印的ASCII字节(0x20~0x7E)会直接显示成字符 +# 其他不可打印/非ASCII的字节会用转义形式显示:\xHH(HH为两位十六进制) +# 即"中"对应的3个字节分别为 0xE4 0xB8 0xAD +s2 = b.decode("utf-8") # bytes -> str +print(s2) # A中 +``` + +#### 3.5.3 常用操作:切片、拼接、查找、替换、分割、join +##### 3.5.3.1 切片(slice) +- 基本:s[start:stop:step](左闭右开) +- 常用写法: +s[:n] 前 n 个 +s[n:] 从 n 到结尾 +s[-n:] 最后 n 个 +s[::-1] 反转(Unicode 字符层面反转;对“人类视觉字符”如某些组合字符/emoji 可能不符合直觉) + +```python +str = 'Runoob' +print(str[0:-1]) # 'Runoo' +print(str[0]) # 'R' 其实也是索引 +print(str[2:5]) # 'noo' +print(str[2:]) # 'noob' +print(str[:2]) # 'Ru' +print(str[::-1]) # 'boonuR',默认步长为1,步长为-1时会反转 +print(str[-1::-1]) # 'boonuR',默认步长为1,截取内的参数[start,end,step],当start为-1,end为空时,默认从右到左 +``` +- 易错点 + - 切片不会越界报错(比索引更安全) +```python +s = "abc" +print(s[:10]) # 'abc' 不报错 +# print(s[10]) # IndexError +``` + - 下标为负时 +依旧左闭右开,例:s = "abcdef": +下标: 0 1 2 3 4 5 +字符: a b c d e f +负下标: -6-5-4-3-2-1 + +##### 3.5.3.2 拼接(concat) +小量拼接:+ / += 可用 +多段拼接(尤其循环):用 ''.join(parts)(性能更好) +```python +a = "hello" +b = "world" +print(a + " " + b) # 'hello world' +``` + +##### 3.5.3.3 查找(search) +常见三套:in / find / index +- "sub" in s:只关心“有没有”(返回 bool) +- s.find("sub"):返回起始下标,找不到返回 -1(不抛异常) +- s.index("sub"):找不到抛 ValueError(适合“必须存在”的逻辑) +```python +s = "hello world" +print("wor" in s) # True +print(s.find("wor")) # 6 +print(s.find("xxx")) # -1 +# print(s.index("xxx")) # ValueError +``` +补充:计数与位置 +s.count(sub):出现次数 +s.rfind(sub) / s.rindex(sub):从右找 + +##### 3.5.3.4 替换(replace) +- s.replace(old, new[, count]):返回新字符串 +补充:count最多替换count次匹配到的old,不写默认替换所有匹配。 +```python +s = "a-b-c-b" +print(s.replace("-", "_")) # 'a_b_c_b' +print(s.replace("b", "B", 1)) # 'a-B-c-b' +``` +- 易错点:替换是“按字面文本”,不是正则;需要模式匹配用,例如要把所有的数字替换成#,replace做不到,要用正则替换:re.sub(pattern, repl, string, count=0) +pattern 是正则表达式,比如 r"\d+" 表示“一个或多个数字” +```python +import re + +s = "a12b003c" +print(re.sub(r"\d+", "#", s)) # 'a#b#c' (把连续数字段替换) +``` +##### 3.5.3.4 格式化:f-string / format / % +- f-string(首选) +可读性强、速度快(一般场景) +- 常用格式: + - 保留小数:{x:.2f}、{:+.2f}(带+符号2位小数) + - 宽度对齐:{s:>10}(右对齐)、{:0>10d}(右对齐10位,不够补0)、{s:<10}(左对齐)、` + - 千分位:{n:,} + - 百分比:{ratio:.2%}(自动 ×100 + %) + - b、d、o、x 分别是二进制、十进制、八进制、十六进制。 +```python +name = "Tom" +score = 93.456 +n = 1234567 + +print(f"{name=} {score:.2f}") # name='Tom' 93.46 +print(f"{n:,}") # 1,234,567 +print(f"{0.1234:.1%}") # 12.3% +``` + +- str.format() +旧项目常见;功能同样强 +`print("name={0}, score={1:.2f}".format("Tom", 93.456))` + +- %(了解即可) +`print("score=%.2f" % 93.456)` +易错点:别拿 % 去拼很复杂的对象,维护性差。 + + +##### 3.5.3.5 转义/原始字符串 r''、多行字符串 +- 转义字符: +\n 换行、\t 制表、\\ 反斜杠、\' \" 引号 +- 原始字符串 r'': +作用:让反斜杠不再当转义(常用于正则、Windows 路径) +```python +path = r"D:\learnforlive\python\20260106" +# 如果不加r其中的\l \p \202 都会被认作转义字符,而且\p这些还会报语法错误 +print(path) +``` +易错点(重要):原始字符串不能以奇数个反斜杠结尾(会把结尾引号“逃逸”) +```python +# bad = r"C:\temp\" # 语法错误 +ok = r"C:\temp" + "\\" +``` +- 多行字符串(三引号) +常用于 docstring(文档字符串,Python 里写在模块、函数、类、方法定义处的第一条字符串字面量,用来给代码写说明文档) 或大段文本 +```python +text = """第一行 +第二行""" +``` + +##### 3.5.3.6 去除空白 strip/lstrip/rstrip +- 默认去掉两端空白(空格、\n、\t 等都算空白) +- 传参时不是“去掉某个子串”,而是“去掉这些字符集合” +```python +s = " hello \n" +print(s.strip()) # 'hello' + +x = "==abc==" +print(x.strip("=")) # 'abc' +``` +- 易错点(字符集合坑): +```python +s = "abcba" +print(s.strip("ab")) # 'c'(去掉两端所有 a/b 字符,不是去掉 "ab" 子串) +``` +##### 3.5.3.7 判断开头结尾 startswith/endswith +- 支持元组:一次判断多个前缀/后缀 +```python +filename = "report.csv" +print(filename.endswith((".csv", ".txt"))) # True +``` + +##### 3.5.3.8 大小写转换:lower/upper/title/casefold +- lower 全部转小写 +- upper 全部转大写 +- title 每个“词”的首字母大写,对缩写、撇号、连字符、特殊词形可能不符合直觉。 +- casefold 更激进、更适合比较的大小写折叠,要做不区分大小的相等比较,优先: +```python +def ieq(x: str, y: str) -> bool: + return x.casefold() == y.casefold() +``` +- swapcase() 大小写反转 +##### 3.5.3.9 分割:split +- s.split(sep=None,maxsplit=1) +- 返回列表,列表元素是分割出来的子串 +- sep是分割标识,默认按照任意空白分割,连续空白会被当成一个分隔,同时会自动去掉两端空白的影响 +```python +s = " a b\tc \n" +print(s.split()) # ['a', 'b', 'c'] +``` +- 常规用法,指定特定的分隔符: + - 分隔符会被精确匹配 + - 连续分隔符会产生空字符串 + - 开头/结尾是分隔符也会产生空字符串 +```python +csv = "tom,18,beijing" +print(csv.split(",")) # ['tom', '18', 'beijing'] + +s = "a,,b," +print(s.split(",")) # ['a', '', 'b', ''] +``` +- maxsplit表示最多分割多少次,默认全部分割,maxsplit=1:最多切一刀(得到最多 2 段),常用于“只想分成两段”的场景 +- rsplit():从右侧开始分割,常用于只切最后一段(文件扩展名、最后一个路径分隔符等) +- 高级用法:s.partition(sep):返回三元组 (head, sep, tail),只分割第一次出现的 sep。rpartition同理,从右往左找。 +```python +csv = "tom,18,beijing" +s = "key=value=more" +print(s.partition("=")) # ('key', '=', 'value=more') +print(s.rpartition("=")) # ('key=value', '=', 'more') +``` + - 找不到分隔符时的行为(很好用) +partition 找不到:返回 (s, '', '') + - 适用场景:只想切成“两段”(key/value、前缀/后缀、文件名/扩展名等),并且希望:总是得到固定 3 段结构(不用判断 list 长度)。“分隔符是否存在”也能从中间那段 sep 是否为空直接判断。 + +##### 3.5.3.10 判断字符串形态:isdecimal / isdigit / isnumeric / isalpha / isalnum / isspace +- 数字判断(从严格到宽松) + - isdecimal():十进制数字(最严格;常用于“输入必须是十进制整数”) + - isdigit():数字字符(包含一些“上标数字”等) + - isnumeric():最宽(还包含一些“罗马数字、中文数字”等数值字符),适用于“各种数字符号都算数字”的情况 + - 补充:判断输入的情况,更多直接用try:int()/float(),因为int()/float()能处理很多isxxx() 覆盖不到的情况,例如: + - 前后空白:" 12 " + - 正负号:"-3", "+7" + - 小数/科学计数法(float):"3.14", "1e-3" +```python +def to_int(s: str): + try: + return int(s) # 自动允许前后空白、+/- 号 + except ValueError: + return None + +def to_float(s: str): + try: + return float(s) # 支持 3.14 / 1e-3 / +inf / nan 等 + except ValueError: + return None + +print(to_int(" -12 ")) # -12 +print(to_int("3.14")) # None(int 不能直接解析小数) +print(to_float("3.14")) # 3.14 +print(to_float("1e-3")) # 0.001 +``` + +##### 3.5.3.11 连接:join/+ +1)`join`把多个字符串高效拼起来(推荐): +- 语法:`sep.join(iterable_of_str)` +- 含义:用 `sep` 作为分隔符,把一组字符串连接成一个新字符串 +- 适用:循环拼接、大量拼接、构造 CSV/日志/多段文本 +```python +parts = ["a", "b", "c"] +s = ",".join(parts) # "a,b,c" +s2 = "".join(parts) # "abc" +``` +- 易错点:待拼接元素必须都是 `str`** +```python +nums = [1, 2, 3] +",".join(map(str, nums)) # "1,2,3" +# ",".join(nums) # TypeError +``` + +2)`+`适合少量拼接 +- `a + b` 会生成新字符串;少量拼接可读性高 +- 但在循环里反复 `+=` 容易产生大量临时字符串(性能差) +```python +# 少量:OK +s = "hello" + "," + "world" + +# 大量:用 join 更好 +parts = [] +for i in range(10000): + parts.append(str(i)) +s = "".join(parts) +``` + + +##### 3.5.3.11 映射:maketrans / translate +1)用途:批量“字符级替换/删除” +- `replace()`:更像“子串替换”(old/new 是字符串片段) +- `translate()`:更像“按字符表逐个替换”,对单字符映射特别高效 +2)用法: +- `str.maketrans(...)`:生成翻译表(table) + - 操作方式1:两个等长字符串:一一映射 +```python +table = str.maketrans("abc", "123") +print("a-b-c".translate(table)) # "1-2-3" +``` + - 操作方式2:用 dict 指定映射(更灵活) +```python +table = {ord("你"): "您", ord("吗"): ""} # 删除可用映射到空字符串 +# 为什么用ord +# 因为str.translate(table) 最终需要的是“码点(int) → 替换内容”的表(这是它的规范)。 +# 你可以用两种方式得到这个表: +# ·自己写 ord(...)(手工把字符转码点) +# ·用 str.maketrans(...)(它会自动把字符键转成 ord 后的整数键) +print("你好吗".translate(table)) # "您好" +``` +- `translate(table)`:按表替换 +- 字符逐个处理:看到某字符就查表替换,映射值可以是: + - 字符串(替换成该字符串) + - `None`(删除该字符) + - 整数(映射到另一个字符的码点) +##### 3.5.3.12 字符串常量 +要先`import string` +- `string.ascii_letters`:包含所有英文字母(大小写)。 +`abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ` +- `string.digits`:包含所有数字字符(0-9)。 +`0123456789'` +- `string.punctuation`:包含所有标点符号。 +`!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~` + +用途:输入校验/过滤 +```python +import string + +s = "a1_b!" +allowed = set(string.ascii_letters + string.digits + "_") +ok = all(ch in allowed for ch in s) # 是否只包含字母数字下划线 +# all这个语法啥意思? +# all(iterable) 是内置函数:当且仅当 iterable 里所有元素都为真值(truthy)时返回 True;只要有一个为 False 就返回 False(而且会短路停止继续检查)。 +``` + +##### 3.5.3.13 其他方法 len / max / min +1)`len(s)`:字符串长度(字符个数,不是字节数) +```python +len("中") # 1 +len("中文") # 2 + +# 字节长度要看编码后的 bytes +len("中".encode("utf-8")) # 3 +``` +2)`max(s)` / `min(s)`:按字符的“码点顺序”比较 +- 会在字符串中找“最大的字符”和“最小的字符” +- 比较规则是 Unicode 码点顺序,并非“字典序/拼音序/大小写无关” +- 适用:简单字符范围判断;不适合做自然语言排序 +```python +max("abc") # 'c' +min("abc") # 'a' + +min("aA") # 'A'(通常大写字母码点更小) +max("aA") # 'a' +``` +**想忽略大小写比较:配合 `key=str.lower`(或 `str.casefold` 更强)** +```python +max("aA", key=str.lower) # 'a' 或 'A'(取决于实现与稳定性,但比较基于 lower 后的值) +``` +- ##### 3.5.3.14 str.lower、str.lower()、x.lower、x.lower()有啥区别? +实质上是两个问题: + - “属性/方法本身” vs “调用方法得到结果” +- str(类型) vs x (某个字符串对象) + +- str.lower +str是字符串类型(class),lower是这个类型上的一个方法对象(函数/描述符)。str.lower表示拿到这个方法本身。 +`print(str.lower) # ` +- str.lower() +一般会报错,原因在于lower是实例方法,调用时需要一个具体的字符串(例如“ABC”)。 +需要手工传入一个字符串当做self才行,例如: +`str.lower("ABC") # 等价于 "ABC".lower() ` +- x.lower +x是一个具体的字符串对象,例如x = "Hello" +x.lower表示:从这个对象上取出绑定方法(bound method),仅是取出,不会执行 +`print("Hello".lower) # ` + +x.lower和str.lower的区别: +- x.lower已经绑定了self=x,也就是准备好了要对谁做lower +- str.lower还没绑定具体对象(没self) + +- x.lower() +这才是真正执行了转小写操作:返回一个新字符串,把x转为小写 +x本身不变(字符串不可变) + +注意:str是类型名,代表字符串这种类型,就像int代表整数类型。 + + + +### 3.6 字节序列(bytes / bytearray)(建议加一节,很多人漏) +- `str` vs `bytes` 的区别 +- 编码/解码:`.encode()` / `.decode()` +- 使用场景:文件、网络、二进制数据 + +### 3.7 序列类型(Sequence):list / tuple / range +字符串和元组都属于序列(Sequence)类型,如下图所示: +Python有6个序列的内置类型,最常见的是列表和元组。 +```python +object + └── collections.abc.Iterable + └── collections.abc.Sequence + ├── list(可变序列) + ├── tuple(不可变序列) + └── str(不可变序列,但元素必须是字符) +#### 3.7.1 list(列表) +##### 3.7.1.1 列表含义 +- 有序(保持插入顺序) +- 可变(mutable):可原地增删改 +- 可存放任意类型对象(可混合) +- 支持重复元素 + +```python +a = [1, "x", 3.14, [1, 2]] +``` +##### 3.7.1.2 创建方式 +- 字面量:`[]` +- `list(iterable)`:把可迭代对象转成列表 +- 列表推导式(常用) +```python +a = [] +b = list("abc") # ['a', 'b', 'c'] +c = [i*i for i in range(5)] # [0, 1, 4, 9, 16] +``` + +##### 3.7.1.3 访问:索引与切片 +- `a[i]` / `a[-1]`:按索引取值 +- `a[start:stop:step]`:切片返回新列表,同字符串 +```python +a = [0, 1, 2, 3, 4] +a[0] # 0 +a[-1] # 4 +a[1:4] # [1, 2, 3] +a[::-1] # [4, 3, 2, 1, 0] +``` +##### 3.7.1.4 列表增 +- `append(x)`:末尾追加一个元素 +- `extend(iterable)`:把 iterable 里的元素逐个加入(相当于拼接) +- `insert(i, x)`:在索引 i 处插入(整体后移) +```python +a = [1, 2] +a.append(3) # [1, 2, 3] +a.extend([4, 5]) # [1, 2, 3, 4, 5] +a.insert(1, 99) # [1, 99, 2, 3, 4, 5] +``` +- 常见坑(append vs extend): +```python +a = [1, 2] +a.append([3, 4]) # [1, 2, [3, 4]] +a.extend([3, 4]) # [1, 2, 3, 4] +``` +##### 3.7.1.5 列表删 +- `pop(i=-1)`:删除并返回索引 i 的元素(默认末尾) +- `remove(x)`:删除第一个等于 x 的元素(找不到抛 ValueError) +- `clear()`:清空列表 +- `del a[i]` / `del a[i:j]`:删除指定位置/切片 +```python +a = [10, 20, 30, 20] +a.pop() # 20,a -> [10, 20, 30] +a.remove(20) # a -> [10, 30](只删第一个 20) +a.clear() # a -> [] +``` +##### 3.7.1.6 列表改:索引赋值 / 切片赋值 +- `a[i] = v`:改单个元素 +- `a[i:j] = iterable`:切片替换/插入/删除(很灵活) +```python +a = [0, 1, 2, 3, 4] +a[0] = 99 # [99, 1, 2, 3, 4] +a[1:3] = [7, 8, 9] # [99, 7, 8, 9, 3, 4](长度可变) +a[2:4] = [] # [99, 7, 3, 4](相当于删除) +``` +##### 3.7.1.7 列表查:in / index / count +- `x in a`:是否包含 +- `a.index(x[, start[, end]])`:返回首个匹配索引(找不到抛 ValueError) +- `a.count(x)`:计数 +```python +a = [1, 2, 2, 3] +2 in a # True +a.index(2) # 1 +a.count(2) # 2 +``` +##### 3.7.1.8 遍历与常见模式 +- 直接遍历元素:`for x in a` +- 遍历索引:`for i in range(len(a))`(不推荐优先) +- 同时拿到索引和值:`enumerate(a)` +```python +for i, x in enumerate(["a", "b"]): + print(i, x) # i是索引,x是列表元素 +``` +##### 3.7.1.9 排序:`sort()` vs `sorted()`(重点) +- `list.sort()`:**原地排序**,返回 `None` +- `sorted(iterable)`:返回**新列表**,原对象不变(可用于任何可迭代对象) + +```python +a = [3, 1, 2] +a.sort() +print(a) # [1, 2, 3] + +b = [3, 1, 2] +c = sorted(b) +print(b) # [3, 1, 2] +print(c) # [1, 2, 3] +``` +- 常用参数: + - `key=`:指定排序关键字(非常常用)。key是一个函数,该函数会作用于可迭代对象的每个元素,返回一个值,再以这个值对可迭代对象中的元素进行排序。 + - 按字符串长度排序:key=len + - 按绝对值排序:key=abs + - 忽略大小写排序:key=str.lower + - 按元组的某个元素排序:key=lambda x: x[1] + - `reverse=`:默认为`False`,即为默认升序(从小到大)。`reverse=True`的时候是降序(从大到小)。字母以及特殊字符的降序规则基于字符的 ASCII/Unicode 值。例如升序情况下,a(ASCII值为97)大于A(ASCII值为65),A排在a之前 +```python +words = ["Bob", "alice", "Tom"] +sorted(words, key=str.lower) # 忽略大小写排序 + +items = [{"age": 18}, {"age": 3}] +sorted(items, key=lambda d: d["age"]) +``` +```python +# 按“元组的第 2 个元素”(下标 1)排序 +data = [("Tom", 18), ("Alice", 16), ("Bob", 20), ("Eve", 18)] + +# 方式1:返回新列表(推荐) +sorted_data = sorted(data, key=lambda x: x[1]) +print(sorted_data) +# [('Alice', 16), ('Tom', 18), ('Eve', 18), ('Bob', 20)] +``` + + +- 稳定性(了解):Python 排序是**稳定排序** +即 key 相同的元素排序后相对顺序不变(利于多关键字排序) +```python +data = [("Tom", 18), ("Eve", 18), ("Bob", 20), ("Alice", 18)] + +# 按年龄排序(key 相同的 18 有三个),这里的key是排序依据 +sorted_data = sorted(data, key=lambda x: x[1]) +print(sorted_data) +# [('Tom', 18), ('Eve', 18), ('Alice', 18), ('Bob', 20)] +``` +此时,年龄都是18的三个人,在原列表内是Tom -> Eve -> Alice,排序后仍然是Tom -> Eve -> Alice,这就叫稳定。基于此,可以先排次要关键字,再排序主要关键字。(符合常识) + +##### 3.7.1.10 复制:引用/浅拷贝(高频坑) +1)`b = a` 不是复制,是“同一个对象的两个名字”(引用) +- `a`、`b` 指向同一份列表对象 +- 修改其中一个,会影响另一个 +```python +a = [1, 2, 3] +b = a +b.append(99) +print(a) # [1, 2, 3, 99] +print(b) # [1, 2, 3, 99] +print(a is b) # True +``` + +2)浅拷贝(shallow copy)是什么? +浅拷贝会创建一个**新的外层列表对象**,但元素仍然是原来那些元素对象的引用。 +常见浅拷贝方式: +- `a.copy()` +- `a[:]` +- `list(a)` + +```python +a = [1, 2, 3] +b = a.copy() # a和b指向不同的对象,但是如果有子对象,子对象还是同一个(引用) + +print(a is b) # False(外层对象不同) +``` + +对“元素是不可变类型”的列表来说,浅拷贝通常足够: +```python +a = [1, 2, 3] +b = a[:] +b[0] = 100 +print(a) # [1, 2, 3] +print(b) # [100, 2, 3] +``` +3)高频坑:嵌套结构(内层仍共享引用) +如果列表里装的是可变对象(如子列表、字典),浅拷贝只复制外层,**内层对象仍然共享**。 +```python +a = [[1], [2], [3]] +b = a.copy() + +print(a is b) # False(外层不同) +print(a[0] is b[0]) # True(内层同一个对象) + +b[0].append(99) +print(a) # [[1, 99], [2], [3]] (a 也变了) +print(b) # [[1, 99], [2], [3]] +``` + +原因:`b[0]` 和 `a[0]` 指向同一个子列表对象。 + +4)深拷贝(deep copy):嵌套结构要真正“全复制” +当你需要把嵌套结构全部复制(包含内层对象也复制),用 `copy.deepcopy()`: + +```python +import copy + +a = [[1], [2], [3]] +b = copy.deepcopy(a) + +b[0].append(99) +print(a) # [[1], [2], [3]] +print(b) # [[1, 99], [2], [3]] +print(a[0] is b[0]) # False +``` +5)经验总结(复习版) +- `b = a`:同一对象两个引用(改一个,另一个也变) +- 浅拷贝:复制外层容器(外层不同;内层元素引用可能共享) +- 深拷贝:递归复制整棵结构(内外都独立,但更耗时/更耗内存) + +##### 3.7.1.11 常用内置函数配合 +1)`len(a)`返回序列中元素个数 +2)`min(a)` / `max(a)`:最小/最大元素 +- 要求元素之间**可以比较**(同类或有可比较规则),空列表会报错 +- 对字符串是按字符的 Unicode 码点顺序比较(不是拼音/字典序意义 +- **key 参数(非常常用)** + - `min(iterable, key=...)` / `max(iterable, key=...)` + - 含义:按 key 函数计算的结果来比较,但返回原元素 +```python +words = ["Bob", "alice", "Tom"] +min(words, key=str.lower) # 'alice'(忽略大小写比较) + +people = [("Tom", 18), ("Alice", 16), ("Bob", 20)] +max(people, key=lambda x: x[1]) # ('Bob', 20)(按年龄最大) +``` +3)`sum(a[, start])`:求和 +- 常用于数值列表求和 +```python +sum([1, 2, 3]) # 6 +sum([1, 2, 3], 10) # 16(从 start=10 开始加) +``` + + +- tuple + - 不可变性与使用场景(作为 dict key、返回多值) +- range + - 惰性序列、常用写法 `range(n)`、`range(start, stop, step)` + + + + + + +### 3.8 映射类型(Mapping):dict +- 创建与访问:`[]` vs `get()` +- 遍历:keys/values/items +- 常用操作:`update()`、`setdefault()`、字典推导式 +- 哈希与 key 的要求(可哈希对象) +- 3.7+ 插入顺序保持(了解) + +### 3.9 集合类型(Set):set / frozenset +- 去重、集合运算:并/交/差/对称差 +- 可变 set vs 不可变 frozenset +- 常见方法:`add/update/remove/discard` + +### 3.10 不可变 vs 可变(重点常见坑) +#### 3.10.1 为什么要区分可变和不可变类型? +不是拍脑袋想出来的,是为了让语言在性能、正确性、安全性以及数据结构上实现上都更好用。具体如下: +- 让“共享引用”变得可控(避免莫名其妙被改动) +Python变量本质是“名字→对象”的引用,同一个对象会被多个名字引用。 +不可变类型一旦被创建就不能修改,多个地方引用也没关系。 +可变类型可以原地修改,某一处的修改会影响其他引用者,存在风险。 +- 支撑“哈希”和“字典/集合”的底层要求 +dict和set这种高性能数据结构,依赖hash(哈希值)来快速定位元素,要求作为key的对象在其生命周期内保持不变,所以key只能用不可变类型。 +- 性能优化:不可变容易复用、更容易缓存 +不可变因为不会被修改,所以可以安全服用,解释器更敢做优化。这里面提到的缓存是什么意思? +- 让函数调用语义更清晰(“会不会改到我传进去的东西”) +Python参数传递是“传对象引用”。可变/不可变直接影响API设计: +传入可变类型(list、dict),在函数内部已改,外面也变(副作用); +传入不可变类型(int、str、tuple),函数内部“修改”的方式其实是创建新的对象,外面不变。 +```python +def f(x): + x += 1 +a = 1 +f(a) +print(a) # 还是输出1,因为int不可变,所以内部的a是对应一个新的int对象,外部的a不变 +``` +能让我在读代码时快速判断:哪些参数可能被原地改动。 + +#### 3.10.2 可变默认参数的坑 +问题:函数的默认参数只在函数定义时计算一次,即def语句执行那一刻就会创建一个函数对象,同时把默认参数表达式立刻求值一次,并把得到的那个列表对象保存到函数对象里(作为“默认值”缓存起来)。后面每次调用如果不传 lst,就会复用这一份列表对象。 +如果默认值是可变对象(如 []、{}),后续每次调用都会复用同一个对象,导致“越用越多”。 + +```python +def add_item(x, lst=[]): + lst.append(x) + return lst + +print(add_item(1)) # [1] +print(add_item(2)) # [1, 2] <-- 你可能以为是 [2],但不是 +print(add_item(3)) # [1, 2, 3] + +# 代码从上到下的顺序执行,对于函数,会先执行def add_item(x,lst=[])这一个函数定义语句,并创建一个函数对象add_item,并把默认参数lst=[]当场求值一次,把这个列表对象存到add_item.__defaults__ +# !!! 函数定义块里面的语句,在函数定义时不会执行,只在调用时执行 +# 默认参数的默认值属于函数对象的一部分:在执行 def 语句创建函数对象时,默认值表达式会被求值一次并保存。 +# 调用函数时,如果某个形参未提供实参,则该形参绑定到保存的默认值对象(引用)。 +# 若默认值是可变对象(如 list/dict),多次调用可能共享并累积修改。 +``` +原因:lst 的默认值那个 [] 只创建了一次,所有调用共享它。 + +正确写法:用 None 做默认值(不可变),在函数内部再创建新列表。 +```python +def add_item(x, lst=None): + if lst is None: + lst = [] # 这个lst会在这次调用函数后消失? + lst.append(x) + return lst +``` + +- 不可变:int/float/bool/str/tuple/frozenset/None +- 可变:list/dict/set +- “修改”与“重新绑定”的区别 +- 可变默认参数陷阱(可放到函数章节,但这里需要预告) + +### 3.11 拷贝与引用(重点) +- 赋值只是引用:`a = b` +- 浅拷贝 vs 深拷贝:`copy.copy()` / `copy.deepcopy()` +- 切片拷贝、`dict.copy()` 的浅拷贝性质 +- 循环引用与对象结构的影响(了解) + +### 3.12 比较与身份:`==` vs `is` +- 值相等(`==`)与对象身份(`is`) +- `id()` 的含义(对象标识;不是“内存地址”的承诺) +- 小整数/字符串驻留现象(了解,避免当作语义依赖) + +### 3.13 类型转换与输入输出常见点 +- 显式转换:`int()`、`float()`、`str()`、`list()`、`tuple()`、`set()` +- `input()` 永远返回 str,如何转换与错误处理 +- `repr()` vs `str()`(与调试输出关联) + +### 3.14 (可选)枚举与常量 +- `Enum` 的用途与基本用法(你笔记里已经有) +- 常量命名约定:全大写 + +### 3.15 hash值与hash算法 + +--- + +## 04 控制流 +- 分支结构 + - `if/elif/else` +- 循环结构 + - `for` / `while` + - `break` / `continue` / `pass` + - for-else 语法与使用场景 +- 推导式 + - 列表/字典/集合推导式 +- (可选)match-case(Python 3.10+) + +--- + +## 05 函数与作用域(重点) +- 函数基础 + - 定义、返回值、docstring +- 参数系统 + - 位置参数/关键字参数 + - 默认参数(常见坑:可变默认参数) + - 可变参数:`*args` / `**kwargs` +- 作用域 + - LEGB 规则 + - `global` / `nonlocal` +- 高级特性 + - 闭包、装饰器 + - 迭代器与生成器(`yield`) + - lambda(了解) + +--- + +## 06 模块 / 包与导入机制 +- 模块与包 + - 包结构、`__init__.py` +- 导入方式 + - 绝对导入 / 相对导入 + - `sys.path` 与导入搜索路径 +- 程序入口 + - `if __name__ == "__main__":` + +--- + +## 07 标准库常用模块(按主题) +- 文件与路径 + - `pathlib`、`os` +- 系统与进程 + - `sys`、`subprocess` +- 时间日期 + - `datetime`、`time` +- 数学与随机 + - `math`、`random` +- 数据结构与工具 + - `collections`、`itertools`(可选) +- 序列化 + - `json`、`pickle`(注意安全边界) +- 正则表达式 + - `re` +- 日志 + - `logging` +- 并发(可选进阶) + - `threading`、`multiprocessing`、`asyncio` + +--- + +## 08 错误 / 异常 / 调试 +- 异常基础 + - 异常体系、常见异常 + - `try/except/else/finally` + - `raise`、自定义异常 +- warnings(了解) +- 调试方法 + - 断点调试、pdb(了解) + - 日志定位(logging) + +--- + +## 09 面向对象(OOP) +- 类与对象 + - 属性、方法、构造器 +- 三大特性 + - 封装 / 继承 / 多态 +- 常用装饰器 + - `@property` + - `@classmethod` / `@staticmethod` +- 数据类(推荐) + - `dataclasses.dataclass` +- 设计习惯 + - 组合优于继承(常见场景说明) + +--- + +## 10 类型注解(Typing) +- 基础注解 + - 参数与返回值注解 + - 常用类型:`list[str]`、`dict[str, int]` +- typing 常用工具 + - `Optional` / `Union` + - `TypedDict`(了解) + - `Protocol`(了解) +- 工具链 + - pyright(VS Code 友好)/ mypy(可选) + +--- + +## 11 魔法方法 / Python 数据模型 +- 表示与调试 + - `__repr__` / `__str__` +- 比较与排序 + - `__lt__` / `__eq__` 等 +- 容器与迭代协议 + - `__len__` / `__iter__` / `__getitem__` +- 上下文管理 + - `with`、`__enter__` / `__exit__` + +--- + +## 12 工程化与质量保障(建议补齐) +- Git 基础工作流 + - 分支、提交、pull/push、PR +- 测试 + - `unittest` / pytest(推荐) +- 代码质量 + - ruff / black / isort(按需) +- 文档与规范 + - README、注释、docstring +- 打包与发布(了解) + - pyproject.toml、依赖与版本号语义 + +--- + +## 附录:速查 / 踩坑 / 片段库 +- 常用代码片段(文件读写、路径处理、JSON 解析等) +- 常见坑清单 + - 可变默认参数、浅拷贝/深拷贝、编码、浮点误差、作用域(global/nonlocal) +- 小练习与小项目索引(按章节对应) \ No newline at end of file